db30da8ffb48afe6ce5510ec7e13e36e7da3b8a4
[portal/sdk.git] /
1 /**
2  * @license AngularJS v1.6.3
3  * (c) 2010-2017 Google, Inc. http://angularjs.org
4  * License: MIT
5  */
6 (function(window) {'use strict';
7
8 /**
9  * @description
10  *
11  * This object provides a utility for producing rich Error messages within
12  * Angular. It can be called as follows:
13  *
14  * var exampleMinErr = minErr('example');
15  * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
16  *
17  * The above creates an instance of minErr in the example namespace. The
18  * resulting error will have a namespaced error code of example.one.  The
19  * resulting error will replace {0} with the value of foo, and {1} with the
20  * value of bar. The object is not restricted in the number of arguments it can
21  * take.
22  *
23  * If fewer arguments are specified than necessary for interpolation, the extra
24  * interpolation markers will be preserved in the final string.
25  *
26  * Since data will be parsed statically during a build step, some restrictions
27  * are applied with respect to how minErr instances are created and called.
28  * Instances should have names of the form namespaceMinErr for a minErr created
29  * using minErr('namespace') . Error codes, namespaces and template strings
30  * should all be static strings, not variables or general expressions.
31  *
32  * @param {string} module The namespace to use for the new minErr instance.
33  * @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  */
37
38 function minErr(module, ErrorConstructor) {
39   ErrorConstructor = ErrorConstructor || Error;
40   return function() {
41     var code = arguments[0],
42       template = arguments[1],
43       message = '[' + (module ? module + ':' : '') + code + '] ',
44       templateArgs = sliceArgs(arguments, 2).map(function(arg) {
45         return toDebugString(arg, minErrConfig.objectMaxDepth);
46       }),
47       paramPrefix, i;
48
49     message += template.replace(/\{\d+\}/g, function(match) {
50       var index = +match.slice(1, -1);
51
52       if (index < templateArgs.length) {
53         return templateArgs[index];
54       }
55
56       return match;
57     });
58
59     message += '\nhttp://errors.angularjs.org/1.6.3/' +
60       (module ? module + '/' : '') + code;
61
62     for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
63       message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
64     }
65
66     return new ErrorConstructor(message);
67   };
68 }
69
70 /* We need to tell ESLint what variables are being exported */
71 /* exported
72   angular,
73   msie,
74   jqLite,
75   jQuery,
76   slice,
77   splice,
78   push,
79   toString,
80   minErrConfig,
81   errorHandlingConfig,
82   isValidObjectMaxDepth,
83   ngMinErr,
84   angularModule,
85   uid,
86   REGEX_STRING_REGEXP,
87   VALIDITY_STATE_PROPERTY,
88
89   lowercase,
90   uppercase,
91   manualLowercase,
92   manualUppercase,
93   nodeName_,
94   isArrayLike,
95   forEach,
96   forEachSorted,
97   reverseParams,
98   nextUid,
99   setHashKey,
100   extend,
101   toInt,
102   inherit,
103   merge,
104   noop,
105   identity,
106   valueFn,
107   isUndefined,
108   isDefined,
109   isObject,
110   isBlankObject,
111   isString,
112   isNumber,
113   isNumberNaN,
114   isDate,
115   isArray,
116   isFunction,
117   isRegExp,
118   isWindow,
119   isScope,
120   isFile,
121   isFormData,
122   isBlob,
123   isBoolean,
124   isPromiseLike,
125   trim,
126   escapeForRegexp,
127   isElement,
128   makeMap,
129   includes,
130   arrayRemove,
131   copy,
132   equals,
133   csp,
134   jq,
135   concat,
136   sliceArgs,
137   bind,
138   toJsonReplacer,
139   toJson,
140   fromJson,
141   convertTimezoneToLocal,
142   timezoneToOffset,
143   startingTag,
144   tryDecodeURIComponent,
145   parseKeyValue,
146   toKeyValue,
147   encodeUriSegment,
148   encodeUriQuery,
149   angularInit,
150   bootstrap,
151   getTestability,
152   snake_case,
153   bindJQuery,
154   assertArg,
155   assertArgFn,
156   assertNotHasOwnProperty,
157   getter,
158   getBlockNodes,
159   hasOwnProperty,
160   createMap,
161   stringify,
162
163   NODE_TYPE_ELEMENT,
164   NODE_TYPE_ATTRIBUTE,
165   NODE_TYPE_TEXT,
166   NODE_TYPE_COMMENT,
167   NODE_TYPE_DOCUMENT,
168   NODE_TYPE_DOCUMENT_FRAGMENT
169 */
170
171 ////////////////////////////////////
172
173 /**
174  * @ngdoc module
175  * @name ng
176  * @module ng
177  * @installation
178  * @description
179  *
180  * # ng (core module)
181  * The ng module is loaded by default when an AngularJS application is started. The module itself
182  * contains the essential components for an AngularJS application to function. The table below
183  * lists a high level breakdown of each of the services/factories, filters, directives and testing
184  * components available within this core module.
185  *
186  * <div doc-module-components="ng"></div>
187  */
188
189 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
190
191 // The name of a form control's ValidityState property.
192 // This is used so that it's possible for internal tests to create mock ValidityStates.
193 var VALIDITY_STATE_PROPERTY = 'validity';
194
195
196 var hasOwnProperty = Object.prototype.hasOwnProperty;
197
198 var minErrConfig = {
199   objectMaxDepth: 5
200 };
201
202  /**
203  * @ngdoc function
204  * @name angular.errorHandlingConfig
205  * @module ng
206  * @kind function
207  *
208  * @description
209  * Configure several aspects of error handling in AngularJS if used as a setter or return the
210  * current configuration if used as a getter. The following options are supported:
211  *
212  * - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages.
213  *
214  * Omitted or undefined options will leave the corresponding configuration values unchanged.
215  *
216  * @param {Object=} config - The configuration object. May only contain the options that need to be
217  *     updated. Supported keys:
218  *
219  * * `objectMaxDepth`  **{Number}** - The max depth for stringifying objects. Setting to a
220  *   non-positive or non-numeric value, removes the max depth limit.
221  *   Default: 5
222  */
223 function errorHandlingConfig(config) {
224   if (isObject(config)) {
225     if (isDefined(config.objectMaxDepth)) {
226       minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
227     }
228   } else {
229     return minErrConfig;
230   }
231 }
232
233 /**
234  * @private
235  * @param {Number} maxDepth
236  * @return {boolean}
237  */
238 function isValidObjectMaxDepth(maxDepth) {
239   return isNumber(maxDepth) && maxDepth > 0;
240 }
241
242 /**
243  * @ngdoc function
244  * @name angular.lowercase
245  * @module ng
246  * @kind function
247  *
248  * @deprecated
249  * sinceVersion="1.5.0"
250  * removeVersion="1.7.0"
251  * Use [String.prototype.toLowerCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase) instead.
252  *
253  * @description Converts the specified string to lowercase.
254  * @param {string} string String to be converted to lowercase.
255  * @returns {string} Lowercased string.
256  */
257 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
258
259 /**
260  * @ngdoc function
261  * @name angular.uppercase
262  * @module ng
263  * @kind function
264  *
265  * @deprecated
266  * sinceVersion="1.5.0"
267  * removeVersion="1.7.0"
268  * Use [String.prototype.toUpperCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) instead.
269  *
270  * @description Converts the specified string to uppercase.
271  * @param {string} string String to be converted to uppercase.
272  * @returns {string} Uppercased string.
273  */
274 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
275
276
277 var manualLowercase = function(s) {
278   /* eslint-disable no-bitwise */
279   return isString(s)
280       ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
281       : s;
282   /* eslint-enable */
283 };
284 var manualUppercase = function(s) {
285   /* eslint-disable no-bitwise */
286   return isString(s)
287       ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
288       : s;
289   /* eslint-enable */
290 };
291
292
293 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
294 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
295 // with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
296 if ('i' !== 'I'.toLowerCase()) {
297   lowercase = manualLowercase;
298   uppercase = manualUppercase;
299 }
300
301
302 var
303     msie,             // holds major version number for IE, or NaN if UA is not IE.
304     jqLite,           // delay binding since jQuery could be loaded after us.
305     jQuery,           // delay binding
306     slice             = [].slice,
307     splice            = [].splice,
308     push              = [].push,
309     toString          = Object.prototype.toString,
310     getPrototypeOf    = Object.getPrototypeOf,
311     ngMinErr          = minErr('ng'),
312
313     /** @name angular */
314     angular           = window.angular || (window.angular = {}),
315     angularModule,
316     uid               = 0;
317
318 // Support: IE 9-11 only
319 /**
320  * documentMode is an IE-only property
321  * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
322  */
323 msie = window.document.documentMode;
324
325
326 /**
327  * @private
328  * @param {*} obj
329  * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
330  *                   String ...)
331  */
332 function isArrayLike(obj) {
333
334   // `null`, `undefined` and `window` are not array-like
335   if (obj == null || isWindow(obj)) return false;
336
337   // arrays, strings and jQuery/jqLite objects are array like
338   // * jqLite is either the jQuery or jqLite constructor function
339   // * we have to check the existence of jqLite first as this method is called
340   //   via the forEach method when constructing the jqLite object in the first place
341   if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
342
343   // Support: iOS 8.2 (not reproducible in simulator)
344   // "length" in obj used to prevent JIT error (gh-11508)
345   var length = 'length' in Object(obj) && obj.length;
346
347   // NodeList objects (with `item` method) and
348   // other objects with suitable length characteristics are array-like
349   return isNumber(length) &&
350     (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item === 'function');
351
352 }
353
354 /**
355  * @ngdoc function
356  * @name angular.forEach
357  * @module ng
358  * @kind function
359  *
360  * @description
361  * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
362  * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
363  * is the value of an object property or an array element, `key` is the object property key or
364  * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
365  *
366  * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
367  * using the `hasOwnProperty` method.
368  *
369  * Unlike ES262's
370  * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
371  * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
372  * return the value provided.
373  *
374    ```js
375      var values = {name: 'misko', gender: 'male'};
376      var log = [];
377      angular.forEach(values, function(value, key) {
378        this.push(key + ': ' + value);
379      }, log);
380      expect(log).toEqual(['name: misko', 'gender: male']);
381    ```
382  *
383  * @param {Object|Array} obj Object to iterate over.
384  * @param {Function} iterator Iterator function.
385  * @param {Object=} context Object to become context (`this`) for the iterator function.
386  * @returns {Object|Array} Reference to `obj`.
387  */
388
389 function forEach(obj, iterator, context) {
390   var key, length;
391   if (obj) {
392     if (isFunction(obj)) {
393       for (key in obj) {
394         if (key !== 'prototype' && key !== 'length' && key !== 'name' && obj.hasOwnProperty(key)) {
395           iterator.call(context, obj[key], key, obj);
396         }
397       }
398     } else if (isArray(obj) || isArrayLike(obj)) {
399       var isPrimitive = typeof obj !== 'object';
400       for (key = 0, length = obj.length; key < length; key++) {
401         if (isPrimitive || key in obj) {
402           iterator.call(context, obj[key], key, obj);
403         }
404       }
405     } else if (obj.forEach && obj.forEach !== forEach) {
406         obj.forEach(iterator, context, obj);
407     } else if (isBlankObject(obj)) {
408       // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
409       for (key in obj) {
410         iterator.call(context, obj[key], key, obj);
411       }
412     } else if (typeof obj.hasOwnProperty === 'function') {
413       // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
414       for (key in obj) {
415         if (obj.hasOwnProperty(key)) {
416           iterator.call(context, obj[key], key, obj);
417         }
418       }
419     } else {
420       // Slow path for objects which do not have a method `hasOwnProperty`
421       for (key in obj) {
422         if (hasOwnProperty.call(obj, key)) {
423           iterator.call(context, obj[key], key, obj);
424         }
425       }
426     }
427   }
428   return obj;
429 }
430
431 function forEachSorted(obj, iterator, context) {
432   var keys = Object.keys(obj).sort();
433   for (var i = 0; i < keys.length; i++) {
434     iterator.call(context, obj[keys[i]], keys[i]);
435   }
436   return keys;
437 }
438
439
440 /**
441  * when using forEach the params are value, key, but it is often useful to have key, value.
442  * @param {function(string, *)} iteratorFn
443  * @returns {function(*, string)}
444  */
445 function reverseParams(iteratorFn) {
446   return function(value, key) {iteratorFn(key, value);};
447 }
448
449 /**
450  * A consistent way of creating unique IDs in angular.
451  *
452  * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
453  * we hit number precision issues in JavaScript.
454  *
455  * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
456  *
457  * @returns {number} an unique alpha-numeric string
458  */
459 function nextUid() {
460   return ++uid;
461 }
462
463
464 /**
465  * Set or clear the hashkey for an object.
466  * @param obj object
467  * @param h the hashkey (!truthy to delete the hashkey)
468  */
469 function setHashKey(obj, h) {
470   if (h) {
471     obj.$$hashKey = h;
472   } else {
473     delete obj.$$hashKey;
474   }
475 }
476
477
478 function baseExtend(dst, objs, deep) {
479   var h = dst.$$hashKey;
480
481   for (var i = 0, ii = objs.length; i < ii; ++i) {
482     var obj = objs[i];
483     if (!isObject(obj) && !isFunction(obj)) continue;
484     var keys = Object.keys(obj);
485     for (var j = 0, jj = keys.length; j < jj; j++) {
486       var key = keys[j];
487       var src = obj[key];
488
489       if (deep && isObject(src)) {
490         if (isDate(src)) {
491           dst[key] = new Date(src.valueOf());
492         } else if (isRegExp(src)) {
493           dst[key] = new RegExp(src);
494         } else if (src.nodeName) {
495           dst[key] = src.cloneNode(true);
496         } else if (isElement(src)) {
497           dst[key] = src.clone();
498         } else {
499           if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
500           baseExtend(dst[key], [src], true);
501         }
502       } else {
503         dst[key] = src;
504       }
505     }
506   }
507
508   setHashKey(dst, h);
509   return dst;
510 }
511
512 /**
513  * @ngdoc function
514  * @name angular.extend
515  * @module ng
516  * @kind function
517  *
518  * @description
519  * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
520  * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
521  * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
522  *
523  * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
524  * {@link angular.merge} for this.
525  *
526  * @param {Object} dst Destination object.
527  * @param {...Object} src Source object(s).
528  * @returns {Object} Reference to `dst`.
529  */
530 function extend(dst) {
531   return baseExtend(dst, slice.call(arguments, 1), false);
532 }
533
534
535 /**
536 * @ngdoc function
537 * @name angular.merge
538 * @module ng
539 * @kind function
540 *
541 * @description
542 * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
543 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
544 * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
545 *
546 * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
547 * objects, performing a deep copy.
548 *
549 * @param {Object} dst Destination object.
550 * @param {...Object} src Source object(s).
551 * @returns {Object} Reference to `dst`.
552 */
553 function merge(dst) {
554   return baseExtend(dst, slice.call(arguments, 1), true);
555 }
556
557
558
559 function toInt(str) {
560   return parseInt(str, 10);
561 }
562
563 var isNumberNaN = Number.isNaN || function isNumberNaN(num) {
564   // eslint-disable-next-line no-self-compare
565   return num !== num;
566 };
567
568
569 function inherit(parent, extra) {
570   return extend(Object.create(parent), extra);
571 }
572
573 /**
574  * @ngdoc function
575  * @name angular.noop
576  * @module ng
577  * @kind function
578  *
579  * @description
580  * A function that performs no operations. This function can be useful when writing code in the
581  * functional style.
582    ```js
583      function foo(callback) {
584        var result = calculateResult();
585        (callback || angular.noop)(result);
586      }
587    ```
588  */
589 function noop() {}
590 noop.$inject = [];
591
592
593 /**
594  * @ngdoc function
595  * @name angular.identity
596  * @module ng
597  * @kind function
598  *
599  * @description
600  * A function that returns its first argument. This function is useful when writing code in the
601  * functional style.
602  *
603    ```js
604    function transformer(transformationFn, value) {
605      return (transformationFn || angular.identity)(value);
606    };
607
608    // E.g.
609    function getResult(fn, input) {
610      return (fn || angular.identity)(input);
611    };
612
613    getResult(function(n) { return n * 2; }, 21);   // returns 42
614    getResult(null, 21);                            // returns 21
615    getResult(undefined, 21);                       // returns 21
616    ```
617  *
618  * @param {*} value to be returned.
619  * @returns {*} the value passed in.
620  */
621 function identity($) {return $;}
622 identity.$inject = [];
623
624
625 function valueFn(value) {return function valueRef() {return value;};}
626
627 function hasCustomToString(obj) {
628   return isFunction(obj.toString) && obj.toString !== toString;
629 }
630
631
632 /**
633  * @ngdoc function
634  * @name angular.isUndefined
635  * @module ng
636  * @kind function
637  *
638  * @description
639  * Determines if a reference is undefined.
640  *
641  * @param {*} value Reference to check.
642  * @returns {boolean} True if `value` is undefined.
643  */
644 function isUndefined(value) {return typeof value === 'undefined';}
645
646
647 /**
648  * @ngdoc function
649  * @name angular.isDefined
650  * @module ng
651  * @kind function
652  *
653  * @description
654  * Determines if a reference is defined.
655  *
656  * @param {*} value Reference to check.
657  * @returns {boolean} True if `value` is defined.
658  */
659 function isDefined(value) {return typeof value !== 'undefined';}
660
661
662 /**
663  * @ngdoc function
664  * @name angular.isObject
665  * @module ng
666  * @kind function
667  *
668  * @description
669  * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
670  * considered to be objects. Note that JavaScript arrays are objects.
671  *
672  * @param {*} value Reference to check.
673  * @returns {boolean} True if `value` is an `Object` but not `null`.
674  */
675 function isObject(value) {
676   // http://jsperf.com/isobject4
677   return value !== null && typeof value === 'object';
678 }
679
680
681 /**
682  * Determine if a value is an object with a null prototype
683  *
684  * @returns {boolean} True if `value` is an `Object` with a null prototype
685  */
686 function isBlankObject(value) {
687   return value !== null && typeof value === 'object' && !getPrototypeOf(value);
688 }
689
690
691 /**
692  * @ngdoc function
693  * @name angular.isString
694  * @module ng
695  * @kind function
696  *
697  * @description
698  * Determines if a reference is a `String`.
699  *
700  * @param {*} value Reference to check.
701  * @returns {boolean} True if `value` is a `String`.
702  */
703 function isString(value) {return typeof value === 'string';}
704
705
706 /**
707  * @ngdoc function
708  * @name angular.isNumber
709  * @module ng
710  * @kind function
711  *
712  * @description
713  * Determines if a reference is a `Number`.
714  *
715  * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
716  *
717  * If you wish to exclude these then you can use the native
718  * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
719  * method.
720  *
721  * @param {*} value Reference to check.
722  * @returns {boolean} True if `value` is a `Number`.
723  */
724 function isNumber(value) {return typeof value === 'number';}
725
726
727 /**
728  * @ngdoc function
729  * @name angular.isDate
730  * @module ng
731  * @kind function
732  *
733  * @description
734  * Determines if a value is a date.
735  *
736  * @param {*} value Reference to check.
737  * @returns {boolean} True if `value` is a `Date`.
738  */
739 function isDate(value) {
740   return toString.call(value) === '[object Date]';
741 }
742
743
744 /**
745  * @ngdoc function
746  * @name angular.isArray
747  * @module ng
748  * @kind function
749  *
750  * @description
751  * Determines if a reference is an `Array`. Alias of Array.isArray.
752  *
753  * @param {*} value Reference to check.
754  * @returns {boolean} True if `value` is an `Array`.
755  */
756 var isArray = Array.isArray;
757
758 /**
759  * @ngdoc function
760  * @name angular.isFunction
761  * @module ng
762  * @kind function
763  *
764  * @description
765  * Determines if a reference is a `Function`.
766  *
767  * @param {*} value Reference to check.
768  * @returns {boolean} True if `value` is a `Function`.
769  */
770 function isFunction(value) {return typeof value === 'function';}
771
772
773 /**
774  * Determines if a value is a regular expression object.
775  *
776  * @private
777  * @param {*} value Reference to check.
778  * @returns {boolean} True if `value` is a `RegExp`.
779  */
780 function isRegExp(value) {
781   return toString.call(value) === '[object RegExp]';
782 }
783
784
785 /**
786  * Checks if `obj` is a window object.
787  *
788  * @private
789  * @param {*} obj Object to check
790  * @returns {boolean} True if `obj` is a window obj.
791  */
792 function isWindow(obj) {
793   return obj && obj.window === obj;
794 }
795
796
797 function isScope(obj) {
798   return obj && obj.$evalAsync && obj.$watch;
799 }
800
801
802 function isFile(obj) {
803   return toString.call(obj) === '[object File]';
804 }
805
806
807 function isFormData(obj) {
808   return toString.call(obj) === '[object FormData]';
809 }
810
811
812 function isBlob(obj) {
813   return toString.call(obj) === '[object Blob]';
814 }
815
816
817 function isBoolean(value) {
818   return typeof value === 'boolean';
819 }
820
821
822 function isPromiseLike(obj) {
823   return obj && isFunction(obj.then);
824 }
825
826
827 var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array]$/;
828 function isTypedArray(value) {
829   return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
830 }
831
832 function isArrayBuffer(obj) {
833   return toString.call(obj) === '[object ArrayBuffer]';
834 }
835
836
837 var trim = function(value) {
838   return isString(value) ? value.trim() : value;
839 };
840
841 // Copied from:
842 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
843 // Prereq: s is a string.
844 var escapeForRegexp = function(s) {
845   return s
846     .replace(/([-()[\]{}+?*.$^|,:#<!\\])/g, '\\$1')
847     // eslint-disable-next-line no-control-regex
848     .replace(/\x08/g, '\\x08');
849 };
850
851
852 /**
853  * @ngdoc function
854  * @name angular.isElement
855  * @module ng
856  * @kind function
857  *
858  * @description
859  * Determines if a reference is a DOM element (or wrapped jQuery element).
860  *
861  * @param {*} value Reference to check.
862  * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
863  */
864 function isElement(node) {
865   return !!(node &&
866     (node.nodeName  // We are a direct element.
867     || (node.prop && node.attr && node.find)));  // We have an on and find method part of jQuery API.
868 }
869
870 /**
871  * @param str 'key1,key2,...'
872  * @returns {object} in the form of {key1:true, key2:true, ...}
873  */
874 function makeMap(str) {
875   var obj = {}, items = str.split(','), i;
876   for (i = 0; i < items.length; i++) {
877     obj[items[i]] = true;
878   }
879   return obj;
880 }
881
882
883 function nodeName_(element) {
884   return lowercase(element.nodeName || (element[0] && element[0].nodeName));
885 }
886
887 function includes(array, obj) {
888   return Array.prototype.indexOf.call(array, obj) !== -1;
889 }
890
891 function arrayRemove(array, value) {
892   var index = array.indexOf(value);
893   if (index >= 0) {
894     array.splice(index, 1);
895   }
896   return index;
897 }
898
899 /**
900  * @ngdoc function
901  * @name angular.copy
902  * @module ng
903  * @kind function
904  *
905  * @description
906  * Creates a deep copy of `source`, which should be an object or an array.
907  *
908  * * If no destination is supplied, a copy of the object or array is created.
909  * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
910  *   are deleted and then all elements/properties from the source are copied to it.
911  * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
912  * * If `source` is identical to `destination` an exception will be thrown.
913  *
914  * <br />
915  * <div class="alert alert-warning">
916  *   Only enumerable properties are taken into account. Non-enumerable properties (both on `source`
917  *   and on `destination`) will be ignored.
918  * </div>
919  *
920  * @param {*} source The source that will be used to make a copy.
921  *                   Can be any type, including primitives, `null`, and `undefined`.
922  * @param {(Object|Array)=} destination Destination into which the source is copied. If
923  *     provided, must be of the same type as `source`.
924  * @returns {*} The copy or updated `destination`, if `destination` was specified.
925  *
926  * @example
927   <example module="copyExample" name="angular-copy">
928     <file name="index.html">
929       <div ng-controller="ExampleController">
930         <form novalidate class="simple-form">
931           <label>Name: <input type="text" ng-model="user.name" /></label><br />
932           <label>Age:  <input type="number" ng-model="user.age" /></label><br />
933           Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label>
934                   <label><input type="radio" ng-model="user.gender" value="female" />female</label><br />
935           <button ng-click="reset()">RESET</button>
936           <button ng-click="update(user)">SAVE</button>
937         </form>
938         <pre>form = {{user | json}}</pre>
939         <pre>master = {{master | json}}</pre>
940       </div>
941     </file>
942     <file name="script.js">
943       // Module: copyExample
944       angular.
945         module('copyExample', []).
946         controller('ExampleController', ['$scope', function($scope) {
947           $scope.master = {};
948
949           $scope.reset = function() {
950             // Example with 1 argument
951             $scope.user = angular.copy($scope.master);
952           };
953
954           $scope.update = function(user) {
955             // Example with 2 arguments
956             angular.copy(user, $scope.master);
957           };
958
959           $scope.reset();
960         }]);
961     </file>
962   </example>
963  */
964 function copy(source, destination, maxDepth) {
965   var stackSource = [];
966   var stackDest = [];
967   maxDepth = isValidObjectMaxDepth(maxDepth) ? maxDepth : NaN;
968
969   if (destination) {
970     if (isTypedArray(destination) || isArrayBuffer(destination)) {
971       throw ngMinErr('cpta', 'Can\'t copy! TypedArray destination cannot be mutated.');
972     }
973     if (source === destination) {
974       throw ngMinErr('cpi', 'Can\'t copy! Source and destination are identical.');
975     }
976
977     // Empty the destination object
978     if (isArray(destination)) {
979       destination.length = 0;
980     } else {
981       forEach(destination, function(value, key) {
982         if (key !== '$$hashKey') {
983           delete destination[key];
984         }
985       });
986     }
987
988     stackSource.push(source);
989     stackDest.push(destination);
990     return copyRecurse(source, destination, maxDepth);
991   }
992
993   return copyElement(source, maxDepth);
994
995   function copyRecurse(source, destination, maxDepth) {
996     maxDepth--;
997     if (maxDepth < 0) {
998       return '...';
999     }
1000     var h = destination.$$hashKey;
1001     var key;
1002     if (isArray(source)) {
1003       for (var i = 0, ii = source.length; i < ii; i++) {
1004         destination.push(copyElement(source[i], maxDepth));
1005       }
1006     } else if (isBlankObject(source)) {
1007       // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
1008       for (key in source) {
1009         destination[key] = copyElement(source[key], maxDepth);
1010       }
1011     } else if (source && typeof source.hasOwnProperty === 'function') {
1012       // Slow path, which must rely on hasOwnProperty
1013       for (key in source) {
1014         if (source.hasOwnProperty(key)) {
1015           destination[key] = copyElement(source[key], maxDepth);
1016         }
1017       }
1018     } else {
1019       // Slowest path --- hasOwnProperty can't be called as a method
1020       for (key in source) {
1021         if (hasOwnProperty.call(source, key)) {
1022           destination[key] = copyElement(source[key], maxDepth);
1023         }
1024       }
1025     }
1026     setHashKey(destination, h);
1027     return destination;
1028   }
1029
1030   function copyElement(source, maxDepth) {
1031     // Simple values
1032     if (!isObject(source)) {
1033       return source;
1034     }
1035
1036     // Already copied values
1037     var index = stackSource.indexOf(source);
1038     if (index !== -1) {
1039       return stackDest[index];
1040     }
1041
1042     if (isWindow(source) || isScope(source)) {
1043       throw ngMinErr('cpws',
1044         'Can\'t copy! Making copies of Window or Scope instances is not supported.');
1045     }
1046
1047     var needsRecurse = false;
1048     var destination = copyType(source);
1049
1050     if (destination === undefined) {
1051       destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
1052       needsRecurse = true;
1053     }
1054
1055     stackSource.push(source);
1056     stackDest.push(destination);
1057
1058     return needsRecurse
1059       ? copyRecurse(source, destination, maxDepth)
1060       : destination;
1061   }
1062
1063   function copyType(source) {
1064     switch (toString.call(source)) {
1065       case '[object Int8Array]':
1066       case '[object Int16Array]':
1067       case '[object Int32Array]':
1068       case '[object Float32Array]':
1069       case '[object Float64Array]':
1070       case '[object Uint8Array]':
1071       case '[object Uint8ClampedArray]':
1072       case '[object Uint16Array]':
1073       case '[object Uint32Array]':
1074         return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length);
1075
1076       case '[object ArrayBuffer]':
1077         // Support: IE10
1078         if (!source.slice) {
1079           // If we're in this case we know the environment supports ArrayBuffer
1080           /* eslint-disable no-undef */
1081           var copied = new ArrayBuffer(source.byteLength);
1082           new Uint8Array(copied).set(new Uint8Array(source));
1083           /* eslint-enable */
1084           return copied;
1085         }
1086         return source.slice(0);
1087
1088       case '[object Boolean]':
1089       case '[object Number]':
1090       case '[object String]':
1091       case '[object Date]':
1092         return new source.constructor(source.valueOf());
1093
1094       case '[object RegExp]':
1095         var re = new RegExp(source.source, source.toString().match(/[^/]*$/)[0]);
1096         re.lastIndex = source.lastIndex;
1097         return re;
1098
1099       case '[object Blob]':
1100         return new source.constructor([source], {type: source.type});
1101     }
1102
1103     if (isFunction(source.cloneNode)) {
1104       return source.cloneNode(true);
1105     }
1106   }
1107 }
1108
1109
1110 /**
1111  * @ngdoc function
1112  * @name angular.equals
1113  * @module ng
1114  * @kind function
1115  *
1116  * @description
1117  * Determines if two objects or two values are equivalent. Supports value types, regular
1118  * expressions, arrays and objects.
1119  *
1120  * Two objects or values are considered equivalent if at least one of the following is true:
1121  *
1122  * * Both objects or values pass `===` comparison.
1123  * * Both objects or values are of the same type and all of their properties are equal by
1124  *   comparing them with `angular.equals`.
1125  * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1126  * * Both values represent the same regular expression (In JavaScript,
1127  *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1128  *   representation matches).
1129  *
1130  * During a property comparison, properties of `function` type and properties with names
1131  * that begin with `$` are ignored.
1132  *
1133  * Scope and DOMWindow objects are being compared only by identify (`===`).
1134  *
1135  * @param {*} o1 Object or value to compare.
1136  * @param {*} o2 Object or value to compare.
1137  * @returns {boolean} True if arguments are equal.
1138  *
1139  * @example
1140    <example module="equalsExample" name="equalsExample">
1141      <file name="index.html">
1142       <div ng-controller="ExampleController">
1143         <form novalidate>
1144           <h3>User 1</h3>
1145           Name: <input type="text" ng-model="user1.name">
1146           Age: <input type="number" ng-model="user1.age">
1147
1148           <h3>User 2</h3>
1149           Name: <input type="text" ng-model="user2.name">
1150           Age: <input type="number" ng-model="user2.age">
1151
1152           <div>
1153             <br/>
1154             <input type="button" value="Compare" ng-click="compare()">
1155           </div>
1156           User 1: <pre>{{user1 | json}}</pre>
1157           User 2: <pre>{{user2 | json}}</pre>
1158           Equal: <pre>{{result}}</pre>
1159         </form>
1160       </div>
1161     </file>
1162     <file name="script.js">
1163         angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
1164           $scope.user1 = {};
1165           $scope.user2 = {};
1166           $scope.compare = function() {
1167             $scope.result = angular.equals($scope.user1, $scope.user2);
1168           };
1169         }]);
1170     </file>
1171   </example>
1172  */
1173 function equals(o1, o2) {
1174   if (o1 === o2) return true;
1175   if (o1 === null || o2 === null) return false;
1176   // eslint-disable-next-line no-self-compare
1177   if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1178   var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1179   if (t1 === t2 && t1 === 'object') {
1180     if (isArray(o1)) {
1181       if (!isArray(o2)) return false;
1182       if ((length = o1.length) === o2.length) {
1183         for (key = 0; key < length; key++) {
1184           if (!equals(o1[key], o2[key])) return false;
1185         }
1186         return true;
1187       }
1188     } else if (isDate(o1)) {
1189       if (!isDate(o2)) return false;
1190       return equals(o1.getTime(), o2.getTime());
1191     } else if (isRegExp(o1)) {
1192       if (!isRegExp(o2)) return false;
1193       return o1.toString() === o2.toString();
1194     } else {
1195       if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1196         isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1197       keySet = createMap();
1198       for (key in o1) {
1199         if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1200         if (!equals(o1[key], o2[key])) return false;
1201         keySet[key] = true;
1202       }
1203       for (key in o2) {
1204         if (!(key in keySet) &&
1205             key.charAt(0) !== '$' &&
1206             isDefined(o2[key]) &&
1207             !isFunction(o2[key])) return false;
1208       }
1209       return true;
1210     }
1211   }
1212   return false;
1213 }
1214
1215 var csp = function() {
1216   if (!isDefined(csp.rules)) {
1217
1218
1219     var ngCspElement = (window.document.querySelector('[ng-csp]') ||
1220                     window.document.querySelector('[data-ng-csp]'));
1221
1222     if (ngCspElement) {
1223       var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1224                     ngCspElement.getAttribute('data-ng-csp');
1225       csp.rules = {
1226         noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1227         noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1228       };
1229     } else {
1230       csp.rules = {
1231         noUnsafeEval: noUnsafeEval(),
1232         noInlineStyle: false
1233       };
1234     }
1235   }
1236
1237   return csp.rules;
1238
1239   function noUnsafeEval() {
1240     try {
1241       // eslint-disable-next-line no-new, no-new-func
1242       new Function('');
1243       return false;
1244     } catch (e) {
1245       return true;
1246     }
1247   }
1248 };
1249
1250 /**
1251  * @ngdoc directive
1252  * @module ng
1253  * @name ngJq
1254  *
1255  * @element ANY
1256  * @param {string=} ngJq the name of the library available under `window`
1257  * to be used for angular.element
1258  * @description
1259  * Use this directive to force the angular.element library.  This should be
1260  * used to force either jqLite by leaving ng-jq blank or setting the name of
1261  * the jquery variable under window (eg. jQuery).
1262  *
1263  * Since angular looks for this directive when it is loaded (doesn't wait for the
1264  * DOMContentLoaded event), it must be placed on an element that comes before the script
1265  * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1266  * others ignored.
1267  *
1268  * @example
1269  * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1270  ```html
1271  <!doctype html>
1272  <html ng-app ng-jq>
1273  ...
1274  ...
1275  </html>
1276  ```
1277  * @example
1278  * This example shows how to use a jQuery based library of a different name.
1279  * The library name must be available at the top most 'window'.
1280  ```html
1281  <!doctype html>
1282  <html ng-app ng-jq="jQueryLib">
1283  ...
1284  ...
1285  </html>
1286  ```
1287  */
1288 var jq = function() {
1289   if (isDefined(jq.name_)) return jq.name_;
1290   var el;
1291   var i, ii = ngAttrPrefixes.length, prefix, name;
1292   for (i = 0; i < ii; ++i) {
1293     prefix = ngAttrPrefixes[i];
1294     el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]');
1295     if (el) {
1296       name = el.getAttribute(prefix + 'jq');
1297       break;
1298     }
1299   }
1300
1301   return (jq.name_ = name);
1302 };
1303
1304 function concat(array1, array2, index) {
1305   return array1.concat(slice.call(array2, index));
1306 }
1307
1308 function sliceArgs(args, startIndex) {
1309   return slice.call(args, startIndex || 0);
1310 }
1311
1312
1313 /**
1314  * @ngdoc function
1315  * @name angular.bind
1316  * @module ng
1317  * @kind function
1318  *
1319  * @description
1320  * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1321  * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1322  * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1323  * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1324  *
1325  * @param {Object} self Context which `fn` should be evaluated in.
1326  * @param {function()} fn Function to be bound.
1327  * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1328  * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1329  */
1330 function bind(self, fn) {
1331   var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1332   if (isFunction(fn) && !(fn instanceof RegExp)) {
1333     return curryArgs.length
1334       ? function() {
1335           return arguments.length
1336             ? fn.apply(self, concat(curryArgs, arguments, 0))
1337             : fn.apply(self, curryArgs);
1338         }
1339       : function() {
1340           return arguments.length
1341             ? fn.apply(self, arguments)
1342             : fn.call(self);
1343         };
1344   } else {
1345     // In IE, native methods are not functions so they cannot be bound (note: they don't need to be).
1346     return fn;
1347   }
1348 }
1349
1350
1351 function toJsonReplacer(key, value) {
1352   var val = value;
1353
1354   if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1355     val = undefined;
1356   } else if (isWindow(value)) {
1357     val = '$WINDOW';
1358   } else if (value &&  window.document === value) {
1359     val = '$DOCUMENT';
1360   } else if (isScope(value)) {
1361     val = '$SCOPE';
1362   }
1363
1364   return val;
1365 }
1366
1367
1368 /**
1369  * @ngdoc function
1370  * @name angular.toJson
1371  * @module ng
1372  * @kind function
1373  *
1374  * @description
1375  * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1376  * stripped since angular uses this notation internally.
1377  *
1378  * @param {Object|Array|Date|string|number|boolean} obj Input to be serialized into JSON.
1379  * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1380  *    If set to an integer, the JSON output will contain that many spaces per indentation.
1381  * @returns {string|undefined} JSON-ified string representing `obj`.
1382  * @knownIssue
1383  *
1384  * The Safari browser throws a `RangeError` instead of returning `null` when it tries to stringify a `Date`
1385  * object with an invalid date value. The only reliable way to prevent this is to monkeypatch the
1386  * `Date.prototype.toJSON` method as follows:
1387  *
1388  * ```
1389  * var _DatetoJSON = Date.prototype.toJSON;
1390  * Date.prototype.toJSON = function() {
1391  *   try {
1392  *     return _DatetoJSON.call(this);
1393  *   } catch(e) {
1394  *     if (e instanceof RangeError) {
1395  *       return null;
1396  *     }
1397  *     throw e;
1398  *   }
1399  * };
1400  * ```
1401  *
1402  * See https://github.com/angular/angular.js/pull/14221 for more information.
1403  */
1404 function toJson(obj, pretty) {
1405   if (isUndefined(obj)) return undefined;
1406   if (!isNumber(pretty)) {
1407     pretty = pretty ? 2 : null;
1408   }
1409   return JSON.stringify(obj, toJsonReplacer, pretty);
1410 }
1411
1412
1413 /**
1414  * @ngdoc function
1415  * @name angular.fromJson
1416  * @module ng
1417  * @kind function
1418  *
1419  * @description
1420  * Deserializes a JSON string.
1421  *
1422  * @param {string} json JSON string to deserialize.
1423  * @returns {Object|Array|string|number} Deserialized JSON string.
1424  */
1425 function fromJson(json) {
1426   return isString(json)
1427       ? JSON.parse(json)
1428       : json;
1429 }
1430
1431
1432 var ALL_COLONS = /:/g;
1433 function timezoneToOffset(timezone, fallback) {
1434   // Support: IE 9-11 only, Edge 13-14+
1435   // IE/Edge do not "understand" colon (`:`) in timezone
1436   timezone = timezone.replace(ALL_COLONS, '');
1437   var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1438   return isNumberNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1439 }
1440
1441
1442 function addDateMinutes(date, minutes) {
1443   date = new Date(date.getTime());
1444   date.setMinutes(date.getMinutes() + minutes);
1445   return date;
1446 }
1447
1448
1449 function convertTimezoneToLocal(date, timezone, reverse) {
1450   reverse = reverse ? -1 : 1;
1451   var dateTimezoneOffset = date.getTimezoneOffset();
1452   var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
1453   return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
1454 }
1455
1456
1457 /**
1458  * @returns {string} Returns the string representation of the element.
1459  */
1460 function startingTag(element) {
1461   element = jqLite(element).clone();
1462   try {
1463     // turns out IE does not let you set .html() on elements which
1464     // are not allowed to have children. So we just ignore it.
1465     element.empty();
1466   } catch (e) { /* empty */ }
1467   var elemHtml = jqLite('<div>').append(element).html();
1468   try {
1469     return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1470         elemHtml.
1471           match(/^(<[^>]+>)/)[1].
1472           replace(/^<([\w-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
1473   } catch (e) {
1474     return lowercase(elemHtml);
1475   }
1476
1477 }
1478
1479
1480 /////////////////////////////////////////////////
1481
1482 /**
1483  * Tries to decode the URI component without throwing an exception.
1484  *
1485  * @private
1486  * @param str value potential URI component to check.
1487  * @returns {boolean} True if `value` can be decoded
1488  * with the decodeURIComponent function.
1489  */
1490 function tryDecodeURIComponent(value) {
1491   try {
1492     return decodeURIComponent(value);
1493   } catch (e) {
1494     // Ignore any invalid uri component.
1495   }
1496 }
1497
1498
1499 /**
1500  * Parses an escaped url query string into key-value pairs.
1501  * @returns {Object.<string,boolean|Array>}
1502  */
1503 function parseKeyValue(/**string*/keyValue) {
1504   var obj = {};
1505   forEach((keyValue || '').split('&'), function(keyValue) {
1506     var splitPoint, key, val;
1507     if (keyValue) {
1508       key = keyValue = keyValue.replace(/\+/g,'%20');
1509       splitPoint = keyValue.indexOf('=');
1510       if (splitPoint !== -1) {
1511         key = keyValue.substring(0, splitPoint);
1512         val = keyValue.substring(splitPoint + 1);
1513       }
1514       key = tryDecodeURIComponent(key);
1515       if (isDefined(key)) {
1516         val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1517         if (!hasOwnProperty.call(obj, key)) {
1518           obj[key] = val;
1519         } else if (isArray(obj[key])) {
1520           obj[key].push(val);
1521         } else {
1522           obj[key] = [obj[key],val];
1523         }
1524       }
1525     }
1526   });
1527   return obj;
1528 }
1529
1530 function toKeyValue(obj) {
1531   var parts = [];
1532   forEach(obj, function(value, key) {
1533     if (isArray(value)) {
1534       forEach(value, function(arrayValue) {
1535         parts.push(encodeUriQuery(key, true) +
1536                    (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1537       });
1538     } else {
1539     parts.push(encodeUriQuery(key, true) +
1540                (value === true ? '' : '=' + encodeUriQuery(value, true)));
1541     }
1542   });
1543   return parts.length ? parts.join('&') : '';
1544 }
1545
1546
1547 /**
1548  * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1549  * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1550  * segments:
1551  *    segment       = *pchar
1552  *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1553  *    pct-encoded   = "%" HEXDIG HEXDIG
1554  *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1555  *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1556  *                     / "*" / "+" / "," / ";" / "="
1557  */
1558 function encodeUriSegment(val) {
1559   return encodeUriQuery(val, true).
1560              replace(/%26/gi, '&').
1561              replace(/%3D/gi, '=').
1562              replace(/%2B/gi, '+');
1563 }
1564
1565
1566 /**
1567  * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1568  * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1569  * encoded per http://tools.ietf.org/html/rfc3986:
1570  *    query         = *( pchar / "/" / "?" )
1571  *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1572  *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1573  *    pct-encoded   = "%" HEXDIG HEXDIG
1574  *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1575  *                     / "*" / "+" / "," / ";" / "="
1576  */
1577 function encodeUriQuery(val, pctEncodeSpaces) {
1578   return encodeURIComponent(val).
1579              replace(/%40/gi, '@').
1580              replace(/%3A/gi, ':').
1581              replace(/%24/g, '$').
1582              replace(/%2C/gi, ',').
1583              replace(/%3B/gi, ';').
1584              replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1585 }
1586
1587 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1588
1589 function getNgAttribute(element, ngAttr) {
1590   var attr, i, ii = ngAttrPrefixes.length;
1591   for (i = 0; i < ii; ++i) {
1592     attr = ngAttrPrefixes[i] + ngAttr;
1593     if (isString(attr = element.getAttribute(attr))) {
1594       return attr;
1595     }
1596   }
1597   return null;
1598 }
1599
1600 function allowAutoBootstrap(document) {
1601   var script = document.currentScript;
1602
1603   if (!script) {
1604     // IE does not have `document.currentScript`
1605     return true;
1606   }
1607
1608   // If the `currentScript` property has been clobbered just return false, since this indicates a probable attack
1609   if (!(script instanceof window.HTMLScriptElement || script instanceof window.SVGScriptElement)) {
1610     return false;
1611   }
1612
1613   var attributes = script.attributes;
1614   var srcs = [attributes.getNamedItem('src'), attributes.getNamedItem('href'), attributes.getNamedItem('xlink:href')];
1615
1616   return srcs.every(function(src) {
1617     if (!src) {
1618       return true;
1619     }
1620     if (!src.value) {
1621       return false;
1622     }
1623
1624     var link = document.createElement('a');
1625     link.href = src.value;
1626
1627     if (document.location.origin === link.origin) {
1628       // Same-origin resources are always allowed, even for non-whitelisted schemes.
1629       return true;
1630     }
1631     // Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
1632     // This is to prevent angular.js bundled with browser extensions from being used to bypass the
1633     // content security policy in web pages and other browser extensions.
1634     switch (link.protocol) {
1635       case 'http:':
1636       case 'https:':
1637       case 'ftp:':
1638       case 'blob:':
1639       case 'file:':
1640       case 'data:':
1641         return true;
1642       default:
1643         return false;
1644     }
1645   });
1646 }
1647
1648 // Cached as it has to run during loading so that document.currentScript is available.
1649 var isAutoBootstrapAllowed = allowAutoBootstrap(window.document);
1650
1651 /**
1652  * @ngdoc directive
1653  * @name ngApp
1654  * @module ng
1655  *
1656  * @element ANY
1657  * @param {angular.Module} ngApp an optional application
1658  *   {@link angular.module module} name to load.
1659  * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1660  *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1661  *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1662  *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1663  *   tracking down the root of these bugs.
1664  *
1665  * @description
1666  *
1667  * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1668  * designates the **root element** of the application and is typically placed near the root element
1669  * of the page - e.g. on the `<body>` or `<html>` tags.
1670  *
1671  * There are a few things to keep in mind when using `ngApp`:
1672  * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1673  *   found in the document will be used to define the root element to auto-bootstrap as an
1674  *   application. To run multiple applications in an HTML document you must manually bootstrap them using
1675  *   {@link angular.bootstrap} instead.
1676  * - AngularJS applications cannot be nested within each other.
1677  * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
1678  *   This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
1679  *   {@link ngRoute.ngView `ngView`}.
1680  *   Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1681  *   causing animations to stop working and making the injector inaccessible from outside the app.
1682  *
1683  * You can specify an **AngularJS module** to be used as the root module for the application.  This
1684  * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1685  * should contain the application code needed or have dependencies on other modules that will
1686  * contain the code. See {@link angular.module} for more information.
1687  *
1688  * In the example below if the `ngApp` directive were not placed on the `html` element then the
1689  * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1690  * would not be resolved to `3`.
1691  *
1692  * `ngApp` is the easiest, and most common way to bootstrap an application.
1693  *
1694  <example module="ngAppDemo" name="ng-app">
1695    <file name="index.html">
1696    <div ng-controller="ngAppDemoController">
1697      I can add: {{a}} + {{b}} =  {{ a+b }}
1698    </div>
1699    </file>
1700    <file name="script.js">
1701    angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1702      $scope.a = 1;
1703      $scope.b = 2;
1704    });
1705    </file>
1706  </example>
1707  *
1708  * Using `ngStrictDi`, you would see something like this:
1709  *
1710  <example ng-app-included="true" name="strict-di">
1711    <file name="index.html">
1712    <div ng-app="ngAppStrictDemo" ng-strict-di>
1713        <div ng-controller="GoodController1">
1714            I can add: {{a}} + {{b}} =  {{ a+b }}
1715
1716            <p>This renders because the controller does not fail to
1717               instantiate, by using explicit annotation style (see
1718               script.js for details)
1719            </p>
1720        </div>
1721
1722        <div ng-controller="GoodController2">
1723            Name: <input ng-model="name"><br />
1724            Hello, {{name}}!
1725
1726            <p>This renders because the controller does not fail to
1727               instantiate, by using explicit annotation style
1728               (see script.js for details)
1729            </p>
1730        </div>
1731
1732        <div ng-controller="BadController">
1733            I can add: {{a}} + {{b}} =  {{ a+b }}
1734
1735            <p>The controller could not be instantiated, due to relying
1736               on automatic function annotations (which are disabled in
1737               strict mode). As such, the content of this section is not
1738               interpolated, and there should be an error in your web console.
1739            </p>
1740        </div>
1741    </div>
1742    </file>
1743    <file name="script.js">
1744    angular.module('ngAppStrictDemo', [])
1745      // BadController will fail to instantiate, due to relying on automatic function annotation,
1746      // rather than an explicit annotation
1747      .controller('BadController', function($scope) {
1748        $scope.a = 1;
1749        $scope.b = 2;
1750      })
1751      // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1752      // due to using explicit annotations using the array style and $inject property, respectively.
1753      .controller('GoodController1', ['$scope', function($scope) {
1754        $scope.a = 1;
1755        $scope.b = 2;
1756      }])
1757      .controller('GoodController2', GoodController2);
1758      function GoodController2($scope) {
1759        $scope.name = 'World';
1760      }
1761      GoodController2.$inject = ['$scope'];
1762    </file>
1763    <file name="style.css">
1764    div[ng-controller] {
1765        margin-bottom: 1em;
1766        -webkit-border-radius: 4px;
1767        border-radius: 4px;
1768        border: 1px solid;
1769        padding: .5em;
1770    }
1771    div[ng-controller^=Good] {
1772        border-color: #d6e9c6;
1773        background-color: #dff0d8;
1774        color: #3c763d;
1775    }
1776    div[ng-controller^=Bad] {
1777        border-color: #ebccd1;
1778        background-color: #f2dede;
1779        color: #a94442;
1780        margin-bottom: 0;
1781    }
1782    </file>
1783  </example>
1784  */
1785 function angularInit(element, bootstrap) {
1786   var appElement,
1787       module,
1788       config = {};
1789
1790   // The element `element` has priority over any other element.
1791   forEach(ngAttrPrefixes, function(prefix) {
1792     var name = prefix + 'app';
1793
1794     if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1795       appElement = element;
1796       module = element.getAttribute(name);
1797     }
1798   });
1799   forEach(ngAttrPrefixes, function(prefix) {
1800     var name = prefix + 'app';
1801     var candidate;
1802
1803     if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1804       appElement = candidate;
1805       module = candidate.getAttribute(name);
1806     }
1807   });
1808   if (appElement) {
1809     if (!isAutoBootstrapAllowed) {
1810       window.console.error('Angular: disabling automatic bootstrap. <script> protocol indicates ' +
1811           'an extension, document.location.href does not match.');
1812       return;
1813     }
1814     config.strictDi = getNgAttribute(appElement, 'strict-di') !== null;
1815     bootstrap(appElement, module ? [module] : [], config);
1816   }
1817 }
1818
1819 /**
1820  * @ngdoc function
1821  * @name angular.bootstrap
1822  * @module ng
1823  * @description
1824  * Use this function to manually start up angular application.
1825  *
1826  * For more information, see the {@link guide/bootstrap Bootstrap guide}.
1827  *
1828  * Angular will detect if it has been loaded into the browser more than once and only allow the
1829  * first loaded script to be bootstrapped and will report a warning to the browser console for
1830  * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1831  * multiple instances of Angular try to work on the DOM.
1832  *
1833  * <div class="alert alert-warning">
1834  * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
1835  * They must use {@link ng.directive:ngApp ngApp}.
1836  * </div>
1837  *
1838  * <div class="alert alert-warning">
1839  * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
1840  * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
1841  * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1842  * causing animations to stop working and making the injector inaccessible from outside the app.
1843  * </div>
1844  *
1845  * ```html
1846  * <!doctype html>
1847  * <html>
1848  * <body>
1849  * <div ng-controller="WelcomeController">
1850  *   {{greeting}}
1851  * </div>
1852  *
1853  * <script src="angular.js"></script>
1854  * <script>
1855  *   var app = angular.module('demo', [])
1856  *   .controller('WelcomeController', function($scope) {
1857  *       $scope.greeting = 'Welcome!';
1858  *   });
1859  *   angular.bootstrap(document, ['demo']);
1860  * </script>
1861  * </body>
1862  * </html>
1863  * ```
1864  *
1865  * @param {DOMElement} element DOM element which is the root of angular application.
1866  * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1867  *     Each item in the array should be the name of a predefined module or a (DI annotated)
1868  *     function that will be invoked by the injector as a `config` block.
1869  *     See: {@link angular.module modules}
1870  * @param {Object=} config an object for defining configuration options for the application. The
1871  *     following keys are supported:
1872  *
1873  * * `strictDi` - disable automatic function annotation for the application. This is meant to
1874  *   assist in finding bugs which break minified code. Defaults to `false`.
1875  *
1876  * @returns {auto.$injector} Returns the newly created injector for this app.
1877  */
1878 function bootstrap(element, modules, config) {
1879   if (!isObject(config)) config = {};
1880   var defaultConfig = {
1881     strictDi: false
1882   };
1883   config = extend(defaultConfig, config);
1884   var doBootstrap = function() {
1885     element = jqLite(element);
1886
1887     if (element.injector()) {
1888       var tag = (element[0] === window.document) ? 'document' : startingTag(element);
1889       // Encode angle brackets to prevent input from being sanitized to empty string #8683.
1890       throw ngMinErr(
1891           'btstrpd',
1892           'App already bootstrapped with this element \'{0}\'',
1893           tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1894     }
1895
1896     modules = modules || [];
1897     modules.unshift(['$provide', function($provide) {
1898       $provide.value('$rootElement', element);
1899     }]);
1900
1901     if (config.debugInfoEnabled) {
1902       // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1903       modules.push(['$compileProvider', function($compileProvider) {
1904         $compileProvider.debugInfoEnabled(true);
1905       }]);
1906     }
1907
1908     modules.unshift('ng');
1909     var injector = createInjector(modules, config.strictDi);
1910     injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1911        function bootstrapApply(scope, element, compile, injector) {
1912         scope.$apply(function() {
1913           element.data('$injector', injector);
1914           compile(element)(scope);
1915         });
1916       }]
1917     );
1918     return injector;
1919   };
1920
1921   var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1922   var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1923
1924   if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1925     config.debugInfoEnabled = true;
1926     window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1927   }
1928
1929   if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1930     return doBootstrap();
1931   }
1932
1933   window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1934   angular.resumeBootstrap = function(extraModules) {
1935     forEach(extraModules, function(module) {
1936       modules.push(module);
1937     });
1938     return doBootstrap();
1939   };
1940
1941   if (isFunction(angular.resumeDeferredBootstrap)) {
1942     angular.resumeDeferredBootstrap();
1943   }
1944 }
1945
1946 /**
1947  * @ngdoc function
1948  * @name angular.reloadWithDebugInfo
1949  * @module ng
1950  * @description
1951  * Use this function to reload the current application with debug information turned on.
1952  * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1953  *
1954  * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1955  */
1956 function reloadWithDebugInfo() {
1957   window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1958   window.location.reload();
1959 }
1960
1961 /**
1962  * @name angular.getTestability
1963  * @module ng
1964  * @description
1965  * Get the testability service for the instance of Angular on the given
1966  * element.
1967  * @param {DOMElement} element DOM element which is the root of angular application.
1968  */
1969 function getTestability(rootElement) {
1970   var injector = angular.element(rootElement).injector();
1971   if (!injector) {
1972     throw ngMinErr('test',
1973       'no injector found for element argument to getTestability');
1974   }
1975   return injector.get('$$testability');
1976 }
1977
1978 var SNAKE_CASE_REGEXP = /[A-Z]/g;
1979 function snake_case(name, separator) {
1980   separator = separator || '_';
1981   return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1982     return (pos ? separator : '') + letter.toLowerCase();
1983   });
1984 }
1985
1986 var bindJQueryFired = false;
1987 function bindJQuery() {
1988   var originalCleanData;
1989
1990   if (bindJQueryFired) {
1991     return;
1992   }
1993
1994   // bind to jQuery if present;
1995   var jqName = jq();
1996   jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
1997            !jqName             ? undefined     :   // use jqLite
1998                                  window[jqName];   // use jQuery specified by `ngJq`
1999
2000   // Use jQuery if it exists with proper functionality, otherwise default to us.
2001   // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
2002   // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
2003   // versions. It will not work for sure with jQuery <1.7, though.
2004   if (jQuery && jQuery.fn.on) {
2005     jqLite = jQuery;
2006     extend(jQuery.fn, {
2007       scope: JQLitePrototype.scope,
2008       isolateScope: JQLitePrototype.isolateScope,
2009       controller: /** @type {?} */ (JQLitePrototype).controller,
2010       injector: JQLitePrototype.injector,
2011       inheritedData: JQLitePrototype.inheritedData
2012     });
2013
2014     // All nodes removed from the DOM via various jQuery APIs like .remove()
2015     // are passed through jQuery.cleanData. Monkey-patch this method to fire
2016     // the $destroy event on all removed nodes.
2017     originalCleanData = jQuery.cleanData;
2018     jQuery.cleanData = function(elems) {
2019       var events;
2020       for (var i = 0, elem; (elem = elems[i]) != null; i++) {
2021         events = jQuery._data(elem, 'events');
2022         if (events && events.$destroy) {
2023           jQuery(elem).triggerHandler('$destroy');
2024         }
2025       }
2026       originalCleanData(elems);
2027     };
2028   } else {
2029     jqLite = JQLite;
2030   }
2031
2032   angular.element = jqLite;
2033
2034   // Prevent double-proxying.
2035   bindJQueryFired = true;
2036 }
2037
2038 /**
2039  * throw error if the argument is falsy.
2040  */
2041 function assertArg(arg, name, reason) {
2042   if (!arg) {
2043     throw ngMinErr('areq', 'Argument \'{0}\' is {1}', (name || '?'), (reason || 'required'));
2044   }
2045   return arg;
2046 }
2047
2048 function assertArgFn(arg, name, acceptArrayAnnotation) {
2049   if (acceptArrayAnnotation && isArray(arg)) {
2050       arg = arg[arg.length - 1];
2051   }
2052
2053   assertArg(isFunction(arg), name, 'not a function, got ' +
2054       (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
2055   return arg;
2056 }
2057
2058 /**
2059  * throw error if the name given is hasOwnProperty
2060  * @param  {String} name    the name to test
2061  * @param  {String} context the context in which the name is used, such as module or directive
2062  */
2063 function assertNotHasOwnProperty(name, context) {
2064   if (name === 'hasOwnProperty') {
2065     throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2066   }
2067 }
2068
2069 /**
2070  * Return the value accessible from the object by path. Any undefined traversals are ignored
2071  * @param {Object} obj starting object
2072  * @param {String} path path to traverse
2073  * @param {boolean} [bindFnToScope=true]
2074  * @returns {Object} value as accessible by path
2075  */
2076 //TODO(misko): this function needs to be removed
2077 function getter(obj, path, bindFnToScope) {
2078   if (!path) return obj;
2079   var keys = path.split('.');
2080   var key;
2081   var lastInstance = obj;
2082   var len = keys.length;
2083
2084   for (var i = 0; i < len; i++) {
2085     key = keys[i];
2086     if (obj) {
2087       obj = (lastInstance = obj)[key];
2088     }
2089   }
2090   if (!bindFnToScope && isFunction(obj)) {
2091     return bind(lastInstance, obj);
2092   }
2093   return obj;
2094 }
2095
2096 /**
2097  * Return the DOM siblings between the first and last node in the given array.
2098  * @param {Array} array like object
2099  * @returns {Array} the inputted object or a jqLite collection containing the nodes
2100  */
2101 function getBlockNodes(nodes) {
2102   // TODO(perf): update `nodes` instead of creating a new object?
2103   var node = nodes[0];
2104   var endNode = nodes[nodes.length - 1];
2105   var blockNodes;
2106
2107   for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
2108     if (blockNodes || nodes[i] !== node) {
2109       if (!blockNodes) {
2110         blockNodes = jqLite(slice.call(nodes, 0, i));
2111       }
2112       blockNodes.push(node);
2113     }
2114   }
2115
2116   return blockNodes || nodes;
2117 }
2118
2119
2120 /**
2121  * Creates a new object without a prototype. This object is useful for lookup without having to
2122  * guard against prototypically inherited properties via hasOwnProperty.
2123  *
2124  * Related micro-benchmarks:
2125  * - http://jsperf.com/object-create2
2126  * - http://jsperf.com/proto-map-lookup/2
2127  * - http://jsperf.com/for-in-vs-object-keys2
2128  *
2129  * @returns {Object}
2130  */
2131 function createMap() {
2132   return Object.create(null);
2133 }
2134
2135 function stringify(value) {
2136   if (value == null) { // null || undefined
2137     return '';
2138   }
2139   switch (typeof value) {
2140     case 'string':
2141       break;
2142     case 'number':
2143       value = '' + value;
2144       break;
2145     default:
2146       if (hasCustomToString(value) && !isArray(value) && !isDate(value)) {
2147         value = value.toString();
2148       } else {
2149         value = toJson(value);
2150       }
2151   }
2152
2153   return value;
2154 }
2155
2156 var NODE_TYPE_ELEMENT = 1;
2157 var NODE_TYPE_ATTRIBUTE = 2;
2158 var NODE_TYPE_TEXT = 3;
2159 var NODE_TYPE_COMMENT = 8;
2160 var NODE_TYPE_DOCUMENT = 9;
2161 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
2162
2163 /**
2164  * @ngdoc type
2165  * @name angular.Module
2166  * @module ng
2167  * @description
2168  *
2169  * Interface for configuring angular {@link angular.module modules}.
2170  */
2171
2172 function setupModuleLoader(window) {
2173
2174   var $injectorMinErr = minErr('$injector');
2175   var ngMinErr = minErr('ng');
2176
2177   function ensure(obj, name, factory) {
2178     return obj[name] || (obj[name] = factory());
2179   }
2180
2181   var angular = ensure(window, 'angular', Object);
2182
2183   // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
2184   angular.$$minErr = angular.$$minErr || minErr;
2185
2186   return ensure(angular, 'module', function() {
2187     /** @type {Object.<string, angular.Module>} */
2188     var modules = {};
2189
2190     /**
2191      * @ngdoc function
2192      * @name angular.module
2193      * @module ng
2194      * @description
2195      *
2196      * The `angular.module` is a global place for creating, registering and retrieving Angular
2197      * modules.
2198      * All modules (angular core or 3rd party) that should be available to an application must be
2199      * registered using this mechanism.
2200      *
2201      * Passing one argument retrieves an existing {@link angular.Module},
2202      * whereas passing more than one argument creates a new {@link angular.Module}
2203      *
2204      *
2205      * # Module
2206      *
2207      * A module is a collection of services, directives, controllers, filters, and configuration information.
2208      * `angular.module` is used to configure the {@link auto.$injector $injector}.
2209      *
2210      * ```js
2211      * // Create a new module
2212      * var myModule = angular.module('myModule', []);
2213      *
2214      * // register a new service
2215      * myModule.value('appName', 'MyCoolApp');
2216      *
2217      * // configure existing services inside initialization blocks.
2218      * myModule.config(['$locationProvider', function($locationProvider) {
2219      *   // Configure existing providers
2220      *   $locationProvider.hashPrefix('!');
2221      * }]);
2222      * ```
2223      *
2224      * Then you can create an injector and load your modules like this:
2225      *
2226      * ```js
2227      * var injector = angular.injector(['ng', 'myModule'])
2228      * ```
2229      *
2230      * However it's more likely that you'll just use
2231      * {@link ng.directive:ngApp ngApp} or
2232      * {@link angular.bootstrap} to simplify this process for you.
2233      *
2234      * @param {!string} name The name of the module to create or retrieve.
2235      * @param {!Array.<string>=} requires If specified then new module is being created. If
2236      *        unspecified then the module is being retrieved for further configuration.
2237      * @param {Function=} configFn Optional configuration function for the module. Same as
2238      *        {@link angular.Module#config Module#config()}.
2239      * @returns {angular.Module} new module with the {@link angular.Module} api.
2240      */
2241     return function module(name, requires, configFn) {
2242
2243       var info = {};
2244
2245       var assertNotHasOwnProperty = function(name, context) {
2246         if (name === 'hasOwnProperty') {
2247           throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2248         }
2249       };
2250
2251       assertNotHasOwnProperty(name, 'module');
2252       if (requires && modules.hasOwnProperty(name)) {
2253         modules[name] = null;
2254       }
2255       return ensure(modules, name, function() {
2256         if (!requires) {
2257           throw $injectorMinErr('nomod', 'Module \'{0}\' is not available! You either misspelled ' +
2258              'the module name or forgot to load it. If registering a module ensure that you ' +
2259              'specify the dependencies as the second argument.', name);
2260         }
2261
2262         /** @type {!Array.<Array.<*>>} */
2263         var invokeQueue = [];
2264
2265         /** @type {!Array.<Function>} */
2266         var configBlocks = [];
2267
2268         /** @type {!Array.<Function>} */
2269         var runBlocks = [];
2270
2271         var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2272
2273         /** @type {angular.Module} */
2274         var moduleInstance = {
2275           // Private state
2276           _invokeQueue: invokeQueue,
2277           _configBlocks: configBlocks,
2278           _runBlocks: runBlocks,
2279
2280           /**
2281            * @ngdoc method
2282            * @name angular.Module#info
2283            * @module ng
2284            *
2285            * @param {Object=} info Information about the module
2286            * @returns {Object|Module} The current info object for this module if called as a getter,
2287            *                          or `this` if called as a setter.
2288            *
2289            * @description
2290            * Read and write custom information about this module.
2291            * For example you could put the version of the module in here.
2292            *
2293            * ```js
2294            * angular.module('myModule', []).info({ version: '1.0.0' });
2295            * ```
2296            *
2297            * The version could then be read back out by accessing the module elsewhere:
2298            *
2299            * ```
2300            * var version = angular.module('myModule').info().version;
2301            * ```
2302            *
2303            * You can also retrieve this information during runtime via the
2304            * {@link $injector#modules `$injector.modules`} property:
2305            *
2306            * ```js
2307            * var version = $injector.modules['myModule'].info().version;
2308            * ```
2309            */
2310           info: function(value) {
2311             if (isDefined(value)) {
2312               if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value');
2313               info = value;
2314               return this;
2315             }
2316             return info;
2317           },
2318
2319           /**
2320            * @ngdoc property
2321            * @name angular.Module#requires
2322            * @module ng
2323            *
2324            * @description
2325            * Holds the list of modules which the injector will load before the current module is
2326            * loaded.
2327            */
2328           requires: requires,
2329
2330           /**
2331            * @ngdoc property
2332            * @name angular.Module#name
2333            * @module ng
2334            *
2335            * @description
2336            * Name of the module.
2337            */
2338           name: name,
2339
2340
2341           /**
2342            * @ngdoc method
2343            * @name angular.Module#provider
2344            * @module ng
2345            * @param {string} name service name
2346            * @param {Function} providerType Construction function for creating new instance of the
2347            *                                service.
2348            * @description
2349            * See {@link auto.$provide#provider $provide.provider()}.
2350            */
2351           provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2352
2353           /**
2354            * @ngdoc method
2355            * @name angular.Module#factory
2356            * @module ng
2357            * @param {string} name service name
2358            * @param {Function} providerFunction Function for creating new instance of the service.
2359            * @description
2360            * See {@link auto.$provide#factory $provide.factory()}.
2361            */
2362           factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2363
2364           /**
2365            * @ngdoc method
2366            * @name angular.Module#service
2367            * @module ng
2368            * @param {string} name service name
2369            * @param {Function} constructor A constructor function that will be instantiated.
2370            * @description
2371            * See {@link auto.$provide#service $provide.service()}.
2372            */
2373           service: invokeLaterAndSetModuleName('$provide', 'service'),
2374
2375           /**
2376            * @ngdoc method
2377            * @name angular.Module#value
2378            * @module ng
2379            * @param {string} name service name
2380            * @param {*} object Service instance object.
2381            * @description
2382            * See {@link auto.$provide#value $provide.value()}.
2383            */
2384           value: invokeLater('$provide', 'value'),
2385
2386           /**
2387            * @ngdoc method
2388            * @name angular.Module#constant
2389            * @module ng
2390            * @param {string} name constant name
2391            * @param {*} object Constant value.
2392            * @description
2393            * Because the constants are fixed, they get applied before other provide methods.
2394            * See {@link auto.$provide#constant $provide.constant()}.
2395            */
2396           constant: invokeLater('$provide', 'constant', 'unshift'),
2397
2398            /**
2399            * @ngdoc method
2400            * @name angular.Module#decorator
2401            * @module ng
2402            * @param {string} name The name of the service to decorate.
2403            * @param {Function} decorFn This function will be invoked when the service needs to be
2404            *                           instantiated and should return the decorated service instance.
2405            * @description
2406            * See {@link auto.$provide#decorator $provide.decorator()}.
2407            */
2408           decorator: invokeLaterAndSetModuleName('$provide', 'decorator', configBlocks),
2409
2410           /**
2411            * @ngdoc method
2412            * @name angular.Module#animation
2413            * @module ng
2414            * @param {string} name animation name
2415            * @param {Function} animationFactory Factory function for creating new instance of an
2416            *                                    animation.
2417            * @description
2418            *
2419            * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2420            *
2421            *
2422            * Defines an animation hook that can be later used with
2423            * {@link $animate $animate} service and directives that use this service.
2424            *
2425            * ```js
2426            * module.animation('.animation-name', function($inject1, $inject2) {
2427            *   return {
2428            *     eventName : function(element, done) {
2429            *       //code to run the animation
2430            *       //once complete, then run done()
2431            *       return function cancellationFunction(element) {
2432            *         //code to cancel the animation
2433            *       }
2434            *     }
2435            *   }
2436            * })
2437            * ```
2438            *
2439            * See {@link ng.$animateProvider#register $animateProvider.register()} and
2440            * {@link ngAnimate ngAnimate module} for more information.
2441            */
2442           animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2443
2444           /**
2445            * @ngdoc method
2446            * @name angular.Module#filter
2447            * @module ng
2448            * @param {string} name Filter name - this must be a valid angular expression identifier
2449            * @param {Function} filterFactory Factory function for creating new instance of filter.
2450            * @description
2451            * See {@link ng.$filterProvider#register $filterProvider.register()}.
2452            *
2453            * <div class="alert alert-warning">
2454            * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2455            * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2456            * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2457            * (`myapp_subsection_filterx`).
2458            * </div>
2459            */
2460           filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2461
2462           /**
2463            * @ngdoc method
2464            * @name angular.Module#controller
2465            * @module ng
2466            * @param {string|Object} name Controller name, or an object map of controllers where the
2467            *    keys are the names and the values are the constructors.
2468            * @param {Function} constructor Controller constructor function.
2469            * @description
2470            * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2471            */
2472           controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2473
2474           /**
2475            * @ngdoc method
2476            * @name angular.Module#directive
2477            * @module ng
2478            * @param {string|Object} name Directive name, or an object map of directives where the
2479            *    keys are the names and the values are the factories.
2480            * @param {Function} directiveFactory Factory function for creating new instance of
2481            * directives.
2482            * @description
2483            * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2484            */
2485           directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2486
2487           /**
2488            * @ngdoc method
2489            * @name angular.Module#component
2490            * @module ng
2491            * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
2492            * @param {Object} options Component definition object (a simplified
2493            *    {@link ng.$compile#directive-definition-object directive definition object})
2494            *
2495            * @description
2496            * See {@link ng.$compileProvider#component $compileProvider.component()}.
2497            */
2498           component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
2499
2500           /**
2501            * @ngdoc method
2502            * @name angular.Module#config
2503            * @module ng
2504            * @param {Function} configFn Execute this function on module load. Useful for service
2505            *    configuration.
2506            * @description
2507            * Use this method to register work which needs to be performed on module loading.
2508            * For more about how to configure services, see
2509            * {@link providers#provider-recipe Provider Recipe}.
2510            */
2511           config: config,
2512
2513           /**
2514            * @ngdoc method
2515            * @name angular.Module#run
2516            * @module ng
2517            * @param {Function} initializationFn Execute this function after injector creation.
2518            *    Useful for application initialization.
2519            * @description
2520            * Use this method to register work which should be performed when the injector is done
2521            * loading all modules.
2522            */
2523           run: function(block) {
2524             runBlocks.push(block);
2525             return this;
2526           }
2527         };
2528
2529         if (configFn) {
2530           config(configFn);
2531         }
2532
2533         return moduleInstance;
2534
2535         /**
2536          * @param {string} provider
2537          * @param {string} method
2538          * @param {String=} insertMethod
2539          * @returns {angular.Module}
2540          */
2541         function invokeLater(provider, method, insertMethod, queue) {
2542           if (!queue) queue = invokeQueue;
2543           return function() {
2544             queue[insertMethod || 'push']([provider, method, arguments]);
2545             return moduleInstance;
2546           };
2547         }
2548
2549         /**
2550          * @param {string} provider
2551          * @param {string} method
2552          * @returns {angular.Module}
2553          */
2554         function invokeLaterAndSetModuleName(provider, method, queue) {
2555           if (!queue) queue = invokeQueue;
2556           return function(recipeName, factoryFunction) {
2557             if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2558             queue.push([provider, method, arguments]);
2559             return moduleInstance;
2560           };
2561         }
2562       });
2563     };
2564   });
2565
2566 }
2567
2568 /* global shallowCopy: true */
2569
2570 /**
2571  * Creates a shallow copy of an object, an array or a primitive.
2572  *
2573  * Assumes that there are no proto properties for objects.
2574  */
2575 function shallowCopy(src, dst) {
2576   if (isArray(src)) {
2577     dst = dst || [];
2578
2579     for (var i = 0, ii = src.length; i < ii; i++) {
2580       dst[i] = src[i];
2581     }
2582   } else if (isObject(src)) {
2583     dst = dst || {};
2584
2585     for (var key in src) {
2586       if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
2587         dst[key] = src[key];
2588       }
2589     }
2590   }
2591
2592   return dst || src;
2593 }
2594
2595 /* global toDebugString: true */
2596
2597 function serializeObject(obj, maxDepth) {
2598   var seen = [];
2599
2600   // There is no direct way to stringify object until reaching a specific depth
2601   // and a very deep object can cause a performance issue, so we copy the object
2602   // based on this specific depth and then stringify it.
2603   if (isValidObjectMaxDepth(maxDepth)) {
2604     obj = copy(obj, null, maxDepth);
2605   }
2606   return JSON.stringify(obj, function(key, val) {
2607     val = toJsonReplacer(key, val);
2608     if (isObject(val)) {
2609
2610       if (seen.indexOf(val) >= 0) return '...';
2611
2612       seen.push(val);
2613     }
2614     return val;
2615   });
2616 }
2617
2618 function toDebugString(obj, maxDepth) {
2619   if (typeof obj === 'function') {
2620     return obj.toString().replace(/ \{[\s\S]*$/, '');
2621   } else if (isUndefined(obj)) {
2622     return 'undefined';
2623   } else if (typeof obj !== 'string') {
2624     return serializeObject(obj, maxDepth);
2625   }
2626   return obj;
2627 }
2628
2629 /* global angularModule: true,
2630   version: true,
2631
2632   $CompileProvider,
2633
2634   htmlAnchorDirective,
2635   inputDirective,
2636   inputDirective,
2637   formDirective,
2638   scriptDirective,
2639   selectDirective,
2640   optionDirective,
2641   ngBindDirective,
2642   ngBindHtmlDirective,
2643   ngBindTemplateDirective,
2644   ngClassDirective,
2645   ngClassEvenDirective,
2646   ngClassOddDirective,
2647   ngCloakDirective,
2648   ngControllerDirective,
2649   ngFormDirective,
2650   ngHideDirective,
2651   ngIfDirective,
2652   ngIncludeDirective,
2653   ngIncludeFillContentDirective,
2654   ngInitDirective,
2655   ngNonBindableDirective,
2656   ngPluralizeDirective,
2657   ngRepeatDirective,
2658   ngShowDirective,
2659   ngStyleDirective,
2660   ngSwitchDirective,
2661   ngSwitchWhenDirective,
2662   ngSwitchDefaultDirective,
2663   ngOptionsDirective,
2664   ngTranscludeDirective,
2665   ngModelDirective,
2666   ngListDirective,
2667   ngChangeDirective,
2668   patternDirective,
2669   patternDirective,
2670   requiredDirective,
2671   requiredDirective,
2672   minlengthDirective,
2673   minlengthDirective,
2674   maxlengthDirective,
2675   maxlengthDirective,
2676   ngValueDirective,
2677   ngModelOptionsDirective,
2678   ngAttributeAliasDirectives,
2679   ngEventDirectives,
2680
2681   $AnchorScrollProvider,
2682   $AnimateProvider,
2683   $CoreAnimateCssProvider,
2684   $$CoreAnimateJsProvider,
2685   $$CoreAnimateQueueProvider,
2686   $$AnimateRunnerFactoryProvider,
2687   $$AnimateAsyncRunFactoryProvider,
2688   $BrowserProvider,
2689   $CacheFactoryProvider,
2690   $ControllerProvider,
2691   $DateProvider,
2692   $DocumentProvider,
2693   $$IsDocumentHiddenProvider,
2694   $ExceptionHandlerProvider,
2695   $FilterProvider,
2696   $$ForceReflowProvider,
2697   $InterpolateProvider,
2698   $IntervalProvider,
2699   $HttpProvider,
2700   $HttpParamSerializerProvider,
2701   $HttpParamSerializerJQLikeProvider,
2702   $HttpBackendProvider,
2703   $xhrFactoryProvider,
2704   $jsonpCallbacksProvider,
2705   $LocationProvider,
2706   $LogProvider,
2707   $$MapProvider,
2708   $ParseProvider,
2709   $RootScopeProvider,
2710   $QProvider,
2711   $$QProvider,
2712   $$SanitizeUriProvider,
2713   $SceProvider,
2714   $SceDelegateProvider,
2715   $SnifferProvider,
2716   $TemplateCacheProvider,
2717   $TemplateRequestProvider,
2718   $$TestabilityProvider,
2719   $TimeoutProvider,
2720   $$RAFProvider,
2721   $WindowProvider,
2722   $$jqLiteProvider,
2723   $$CookieReaderProvider
2724 */
2725
2726
2727 /**
2728  * @ngdoc object
2729  * @name angular.version
2730  * @module ng
2731  * @description
2732  * An object that contains information about the current AngularJS version.
2733  *
2734  * This object has the following properties:
2735  *
2736  * - `full` â€“ `{string}` â€“ Full version string, such as "0.9.18".
2737  * - `major` â€“ `{number}` â€“ Major version number, such as "0".
2738  * - `minor` â€“ `{number}` â€“ Minor version number, such as "9".
2739  * - `dot` â€“ `{number}` â€“ Dot version number, such as "18".
2740  * - `codeName` â€“ `{string}` â€“ Code name of the release, such as "jiggling-armfat".
2741  */
2742 var version = {
2743   // These placeholder strings will be replaced by grunt's `build` task.
2744   // They need to be double- or single-quoted.
2745   full: '1.6.3',
2746   major: 1,
2747   minor: 6,
2748   dot: 3,
2749   codeName: 'scriptalicious-bootstrapping'
2750 };
2751
2752
2753 function publishExternalAPI(angular) {
2754   extend(angular, {
2755     'errorHandlingConfig': errorHandlingConfig,
2756     'bootstrap': bootstrap,
2757     'copy': copy,
2758     'extend': extend,
2759     'merge': merge,
2760     'equals': equals,
2761     'element': jqLite,
2762     'forEach': forEach,
2763     'injector': createInjector,
2764     'noop': noop,
2765     'bind': bind,
2766     'toJson': toJson,
2767     'fromJson': fromJson,
2768     'identity': identity,
2769     'isUndefined': isUndefined,
2770     'isDefined': isDefined,
2771     'isString': isString,
2772     'isFunction': isFunction,
2773     'isObject': isObject,
2774     'isNumber': isNumber,
2775     'isElement': isElement,
2776     'isArray': isArray,
2777     'version': version,
2778     'isDate': isDate,
2779     'lowercase': lowercase,
2780     'uppercase': uppercase,
2781     'callbacks': {$$counter: 0},
2782     'getTestability': getTestability,
2783     'reloadWithDebugInfo': reloadWithDebugInfo,
2784     '$$minErr': minErr,
2785     '$$csp': csp,
2786     '$$encodeUriSegment': encodeUriSegment,
2787     '$$encodeUriQuery': encodeUriQuery,
2788     '$$stringify': stringify
2789   });
2790
2791   angularModule = setupModuleLoader(window);
2792
2793   angularModule('ng', ['ngLocale'], ['$provide',
2794     function ngModule($provide) {
2795       // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2796       $provide.provider({
2797         $$sanitizeUri: $$SanitizeUriProvider
2798       });
2799       $provide.provider('$compile', $CompileProvider).
2800         directive({
2801             a: htmlAnchorDirective,
2802             input: inputDirective,
2803             textarea: inputDirective,
2804             form: formDirective,
2805             script: scriptDirective,
2806             select: selectDirective,
2807             option: optionDirective,
2808             ngBind: ngBindDirective,
2809             ngBindHtml: ngBindHtmlDirective,
2810             ngBindTemplate: ngBindTemplateDirective,
2811             ngClass: ngClassDirective,
2812             ngClassEven: ngClassEvenDirective,
2813             ngClassOdd: ngClassOddDirective,
2814             ngCloak: ngCloakDirective,
2815             ngController: ngControllerDirective,
2816             ngForm: ngFormDirective,
2817             ngHide: ngHideDirective,
2818             ngIf: ngIfDirective,
2819             ngInclude: ngIncludeDirective,
2820             ngInit: ngInitDirective,
2821             ngNonBindable: ngNonBindableDirective,
2822             ngPluralize: ngPluralizeDirective,
2823             ngRepeat: ngRepeatDirective,
2824             ngShow: ngShowDirective,
2825             ngStyle: ngStyleDirective,
2826             ngSwitch: ngSwitchDirective,
2827             ngSwitchWhen: ngSwitchWhenDirective,
2828             ngSwitchDefault: ngSwitchDefaultDirective,
2829             ngOptions: ngOptionsDirective,
2830             ngTransclude: ngTranscludeDirective,
2831             ngModel: ngModelDirective,
2832             ngList: ngListDirective,
2833             ngChange: ngChangeDirective,
2834             pattern: patternDirective,
2835             ngPattern: patternDirective,
2836             required: requiredDirective,
2837             ngRequired: requiredDirective,
2838             minlength: minlengthDirective,
2839             ngMinlength: minlengthDirective,
2840             maxlength: maxlengthDirective,
2841             ngMaxlength: maxlengthDirective,
2842             ngValue: ngValueDirective,
2843             ngModelOptions: ngModelOptionsDirective
2844         }).
2845         directive({
2846           ngInclude: ngIncludeFillContentDirective
2847         }).
2848         directive(ngAttributeAliasDirectives).
2849         directive(ngEventDirectives);
2850       $provide.provider({
2851         $anchorScroll: $AnchorScrollProvider,
2852         $animate: $AnimateProvider,
2853         $animateCss: $CoreAnimateCssProvider,
2854         $$animateJs: $$CoreAnimateJsProvider,
2855         $$animateQueue: $$CoreAnimateQueueProvider,
2856         $$AnimateRunner: $$AnimateRunnerFactoryProvider,
2857         $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
2858         $browser: $BrowserProvider,
2859         $cacheFactory: $CacheFactoryProvider,
2860         $controller: $ControllerProvider,
2861         $document: $DocumentProvider,
2862         $$isDocumentHidden: $$IsDocumentHiddenProvider,
2863         $exceptionHandler: $ExceptionHandlerProvider,
2864         $filter: $FilterProvider,
2865         $$forceReflow: $$ForceReflowProvider,
2866         $interpolate: $InterpolateProvider,
2867         $interval: $IntervalProvider,
2868         $http: $HttpProvider,
2869         $httpParamSerializer: $HttpParamSerializerProvider,
2870         $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2871         $httpBackend: $HttpBackendProvider,
2872         $xhrFactory: $xhrFactoryProvider,
2873         $jsonpCallbacks: $jsonpCallbacksProvider,
2874         $location: $LocationProvider,
2875         $log: $LogProvider,
2876         $parse: $ParseProvider,
2877         $rootScope: $RootScopeProvider,
2878         $q: $QProvider,
2879         $$q: $$QProvider,
2880         $sce: $SceProvider,
2881         $sceDelegate: $SceDelegateProvider,
2882         $sniffer: $SnifferProvider,
2883         $templateCache: $TemplateCacheProvider,
2884         $templateRequest: $TemplateRequestProvider,
2885         $$testability: $$TestabilityProvider,
2886         $timeout: $TimeoutProvider,
2887         $window: $WindowProvider,
2888         $$rAF: $$RAFProvider,
2889         $$jqLite: $$jqLiteProvider,
2890         $$Map: $$MapProvider,
2891         $$cookieReader: $$CookieReaderProvider
2892       });
2893     }
2894   ])
2895   .info({ angularVersion: '1.6.3' });
2896 }
2897
2898 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2899  *     Any commits to this file should be reviewed with security in mind.  *
2900  *   Changes to this file can potentially create security vulnerabilities. *
2901  *          An approval from 2 Core members with history of modifying      *
2902  *                         this file is required.                          *
2903  *                                                                         *
2904  *  Does the change somehow allow for arbitrary javascript to be executed? *
2905  *    Or allows for someone to change the prototype of built-in objects?   *
2906  *     Or gives undesired access to variables likes document or window?    *
2907  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2908
2909 /* global
2910   JQLitePrototype: true,
2911   BOOLEAN_ATTR: true,
2912   ALIASED_ATTR: true
2913 */
2914
2915 //////////////////////////////////
2916 //JQLite
2917 //////////////////////////////////
2918
2919 /**
2920  * @ngdoc function
2921  * @name angular.element
2922  * @module ng
2923  * @kind function
2924  *
2925  * @description
2926  * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2927  *
2928  * If jQuery is available, `angular.element` is an alias for the
2929  * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2930  * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
2931  *
2932  * jqLite is a tiny, API-compatible subset of jQuery that allows
2933  * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
2934  * commonly needed functionality with the goal of having a very small footprint.
2935  *
2936  * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
2937  * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
2938  * specific version of jQuery if multiple versions exist on the page.
2939  *
2940  * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
2941  * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
2942  *
2943  * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
2944  * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
2945  * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
2946  *
2947  * ## Angular's jqLite
2948  * jqLite provides only the following jQuery methods:
2949  *
2950  * - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument
2951  * - [`after()`](http://api.jquery.com/after/)
2952  * - [`append()`](http://api.jquery.com/append/)
2953  * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2954  * - [`bind()`](http://api.jquery.com/bind/) (_deprecated_, use [`on()`](http://api.jquery.com/on/)) - Does not support namespaces, selectors or eventData
2955  * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2956  * - [`clone()`](http://api.jquery.com/clone/)
2957  * - [`contents()`](http://api.jquery.com/contents/)
2958  * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
2959  *   As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
2960  * - [`data()`](http://api.jquery.com/data/)
2961  * - [`detach()`](http://api.jquery.com/detach/)
2962  * - [`empty()`](http://api.jquery.com/empty/)
2963  * - [`eq()`](http://api.jquery.com/eq/)
2964  * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2965  * - [`hasClass()`](http://api.jquery.com/hasClass/)
2966  * - [`html()`](http://api.jquery.com/html/)
2967  * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2968  * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2969  * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2970  * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2971  * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2972  * - [`prepend()`](http://api.jquery.com/prepend/)
2973  * - [`prop()`](http://api.jquery.com/prop/)
2974  * - [`ready()`](http://api.jquery.com/ready/) (_deprecated_, use `angular.element(callback)` instead of `angular.element(document).ready(callback)`)
2975  * - [`remove()`](http://api.jquery.com/remove/)
2976  * - [`removeAttr()`](http://api.jquery.com/removeAttr/) - Does not support multiple attributes
2977  * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument
2978  * - [`removeData()`](http://api.jquery.com/removeData/)
2979  * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2980  * - [`text()`](http://api.jquery.com/text/)
2981  * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument
2982  * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers
2983  * - [`unbind()`](http://api.jquery.com/unbind/) (_deprecated_, use [`off()`](http://api.jquery.com/off/)) - Does not support namespaces or event object as parameter
2984  * - [`val()`](http://api.jquery.com/val/)
2985  * - [`wrap()`](http://api.jquery.com/wrap/)
2986  *
2987  * ## jQuery/jqLite Extras
2988  * Angular also provides the following additional methods and events to both jQuery and jqLite:
2989  *
2990  * ### Events
2991  * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2992  *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2993  *    element before it is removed.
2994  *
2995  * ### Methods
2996  * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2997  *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2998  *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2999  *   `'ngModel'`).
3000  * - `injector()` - retrieves the injector of the current element or its parent.
3001  * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
3002  *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
3003  *   be enabled.
3004  * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
3005  *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
3006  *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
3007  *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
3008  * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
3009  *   parent element is reached.
3010  *
3011  * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
3012  * https://github.com/angular/angular.js/issues/14251 for more information.
3013  *
3014  * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
3015  * @returns {Object} jQuery object.
3016  */
3017
3018 JQLite.expando = 'ng339';
3019
3020 var jqCache = JQLite.cache = {},
3021     jqId = 1;
3022
3023 /*
3024  * !!! This is an undocumented "private" function !!!
3025  */
3026 JQLite._data = function(node) {
3027   //jQuery always returns an object on cache miss
3028   return this.cache[node[this.expando]] || {};
3029 };
3030
3031 function jqNextId() { return ++jqId; }
3032
3033
3034 var DASH_LOWERCASE_REGEXP = /-([a-z])/g;
3035 var MS_HACK_REGEXP = /^-ms-/;
3036 var MOUSE_EVENT_MAP = { mouseleave: 'mouseout', mouseenter: 'mouseover' };
3037 var jqLiteMinErr = minErr('jqLite');
3038
3039 /**
3040  * Converts kebab-case to camelCase.
3041  * There is also a special case for the ms prefix starting with a lowercase letter.
3042  * @param name Name to normalize
3043  */
3044 function cssKebabToCamel(name) {
3045     return kebabToCamel(name.replace(MS_HACK_REGEXP, 'ms-'));
3046 }
3047
3048 function fnCamelCaseReplace(all, letter) {
3049   return letter.toUpperCase();
3050 }
3051
3052 /**
3053  * Converts kebab-case to camelCase.
3054  * @param name Name to normalize
3055  */
3056 function kebabToCamel(name) {
3057   return name
3058     .replace(DASH_LOWERCASE_REGEXP, fnCamelCaseReplace);
3059 }
3060
3061 var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
3062 var HTML_REGEXP = /<|&#?\w+;/;
3063 var TAG_NAME_REGEXP = /<([\w:-]+)/;
3064 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
3065
3066 var wrapMap = {
3067   'option': [1, '<select multiple="multiple">', '</select>'],
3068
3069   'thead': [1, '<table>', '</table>'],
3070   'col': [2, '<table><colgroup>', '</colgroup></table>'],
3071   'tr': [2, '<table><tbody>', '</tbody></table>'],
3072   'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
3073   '_default': [0, '', '']
3074 };
3075
3076 wrapMap.optgroup = wrapMap.option;
3077 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
3078 wrapMap.th = wrapMap.td;
3079
3080
3081 function jqLiteIsTextNode(html) {
3082   return !HTML_REGEXP.test(html);
3083 }
3084
3085 function jqLiteAcceptsData(node) {
3086   // The window object can accept data but has no nodeType
3087   // Otherwise we are only interested in elements (1) and documents (9)
3088   var nodeType = node.nodeType;
3089   return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
3090 }
3091
3092 function jqLiteHasData(node) {
3093   for (var key in jqCache[node.ng339]) {
3094     return true;
3095   }
3096   return false;
3097 }
3098
3099 function jqLiteCleanData(nodes) {
3100   for (var i = 0, ii = nodes.length; i < ii; i++) {
3101     jqLiteRemoveData(nodes[i]);
3102   }
3103 }
3104
3105 function jqLiteBuildFragment(html, context) {
3106   var tmp, tag, wrap,
3107       fragment = context.createDocumentFragment(),
3108       nodes = [], i;
3109
3110   if (jqLiteIsTextNode(html)) {
3111     // Convert non-html into a text node
3112     nodes.push(context.createTextNode(html));
3113   } else {
3114     // Convert html into DOM nodes
3115     tmp = fragment.appendChild(context.createElement('div'));
3116     tag = (TAG_NAME_REGEXP.exec(html) || ['', ''])[1].toLowerCase();
3117     wrap = wrapMap[tag] || wrapMap._default;
3118     tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, '<$1></$2>') + wrap[2];
3119
3120     // Descend through wrappers to the right content
3121     i = wrap[0];
3122     while (i--) {
3123       tmp = tmp.lastChild;
3124     }
3125
3126     nodes = concat(nodes, tmp.childNodes);
3127
3128     tmp = fragment.firstChild;
3129     tmp.textContent = '';
3130   }
3131
3132   // Remove wrapper from fragment
3133   fragment.textContent = '';
3134   fragment.innerHTML = ''; // Clear inner HTML
3135   forEach(nodes, function(node) {
3136     fragment.appendChild(node);
3137   });
3138
3139   return fragment;
3140 }
3141
3142 function jqLiteParseHTML(html, context) {
3143   context = context || window.document;
3144   var parsed;
3145
3146   if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
3147     return [context.createElement(parsed[1])];
3148   }
3149
3150   if ((parsed = jqLiteBuildFragment(html, context))) {
3151     return parsed.childNodes;
3152   }
3153
3154   return [];
3155 }
3156
3157 function jqLiteWrapNode(node, wrapper) {
3158   var parent = node.parentNode;
3159
3160   if (parent) {
3161     parent.replaceChild(wrapper, node);
3162   }
3163
3164   wrapper.appendChild(node);
3165 }
3166
3167
3168 // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
3169 var jqLiteContains = window.Node.prototype.contains || /** @this */ function(arg) {
3170   // eslint-disable-next-line no-bitwise
3171   return !!(this.compareDocumentPosition(arg) & 16);
3172 };
3173
3174 /////////////////////////////////////////////
3175 function JQLite(element) {
3176   if (element instanceof JQLite) {
3177     return element;
3178   }
3179
3180   var argIsString;
3181
3182   if (isString(element)) {
3183     element = trim(element);
3184     argIsString = true;
3185   }
3186   if (!(this instanceof JQLite)) {
3187     if (argIsString && element.charAt(0) !== '<') {
3188       throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
3189     }
3190     return new JQLite(element);
3191   }
3192
3193   if (argIsString) {
3194     jqLiteAddNodes(this, jqLiteParseHTML(element));
3195   } else if (isFunction(element)) {
3196     jqLiteReady(element);
3197   } else {
3198     jqLiteAddNodes(this, element);
3199   }
3200 }
3201
3202 function jqLiteClone(element) {
3203   return element.cloneNode(true);
3204 }
3205
3206 function jqLiteDealoc(element, onlyDescendants) {
3207   if (!onlyDescendants) jqLiteRemoveData(element);
3208
3209   if (element.querySelectorAll) {
3210     var descendants = element.querySelectorAll('*');
3211     for (var i = 0, l = descendants.length; i < l; i++) {
3212       jqLiteRemoveData(descendants[i]);
3213     }
3214   }
3215 }
3216
3217 function jqLiteOff(element, type, fn, unsupported) {
3218   if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
3219
3220   var expandoStore = jqLiteExpandoStore(element);
3221   var events = expandoStore && expandoStore.events;
3222   var handle = expandoStore && expandoStore.handle;
3223
3224   if (!handle) return; //no listeners registered
3225
3226   if (!type) {
3227     for (type in events) {
3228       if (type !== '$destroy') {
3229         element.removeEventListener(type, handle);
3230       }
3231       delete events[type];
3232     }
3233   } else {
3234
3235     var removeHandler = function(type) {
3236       var listenerFns = events[type];
3237       if (isDefined(fn)) {
3238         arrayRemove(listenerFns || [], fn);
3239       }
3240       if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
3241         element.removeEventListener(type, handle);
3242         delete events[type];
3243       }
3244     };
3245
3246     forEach(type.split(' '), function(type) {
3247       removeHandler(type);
3248       if (MOUSE_EVENT_MAP[type]) {
3249         removeHandler(MOUSE_EVENT_MAP[type]);
3250       }
3251     });
3252   }
3253 }
3254
3255 function jqLiteRemoveData(element, name) {
3256   var expandoId = element.ng339;
3257   var expandoStore = expandoId && jqCache[expandoId];
3258
3259   if (expandoStore) {
3260     if (name) {
3261       delete expandoStore.data[name];
3262       return;
3263     }
3264
3265     if (expandoStore.handle) {
3266       if (expandoStore.events.$destroy) {
3267         expandoStore.handle({}, '$destroy');
3268       }
3269       jqLiteOff(element);
3270     }
3271     delete jqCache[expandoId];
3272     element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
3273   }
3274 }
3275
3276
3277 function jqLiteExpandoStore(element, createIfNecessary) {
3278   var expandoId = element.ng339,
3279       expandoStore = expandoId && jqCache[expandoId];
3280
3281   if (createIfNecessary && !expandoStore) {
3282     element.ng339 = expandoId = jqNextId();
3283     expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
3284   }
3285
3286   return expandoStore;
3287 }
3288
3289
3290 function jqLiteData(element, key, value) {
3291   if (jqLiteAcceptsData(element)) {
3292     var prop;
3293
3294     var isSimpleSetter = isDefined(value);
3295     var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
3296     var massGetter = !key;
3297     var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
3298     var data = expandoStore && expandoStore.data;
3299
3300     if (isSimpleSetter) { // data('key', value)
3301       data[kebabToCamel(key)] = value;
3302     } else {
3303       if (massGetter) {  // data()
3304         return data;
3305       } else {
3306         if (isSimpleGetter) { // data('key')
3307           // don't force creation of expandoStore if it doesn't exist yet
3308           return data && data[kebabToCamel(key)];
3309         } else { // mass-setter: data({key1: val1, key2: val2})
3310           for (prop in key) {
3311             data[kebabToCamel(prop)] = key[prop];
3312           }
3313         }
3314       }
3315     }
3316   }
3317 }
3318
3319 function jqLiteHasClass(element, selector) {
3320   if (!element.getAttribute) return false;
3321   return ((' ' + (element.getAttribute('class') || '') + ' ').replace(/[\n\t]/g, ' ').
3322       indexOf(' ' + selector + ' ') > -1);
3323 }
3324
3325 function jqLiteRemoveClass(element, cssClasses) {
3326   if (cssClasses && element.setAttribute) {
3327     forEach(cssClasses.split(' '), function(cssClass) {
3328       element.setAttribute('class', trim(
3329           (' ' + (element.getAttribute('class') || '') + ' ')
3330           .replace(/[\n\t]/g, ' ')
3331           .replace(' ' + trim(cssClass) + ' ', ' '))
3332       );
3333     });
3334   }
3335 }
3336
3337 function jqLiteAddClass(element, cssClasses) {
3338   if (cssClasses && element.setAttribute) {
3339     var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3340                             .replace(/[\n\t]/g, ' ');
3341
3342     forEach(cssClasses.split(' '), function(cssClass) {
3343       cssClass = trim(cssClass);
3344       if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3345         existingClasses += cssClass + ' ';
3346       }
3347     });
3348
3349     element.setAttribute('class', trim(existingClasses));
3350   }
3351 }
3352
3353
3354 function jqLiteAddNodes(root, elements) {
3355   // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3356
3357   if (elements) {
3358
3359     // if a Node (the most common case)
3360     if (elements.nodeType) {
3361       root[root.length++] = elements;
3362     } else {
3363       var length = elements.length;
3364
3365       // if an Array or NodeList and not a Window
3366       if (typeof length === 'number' && elements.window !== elements) {
3367         if (length) {
3368           for (var i = 0; i < length; i++) {
3369             root[root.length++] = elements[i];
3370           }
3371         }
3372       } else {
3373         root[root.length++] = elements;
3374       }
3375     }
3376   }
3377 }
3378
3379
3380 function jqLiteController(element, name) {
3381   return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3382 }
3383
3384 function jqLiteInheritedData(element, name, value) {
3385   // if element is the document object work with the html element instead
3386   // this makes $(document).scope() possible
3387   if (element.nodeType === NODE_TYPE_DOCUMENT) {
3388     element = element.documentElement;
3389   }
3390   var names = isArray(name) ? name : [name];
3391
3392   while (element) {
3393     for (var i = 0, ii = names.length; i < ii; i++) {
3394       if (isDefined(value = jqLite.data(element, names[i]))) return value;
3395     }
3396
3397     // If dealing with a document fragment node with a host element, and no parent, use the host
3398     // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3399     // to lookup parent controllers.
3400     element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3401   }
3402 }
3403
3404 function jqLiteEmpty(element) {
3405   jqLiteDealoc(element, true);
3406   while (element.firstChild) {
3407     element.removeChild(element.firstChild);
3408   }
3409 }
3410
3411 function jqLiteRemove(element, keepData) {
3412   if (!keepData) jqLiteDealoc(element);
3413   var parent = element.parentNode;
3414   if (parent) parent.removeChild(element);
3415 }
3416
3417
3418 function jqLiteDocumentLoaded(action, win) {
3419   win = win || window;
3420   if (win.document.readyState === 'complete') {
3421     // Force the action to be run async for consistent behavior
3422     // from the action's point of view
3423     // i.e. it will definitely not be in a $apply
3424     win.setTimeout(action);
3425   } else {
3426     // No need to unbind this handler as load is only ever called once
3427     jqLite(win).on('load', action);
3428   }
3429 }
3430
3431 function jqLiteReady(fn) {
3432   function trigger() {
3433     window.document.removeEventListener('DOMContentLoaded', trigger);
3434     window.removeEventListener('load', trigger);
3435     fn();
3436   }
3437
3438   // check if document is already loaded
3439   if (window.document.readyState === 'complete') {
3440     window.setTimeout(fn);
3441   } else {
3442     // We can not use jqLite since we are not done loading and jQuery could be loaded later.
3443
3444     // Works for modern browsers and IE9
3445     window.document.addEventListener('DOMContentLoaded', trigger);
3446
3447     // Fallback to window.onload for others
3448     window.addEventListener('load', trigger);
3449   }
3450 }
3451
3452 //////////////////////////////////////////
3453 // Functions which are declared directly.
3454 //////////////////////////////////////////
3455 var JQLitePrototype = JQLite.prototype = {
3456   ready: jqLiteReady,
3457   toString: function() {
3458     var value = [];
3459     forEach(this, function(e) { value.push('' + e);});
3460     return '[' + value.join(', ') + ']';
3461   },
3462
3463   eq: function(index) {
3464       return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3465   },
3466
3467   length: 0,
3468   push: push,
3469   sort: [].sort,
3470   splice: [].splice
3471 };
3472
3473 //////////////////////////////////////////
3474 // Functions iterating getter/setters.
3475 // these functions return self on setter and
3476 // value on get.
3477 //////////////////////////////////////////
3478 var BOOLEAN_ATTR = {};
3479 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3480   BOOLEAN_ATTR[lowercase(value)] = value;
3481 });
3482 var BOOLEAN_ELEMENTS = {};
3483 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3484   BOOLEAN_ELEMENTS[value] = true;
3485 });
3486 var ALIASED_ATTR = {
3487   'ngMinlength': 'minlength',
3488   'ngMaxlength': 'maxlength',
3489   'ngMin': 'min',
3490   'ngMax': 'max',
3491   'ngPattern': 'pattern',
3492   'ngStep': 'step'
3493 };
3494
3495 function getBooleanAttrName(element, name) {
3496   // check dom last since we will most likely fail on name
3497   var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3498
3499   // booleanAttr is here twice to minimize DOM access
3500   return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3501 }
3502
3503 function getAliasedAttrName(name) {
3504   return ALIASED_ATTR[name];
3505 }
3506
3507 forEach({
3508   data: jqLiteData,
3509   removeData: jqLiteRemoveData,
3510   hasData: jqLiteHasData,
3511   cleanData: jqLiteCleanData
3512 }, function(fn, name) {
3513   JQLite[name] = fn;
3514 });
3515
3516 forEach({
3517   data: jqLiteData,
3518   inheritedData: jqLiteInheritedData,
3519
3520   scope: function(element) {
3521     // Can't use jqLiteData here directly so we stay compatible with jQuery!
3522     return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3523   },
3524
3525   isolateScope: function(element) {
3526     // Can't use jqLiteData here directly so we stay compatible with jQuery!
3527     return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3528   },
3529
3530   controller: jqLiteController,
3531
3532   injector: function(element) {
3533     return jqLiteInheritedData(element, '$injector');
3534   },
3535
3536   removeAttr: function(element, name) {
3537     element.removeAttribute(name);
3538   },
3539
3540   hasClass: jqLiteHasClass,
3541
3542   css: function(element, name, value) {
3543     name = cssKebabToCamel(name);
3544
3545     if (isDefined(value)) {
3546       element.style[name] = value;
3547     } else {
3548       return element.style[name];
3549     }
3550   },
3551
3552   attr: function(element, name, value) {
3553     var ret;
3554     var nodeType = element.nodeType;
3555     if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT ||
3556       !element.getAttribute) {
3557       return;
3558     }
3559
3560     var lowercasedName = lowercase(name);
3561     var isBooleanAttr = BOOLEAN_ATTR[lowercasedName];
3562
3563     if (isDefined(value)) {
3564       // setter
3565
3566       if (value === null || (value === false && isBooleanAttr)) {
3567         element.removeAttribute(name);
3568       } else {
3569         element.setAttribute(name, isBooleanAttr ? lowercasedName : value);
3570       }
3571     } else {
3572       // getter
3573
3574       ret = element.getAttribute(name);
3575
3576       if (isBooleanAttr && ret !== null) {
3577         ret = lowercasedName;
3578       }
3579       // Normalize non-existing attributes to undefined (as jQuery).
3580       return ret === null ? undefined : ret;
3581     }
3582   },
3583
3584   prop: function(element, name, value) {
3585     if (isDefined(value)) {
3586       element[name] = value;
3587     } else {
3588       return element[name];
3589     }
3590   },
3591
3592   text: (function() {
3593     getText.$dv = '';
3594     return getText;
3595
3596     function getText(element, value) {
3597       if (isUndefined(value)) {
3598         var nodeType = element.nodeType;
3599         return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3600       }
3601       element.textContent = value;
3602     }
3603   })(),
3604
3605   val: function(element, value) {
3606     if (isUndefined(value)) {
3607       if (element.multiple && nodeName_(element) === 'select') {
3608         var result = [];
3609         forEach(element.options, function(option) {
3610           if (option.selected) {
3611             result.push(option.value || option.text);
3612           }
3613         });
3614         return result;
3615       }
3616       return element.value;
3617     }
3618     element.value = value;
3619   },
3620
3621   html: function(element, value) {
3622     if (isUndefined(value)) {
3623       return element.innerHTML;
3624     }
3625     jqLiteDealoc(element, true);
3626     element.innerHTML = value;
3627   },
3628
3629   empty: jqLiteEmpty
3630 }, function(fn, name) {
3631   /**
3632    * Properties: writes return selection, reads return first value
3633    */
3634   JQLite.prototype[name] = function(arg1, arg2) {
3635     var i, key;
3636     var nodeCount = this.length;
3637
3638     // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3639     // in a way that survives minification.
3640     // jqLiteEmpty takes no arguments but is a setter.
3641     if (fn !== jqLiteEmpty &&
3642         (isUndefined((fn.length === 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3643       if (isObject(arg1)) {
3644
3645         // we are a write, but the object properties are the key/values
3646         for (i = 0; i < nodeCount; i++) {
3647           if (fn === jqLiteData) {
3648             // data() takes the whole object in jQuery
3649             fn(this[i], arg1);
3650           } else {
3651             for (key in arg1) {
3652               fn(this[i], key, arg1[key]);
3653             }
3654           }
3655         }
3656         // return self for chaining
3657         return this;
3658       } else {
3659         // we are a read, so read the first child.
3660         // TODO: do we still need this?
3661         var value = fn.$dv;
3662         // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3663         var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3664         for (var j = 0; j < jj; j++) {
3665           var nodeValue = fn(this[j], arg1, arg2);
3666           value = value ? value + nodeValue : nodeValue;
3667         }
3668         return value;
3669       }
3670     } else {
3671       // we are a write, so apply to all children
3672       for (i = 0; i < nodeCount; i++) {
3673         fn(this[i], arg1, arg2);
3674       }
3675       // return self for chaining
3676       return this;
3677     }
3678   };
3679 });
3680
3681 function createEventHandler(element, events) {
3682   var eventHandler = function(event, type) {
3683     // jQuery specific api
3684     event.isDefaultPrevented = function() {
3685       return event.defaultPrevented;
3686     };
3687
3688     var eventFns = events[type || event.type];
3689     var eventFnsLength = eventFns ? eventFns.length : 0;
3690
3691     if (!eventFnsLength) return;
3692
3693     if (isUndefined(event.immediatePropagationStopped)) {
3694       var originalStopImmediatePropagation = event.stopImmediatePropagation;
3695       event.stopImmediatePropagation = function() {
3696         event.immediatePropagationStopped = true;
3697
3698         if (event.stopPropagation) {
3699           event.stopPropagation();
3700         }
3701
3702         if (originalStopImmediatePropagation) {
3703           originalStopImmediatePropagation.call(event);
3704         }
3705       };
3706     }
3707
3708     event.isImmediatePropagationStopped = function() {
3709       return event.immediatePropagationStopped === true;
3710     };
3711
3712     // Some events have special handlers that wrap the real handler
3713     var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3714
3715     // Copy event handlers in case event handlers array is modified during execution.
3716     if ((eventFnsLength > 1)) {
3717       eventFns = shallowCopy(eventFns);
3718     }
3719
3720     for (var i = 0; i < eventFnsLength; i++) {
3721       if (!event.isImmediatePropagationStopped()) {
3722         handlerWrapper(element, event, eventFns[i]);
3723       }
3724     }
3725   };
3726
3727   // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3728   //       events on `element`
3729   eventHandler.elem = element;
3730   return eventHandler;
3731 }
3732
3733 function defaultHandlerWrapper(element, event, handler) {
3734   handler.call(element, event);
3735 }
3736
3737 function specialMouseHandlerWrapper(target, event, handler) {
3738   // Refer to jQuery's implementation of mouseenter & mouseleave
3739   // Read about mouseenter and mouseleave:
3740   // http://www.quirksmode.org/js/events_mouse.html#link8
3741   var related = event.relatedTarget;
3742   // For mousenter/leave call the handler if related is outside the target.
3743   // NB: No relatedTarget if the mouse left/entered the browser window
3744   if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3745     handler.call(target, event);
3746   }
3747 }
3748
3749 //////////////////////////////////////////
3750 // Functions iterating traversal.
3751 // These functions chain results into a single
3752 // selector.
3753 //////////////////////////////////////////
3754 forEach({
3755   removeData: jqLiteRemoveData,
3756
3757   on: function jqLiteOn(element, type, fn, unsupported) {
3758     if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3759
3760     // Do not add event handlers to non-elements because they will not be cleaned up.
3761     if (!jqLiteAcceptsData(element)) {
3762       return;
3763     }
3764
3765     var expandoStore = jqLiteExpandoStore(element, true);
3766     var events = expandoStore.events;
3767     var handle = expandoStore.handle;
3768
3769     if (!handle) {
3770       handle = expandoStore.handle = createEventHandler(element, events);
3771     }
3772
3773     // http://jsperf.com/string-indexof-vs-split
3774     var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3775     var i = types.length;
3776
3777     var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3778       var eventFns = events[type];
3779
3780       if (!eventFns) {
3781         eventFns = events[type] = [];
3782         eventFns.specialHandlerWrapper = specialHandlerWrapper;
3783         if (type !== '$destroy' && !noEventListener) {
3784           element.addEventListener(type, handle);
3785         }
3786       }
3787
3788       eventFns.push(fn);
3789     };
3790
3791     while (i--) {
3792       type = types[i];
3793       if (MOUSE_EVENT_MAP[type]) {
3794         addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3795         addHandler(type, undefined, true);
3796       } else {
3797         addHandler(type);
3798       }
3799     }
3800   },
3801
3802   off: jqLiteOff,
3803
3804   one: function(element, type, fn) {
3805     element = jqLite(element);
3806
3807     //add the listener twice so that when it is called
3808     //you can remove the original function and still be
3809     //able to call element.off(ev, fn) normally
3810     element.on(type, function onFn() {
3811       element.off(type, fn);
3812       element.off(type, onFn);
3813     });
3814     element.on(type, fn);
3815   },
3816
3817   replaceWith: function(element, replaceNode) {
3818     var index, parent = element.parentNode;
3819     jqLiteDealoc(element);
3820     forEach(new JQLite(replaceNode), function(node) {
3821       if (index) {
3822         parent.insertBefore(node, index.nextSibling);
3823       } else {
3824         parent.replaceChild(node, element);
3825       }
3826       index = node;
3827     });
3828   },
3829
3830   children: function(element) {
3831     var children = [];
3832     forEach(element.childNodes, function(element) {
3833       if (element.nodeType === NODE_TYPE_ELEMENT) {
3834         children.push(element);
3835       }
3836     });
3837     return children;
3838   },
3839
3840   contents: function(element) {
3841     return element.contentDocument || element.childNodes || [];
3842   },
3843
3844   append: function(element, node) {
3845     var nodeType = element.nodeType;
3846     if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3847
3848     node = new JQLite(node);
3849
3850     for (var i = 0, ii = node.length; i < ii; i++) {
3851       var child = node[i];
3852       element.appendChild(child);
3853     }
3854   },
3855
3856   prepend: function(element, node) {
3857     if (element.nodeType === NODE_TYPE_ELEMENT) {
3858       var index = element.firstChild;
3859       forEach(new JQLite(node), function(child) {
3860         element.insertBefore(child, index);
3861       });
3862     }
3863   },
3864
3865   wrap: function(element, wrapNode) {
3866     jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
3867   },
3868
3869   remove: jqLiteRemove,
3870
3871   detach: function(element) {
3872     jqLiteRemove(element, true);
3873   },
3874
3875   after: function(element, newElement) {
3876     var index = element, parent = element.parentNode;
3877
3878     if (parent) {
3879       newElement = new JQLite(newElement);
3880
3881       for (var i = 0, ii = newElement.length; i < ii; i++) {
3882         var node = newElement[i];
3883         parent.insertBefore(node, index.nextSibling);
3884         index = node;
3885       }
3886     }
3887   },
3888
3889   addClass: jqLiteAddClass,
3890   removeClass: jqLiteRemoveClass,
3891
3892   toggleClass: function(element, selector, condition) {
3893     if (selector) {
3894       forEach(selector.split(' '), function(className) {
3895         var classCondition = condition;
3896         if (isUndefined(classCondition)) {
3897           classCondition = !jqLiteHasClass(element, className);
3898         }
3899         (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3900       });
3901     }
3902   },
3903
3904   parent: function(element) {
3905     var parent = element.parentNode;
3906     return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3907   },
3908
3909   next: function(element) {
3910     return element.nextElementSibling;
3911   },
3912
3913   find: function(element, selector) {
3914     if (element.getElementsByTagName) {
3915       return element.getElementsByTagName(selector);
3916     } else {
3917       return [];
3918     }
3919   },
3920
3921   clone: jqLiteClone,
3922
3923   triggerHandler: function(element, event, extraParameters) {
3924
3925     var dummyEvent, eventFnsCopy, handlerArgs;
3926     var eventName = event.type || event;
3927     var expandoStore = jqLiteExpandoStore(element);
3928     var events = expandoStore && expandoStore.events;
3929     var eventFns = events && events[eventName];
3930
3931     if (eventFns) {
3932       // Create a dummy event to pass to the handlers
3933       dummyEvent = {
3934         preventDefault: function() { this.defaultPrevented = true; },
3935         isDefaultPrevented: function() { return this.defaultPrevented === true; },
3936         stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3937         isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3938         stopPropagation: noop,
3939         type: eventName,
3940         target: element
3941       };
3942
3943       // If a custom event was provided then extend our dummy event with it
3944       if (event.type) {
3945         dummyEvent = extend(dummyEvent, event);
3946       }
3947
3948       // Copy event handlers in case event handlers array is modified during execution.
3949       eventFnsCopy = shallowCopy(eventFns);
3950       handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3951
3952       forEach(eventFnsCopy, function(fn) {
3953         if (!dummyEvent.isImmediatePropagationStopped()) {
3954           fn.apply(element, handlerArgs);
3955         }
3956       });
3957     }
3958   }
3959 }, function(fn, name) {
3960   /**
3961    * chaining functions
3962    */
3963   JQLite.prototype[name] = function(arg1, arg2, arg3) {
3964     var value;
3965
3966     for (var i = 0, ii = this.length; i < ii; i++) {
3967       if (isUndefined(value)) {
3968         value = fn(this[i], arg1, arg2, arg3);
3969         if (isDefined(value)) {
3970           // any function which returns a value needs to be wrapped
3971           value = jqLite(value);
3972         }
3973       } else {
3974         jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3975       }
3976     }
3977     return isDefined(value) ? value : this;
3978   };
3979 });
3980
3981 // bind legacy bind/unbind to on/off
3982 JQLite.prototype.bind = JQLite.prototype.on;
3983 JQLite.prototype.unbind = JQLite.prototype.off;
3984
3985
3986 // Provider for private $$jqLite service
3987 /** @this */
3988 function $$jqLiteProvider() {
3989   this.$get = function $$jqLite() {
3990     return extend(JQLite, {
3991       hasClass: function(node, classes) {
3992         if (node.attr) node = node[0];
3993         return jqLiteHasClass(node, classes);
3994       },
3995       addClass: function(node, classes) {
3996         if (node.attr) node = node[0];
3997         return jqLiteAddClass(node, classes);
3998       },
3999       removeClass: function(node, classes) {
4000         if (node.attr) node = node[0];
4001         return jqLiteRemoveClass(node, classes);
4002       }
4003     });
4004   };
4005 }
4006
4007 /**
4008  * Computes a hash of an 'obj'.
4009  * Hash of a:
4010  *  string is string
4011  *  number is number as string
4012  *  object is either result of calling $$hashKey function on the object or uniquely generated id,
4013  *         that is also assigned to the $$hashKey property of the object.
4014  *
4015  * @param obj
4016  * @returns {string} hash string such that the same input will have the same hash string.
4017  *         The resulting string key is in 'type:hashKey' format.
4018  */
4019 function hashKey(obj, nextUidFn) {
4020   var key = obj && obj.$$hashKey;
4021
4022   if (key) {
4023     if (typeof key === 'function') {
4024       key = obj.$$hashKey();
4025     }
4026     return key;
4027   }
4028
4029   var objType = typeof obj;
4030   if (objType === 'function' || (objType === 'object' && obj !== null)) {
4031     key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
4032   } else {
4033     key = objType + ':' + obj;
4034   }
4035
4036   return key;
4037 }
4038
4039 // A minimal ES2015 Map implementation.
4040 // Should be bug/feature equivalent to the native implementations of supported browsers
4041 // (for the features required in Angular).
4042 // See https://kangax.github.io/compat-table/es6/#test-Map
4043 var nanKey = Object.create(null);
4044 function NgMapShim() {
4045   this._keys = [];
4046   this._values = [];
4047   this._lastKey = NaN;
4048   this._lastIndex = -1;
4049 }
4050 NgMapShim.prototype = {
4051   _idx: function(key) {
4052     if (key === this._lastKey) {
4053       return this._lastIndex;
4054     }
4055     this._lastKey = key;
4056     this._lastIndex = this._keys.indexOf(key);
4057     return this._lastIndex;
4058   },
4059   _transformKey: function(key) {
4060     return isNumberNaN(key) ? nanKey : key;
4061   },
4062   get: function(key) {
4063     key = this._transformKey(key);
4064     var idx = this._idx(key);
4065     if (idx !== -1) {
4066       return this._values[idx];
4067     }
4068   },
4069   set: function(key, value) {
4070     key = this._transformKey(key);
4071     var idx = this._idx(key);
4072     if (idx === -1) {
4073       idx = this._lastIndex = this._keys.length;
4074     }
4075     this._keys[idx] = key;
4076     this._values[idx] = value;
4077
4078     // Support: IE11
4079     // Do not `return this` to simulate the partial IE11 implementation
4080   },
4081   delete: function(key) {
4082     key = this._transformKey(key);
4083     var idx = this._idx(key);
4084     if (idx === -1) {
4085       return false;
4086     }
4087     this._keys.splice(idx, 1);
4088     this._values.splice(idx, 1);
4089     this._lastKey = NaN;
4090     this._lastIndex = -1;
4091     return true;
4092   }
4093 };
4094
4095 // For now, always use `NgMapShim`, even if `window.Map` is available. Some native implementations
4096 // are still buggy (often in subtle ways) and can cause hard-to-debug failures. When native `Map`
4097 // implementations get more stable, we can reconsider switching to `window.Map` (when available).
4098 var NgMap = NgMapShim;
4099
4100 var $$MapProvider = [/** @this */function() {
4101   this.$get = [function() {
4102     return NgMap;
4103   }];
4104 }];
4105
4106 /**
4107  * @ngdoc function
4108  * @module ng
4109  * @name angular.injector
4110  * @kind function
4111  *
4112  * @description
4113  * Creates an injector object that can be used for retrieving services as well as for
4114  * dependency injection (see {@link guide/di dependency injection}).
4115  *
4116  * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
4117  *     {@link angular.module}. The `ng` module must be explicitly added.
4118  * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
4119  *     disallows argument name annotation inference.
4120  * @returns {injector} Injector object. See {@link auto.$injector $injector}.
4121  *
4122  * @example
4123  * Typical usage
4124  * ```js
4125  *   // create an injector
4126  *   var $injector = angular.injector(['ng']);
4127  *
4128  *   // use the injector to kick off your application
4129  *   // use the type inference to auto inject arguments, or use implicit injection
4130  *   $injector.invoke(function($rootScope, $compile, $document) {
4131  *     $compile($document)($rootScope);
4132  *     $rootScope.$digest();
4133  *   });
4134  * ```
4135  *
4136  * Sometimes you want to get access to the injector of a currently running Angular app
4137  * from outside Angular. Perhaps, you want to inject and compile some markup after the
4138  * application has been bootstrapped. You can do this using the extra `injector()` added
4139  * to JQuery/jqLite elements. See {@link angular.element}.
4140  *
4141  * *This is fairly rare but could be the case if a third party library is injecting the
4142  * markup.*
4143  *
4144  * In the following example a new block of HTML containing a `ng-controller`
4145  * directive is added to the end of the document body by JQuery. We then compile and link
4146  * it into the current AngularJS scope.
4147  *
4148  * ```js
4149  * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
4150  * $(document.body).append($div);
4151  *
4152  * angular.element(document).injector().invoke(function($compile) {
4153  *   var scope = angular.element($div).scope();
4154  *   $compile($div)(scope);
4155  * });
4156  * ```
4157  */
4158
4159
4160 /**
4161  * @ngdoc module
4162  * @name auto
4163  * @installation
4164  * @description
4165  *
4166  * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
4167  */
4168
4169 var ARROW_ARG = /^([^(]+?)=>/;
4170 var FN_ARGS = /^[^(]*\(\s*([^)]*)\)/m;
4171 var FN_ARG_SPLIT = /,/;
4172 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
4173 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
4174 var $injectorMinErr = minErr('$injector');
4175
4176 function stringifyFn(fn) {
4177   return Function.prototype.toString.call(fn);
4178 }
4179
4180 function extractArgs(fn) {
4181   var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''),
4182       args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
4183   return args;
4184 }
4185
4186 function anonFn(fn) {
4187   // For anonymous functions, showing at the very least the function signature can help in
4188   // debugging.
4189   var args = extractArgs(fn);
4190   if (args) {
4191     return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
4192   }
4193   return 'fn';
4194 }
4195
4196 function annotate(fn, strictDi, name) {
4197   var $inject,
4198       argDecl,
4199       last;
4200
4201   if (typeof fn === 'function') {
4202     if (!($inject = fn.$inject)) {
4203       $inject = [];
4204       if (fn.length) {
4205         if (strictDi) {
4206           if (!isString(name) || !name) {
4207             name = fn.name || anonFn(fn);
4208           }
4209           throw $injectorMinErr('strictdi',
4210             '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
4211         }
4212         argDecl = extractArgs(fn);
4213         forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
4214           arg.replace(FN_ARG, function(all, underscore, name) {
4215             $inject.push(name);
4216           });
4217         });
4218       }
4219       fn.$inject = $inject;
4220     }
4221   } else if (isArray(fn)) {
4222     last = fn.length - 1;
4223     assertArgFn(fn[last], 'fn');
4224     $inject = fn.slice(0, last);
4225   } else {
4226     assertArgFn(fn, 'fn', true);
4227   }
4228   return $inject;
4229 }
4230
4231 ///////////////////////////////////////
4232
4233 /**
4234  * @ngdoc service
4235  * @name $injector
4236  *
4237  * @description
4238  *
4239  * `$injector` is used to retrieve object instances as defined by
4240  * {@link auto.$provide provider}, instantiate types, invoke methods,
4241  * and load modules.
4242  *
4243  * The following always holds true:
4244  *
4245  * ```js
4246  *   var $injector = angular.injector();
4247  *   expect($injector.get('$injector')).toBe($injector);
4248  *   expect($injector.invoke(function($injector) {
4249  *     return $injector;
4250  *   })).toBe($injector);
4251  * ```
4252  *
4253  * # Injection Function Annotation
4254  *
4255  * JavaScript does not have annotations, and annotations are needed for dependency injection. The
4256  * following are all valid ways of annotating function with injection arguments and are equivalent.
4257  *
4258  * ```js
4259  *   // inferred (only works if code not minified/obfuscated)
4260  *   $injector.invoke(function(serviceA){});
4261  *
4262  *   // annotated
4263  *   function explicit(serviceA) {};
4264  *   explicit.$inject = ['serviceA'];
4265  *   $injector.invoke(explicit);
4266  *
4267  *   // inline
4268  *   $injector.invoke(['serviceA', function(serviceA){}]);
4269  * ```
4270  *
4271  * ## Inference
4272  *
4273  * In JavaScript calling `toString()` on a function returns the function definition. The definition
4274  * can then be parsed and the function arguments can be extracted. This method of discovering
4275  * annotations is disallowed when the injector is in strict mode.
4276  * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
4277  * argument names.
4278  *
4279  * ## `$inject` Annotation
4280  * By adding an `$inject` property onto a function the injection parameters can be specified.
4281  *
4282  * ## Inline
4283  * As an array of injection names, where the last item in the array is the function to call.
4284  */
4285
4286 /**
4287  * @ngdoc property
4288  * @name $injector#modules
4289  * @type {Object}
4290  * @description
4291  * A hash containing all the modules that have been loaded into the
4292  * $injector.
4293  *
4294  * You can use this property to find out information about a module via the
4295  * {@link angular.Module#info `myModule.info(...)`} method.
4296  *
4297  * For example:
4298  *
4299  * ```
4300  * var info = $injector.modules['ngAnimate'].info();
4301  * ```
4302  *
4303  * **Do not use this property to attempt to modify the modules after the application
4304  * has been bootstrapped.**
4305  */
4306
4307
4308 /**
4309  * @ngdoc method
4310  * @name $injector#get
4311  *
4312  * @description
4313  * Return an instance of the service.
4314  *
4315  * @param {string} name The name of the instance to retrieve.
4316  * @param {string=} caller An optional string to provide the origin of the function call for error messages.
4317  * @return {*} The instance.
4318  */
4319
4320 /**
4321  * @ngdoc method
4322  * @name $injector#invoke
4323  *
4324  * @description
4325  * Invoke the method and supply the method arguments from the `$injector`.
4326  *
4327  * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
4328  *   injected according to the {@link guide/di $inject Annotation} rules.
4329  * @param {Object=} self The `this` for the invoked method.
4330  * @param {Object=} locals Optional object. If preset then any argument names are read from this
4331  *                         object first, before the `$injector` is consulted.
4332  * @returns {*} the value returned by the invoked `fn` function.
4333  */
4334
4335 /**
4336  * @ngdoc method
4337  * @name $injector#has
4338  *
4339  * @description
4340  * Allows the user to query if the particular service exists.
4341  *
4342  * @param {string} name Name of the service to query.
4343  * @returns {boolean} `true` if injector has given service.
4344  */
4345
4346 /**
4347  * @ngdoc method
4348  * @name $injector#instantiate
4349  * @description
4350  * Create a new instance of JS type. The method takes a constructor function, invokes the new
4351  * operator, and supplies all of the arguments to the constructor function as specified by the
4352  * constructor annotation.
4353  *
4354  * @param {Function} Type Annotated constructor function.
4355  * @param {Object=} locals Optional object. If preset then any argument names are read from this
4356  * object first, before the `$injector` is consulted.
4357  * @returns {Object} new instance of `Type`.
4358  */
4359
4360 /**
4361  * @ngdoc method
4362  * @name $injector#annotate
4363  *
4364  * @description
4365  * Returns an array of service names which the function is requesting for injection. This API is
4366  * used by the injector to determine which services need to be injected into the function when the
4367  * function is invoked. There are three ways in which the function can be annotated with the needed
4368  * dependencies.
4369  *
4370  * # Argument names
4371  *
4372  * The simplest form is to extract the dependencies from the arguments of the function. This is done
4373  * by converting the function into a string using `toString()` method and extracting the argument
4374  * names.
4375  * ```js
4376  *   // Given
4377  *   function MyController($scope, $route) {
4378  *     // ...
4379  *   }
4380  *
4381  *   // Then
4382  *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4383  * ```
4384  *
4385  * You can disallow this method by using strict injection mode.
4386  *
4387  * This method does not work with code minification / obfuscation. For this reason the following
4388  * annotation strategies are supported.
4389  *
4390  * # The `$inject` property
4391  *
4392  * If a function has an `$inject` property and its value is an array of strings, then the strings
4393  * represent names of services to be injected into the function.
4394  * ```js
4395  *   // Given
4396  *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
4397  *     // ...
4398  *   }
4399  *   // Define function dependencies
4400  *   MyController['$inject'] = ['$scope', '$route'];
4401  *
4402  *   // Then
4403  *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4404  * ```
4405  *
4406  * # The array notation
4407  *
4408  * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4409  * is very inconvenient. In these situations using the array notation to specify the dependencies in
4410  * a way that survives minification is a better choice:
4411  *
4412  * ```js
4413  *   // We wish to write this (not minification / obfuscation safe)
4414  *   injector.invoke(function($compile, $rootScope) {
4415  *     // ...
4416  *   });
4417  *
4418  *   // We are forced to write break inlining
4419  *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4420  *     // ...
4421  *   };
4422  *   tmpFn.$inject = ['$compile', '$rootScope'];
4423  *   injector.invoke(tmpFn);
4424  *
4425  *   // To better support inline function the inline annotation is supported
4426  *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4427  *     // ...
4428  *   }]);
4429  *
4430  *   // Therefore
4431  *   expect(injector.annotate(
4432  *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4433  *    ).toEqual(['$compile', '$rootScope']);
4434  * ```
4435  *
4436  * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4437  * be retrieved as described above.
4438  *
4439  * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4440  *
4441  * @returns {Array.<string>} The names of the services which the function requires.
4442  */
4443
4444
4445
4446 /**
4447  * @ngdoc service
4448  * @name $provide
4449  *
4450  * @description
4451  *
4452  * The {@link auto.$provide $provide} service has a number of methods for registering components
4453  * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4454  * {@link angular.Module}.
4455  *
4456  * An Angular **service** is a singleton object created by a **service factory**.  These **service
4457  * factories** are functions which, in turn, are created by a **service provider**.
4458  * The **service providers** are constructor functions. When instantiated they must contain a
4459  * property called `$get`, which holds the **service factory** function.
4460  *
4461  * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4462  * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4463  * function to get the instance of the **service**.
4464  *
4465  * Often services have no configuration options and there is no need to add methods to the service
4466  * provider.  The provider will be no more than a constructor function with a `$get` property. For
4467  * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4468  * services without specifying a provider.
4469  *
4470  * * {@link auto.$provide#provider provider(name, provider)} - registers a **service provider** with the
4471  *     {@link auto.$injector $injector}
4472  * * {@link auto.$provide#constant constant(name, obj)} - registers a value/object that can be accessed by
4473  *     providers and services.
4474  * * {@link auto.$provide#value value(name, obj)} - registers a value/object that can only be accessed by
4475  *     services, not providers.
4476  * * {@link auto.$provide#factory factory(name, fn)} - registers a service **factory function**
4477  *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
4478  *     given factory function.
4479  * * {@link auto.$provide#service service(name, Fn)} - registers a **constructor function**
4480  *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4481  *      a new object using the given constructor function.
4482  * * {@link auto.$provide#decorator decorator(name, decorFn)} - registers a **decorator function** that
4483  *      will be able to modify or replace the implementation of another service.
4484  *
4485  * See the individual methods for more information and examples.
4486  */
4487
4488 /**
4489  * @ngdoc method
4490  * @name $provide#provider
4491  * @description
4492  *
4493  * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4494  * are constructor functions, whose instances are responsible for "providing" a factory for a
4495  * service.
4496  *
4497  * Service provider names start with the name of the service they provide followed by `Provider`.
4498  * For example, the {@link ng.$log $log} service has a provider called
4499  * {@link ng.$logProvider $logProvider}.
4500  *
4501  * Service provider objects can have additional methods which allow configuration of the provider
4502  * and its service. Importantly, you can configure what kind of service is created by the `$get`
4503  * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4504  * method {@link ng.$logProvider#debugEnabled debugEnabled}
4505  * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4506  * console or not.
4507  *
4508  * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4509                         'Provider'` key.
4510  * @param {(Object|function())} provider If the provider is:
4511  *
4512  *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4513  *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4514  *   - `Constructor`: a new instance of the provider will be created using
4515  *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4516  *
4517  * @returns {Object} registered provider instance
4518
4519  * @example
4520  *
4521  * The following example shows how to create a simple event tracking service and register it using
4522  * {@link auto.$provide#provider $provide.provider()}.
4523  *
4524  * ```js
4525  *  // Define the eventTracker provider
4526  *  function EventTrackerProvider() {
4527  *    var trackingUrl = '/track';
4528  *
4529  *    // A provider method for configuring where the tracked events should been saved
4530  *    this.setTrackingUrl = function(url) {
4531  *      trackingUrl = url;
4532  *    };
4533  *
4534  *    // The service factory function
4535  *    this.$get = ['$http', function($http) {
4536  *      var trackedEvents = {};
4537  *      return {
4538  *        // Call this to track an event
4539  *        event: function(event) {
4540  *          var count = trackedEvents[event] || 0;
4541  *          count += 1;
4542  *          trackedEvents[event] = count;
4543  *          return count;
4544  *        },
4545  *        // Call this to save the tracked events to the trackingUrl
4546  *        save: function() {
4547  *          $http.post(trackingUrl, trackedEvents);
4548  *        }
4549  *      };
4550  *    }];
4551  *  }
4552  *
4553  *  describe('eventTracker', function() {
4554  *    var postSpy;
4555  *
4556  *    beforeEach(module(function($provide) {
4557  *      // Register the eventTracker provider
4558  *      $provide.provider('eventTracker', EventTrackerProvider);
4559  *    }));
4560  *
4561  *    beforeEach(module(function(eventTrackerProvider) {
4562  *      // Configure eventTracker provider
4563  *      eventTrackerProvider.setTrackingUrl('/custom-track');
4564  *    }));
4565  *
4566  *    it('tracks events', inject(function(eventTracker) {
4567  *      expect(eventTracker.event('login')).toEqual(1);
4568  *      expect(eventTracker.event('login')).toEqual(2);
4569  *    }));
4570  *
4571  *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4572  *      postSpy = spyOn($http, 'post');
4573  *      eventTracker.event('login');
4574  *      eventTracker.save();
4575  *      expect(postSpy).toHaveBeenCalled();
4576  *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4577  *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4578  *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4579  *    }));
4580  *  });
4581  * ```
4582  */
4583
4584 /**
4585  * @ngdoc method
4586  * @name $provide#factory
4587  * @description
4588  *
4589  * Register a **service factory**, which will be called to return the service instance.
4590  * This is short for registering a service where its provider consists of only a `$get` property,
4591  * which is the given service factory function.
4592  * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4593  * configure your service in a provider.
4594  *
4595  * @param {string} name The name of the instance.
4596  * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4597  *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4598  * @returns {Object} registered provider instance
4599  *
4600  * @example
4601  * Here is an example of registering a service
4602  * ```js
4603  *   $provide.factory('ping', ['$http', function($http) {
4604  *     return function ping() {
4605  *       return $http.send('/ping');
4606  *     };
4607  *   }]);
4608  * ```
4609  * You would then inject and use this service like this:
4610  * ```js
4611  *   someModule.controller('Ctrl', ['ping', function(ping) {
4612  *     ping();
4613  *   }]);
4614  * ```
4615  */
4616
4617
4618 /**
4619  * @ngdoc method
4620  * @name $provide#service
4621  * @description
4622  *
4623  * Register a **service constructor**, which will be invoked with `new` to create the service
4624  * instance.
4625  * This is short for registering a service where its provider's `$get` property is a factory
4626  * function that returns an instance instantiated by the injector from the service constructor
4627  * function.
4628  *
4629  * Internally it looks a bit like this:
4630  *
4631  * ```
4632  * {
4633  *   $get: function() {
4634  *     return $injector.instantiate(constructor);
4635  *   }
4636  * }
4637  * ```
4638  *
4639  *
4640  * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4641  * as a type/class.
4642  *
4643  * @param {string} name The name of the instance.
4644  * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4645  *     that will be instantiated.
4646  * @returns {Object} registered provider instance
4647  *
4648  * @example
4649  * Here is an example of registering a service using
4650  * {@link auto.$provide#service $provide.service(class)}.
4651  * ```js
4652  *   var Ping = function($http) {
4653  *     this.$http = $http;
4654  *   };
4655  *
4656  *   Ping.$inject = ['$http'];
4657  *
4658  *   Ping.prototype.send = function() {
4659  *     return this.$http.get('/ping');
4660  *   };
4661  *   $provide.service('ping', Ping);
4662  * ```
4663  * You would then inject and use this service like this:
4664  * ```js
4665  *   someModule.controller('Ctrl', ['ping', function(ping) {
4666  *     ping.send();
4667  *   }]);
4668  * ```
4669  */
4670
4671
4672 /**
4673  * @ngdoc method
4674  * @name $provide#value
4675  * @description
4676  *
4677  * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4678  * number, an array, an object or a function. This is short for registering a service where its
4679  * provider's `$get` property is a factory function that takes no arguments and returns the **value
4680  * service**. That also means it is not possible to inject other services into a value service.
4681  *
4682  * Value services are similar to constant services, except that they cannot be injected into a
4683  * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4684  * an Angular {@link auto.$provide#decorator decorator}.
4685  *
4686  * @param {string} name The name of the instance.
4687  * @param {*} value The value.
4688  * @returns {Object} registered provider instance
4689  *
4690  * @example
4691  * Here are some examples of creating value services.
4692  * ```js
4693  *   $provide.value('ADMIN_USER', 'admin');
4694  *
4695  *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4696  *
4697  *   $provide.value('halfOf', function(value) {
4698  *     return value / 2;
4699  *   });
4700  * ```
4701  */
4702
4703
4704 /**
4705  * @ngdoc method
4706  * @name $provide#constant
4707  * @description
4708  *
4709  * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
4710  * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
4711  * possible to inject other services into a constant.
4712  *
4713  * But unlike {@link auto.$provide#value value}, a constant can be
4714  * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4715  * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4716  *
4717  * @param {string} name The name of the constant.
4718  * @param {*} value The constant value.
4719  * @returns {Object} registered instance
4720  *
4721  * @example
4722  * Here a some examples of creating constants:
4723  * ```js
4724  *   $provide.constant('SHARD_HEIGHT', 306);
4725  *
4726  *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4727  *
4728  *   $provide.constant('double', function(value) {
4729  *     return value * 2;
4730  *   });
4731  * ```
4732  */
4733
4734
4735 /**
4736  * @ngdoc method
4737  * @name $provide#decorator
4738  * @description
4739  *
4740  * Register a **decorator function** with the {@link auto.$injector $injector}. A decorator function
4741  * intercepts the creation of a service, allowing it to override or modify the behavior of the
4742  * service. The return value of the decorator function may be the original service, or a new service
4743  * that replaces (or wraps and delegates to) the original service.
4744  *
4745  * You can find out more about using decorators in the {@link guide/decorators} guide.
4746  *
4747  * @param {string} name The name of the service to decorate.
4748  * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4749  *    provided and should return the decorated service instance. The function is called using
4750  *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4751  *    Local injection arguments:
4752  *
4753  *    * `$delegate` - The original service instance, which can be replaced, monkey patched, configured,
4754  *      decorated or delegated to.
4755  *
4756  * @example
4757  * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4758  * calls to {@link ng.$log#error $log.warn()}.
4759  * ```js
4760  *   $provide.decorator('$log', ['$delegate', function($delegate) {
4761  *     $delegate.warn = $delegate.error;
4762  *     return $delegate;
4763  *   }]);
4764  * ```
4765  */
4766
4767
4768 function createInjector(modulesToLoad, strictDi) {
4769   strictDi = (strictDi === true);
4770   var INSTANTIATING = {},
4771       providerSuffix = 'Provider',
4772       path = [],
4773       loadedModules = new NgMap(),
4774       providerCache = {
4775         $provide: {
4776             provider: supportObject(provider),
4777             factory: supportObject(factory),
4778             service: supportObject(service),
4779             value: supportObject(value),
4780             constant: supportObject(constant),
4781             decorator: decorator
4782           }
4783       },
4784       providerInjector = (providerCache.$injector =
4785           createInternalInjector(providerCache, function(serviceName, caller) {
4786             if (angular.isString(caller)) {
4787               path.push(caller);
4788             }
4789             throw $injectorMinErr('unpr', 'Unknown provider: {0}', path.join(' <- '));
4790           })),
4791       instanceCache = {},
4792       protoInstanceInjector =
4793           createInternalInjector(instanceCache, function(serviceName, caller) {
4794             var provider = providerInjector.get(serviceName + providerSuffix, caller);
4795             return instanceInjector.invoke(
4796                 provider.$get, provider, undefined, serviceName);
4797           }),
4798       instanceInjector = protoInstanceInjector;
4799
4800   providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
4801   instanceInjector.modules = providerInjector.modules = createMap();
4802   var runBlocks = loadModules(modulesToLoad);
4803   instanceInjector = protoInstanceInjector.get('$injector');
4804   instanceInjector.strictDi = strictDi;
4805   forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
4806
4807   return instanceInjector;
4808
4809   ////////////////////////////////////
4810   // $provider
4811   ////////////////////////////////////
4812
4813   function supportObject(delegate) {
4814     return function(key, value) {
4815       if (isObject(key)) {
4816         forEach(key, reverseParams(delegate));
4817       } else {
4818         return delegate(key, value);
4819       }
4820     };
4821   }
4822
4823   function provider(name, provider_) {
4824     assertNotHasOwnProperty(name, 'service');
4825     if (isFunction(provider_) || isArray(provider_)) {
4826       provider_ = providerInjector.instantiate(provider_);
4827     }
4828     if (!provider_.$get) {
4829       throw $injectorMinErr('pget', 'Provider \'{0}\' must define $get factory method.', name);
4830     }
4831     return (providerCache[name + providerSuffix] = provider_);
4832   }
4833
4834   function enforceReturnValue(name, factory) {
4835     return /** @this */ function enforcedReturnValue() {
4836       var result = instanceInjector.invoke(factory, this);
4837       if (isUndefined(result)) {
4838         throw $injectorMinErr('undef', 'Provider \'{0}\' must return a value from $get factory method.', name);
4839       }
4840       return result;
4841     };
4842   }
4843
4844   function factory(name, factoryFn, enforce) {
4845     return provider(name, {
4846       $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4847     });
4848   }
4849
4850   function service(name, constructor) {
4851     return factory(name, ['$injector', function($injector) {
4852       return $injector.instantiate(constructor);
4853     }]);
4854   }
4855
4856   function value(name, val) { return factory(name, valueFn(val), false); }
4857
4858   function constant(name, value) {
4859     assertNotHasOwnProperty(name, 'constant');
4860     providerCache[name] = value;
4861     instanceCache[name] = value;
4862   }
4863
4864   function decorator(serviceName, decorFn) {
4865     var origProvider = providerInjector.get(serviceName + providerSuffix),
4866         orig$get = origProvider.$get;
4867
4868     origProvider.$get = function() {
4869       var origInstance = instanceInjector.invoke(orig$get, origProvider);
4870       return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4871     };
4872   }
4873
4874   ////////////////////////////////////
4875   // Module Loading
4876   ////////////////////////////////////
4877   function loadModules(modulesToLoad) {
4878     assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4879     var runBlocks = [], moduleFn;
4880     forEach(modulesToLoad, function(module) {
4881       if (loadedModules.get(module)) return;
4882       loadedModules.set(module, true);
4883
4884       function runInvokeQueue(queue) {
4885         var i, ii;
4886         for (i = 0, ii = queue.length; i < ii; i++) {
4887           var invokeArgs = queue[i],
4888               provider = providerInjector.get(invokeArgs[0]);
4889
4890           provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4891         }
4892       }
4893
4894       try {
4895         if (isString(module)) {
4896           moduleFn = angularModule(module);
4897           instanceInjector.modules[module] = moduleFn;
4898           runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4899           runInvokeQueue(moduleFn._invokeQueue);
4900           runInvokeQueue(moduleFn._configBlocks);
4901         } else if (isFunction(module)) {
4902             runBlocks.push(providerInjector.invoke(module));
4903         } else if (isArray(module)) {
4904             runBlocks.push(providerInjector.invoke(module));
4905         } else {
4906           assertArgFn(module, 'module');
4907         }
4908       } catch (e) {
4909         if (isArray(module)) {
4910           module = module[module.length - 1];
4911         }
4912         if (e.message && e.stack && e.stack.indexOf(e.message) === -1) {
4913           // Safari & FF's stack traces don't contain error.message content
4914           // unlike those of Chrome and IE
4915           // So if stack doesn't contain message, we create a new string that contains both.
4916           // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4917           // eslint-disable-next-line no-ex-assign
4918           e = e.message + '\n' + e.stack;
4919         }
4920         throw $injectorMinErr('modulerr', 'Failed to instantiate module {0} due to:\n{1}',
4921                   module, e.stack || e.message || e);
4922       }
4923     });
4924     return runBlocks;
4925   }
4926
4927   ////////////////////////////////////
4928   // internal Injector
4929   ////////////////////////////////////
4930
4931   function createInternalInjector(cache, factory) {
4932
4933     function getService(serviceName, caller) {
4934       if (cache.hasOwnProperty(serviceName)) {
4935         if (cache[serviceName] === INSTANTIATING) {
4936           throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4937                     serviceName + ' <- ' + path.join(' <- '));
4938         }
4939         return cache[serviceName];
4940       } else {
4941         try {
4942           path.unshift(serviceName);
4943           cache[serviceName] = INSTANTIATING;
4944           cache[serviceName] = factory(serviceName, caller);
4945           return cache[serviceName];
4946         } catch (err) {
4947           if (cache[serviceName] === INSTANTIATING) {
4948             delete cache[serviceName];
4949           }
4950           throw err;
4951         } finally {
4952           path.shift();
4953         }
4954       }
4955     }
4956
4957
4958     function injectionArgs(fn, locals, serviceName) {
4959       var args = [],
4960           $inject = createInjector.$$annotate(fn, strictDi, serviceName);
4961
4962       for (var i = 0, length = $inject.length; i < length; i++) {
4963         var key = $inject[i];
4964         if (typeof key !== 'string') {
4965           throw $injectorMinErr('itkn',
4966                   'Incorrect injection token! Expected service name as string, got {0}', key);
4967         }
4968         args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
4969                                                          getService(key, serviceName));
4970       }
4971       return args;
4972     }
4973
4974     function isClass(func) {
4975       // Support: IE 9-11 only
4976       // IE 9-11 do not support classes and IE9 leaks with the code below.
4977       if (msie || typeof func !== 'function') {
4978         return false;
4979       }
4980       var result = func.$$ngIsClass;
4981       if (!isBoolean(result)) {
4982         // Support: Edge 12-13 only
4983         // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/
4984         result = func.$$ngIsClass = /^(?:class\b|constructor\()/.test(stringifyFn(func));
4985       }
4986       return result;
4987     }
4988
4989     function invoke(fn, self, locals, serviceName) {
4990       if (typeof locals === 'string') {
4991         serviceName = locals;
4992         locals = null;
4993       }
4994
4995       var args = injectionArgs(fn, locals, serviceName);
4996       if (isArray(fn)) {
4997         fn = fn[fn.length - 1];
4998       }
4999
5000       if (!isClass(fn)) {
5001         // http://jsperf.com/angularjs-invoke-apply-vs-switch
5002         // #5388
5003         return fn.apply(self, args);
5004       } else {
5005         args.unshift(null);
5006         return new (Function.prototype.bind.apply(fn, args))();
5007       }
5008     }
5009
5010
5011     function instantiate(Type, locals, serviceName) {
5012       // Check if Type is annotated and use just the given function at n-1 as parameter
5013       // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
5014       var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
5015       var args = injectionArgs(Type, locals, serviceName);
5016       // Empty object at position 0 is ignored for invocation with `new`, but required.
5017       args.unshift(null);
5018       return new (Function.prototype.bind.apply(ctor, args))();
5019     }
5020
5021
5022     return {
5023       invoke: invoke,
5024       instantiate: instantiate,
5025       get: getService,
5026       annotate: createInjector.$$annotate,
5027       has: function(name) {
5028         return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
5029       }
5030     };
5031   }
5032 }
5033
5034 createInjector.$$annotate = annotate;
5035
5036 /**
5037  * @ngdoc provider
5038  * @name $anchorScrollProvider
5039  * @this
5040  *
5041  * @description
5042  * Use `$anchorScrollProvider` to disable automatic scrolling whenever
5043  * {@link ng.$location#hash $location.hash()} changes.
5044  */
5045 function $AnchorScrollProvider() {
5046
5047   var autoScrollingEnabled = true;
5048
5049   /**
5050    * @ngdoc method
5051    * @name $anchorScrollProvider#disableAutoScrolling
5052    *
5053    * @description
5054    * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
5055    * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
5056    * Use this method to disable automatic scrolling.
5057    *
5058    * If automatic scrolling is disabled, one must explicitly call
5059    * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
5060    * current hash.
5061    */
5062   this.disableAutoScrolling = function() {
5063     autoScrollingEnabled = false;
5064   };
5065
5066   /**
5067    * @ngdoc service
5068    * @name $anchorScroll
5069    * @kind function
5070    * @requires $window
5071    * @requires $location
5072    * @requires $rootScope
5073    *
5074    * @description
5075    * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
5076    * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
5077    * in the
5078    * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document).
5079    *
5080    * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
5081    * match any anchor whenever it changes. This can be disabled by calling
5082    * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
5083    *
5084    * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
5085    * vertical scroll-offset (either fixed or dynamic).
5086    *
5087    * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
5088    *                       {@link ng.$location#hash $location.hash()} will be used.
5089    *
5090    * @property {(number|function|jqLite)} yOffset
5091    * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
5092    * positioned elements at the top of the page, such as navbars, headers etc.
5093    *
5094    * `yOffset` can be specified in various ways:
5095    * - **number**: A fixed number of pixels to be used as offset.<br /><br />
5096    * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
5097    *   a number representing the offset (in pixels).<br /><br />
5098    * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
5099    *   the top of the page to the element's bottom will be used as offset.<br />
5100    *   **Note**: The element will be taken into account only as long as its `position` is set to
5101    *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
5102    *   their height and/or positioning according to the viewport's size.
5103    *
5104    * <br />
5105    * <div class="alert alert-warning">
5106    * In order for `yOffset` to work properly, scrolling should take place on the document's root and
5107    * not some child element.
5108    * </div>
5109    *
5110    * @example
5111      <example module="anchorScrollExample" name="anchor-scroll">
5112        <file name="index.html">
5113          <div id="scrollArea" ng-controller="ScrollController">
5114            <a ng-click="gotoBottom()">Go to bottom</a>
5115            <a id="bottom"></a> You're at the bottom!
5116          </div>
5117        </file>
5118        <file name="script.js">
5119          angular.module('anchorScrollExample', [])
5120            .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
5121              function($scope, $location, $anchorScroll) {
5122                $scope.gotoBottom = function() {
5123                  // set the location.hash to the id of
5124                  // the element you wish to scroll to.
5125                  $location.hash('bottom');
5126
5127                  // call $anchorScroll()
5128                  $anchorScroll();
5129                };
5130              }]);
5131        </file>
5132        <file name="style.css">
5133          #scrollArea {
5134            height: 280px;
5135            overflow: auto;
5136          }
5137
5138          #bottom {
5139            display: block;
5140            margin-top: 2000px;
5141          }
5142        </file>
5143      </example>
5144    *
5145    * <hr />
5146    * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
5147    * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
5148    *
5149    * @example
5150      <example module="anchorScrollOffsetExample" name="anchor-scroll-offset">
5151        <file name="index.html">
5152          <div class="fixed-header" ng-controller="headerCtrl">
5153            <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
5154              Go to anchor {{x}}
5155            </a>
5156          </div>
5157          <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
5158            Anchor {{x}} of 5
5159          </div>
5160        </file>
5161        <file name="script.js">
5162          angular.module('anchorScrollOffsetExample', [])
5163            .run(['$anchorScroll', function($anchorScroll) {
5164              $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
5165            }])
5166            .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
5167              function($anchorScroll, $location, $scope) {
5168                $scope.gotoAnchor = function(x) {
5169                  var newHash = 'anchor' + x;
5170                  if ($location.hash() !== newHash) {
5171                    // set the $location.hash to `newHash` and
5172                    // $anchorScroll will automatically scroll to it
5173                    $location.hash('anchor' + x);
5174                  } else {
5175                    // call $anchorScroll() explicitly,
5176                    // since $location.hash hasn't changed
5177                    $anchorScroll();
5178                  }
5179                };
5180              }
5181            ]);
5182        </file>
5183        <file name="style.css">
5184          body {
5185            padding-top: 50px;
5186          }
5187
5188          .anchor {
5189            border: 2px dashed DarkOrchid;
5190            padding: 10px 10px 200px 10px;
5191          }
5192
5193          .fixed-header {
5194            background-color: rgba(0, 0, 0, 0.2);
5195            height: 50px;
5196            position: fixed;
5197            top: 0; left: 0; right: 0;
5198          }
5199
5200          .fixed-header > a {
5201            display: inline-block;
5202            margin: 5px 15px;
5203          }
5204        </file>
5205      </example>
5206    */
5207   this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
5208     var document = $window.document;
5209
5210     // Helper function to get first anchor from a NodeList
5211     // (using `Array#some()` instead of `angular#forEach()` since it's more performant
5212     //  and working in all supported browsers.)
5213     function getFirstAnchor(list) {
5214       var result = null;
5215       Array.prototype.some.call(list, function(element) {
5216         if (nodeName_(element) === 'a') {
5217           result = element;
5218           return true;
5219         }
5220       });
5221       return result;
5222     }
5223
5224     function getYOffset() {
5225
5226       var offset = scroll.yOffset;
5227
5228       if (isFunction(offset)) {
5229         offset = offset();
5230       } else if (isElement(offset)) {
5231         var elem = offset[0];
5232         var style = $window.getComputedStyle(elem);
5233         if (style.position !== 'fixed') {
5234           offset = 0;
5235         } else {
5236           offset = elem.getBoundingClientRect().bottom;
5237         }
5238       } else if (!isNumber(offset)) {
5239         offset = 0;
5240       }
5241
5242       return offset;
5243     }
5244
5245     function scrollTo(elem) {
5246       if (elem) {
5247         elem.scrollIntoView();
5248
5249         var offset = getYOffset();
5250
5251         if (offset) {
5252           // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
5253           // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
5254           // top of the viewport.
5255           //
5256           // IF the number of pixels from the top of `elem` to the end of the page's content is less
5257           // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
5258           // way down the page.
5259           //
5260           // This is often the case for elements near the bottom of the page.
5261           //
5262           // In such cases we do not need to scroll the whole `offset` up, just the difference between
5263           // the top of the element and the offset, which is enough to align the top of `elem` at the
5264           // desired position.
5265           var elemTop = elem.getBoundingClientRect().top;
5266           $window.scrollBy(0, elemTop - offset);
5267         }
5268       } else {
5269         $window.scrollTo(0, 0);
5270       }
5271     }
5272
5273     function scroll(hash) {
5274       // Allow numeric hashes
5275       hash = isString(hash) ? hash : isNumber(hash) ? hash.toString() : $location.hash();
5276       var elm;
5277
5278       // empty hash, scroll to the top of the page
5279       if (!hash) scrollTo(null);
5280
5281       // element with given id
5282       else if ((elm = document.getElementById(hash))) scrollTo(elm);
5283
5284       // first anchor with given name :-D
5285       else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
5286
5287       // no element and hash === 'top', scroll to the top of the page
5288       else if (hash === 'top') scrollTo(null);
5289     }
5290
5291     // does not scroll when user clicks on anchor link that is currently on
5292     // (no url change, no $location.hash() change), browser native does scroll
5293     if (autoScrollingEnabled) {
5294       $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
5295         function autoScrollWatchAction(newVal, oldVal) {
5296           // skip the initial scroll if $location.hash is empty
5297           if (newVal === oldVal && newVal === '') return;
5298
5299           jqLiteDocumentLoaded(function() {
5300             $rootScope.$evalAsync(scroll);
5301           });
5302         });
5303     }
5304
5305     return scroll;
5306   }];
5307 }
5308
5309 var $animateMinErr = minErr('$animate');
5310 var ELEMENT_NODE = 1;
5311 var NG_ANIMATE_CLASSNAME = 'ng-animate';
5312
5313 function mergeClasses(a,b) {
5314   if (!a && !b) return '';
5315   if (!a) return b;
5316   if (!b) return a;
5317   if (isArray(a)) a = a.join(' ');
5318   if (isArray(b)) b = b.join(' ');
5319   return a + ' ' + b;
5320 }
5321
5322 function extractElementNode(element) {
5323   for (var i = 0; i < element.length; i++) {
5324     var elm = element[i];
5325     if (elm.nodeType === ELEMENT_NODE) {
5326       return elm;
5327     }
5328   }
5329 }
5330
5331 function splitClasses(classes) {
5332   if (isString(classes)) {
5333     classes = classes.split(' ');
5334   }
5335
5336   // Use createMap() to prevent class assumptions involving property names in
5337   // Object.prototype
5338   var obj = createMap();
5339   forEach(classes, function(klass) {
5340     // sometimes the split leaves empty string values
5341     // incase extra spaces were applied to the options
5342     if (klass.length) {
5343       obj[klass] = true;
5344     }
5345   });
5346   return obj;
5347 }
5348
5349 // if any other type of options value besides an Object value is
5350 // passed into the $animate.method() animation then this helper code
5351 // will be run which will ignore it. While this patch is not the
5352 // greatest solution to this, a lot of existing plugins depend on
5353 // $animate to either call the callback (< 1.2) or return a promise
5354 // that can be changed. This helper function ensures that the options
5355 // are wiped clean incase a callback function is provided.
5356 function prepareAnimateOptions(options) {
5357   return isObject(options)
5358       ? options
5359       : {};
5360 }
5361
5362 var $$CoreAnimateJsProvider = /** @this */ function() {
5363   this.$get = noop;
5364 };
5365
5366 // this is prefixed with Core since it conflicts with
5367 // the animateQueueProvider defined in ngAnimate/animateQueue.js
5368 var $$CoreAnimateQueueProvider = /** @this */ function() {
5369   var postDigestQueue = new NgMap();
5370   var postDigestElements = [];
5371
5372   this.$get = ['$$AnimateRunner', '$rootScope',
5373        function($$AnimateRunner,   $rootScope) {
5374     return {
5375       enabled: noop,
5376       on: noop,
5377       off: noop,
5378       pin: noop,
5379
5380       push: function(element, event, options, domOperation) {
5381         if (domOperation) {
5382           domOperation();
5383         }
5384
5385         options = options || {};
5386         if (options.from) {
5387           element.css(options.from);
5388         }
5389         if (options.to) {
5390           element.css(options.to);
5391         }
5392
5393         if (options.addClass || options.removeClass) {
5394           addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
5395         }
5396
5397         var runner = new $$AnimateRunner();
5398
5399         // since there are no animations to run the runner needs to be
5400         // notified that the animation call is complete.
5401         runner.complete();
5402         return runner;
5403       }
5404     };
5405
5406
5407     function updateData(data, classes, value) {
5408       var changed = false;
5409       if (classes) {
5410         classes = isString(classes) ? classes.split(' ') :
5411                   isArray(classes) ? classes : [];
5412         forEach(classes, function(className) {
5413           if (className) {
5414             changed = true;
5415             data[className] = value;
5416           }
5417         });
5418       }
5419       return changed;
5420     }
5421
5422     function handleCSSClassChanges() {
5423       forEach(postDigestElements, function(element) {
5424         var data = postDigestQueue.get(element);
5425         if (data) {
5426           var existing = splitClasses(element.attr('class'));
5427           var toAdd = '';
5428           var toRemove = '';
5429           forEach(data, function(status, className) {
5430             var hasClass = !!existing[className];
5431             if (status !== hasClass) {
5432               if (status) {
5433                 toAdd += (toAdd.length ? ' ' : '') + className;
5434               } else {
5435                 toRemove += (toRemove.length ? ' ' : '') + className;
5436               }
5437             }
5438           });
5439
5440           forEach(element, function(elm) {
5441             if (toAdd) {
5442               jqLiteAddClass(elm, toAdd);
5443             }
5444             if (toRemove) {
5445               jqLiteRemoveClass(elm, toRemove);
5446             }
5447           });
5448           postDigestQueue.delete(element);
5449         }
5450       });
5451       postDigestElements.length = 0;
5452     }
5453
5454
5455     function addRemoveClassesPostDigest(element, add, remove) {
5456       var data = postDigestQueue.get(element) || {};
5457
5458       var classesAdded = updateData(data, add, true);
5459       var classesRemoved = updateData(data, remove, false);
5460
5461       if (classesAdded || classesRemoved) {
5462
5463         postDigestQueue.set(element, data);
5464         postDigestElements.push(element);
5465
5466         if (postDigestElements.length === 1) {
5467           $rootScope.$$postDigest(handleCSSClassChanges);
5468         }
5469       }
5470     }
5471   }];
5472 };
5473
5474 /**
5475  * @ngdoc provider
5476  * @name $animateProvider
5477  *
5478  * @description
5479  * Default implementation of $animate that doesn't perform any animations, instead just
5480  * synchronously performs DOM updates and resolves the returned runner promise.
5481  *
5482  * In order to enable animations the `ngAnimate` module has to be loaded.
5483  *
5484  * To see the functional implementation check out `src/ngAnimate/animate.js`.
5485  */
5486 var $AnimateProvider = ['$provide', /** @this */ function($provide) {
5487   var provider = this;
5488   var classNameFilter = null;
5489
5490   this.$$registeredAnimations = Object.create(null);
5491
5492    /**
5493    * @ngdoc method
5494    * @name $animateProvider#register
5495    *
5496    * @description
5497    * Registers a new injectable animation factory function. The factory function produces the
5498    * animation object which contains callback functions for each event that is expected to be
5499    * animated.
5500    *
5501    *   * `eventFn`: `function(element, ... , doneFunction, options)`
5502    *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
5503    *   on the type of animation additional arguments will be injected into the animation function. The
5504    *   list below explains the function signatures for the different animation methods:
5505    *
5506    *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5507    *   - addClass: function(element, addedClasses, doneFunction, options)
5508    *   - removeClass: function(element, removedClasses, doneFunction, options)
5509    *   - enter, leave, move: function(element, doneFunction, options)
5510    *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
5511    *
5512    *   Make sure to trigger the `doneFunction` once the animation is fully complete.
5513    *
5514    * ```js
5515    *   return {
5516    *     //enter, leave, move signature
5517    *     eventFn : function(element, done, options) {
5518    *       //code to run the animation
5519    *       //once complete, then run done()
5520    *       return function endFunction(wasCancelled) {
5521    *         //code to cancel the animation
5522    *       }
5523    *     }
5524    *   }
5525    * ```
5526    *
5527    * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5528    * @param {Function} factory The factory function that will be executed to return the animation
5529    *                           object.
5530    */
5531   this.register = function(name, factory) {
5532     if (name && name.charAt(0) !== '.') {
5533       throw $animateMinErr('notcsel', 'Expecting class selector starting with \'.\' got \'{0}\'.', name);
5534     }
5535
5536     var key = name + '-animation';
5537     provider.$$registeredAnimations[name.substr(1)] = key;
5538     $provide.factory(key, factory);
5539   };
5540
5541   /**
5542    * @ngdoc method
5543    * @name $animateProvider#classNameFilter
5544    *
5545    * @description
5546    * Sets and/or returns the CSS class regular expression that is checked when performing
5547    * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5548    * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5549    * When setting the `classNameFilter` value, animations will only be performed on elements
5550    * that successfully match the filter expression. This in turn can boost performance
5551    * for low-powered devices as well as applications containing a lot of structural operations.
5552    * @param {RegExp=} expression The className expression which will be checked against all animations
5553    * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5554    */
5555   this.classNameFilter = function(expression) {
5556     if (arguments.length === 1) {
5557       classNameFilter = (expression instanceof RegExp) ? expression : null;
5558       if (classNameFilter) {
5559         var reservedRegex = new RegExp('[(\\s|\\/)]' + NG_ANIMATE_CLASSNAME + '[(\\s|\\/)]');
5560         if (reservedRegex.test(classNameFilter.toString())) {
5561           classNameFilter = null;
5562           throw $animateMinErr('nongcls', '$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5563         }
5564       }
5565     }
5566     return classNameFilter;
5567   };
5568
5569   this.$get = ['$$animateQueue', function($$animateQueue) {
5570     function domInsert(element, parentElement, afterElement) {
5571       // if for some reason the previous element was removed
5572       // from the dom sometime before this code runs then let's
5573       // just stick to using the parent element as the anchor
5574       if (afterElement) {
5575         var afterNode = extractElementNode(afterElement);
5576         if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5577           afterElement = null;
5578         }
5579       }
5580       if (afterElement) {
5581         afterElement.after(element);
5582       } else {
5583         parentElement.prepend(element);
5584       }
5585     }
5586
5587     /**
5588      * @ngdoc service
5589      * @name $animate
5590      * @description The $animate service exposes a series of DOM utility methods that provide support
5591      * for animation hooks. The default behavior is the application of DOM operations, however,
5592      * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5593      * to ensure that animation runs with the triggered DOM operation.
5594      *
5595      * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5596      * included and only when it is active then the animation hooks that `$animate` triggers will be
5597      * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5598      * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5599      * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5600      *
5601      * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5602      *
5603      * To learn more about enabling animation support, click here to visit the
5604      * {@link ngAnimate ngAnimate module page}.
5605      */
5606     return {
5607       // we don't call it directly since non-existant arguments may
5608       // be interpreted as null within the sub enabled function
5609
5610       /**
5611        *
5612        * @ngdoc method
5613        * @name $animate#on
5614        * @kind function
5615        * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5616        *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5617        *    is fired with the following params:
5618        *
5619        * ```js
5620        * $animate.on('enter', container,
5621        *    function callback(element, phase) {
5622        *      // cool we detected an enter animation within the container
5623        *    }
5624        * );
5625        * ```
5626        *
5627        * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5628        * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5629        *     as well as among its children
5630        * @param {Function} callback the callback function that will be fired when the listener is triggered
5631        *
5632        * The arguments present in the callback function are:
5633        * * `element` - The captured DOM element that the animation was fired on.
5634        * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5635        */
5636       on: $$animateQueue.on,
5637
5638       /**
5639        *
5640        * @ngdoc method
5641        * @name $animate#off
5642        * @kind function
5643        * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5644        * can be used in three different ways depending on the arguments:
5645        *
5646        * ```js
5647        * // remove all the animation event listeners listening for `enter`
5648        * $animate.off('enter');
5649        *
5650        * // remove listeners for all animation events from the container element
5651        * $animate.off(container);
5652        *
5653        * // remove all the animation event listeners listening for `enter` on the given element and its children
5654        * $animate.off('enter', container);
5655        *
5656        * // remove the event listener function provided by `callback` that is set
5657        * // to listen for `enter` on the given `container` as well as its children
5658        * $animate.off('enter', container, callback);
5659        * ```
5660        *
5661        * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
5662        * addClass, removeClass, etc...), or the container element. If it is the element, all other
5663        * arguments are ignored.
5664        * @param {DOMElement=} container the container element the event listener was placed on
5665        * @param {Function=} callback the callback function that was registered as the listener
5666        */
5667       off: $$animateQueue.off,
5668
5669       /**
5670        * @ngdoc method
5671        * @name $animate#pin
5672        * @kind function
5673        * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5674        *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5675        *    element despite being outside the realm of the application or within another application. Say for example if the application
5676        *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5677        *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5678        *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5679        *
5680        *    Note that this feature is only active when the `ngAnimate` module is used.
5681        *
5682        * @param {DOMElement} element the external element that will be pinned
5683        * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5684        */
5685       pin: $$animateQueue.pin,
5686
5687       /**
5688        *
5689        * @ngdoc method
5690        * @name $animate#enabled
5691        * @kind function
5692        * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5693        * function can be called in four ways:
5694        *
5695        * ```js
5696        * // returns true or false
5697        * $animate.enabled();
5698        *
5699        * // changes the enabled state for all animations
5700        * $animate.enabled(false);
5701        * $animate.enabled(true);
5702        *
5703        * // returns true or false if animations are enabled for an element
5704        * $animate.enabled(element);
5705        *
5706        * // changes the enabled state for an element and its children
5707        * $animate.enabled(element, true);
5708        * $animate.enabled(element, false);
5709        * ```
5710        *
5711        * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5712        * @param {boolean=} enabled whether or not the animations will be enabled for the element
5713        *
5714        * @return {boolean} whether or not animations are enabled
5715        */
5716       enabled: $$animateQueue.enabled,
5717
5718       /**
5719        * @ngdoc method
5720        * @name $animate#cancel
5721        * @kind function
5722        * @description Cancels the provided animation.
5723        *
5724        * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5725        */
5726       cancel: function(runner) {
5727         if (runner.end) {
5728           runner.end();
5729         }
5730       },
5731
5732       /**
5733        *
5734        * @ngdoc method
5735        * @name $animate#enter
5736        * @kind function
5737        * @description Inserts the element into the DOM either after the `after` element (if provided) or
5738        *   as the first child within the `parent` element and then triggers an animation.
5739        *   A promise is returned that will be resolved during the next digest once the animation
5740        *   has completed.
5741        *
5742        * @param {DOMElement} element the element which will be inserted into the DOM
5743        * @param {DOMElement} parent the parent element which will append the element as
5744        *   a child (so long as the after element is not present)
5745        * @param {DOMElement=} after the sibling element after which the element will be appended
5746        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5747        *   The object can have the following properties:
5748        *
5749        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5750        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5751        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5752        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5753        *
5754        * @return {Promise} the animation callback promise
5755        */
5756       enter: function(element, parent, after, options) {
5757         parent = parent && jqLite(parent);
5758         after = after && jqLite(after);
5759         parent = parent || after.parent();
5760         domInsert(element, parent, after);
5761         return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5762       },
5763
5764       /**
5765        *
5766        * @ngdoc method
5767        * @name $animate#move
5768        * @kind function
5769        * @description Inserts (moves) the element into its new position in the DOM either after
5770        *   the `after` element (if provided) or as the first child within the `parent` element
5771        *   and then triggers an animation. A promise is returned that will be resolved
5772        *   during the next digest once the animation has completed.
5773        *
5774        * @param {DOMElement} element the element which will be moved into the new DOM position
5775        * @param {DOMElement} parent the parent element which will append the element as
5776        *   a child (so long as the after element is not present)
5777        * @param {DOMElement=} after the sibling element after which the element will be appended
5778        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5779        *   The object can have the following properties:
5780        *
5781        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5782        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5783        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5784        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5785        *
5786        * @return {Promise} the animation callback promise
5787        */
5788       move: function(element, parent, after, options) {
5789         parent = parent && jqLite(parent);
5790         after = after && jqLite(after);
5791         parent = parent || after.parent();
5792         domInsert(element, parent, after);
5793         return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5794       },
5795
5796       /**
5797        * @ngdoc method
5798        * @name $animate#leave
5799        * @kind function
5800        * @description Triggers an animation and then removes the element from the DOM.
5801        * When the function is called a promise is returned that will be resolved during the next
5802        * digest once the animation has completed.
5803        *
5804        * @param {DOMElement} element the element which will be removed from the DOM
5805        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5806        *   The object can have the following properties:
5807        *
5808        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5809        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5810        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5811        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5812        *
5813        * @return {Promise} the animation callback promise
5814        */
5815       leave: function(element, options) {
5816         return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5817           element.remove();
5818         });
5819       },
5820
5821       /**
5822        * @ngdoc method
5823        * @name $animate#addClass
5824        * @kind function
5825        *
5826        * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5827        *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5828        *   animation if element already contains the CSS class or if the class is removed at a later step.
5829        *   Note that class-based animations are treated differently compared to structural animations
5830        *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5831        *   depending if CSS or JavaScript animations are used.
5832        *
5833        * @param {DOMElement} element the element which the CSS classes will be applied to
5834        * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5835        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5836        *   The object can have the following properties:
5837        *
5838        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5839        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5840        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5841        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5842        *
5843        * @return {Promise} the animation callback promise
5844        */
5845       addClass: function(element, className, options) {
5846         options = prepareAnimateOptions(options);
5847         options.addClass = mergeClasses(options.addclass, className);
5848         return $$animateQueue.push(element, 'addClass', options);
5849       },
5850
5851       /**
5852        * @ngdoc method
5853        * @name $animate#removeClass
5854        * @kind function
5855        *
5856        * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5857        *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5858        *   animation if element does not contain the CSS class or if the class is added at a later step.
5859        *   Note that class-based animations are treated differently compared to structural animations
5860        *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5861        *   depending if CSS or JavaScript animations are used.
5862        *
5863        * @param {DOMElement} element the element which the CSS classes will be applied to
5864        * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5865        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5866        *   The object can have the following properties:
5867        *
5868        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5869        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5870        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5871        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5872        *
5873        * @return {Promise} the animation callback promise
5874        */
5875       removeClass: function(element, className, options) {
5876         options = prepareAnimateOptions(options);
5877         options.removeClass = mergeClasses(options.removeClass, className);
5878         return $$animateQueue.push(element, 'removeClass', options);
5879       },
5880
5881       /**
5882        * @ngdoc method
5883        * @name $animate#setClass
5884        * @kind function
5885        *
5886        * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5887        *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5888        *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5889        *    passed. Note that class-based animations are treated differently compared to structural animations
5890        *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5891        *    depending if CSS or JavaScript animations are used.
5892        *
5893        * @param {DOMElement} element the element which the CSS classes will be applied to
5894        * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5895        * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5896        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5897        *   The object can have the following properties:
5898        *
5899        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5900        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5901        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5902        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5903        *
5904        * @return {Promise} the animation callback promise
5905        */
5906       setClass: function(element, add, remove, options) {
5907         options = prepareAnimateOptions(options);
5908         options.addClass = mergeClasses(options.addClass, add);
5909         options.removeClass = mergeClasses(options.removeClass, remove);
5910         return $$animateQueue.push(element, 'setClass', options);
5911       },
5912
5913       /**
5914        * @ngdoc method
5915        * @name $animate#animate
5916        * @kind function
5917        *
5918        * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5919        * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
5920        * on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and
5921        * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
5922        * style in `to`, the style in `from` is applied immediately, and no animation is run.
5923        * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
5924        * method (or as part of the `options` parameter):
5925        *
5926        * ```js
5927        * ngModule.animation('.my-inline-animation', function() {
5928        *   return {
5929        *     animate : function(element, from, to, done, options) {
5930        *       //animation
5931        *       done();
5932        *     }
5933        *   }
5934        * });
5935        * ```
5936        *
5937        * @param {DOMElement} element the element which the CSS styles will be applied to
5938        * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5939        * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5940        * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5941        *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5942        *    (Note that if no animation is detected then this value will not be applied to the element.)
5943        * @param {object=} options an optional collection of options/styles that will be applied to the element.
5944        *   The object can have the following properties:
5945        *
5946        *   - **addClass** - `{string}` - space-separated CSS classes to add to element
5947        *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5948        *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5949        *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5950        *
5951        * @return {Promise} the animation callback promise
5952        */
5953       animate: function(element, from, to, className, options) {
5954         options = prepareAnimateOptions(options);
5955         options.from = options.from ? extend(options.from, from) : from;
5956         options.to   = options.to   ? extend(options.to, to)     : to;
5957
5958         className = className || 'ng-inline-animate';
5959         options.tempClasses = mergeClasses(options.tempClasses, className);
5960         return $$animateQueue.push(element, 'animate', options);
5961       }
5962     };
5963   }];
5964 }];
5965
5966 var $$AnimateAsyncRunFactoryProvider = /** @this */ function() {
5967   this.$get = ['$$rAF', function($$rAF) {
5968     var waitQueue = [];
5969
5970     function waitForTick(fn) {
5971       waitQueue.push(fn);
5972       if (waitQueue.length > 1) return;
5973       $$rAF(function() {
5974         for (var i = 0; i < waitQueue.length; i++) {
5975           waitQueue[i]();
5976         }
5977         waitQueue = [];
5978       });
5979     }
5980
5981     return function() {
5982       var passed = false;
5983       waitForTick(function() {
5984         passed = true;
5985       });
5986       return function(callback) {
5987         if (passed) {
5988           callback();
5989         } else {
5990           waitForTick(callback);
5991         }
5992       };
5993     };
5994   }];
5995 };
5996
5997 var $$AnimateRunnerFactoryProvider = /** @this */ function() {
5998   this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$$isDocumentHidden', '$timeout',
5999        function($q,   $sniffer,   $$animateAsyncRun,   $$isDocumentHidden,   $timeout) {
6000
6001     var INITIAL_STATE = 0;
6002     var DONE_PENDING_STATE = 1;
6003     var DONE_COMPLETE_STATE = 2;
6004
6005     AnimateRunner.chain = function(chain, callback) {
6006       var index = 0;
6007
6008       next();
6009       function next() {
6010         if (index === chain.length) {
6011           callback(true);
6012           return;
6013         }
6014
6015         chain[index](function(response) {
6016           if (response === false) {
6017             callback(false);
6018             return;
6019           }
6020           index++;
6021           next();
6022         });
6023       }
6024     };
6025
6026     AnimateRunner.all = function(runners, callback) {
6027       var count = 0;
6028       var status = true;
6029       forEach(runners, function(runner) {
6030         runner.done(onProgress);
6031       });
6032
6033       function onProgress(response) {
6034         status = status && response;
6035         if (++count === runners.length) {
6036           callback(status);
6037         }
6038       }
6039     };
6040
6041     function AnimateRunner(host) {
6042       this.setHost(host);
6043
6044       var rafTick = $$animateAsyncRun();
6045       var timeoutTick = function(fn) {
6046         $timeout(fn, 0, false);
6047       };
6048
6049       this._doneCallbacks = [];
6050       this._tick = function(fn) {
6051         if ($$isDocumentHidden()) {
6052           timeoutTick(fn);
6053         } else {
6054           rafTick(fn);
6055         }
6056       };
6057       this._state = 0;
6058     }
6059
6060     AnimateRunner.prototype = {
6061       setHost: function(host) {
6062         this.host = host || {};
6063       },
6064
6065       done: function(fn) {
6066         if (this._state === DONE_COMPLETE_STATE) {
6067           fn();
6068         } else {
6069           this._doneCallbacks.push(fn);
6070         }
6071       },
6072
6073       progress: noop,
6074
6075       getPromise: function() {
6076         if (!this.promise) {
6077           var self = this;
6078           this.promise = $q(function(resolve, reject) {
6079             self.done(function(status) {
6080               if (status === false) {
6081                 reject();
6082               } else {
6083                 resolve();
6084               }
6085             });
6086           });
6087         }
6088         return this.promise;
6089       },
6090
6091       then: function(resolveHandler, rejectHandler) {
6092         return this.getPromise().then(resolveHandler, rejectHandler);
6093       },
6094
6095       'catch': function(handler) {
6096         return this.getPromise()['catch'](handler);
6097       },
6098
6099       'finally': function(handler) {
6100         return this.getPromise()['finally'](handler);
6101       },
6102
6103       pause: function() {
6104         if (this.host.pause) {
6105           this.host.pause();
6106         }
6107       },
6108
6109       resume: function() {
6110         if (this.host.resume) {
6111           this.host.resume();
6112         }
6113       },
6114
6115       end: function() {
6116         if (this.host.end) {
6117           this.host.end();
6118         }
6119         this._resolve(true);
6120       },
6121
6122       cancel: function() {
6123         if (this.host.cancel) {
6124           this.host.cancel();
6125         }
6126         this._resolve(false);
6127       },
6128
6129       complete: function(response) {
6130         var self = this;
6131         if (self._state === INITIAL_STATE) {
6132           self._state = DONE_PENDING_STATE;
6133           self._tick(function() {
6134             self._resolve(response);
6135           });
6136         }
6137       },
6138
6139       _resolve: function(response) {
6140         if (this._state !== DONE_COMPLETE_STATE) {
6141           forEach(this._doneCallbacks, function(fn) {
6142             fn(response);
6143           });
6144           this._doneCallbacks.length = 0;
6145           this._state = DONE_COMPLETE_STATE;
6146         }
6147       }
6148     };
6149
6150     return AnimateRunner;
6151   }];
6152 };
6153
6154 /* exported $CoreAnimateCssProvider */
6155
6156 /**
6157  * @ngdoc service
6158  * @name $animateCss
6159  * @kind object
6160  * @this
6161  *
6162  * @description
6163  * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
6164  * then the `$animateCss` service will actually perform animations.
6165  *
6166  * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
6167  */
6168 var $CoreAnimateCssProvider = function() {
6169   this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
6170
6171     return function(element, initialOptions) {
6172       // all of the animation functions should create
6173       // a copy of the options data, however, if a
6174       // parent service has already created a copy then
6175       // we should stick to using that
6176       var options = initialOptions || {};
6177       if (!options.$$prepared) {
6178         options = copy(options);
6179       }
6180
6181       // there is no point in applying the styles since
6182       // there is no animation that goes on at all in
6183       // this version of $animateCss.
6184       if (options.cleanupStyles) {
6185         options.from = options.to = null;
6186       }
6187
6188       if (options.from) {
6189         element.css(options.from);
6190         options.from = null;
6191       }
6192
6193       var closed, runner = new $$AnimateRunner();
6194       return {
6195         start: run,
6196         end: run
6197       };
6198
6199       function run() {
6200         $$rAF(function() {
6201           applyAnimationContents();
6202           if (!closed) {
6203             runner.complete();
6204           }
6205           closed = true;
6206         });
6207         return runner;
6208       }
6209
6210       function applyAnimationContents() {
6211         if (options.addClass) {
6212           element.addClass(options.addClass);
6213           options.addClass = null;
6214         }
6215         if (options.removeClass) {
6216           element.removeClass(options.removeClass);
6217           options.removeClass = null;
6218         }
6219         if (options.to) {
6220           element.css(options.to);
6221           options.to = null;
6222         }
6223       }
6224     };
6225   }];
6226 };
6227
6228 /* global stripHash: true */
6229
6230 /**
6231  * ! This is a private undocumented service !
6232  *
6233  * @name $browser
6234  * @requires $log
6235  * @description
6236  * This object has two goals:
6237  *
6238  * - hide all the global state in the browser caused by the window object
6239  * - abstract away all the browser specific features and inconsistencies
6240  *
6241  * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
6242  * service, which can be used for convenient testing of the application without the interaction with
6243  * the real browser apis.
6244  */
6245 /**
6246  * @param {object} window The global window object.
6247  * @param {object} document jQuery wrapped document.
6248  * @param {object} $log window.console or an object with the same interface.
6249  * @param {object} $sniffer $sniffer service
6250  */
6251 function Browser(window, document, $log, $sniffer) {
6252   var self = this,
6253       location = window.location,
6254       history = window.history,
6255       setTimeout = window.setTimeout,
6256       clearTimeout = window.clearTimeout,
6257       pendingDeferIds = {};
6258
6259   self.isMock = false;
6260
6261   var outstandingRequestCount = 0;
6262   var outstandingRequestCallbacks = [];
6263
6264   // TODO(vojta): remove this temporary api
6265   self.$$completeOutstandingRequest = completeOutstandingRequest;
6266   self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
6267
6268   /**
6269    * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
6270    * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
6271    */
6272   function completeOutstandingRequest(fn) {
6273     try {
6274       fn.apply(null, sliceArgs(arguments, 1));
6275     } finally {
6276       outstandingRequestCount--;
6277       if (outstandingRequestCount === 0) {
6278         while (outstandingRequestCallbacks.length) {
6279           try {
6280             outstandingRequestCallbacks.pop()();
6281           } catch (e) {
6282             $log.error(e);
6283           }
6284         }
6285       }
6286     }
6287   }
6288
6289   function getHash(url) {
6290     var index = url.indexOf('#');
6291     return index === -1 ? '' : url.substr(index);
6292   }
6293
6294   /**
6295    * @private
6296    * Note: this method is used only by scenario runner
6297    * TODO(vojta): prefix this method with $$ ?
6298    * @param {function()} callback Function that will be called when no outstanding request
6299    */
6300   self.notifyWhenNoOutstandingRequests = function(callback) {
6301     if (outstandingRequestCount === 0) {
6302       callback();
6303     } else {
6304       outstandingRequestCallbacks.push(callback);
6305     }
6306   };
6307
6308   //////////////////////////////////////////////////////////////
6309   // URL API
6310   //////////////////////////////////////////////////////////////
6311
6312   var cachedState, lastHistoryState,
6313       lastBrowserUrl = location.href,
6314       baseElement = document.find('base'),
6315       pendingLocation = null,
6316       getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
6317         try {
6318           return history.state;
6319         } catch (e) {
6320           // MSIE can reportedly throw when there is no state (UNCONFIRMED).
6321         }
6322       };
6323
6324   cacheState();
6325
6326   /**
6327    * @name $browser#url
6328    *
6329    * @description
6330    * GETTER:
6331    * Without any argument, this method just returns current value of location.href.
6332    *
6333    * SETTER:
6334    * With at least one argument, this method sets url to new value.
6335    * If html5 history api supported, pushState/replaceState is used, otherwise
6336    * location.href/location.replace is used.
6337    * Returns its own instance to allow chaining
6338    *
6339    * NOTE: this api is intended for use only by the $location service. Please use the
6340    * {@link ng.$location $location service} to change url.
6341    *
6342    * @param {string} url New url (when used as setter)
6343    * @param {boolean=} replace Should new url replace current history record?
6344    * @param {object=} state object to use with pushState/replaceState
6345    */
6346   self.url = function(url, replace, state) {
6347     // In modern browsers `history.state` is `null` by default; treating it separately
6348     // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
6349     // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
6350     if (isUndefined(state)) {
6351       state = null;
6352     }
6353
6354     // Android Browser BFCache causes location, history reference to become stale.
6355     if (location !== window.location) location = window.location;
6356     if (history !== window.history) history = window.history;
6357
6358     // setter
6359     if (url) {
6360       var sameState = lastHistoryState === state;
6361
6362       // Don't change anything if previous and current URLs and states match. This also prevents
6363       // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
6364       // See https://github.com/angular/angular.js/commit/ffb2701
6365       if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
6366         return self;
6367       }
6368       var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
6369       lastBrowserUrl = url;
6370       lastHistoryState = state;
6371       // Don't use history API if only the hash changed
6372       // due to a bug in IE10/IE11 which leads
6373       // to not firing a `hashchange` nor `popstate` event
6374       // in some cases (see #9143).
6375       if ($sniffer.history && (!sameBase || !sameState)) {
6376         history[replace ? 'replaceState' : 'pushState'](state, '', url);
6377         cacheState();
6378       } else {
6379         if (!sameBase) {
6380           pendingLocation = url;
6381         }
6382         if (replace) {
6383           location.replace(url);
6384         } else if (!sameBase) {
6385           location.href = url;
6386         } else {
6387           location.hash = getHash(url);
6388         }
6389         if (location.href !== url) {
6390           pendingLocation = url;
6391         }
6392       }
6393       if (pendingLocation) {
6394         pendingLocation = url;
6395       }
6396       return self;
6397     // getter
6398     } else {
6399       // - pendingLocation is needed as browsers don't allow to read out
6400       //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
6401       //   https://openradar.appspot.com/22186109).
6402       // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
6403       return pendingLocation || location.href.replace(/%27/g,'\'');
6404     }
6405   };
6406
6407   /**
6408    * @name $browser#state
6409    *
6410    * @description
6411    * This method is a getter.
6412    *
6413    * Return history.state or null if history.state is undefined.
6414    *
6415    * @returns {object} state
6416    */
6417   self.state = function() {
6418     return cachedState;
6419   };
6420
6421   var urlChangeListeners = [],
6422       urlChangeInit = false;
6423
6424   function cacheStateAndFireUrlChange() {
6425     pendingLocation = null;
6426     fireStateOrUrlChange();
6427   }
6428
6429   // This variable should be used *only* inside the cacheState function.
6430   var lastCachedState = null;
6431   function cacheState() {
6432     // This should be the only place in $browser where `history.state` is read.
6433     cachedState = getCurrentState();
6434     cachedState = isUndefined(cachedState) ? null : cachedState;
6435
6436     // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
6437     if (equals(cachedState, lastCachedState)) {
6438       cachedState = lastCachedState;
6439     }
6440
6441     lastCachedState = cachedState;
6442     lastHistoryState = cachedState;
6443   }
6444
6445   function fireStateOrUrlChange() {
6446     var prevLastHistoryState = lastHistoryState;
6447     cacheState();
6448
6449     if (lastBrowserUrl === self.url() && prevLastHistoryState === cachedState) {
6450       return;
6451     }
6452
6453     lastBrowserUrl = self.url();
6454     lastHistoryState = cachedState;
6455     forEach(urlChangeListeners, function(listener) {
6456       listener(self.url(), cachedState);
6457     });
6458   }
6459
6460   /**
6461    * @name $browser#onUrlChange
6462    *
6463    * @description
6464    * Register callback function that will be called, when url changes.
6465    *
6466    * It's only called when the url is changed from outside of angular:
6467    * - user types different url into address bar
6468    * - user clicks on history (forward/back) button
6469    * - user clicks on a link
6470    *
6471    * It's not called when url is changed by $browser.url() method
6472    *
6473    * The listener gets called with new url as parameter.
6474    *
6475    * NOTE: this api is intended for use only by the $location service. Please use the
6476    * {@link ng.$location $location service} to monitor url changes in angular apps.
6477    *
6478    * @param {function(string)} listener Listener function to be called when url changes.
6479    * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
6480    */
6481   self.onUrlChange = function(callback) {
6482     // TODO(vojta): refactor to use node's syntax for events
6483     if (!urlChangeInit) {
6484       // We listen on both (hashchange/popstate) when available, as some browsers don't
6485       // fire popstate when user changes the address bar and don't fire hashchange when url
6486       // changed by push/replaceState
6487
6488       // html5 history api - popstate event
6489       if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
6490       // hashchange event
6491       jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
6492
6493       urlChangeInit = true;
6494     }
6495
6496     urlChangeListeners.push(callback);
6497     return callback;
6498   };
6499
6500   /**
6501    * @private
6502    * Remove popstate and hashchange handler from window.
6503    *
6504    * NOTE: this api is intended for use only by $rootScope.
6505    */
6506   self.$$applicationDestroyed = function() {
6507     jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
6508   };
6509
6510   /**
6511    * Checks whether the url has changed outside of Angular.
6512    * Needs to be exported to be able to check for changes that have been done in sync,
6513    * as hashchange/popstate events fire in async.
6514    */
6515   self.$$checkUrlChange = fireStateOrUrlChange;
6516
6517   //////////////////////////////////////////////////////////////
6518   // Misc API
6519   //////////////////////////////////////////////////////////////
6520
6521   /**
6522    * @name $browser#baseHref
6523    *
6524    * @description
6525    * Returns current <base href>
6526    * (always relative - without domain)
6527    *
6528    * @returns {string} The current base href
6529    */
6530   self.baseHref = function() {
6531     var href = baseElement.attr('href');
6532     return href ? href.replace(/^(https?:)?\/\/[^/]*/, '') : '';
6533   };
6534
6535   /**
6536    * @name $browser#defer
6537    * @param {function()} fn A function, who's execution should be deferred.
6538    * @param {number=} [delay=0] of milliseconds to defer the function execution.
6539    * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
6540    *
6541    * @description
6542    * Executes a fn asynchronously via `setTimeout(fn, delay)`.
6543    *
6544    * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
6545    * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
6546    * via `$browser.defer.flush()`.
6547    *
6548    */
6549   self.defer = function(fn, delay) {
6550     var timeoutId;
6551     outstandingRequestCount++;
6552     timeoutId = setTimeout(function() {
6553       delete pendingDeferIds[timeoutId];
6554       completeOutstandingRequest(fn);
6555     }, delay || 0);
6556     pendingDeferIds[timeoutId] = true;
6557     return timeoutId;
6558   };
6559
6560
6561   /**
6562    * @name $browser#defer.cancel
6563    *
6564    * @description
6565    * Cancels a deferred task identified with `deferId`.
6566    *
6567    * @param {*} deferId Token returned by the `$browser.defer` function.
6568    * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
6569    *                    canceled.
6570    */
6571   self.defer.cancel = function(deferId) {
6572     if (pendingDeferIds[deferId]) {
6573       delete pendingDeferIds[deferId];
6574       clearTimeout(deferId);
6575       completeOutstandingRequest(noop);
6576       return true;
6577     }
6578     return false;
6579   };
6580
6581 }
6582
6583 /** @this */
6584 function $BrowserProvider() {
6585   this.$get = ['$window', '$log', '$sniffer', '$document',
6586       function($window, $log, $sniffer, $document) {
6587         return new Browser($window, $document, $log, $sniffer);
6588       }];
6589 }
6590
6591 /**
6592  * @ngdoc service
6593  * @name $cacheFactory
6594  * @this
6595  *
6596  * @description
6597  * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
6598  * them.
6599  *
6600  * ```js
6601  *
6602  *  var cache = $cacheFactory('cacheId');
6603  *  expect($cacheFactory.get('cacheId')).toBe(cache);
6604  *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
6605  *
6606  *  cache.put("key", "value");
6607  *  cache.put("another key", "another value");
6608  *
6609  *  // We've specified no options on creation
6610  *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
6611  *
6612  * ```
6613  *
6614  *
6615  * @param {string} cacheId Name or id of the newly created cache.
6616  * @param {object=} options Options object that specifies the cache behavior. Properties:
6617  *
6618  *   - `{number=}` `capacity` â€” turns the cache into LRU cache.
6619  *
6620  * @returns {object} Newly created cache object with the following set of methods:
6621  *
6622  * - `{object}` `info()` â€” Returns id, size, and options of cache.
6623  * - `{{*}}` `put({string} key, {*} value)` â€” Puts a new key-value pair into the cache and returns
6624  *   it.
6625  * - `{{*}}` `get({string} key)` â€” Returns cached value for `key` or undefined for cache miss.
6626  * - `{void}` `remove({string} key)` â€” Removes a key-value pair from the cache.
6627  * - `{void}` `removeAll()` â€” Removes all cached values.
6628  * - `{void}` `destroy()` â€” Removes references to this cache from $cacheFactory.
6629  *
6630  * @example
6631    <example module="cacheExampleApp" name="cache-factory">
6632      <file name="index.html">
6633        <div ng-controller="CacheController">
6634          <input ng-model="newCacheKey" placeholder="Key">
6635          <input ng-model="newCacheValue" placeholder="Value">
6636          <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
6637
6638          <p ng-if="keys.length">Cached Values</p>
6639          <div ng-repeat="key in keys">
6640            <span ng-bind="key"></span>
6641            <span>: </span>
6642            <b ng-bind="cache.get(key)"></b>
6643          </div>
6644
6645          <p>Cache Info</p>
6646          <div ng-repeat="(key, value) in cache.info()">
6647            <span ng-bind="key"></span>
6648            <span>: </span>
6649            <b ng-bind="value"></b>
6650          </div>
6651        </div>
6652      </file>
6653      <file name="script.js">
6654        angular.module('cacheExampleApp', []).
6655          controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6656            $scope.keys = [];
6657            $scope.cache = $cacheFactory('cacheId');
6658            $scope.put = function(key, value) {
6659              if (angular.isUndefined($scope.cache.get(key))) {
6660                $scope.keys.push(key);
6661              }
6662              $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6663            };
6664          }]);
6665      </file>
6666      <file name="style.css">
6667        p {
6668          margin: 10px 0 3px;
6669        }
6670      </file>
6671    </example>
6672  */
6673 function $CacheFactoryProvider() {
6674
6675   this.$get = function() {
6676     var caches = {};
6677
6678     function cacheFactory(cacheId, options) {
6679       if (cacheId in caches) {
6680         throw minErr('$cacheFactory')('iid', 'CacheId \'{0}\' is already taken!', cacheId);
6681       }
6682
6683       var size = 0,
6684           stats = extend({}, options, {id: cacheId}),
6685           data = createMap(),
6686           capacity = (options && options.capacity) || Number.MAX_VALUE,
6687           lruHash = createMap(),
6688           freshEnd = null,
6689           staleEnd = null;
6690
6691       /**
6692        * @ngdoc type
6693        * @name $cacheFactory.Cache
6694        *
6695        * @description
6696        * A cache object used to store and retrieve data, primarily used by
6697        * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6698        * templates and other data.
6699        *
6700        * ```js
6701        *  angular.module('superCache')
6702        *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6703        *      return $cacheFactory('super-cache');
6704        *    }]);
6705        * ```
6706        *
6707        * Example test:
6708        *
6709        * ```js
6710        *  it('should behave like a cache', inject(function(superCache) {
6711        *    superCache.put('key', 'value');
6712        *    superCache.put('another key', 'another value');
6713        *
6714        *    expect(superCache.info()).toEqual({
6715        *      id: 'super-cache',
6716        *      size: 2
6717        *    });
6718        *
6719        *    superCache.remove('another key');
6720        *    expect(superCache.get('another key')).toBeUndefined();
6721        *
6722        *    superCache.removeAll();
6723        *    expect(superCache.info()).toEqual({
6724        *      id: 'super-cache',
6725        *      size: 0
6726        *    });
6727        *  }));
6728        * ```
6729        */
6730       return (caches[cacheId] = {
6731
6732         /**
6733          * @ngdoc method
6734          * @name $cacheFactory.Cache#put
6735          * @kind function
6736          *
6737          * @description
6738          * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6739          * retrieved later, and incrementing the size of the cache if the key was not already
6740          * present in the cache. If behaving like an LRU cache, it will also remove stale
6741          * entries from the set.
6742          *
6743          * It will not insert undefined values into the cache.
6744          *
6745          * @param {string} key the key under which the cached data is stored.
6746          * @param {*} value the value to store alongside the key. If it is undefined, the key
6747          *    will not be stored.
6748          * @returns {*} the value stored.
6749          */
6750         put: function(key, value) {
6751           if (isUndefined(value)) return;
6752           if (capacity < Number.MAX_VALUE) {
6753             var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6754
6755             refresh(lruEntry);
6756           }
6757
6758           if (!(key in data)) size++;
6759           data[key] = value;
6760
6761           if (size > capacity) {
6762             this.remove(staleEnd.key);
6763           }
6764
6765           return value;
6766         },
6767
6768         /**
6769          * @ngdoc method
6770          * @name $cacheFactory.Cache#get
6771          * @kind function
6772          *
6773          * @description
6774          * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6775          *
6776          * @param {string} key the key of the data to be retrieved
6777          * @returns {*} the value stored.
6778          */
6779         get: function(key) {
6780           if (capacity < Number.MAX_VALUE) {
6781             var lruEntry = lruHash[key];
6782
6783             if (!lruEntry) return;
6784
6785             refresh(lruEntry);
6786           }
6787
6788           return data[key];
6789         },
6790
6791
6792         /**
6793          * @ngdoc method
6794          * @name $cacheFactory.Cache#remove
6795          * @kind function
6796          *
6797          * @description
6798          * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6799          *
6800          * @param {string} key the key of the entry to be removed
6801          */
6802         remove: function(key) {
6803           if (capacity < Number.MAX_VALUE) {
6804             var lruEntry = lruHash[key];
6805
6806             if (!lruEntry) return;
6807
6808             if (lruEntry === freshEnd) freshEnd = lruEntry.p;
6809             if (lruEntry === staleEnd) staleEnd = lruEntry.n;
6810             link(lruEntry.n,lruEntry.p);
6811
6812             delete lruHash[key];
6813           }
6814
6815           if (!(key in data)) return;
6816
6817           delete data[key];
6818           size--;
6819         },
6820
6821
6822         /**
6823          * @ngdoc method
6824          * @name $cacheFactory.Cache#removeAll
6825          * @kind function
6826          *
6827          * @description
6828          * Clears the cache object of any entries.
6829          */
6830         removeAll: function() {
6831           data = createMap();
6832           size = 0;
6833           lruHash = createMap();
6834           freshEnd = staleEnd = null;
6835         },
6836
6837
6838         /**
6839          * @ngdoc method
6840          * @name $cacheFactory.Cache#destroy
6841          * @kind function
6842          *
6843          * @description
6844          * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6845          * removing it from the {@link $cacheFactory $cacheFactory} set.
6846          */
6847         destroy: function() {
6848           data = null;
6849           stats = null;
6850           lruHash = null;
6851           delete caches[cacheId];
6852         },
6853
6854
6855         /**
6856          * @ngdoc method
6857          * @name $cacheFactory.Cache#info
6858          * @kind function
6859          *
6860          * @description
6861          * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6862          *
6863          * @returns {object} an object with the following properties:
6864          *   <ul>
6865          *     <li>**id**: the id of the cache instance</li>
6866          *     <li>**size**: the number of entries kept in the cache instance</li>
6867          *     <li>**...**: any additional properties from the options object when creating the
6868          *       cache.</li>
6869          *   </ul>
6870          */
6871         info: function() {
6872           return extend({}, stats, {size: size});
6873         }
6874       });
6875
6876
6877       /**
6878        * makes the `entry` the freshEnd of the LRU linked list
6879        */
6880       function refresh(entry) {
6881         if (entry !== freshEnd) {
6882           if (!staleEnd) {
6883             staleEnd = entry;
6884           } else if (staleEnd === entry) {
6885             staleEnd = entry.n;
6886           }
6887
6888           link(entry.n, entry.p);
6889           link(entry, freshEnd);
6890           freshEnd = entry;
6891           freshEnd.n = null;
6892         }
6893       }
6894
6895
6896       /**
6897        * bidirectionally links two entries of the LRU linked list
6898        */
6899       function link(nextEntry, prevEntry) {
6900         if (nextEntry !== prevEntry) {
6901           if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6902           if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6903         }
6904       }
6905     }
6906
6907
6908   /**
6909    * @ngdoc method
6910    * @name $cacheFactory#info
6911    *
6912    * @description
6913    * Get information about all the caches that have been created
6914    *
6915    * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6916    */
6917     cacheFactory.info = function() {
6918       var info = {};
6919       forEach(caches, function(cache, cacheId) {
6920         info[cacheId] = cache.info();
6921       });
6922       return info;
6923     };
6924
6925
6926   /**
6927    * @ngdoc method
6928    * @name $cacheFactory#get
6929    *
6930    * @description
6931    * Get access to a cache object by the `cacheId` used when it was created.
6932    *
6933    * @param {string} cacheId Name or id of a cache to access.
6934    * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6935    */
6936     cacheFactory.get = function(cacheId) {
6937       return caches[cacheId];
6938     };
6939
6940
6941     return cacheFactory;
6942   };
6943 }
6944
6945 /**
6946  * @ngdoc service
6947  * @name $templateCache
6948  * @this
6949  *
6950  * @description
6951  * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6952  * can load templates directly into the cache in a `script` tag, or by consuming the
6953  * `$templateCache` service directly.
6954  *
6955  * Adding via the `script` tag:
6956  *
6957  * ```html
6958  *   <script type="text/ng-template" id="templateId.html">
6959  *     <p>This is the content of the template</p>
6960  *   </script>
6961  * ```
6962  *
6963  * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6964  * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6965  * element with ng-app attribute), otherwise the template will be ignored.
6966  *
6967  * Adding via the `$templateCache` service:
6968  *
6969  * ```js
6970  * var myApp = angular.module('myApp', []);
6971  * myApp.run(function($templateCache) {
6972  *   $templateCache.put('templateId.html', 'This is the content of the template');
6973  * });
6974  * ```
6975  *
6976  * To retrieve the template later, simply use it in your component:
6977  * ```js
6978  * myApp.component('myComponent', {
6979  *    templateUrl: 'templateId.html'
6980  * });
6981  * ```
6982  *
6983  * or get it via the `$templateCache` service:
6984  * ```js
6985  * $templateCache.get('templateId.html')
6986  * ```
6987  *
6988  * See {@link ng.$cacheFactory $cacheFactory}.
6989  *
6990  */
6991 function $TemplateCacheProvider() {
6992   this.$get = ['$cacheFactory', function($cacheFactory) {
6993     return $cacheFactory('templates');
6994   }];
6995 }
6996
6997 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6998  *     Any commits to this file should be reviewed with security in mind.  *
6999  *   Changes to this file can potentially create security vulnerabilities. *
7000  *          An approval from 2 Core members with history of modifying      *
7001  *                         this file is required.                          *
7002  *                                                                         *
7003  *  Does the change somehow allow for arbitrary javascript to be executed? *
7004  *    Or allows for someone to change the prototype of built-in objects?   *
7005  *     Or gives undesired access to variables like document or window?    *
7006  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
7007
7008 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
7009  *
7010  * DOM-related variables:
7011  *
7012  * - "node" - DOM Node
7013  * - "element" - DOM Element or Node
7014  * - "$node" or "$element" - jqLite-wrapped node or element
7015  *
7016  *
7017  * Compiler related stuff:
7018  *
7019  * - "linkFn" - linking fn of a single directive
7020  * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
7021  * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
7022  * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
7023  */
7024
7025
7026 /**
7027  * @ngdoc service
7028  * @name $compile
7029  * @kind function
7030  *
7031  * @description
7032  * Compiles an HTML string or DOM into a template and produces a template function, which
7033  * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
7034  *
7035  * The compilation is a process of walking the DOM tree and matching DOM elements to
7036  * {@link ng.$compileProvider#directive directives}.
7037  *
7038  * <div class="alert alert-warning">
7039  * **Note:** This document is an in-depth reference of all directive options.
7040  * For a gentle introduction to directives with examples of common use cases,
7041  * see the {@link guide/directive directive guide}.
7042  * </div>
7043  *
7044  * ## Comprehensive Directive API
7045  *
7046  * There are many different options for a directive.
7047  *
7048  * The difference resides in the return value of the factory function.
7049  * You can either return a {@link $compile#directive-definition-object Directive Definition Object (see below)}
7050  * that defines the directive properties, or just the `postLink` function (all other properties will have
7051  * the default values).
7052  *
7053  * <div class="alert alert-success">
7054  * **Best Practice:** It's recommended to use the "directive definition object" form.
7055  * </div>
7056  *
7057  * Here's an example directive declared with a Directive Definition Object:
7058  *
7059  * ```js
7060  *   var myModule = angular.module(...);
7061  *
7062  *   myModule.directive('directiveName', function factory(injectables) {
7063  *     var directiveDefinitionObject = {
7064  *       {@link $compile#-priority- priority}: 0,
7065  *       {@link $compile#-template- template}: '<div></div>', // or // function(tElement, tAttrs) { ... },
7066  *       // or
7067  *       // {@link $compile#-templateurl- templateUrl}: 'directive.html', // or // function(tElement, tAttrs) { ... },
7068  *       {@link $compile#-transclude- transclude}: false,
7069  *       {@link $compile#-restrict- restrict}: 'A',
7070  *       {@link $compile#-templatenamespace- templateNamespace}: 'html',
7071  *       {@link $compile#-scope- scope}: false,
7072  *       {@link $compile#-controller- controller}: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
7073  *       {@link $compile#-controlleras- controllerAs}: 'stringIdentifier',
7074  *       {@link $compile#-bindtocontroller- bindToController}: false,
7075  *       {@link $compile#-require- require}: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
7076  *       {@link $compile#-multielement- multiElement}: false,
7077  *       {@link $compile#-compile- compile}: function compile(tElement, tAttrs, transclude) {
7078  *         return {
7079  *            {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... },
7080  *            {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... }
7081  *         }
7082  *         // or
7083  *         // return function postLink( ... ) { ... }
7084  *       },
7085  *       // or
7086  *       // {@link $compile#-link- link}: {
7087  *       //  {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... },
7088  *       //  {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... }
7089  *       // }
7090  *       // or
7091  *       // {@link $compile#-link- link}: function postLink( ... ) { ... }
7092  *     };
7093  *     return directiveDefinitionObject;
7094  *   });
7095  * ```
7096  *
7097  * <div class="alert alert-warning">
7098  * **Note:** Any unspecified options will use the default value. You can see the default values below.
7099  * </div>
7100  *
7101  * Therefore the above can be simplified as:
7102  *
7103  * ```js
7104  *   var myModule = angular.module(...);
7105  *
7106  *   myModule.directive('directiveName', function factory(injectables) {
7107  *     var directiveDefinitionObject = {
7108  *       link: function postLink(scope, iElement, iAttrs) { ... }
7109  *     };
7110  *     return directiveDefinitionObject;
7111  *     // or
7112  *     // return function postLink(scope, iElement, iAttrs) { ... }
7113  *   });
7114  * ```
7115  *
7116  * ### Life-cycle hooks
7117  * Directive controllers can provide the following methods that are called by Angular at points in the life-cycle of the
7118  * directive:
7119  * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
7120  *   had their bindings initialized (and before the pre &amp; post linking functions for the directives on
7121  *   this element). This is a good place to put initialization code for your controller.
7122  * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
7123  *   `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
7124  *   object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
7125  *   component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will
7126  *   also be called when your bindings are initialized.
7127  * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on
7128  *   changes. Any actions that you wish to take in response to the changes that you detect must be
7129  *   invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
7130  *   could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not
7131  *   be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments;
7132  *   if detecting changes, you must store the previous value(s) for comparison to the current values.
7133  * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
7134  *   external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
7135  *   the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
7136  *   components will have their `$onDestroy()` hook called before child components.
7137  * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
7138  *   function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
7139  *   Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
7140  *   they are waiting for their template to load asynchronously and their own compilation and linking has been
7141  *   suspended until that occurs.
7142  *
7143  * #### Comparison with Angular 2 life-cycle hooks
7144  * Angular 2 also uses life-cycle hooks for its components. While the Angular 1 life-cycle hooks are similar there are
7145  * some differences that you should be aware of, especially when it comes to moving your code from Angular 1 to Angular 2:
7146  *
7147  * * Angular 1 hooks are prefixed with `$`, such as `$onInit`. Angular 2 hooks are prefixed with `ng`, such as `ngOnInit`.
7148  * * Angular 1 hooks can be defined on the controller prototype or added to the controller inside its constructor.
7149  *   In Angular 2 you can only define hooks on the prototype of the Component class.
7150  * * Due to the differences in change-detection, you may get many more calls to `$doCheck` in Angular 1 than you would to
7151  *   `ngDoCheck` in Angular 2
7152  * * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be
7153  *   propagated throughout the application.
7154  *   Angular 2 does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an
7155  *   error or do nothing depending upon the state of `enableProdMode()`.
7156  *
7157  * #### Life-cycle hook examples
7158  *
7159  * This example shows how you can check for mutations to a Date object even though the identity of the object
7160  * has not changed.
7161  *
7162  * <example name="doCheckDateExample" module="do-check-module">
7163  *   <file name="app.js">
7164  *     angular.module('do-check-module', [])
7165  *       .component('app', {
7166  *         template:
7167  *           'Month: <input ng-model="$ctrl.month" ng-change="$ctrl.updateDate()">' +
7168  *           'Date: {{ $ctrl.date }}' +
7169  *           '<test date="$ctrl.date"></test>',
7170  *         controller: function() {
7171  *           this.date = new Date();
7172  *           this.month = this.date.getMonth();
7173  *           this.updateDate = function() {
7174  *             this.date.setMonth(this.month);
7175  *           };
7176  *         }
7177  *       })
7178  *       .component('test', {
7179  *         bindings: { date: '<' },
7180  *         template:
7181  *           '<pre>{{ $ctrl.log | json }}</pre>',
7182  *         controller: function() {
7183  *           var previousValue;
7184  *           this.log = [];
7185  *           this.$doCheck = function() {
7186  *             var currentValue = this.date && this.date.valueOf();
7187  *             if (previousValue !== currentValue) {
7188  *               this.log.push('doCheck: date mutated: ' + this.date);
7189  *               previousValue = currentValue;
7190  *             }
7191  *           };
7192  *         }
7193  *       });
7194  *   </file>
7195  *   <file name="index.html">
7196  *     <app></app>
7197  *   </file>
7198  * </example>
7199  *
7200  * This example show how you might use `$doCheck` to trigger changes in your component's inputs even if the
7201  * actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large
7202  * arrays or objects can have a negative impact on your application performance)
7203  *
7204  * <example name="doCheckArrayExample" module="do-check-module">
7205  *   <file name="index.html">
7206  *     <div ng-init="items = []">
7207  *       <button ng-click="items.push(items.length)">Add Item</button>
7208  *       <button ng-click="items = []">Reset Items</button>
7209  *       <pre>{{ items }}</pre>
7210  *       <test items="items"></test>
7211  *     </div>
7212  *   </file>
7213  *   <file name="app.js">
7214  *      angular.module('do-check-module', [])
7215  *        .component('test', {
7216  *          bindings: { items: '<' },
7217  *          template:
7218  *            '<pre>{{ $ctrl.log | json }}</pre>',
7219  *          controller: function() {
7220  *            this.log = [];
7221  *
7222  *            this.$doCheck = function() {
7223  *              if (this.items_ref !== this.items) {
7224  *                this.log.push('doCheck: items changed');
7225  *                this.items_ref = this.items;
7226  *              }
7227  *              if (!angular.equals(this.items_clone, this.items)) {
7228  *                this.log.push('doCheck: items mutated');
7229  *                this.items_clone = angular.copy(this.items);
7230  *              }
7231  *            };
7232  *          }
7233  *        });
7234  *   </file>
7235  * </example>
7236  *
7237  *
7238  * ### Directive Definition Object
7239  *
7240  * The directive definition object provides instructions to the {@link ng.$compile
7241  * compiler}. The attributes are:
7242  *
7243  * #### `multiElement`
7244  * When this property is set to true (default is `false`), the HTML compiler will collect DOM nodes between
7245  * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
7246  * together as the directive elements. It is recommended that this feature be used on directives
7247  * which are not strictly behavioral (such as {@link ngClick}), and which
7248  * do not manipulate or replace child nodes (such as {@link ngInclude}).
7249  *
7250  * #### `priority`
7251  * When there are multiple directives defined on a single DOM element, sometimes it
7252  * is necessary to specify the order in which the directives are applied. The `priority` is used
7253  * to sort the directives before their `compile` functions get called. Priority is defined as a
7254  * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
7255  * are also run in priority order, but post-link functions are run in reverse order. The order
7256  * of directives with the same priority is undefined. The default priority is `0`.
7257  *
7258  * #### `terminal`
7259  * If set to true then the current `priority` will be the last set of directives
7260  * which will execute (any directives at the current priority will still execute
7261  * as the order of execution on same `priority` is undefined). Note that expressions
7262  * and other directives used in the directive's template will also be excluded from execution.
7263  *
7264  * #### `scope`
7265  * The scope property can be `false`, `true`, or an object:
7266  *
7267  * * **`false` (default):** No scope will be created for the directive. The directive will use its
7268  * parent's scope.
7269  *
7270  * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
7271  * the directive's element. If multiple directives on the same element request a new scope,
7272  * only one new scope is created.
7273  *
7274  * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's template.
7275  * The 'isolate' scope differs from normal scope in that it does not prototypically
7276  * inherit from its parent scope. This is useful when creating reusable components, which should not
7277  * accidentally read or modify data in the parent scope. Note that an isolate scope
7278  * directive without a `template` or `templateUrl` will not apply the isolate scope
7279  * to its children elements.
7280  *
7281  * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
7282  * directive's element. These local properties are useful for aliasing values for templates. The keys in
7283  * the object hash map to the name of the property on the isolate scope; the values define how the property
7284  * is bound to the parent scope, via matching attributes on the directive's element:
7285  *
7286  * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
7287  *   always a string since DOM attributes are strings. If no `attr` name is specified then the
7288  *   attribute name is assumed to be the same as the local name. Given `<my-component
7289  *   my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
7290  *   the directive's scope property `localName` will reflect the interpolated value of `hello
7291  *   {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
7292  *   scope. The `name` is read from the parent scope (not the directive's scope).
7293  *
7294  * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
7295  *   passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
7296  *   If no `attr` name is specified then the attribute name is assumed to be the same as the local
7297  *   name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
7298  *   localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
7299  *   value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
7300  *   `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
7301  *   `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
7302  *   optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
7303  *   will be thrown upon discovering changes to the local value, since it will be impossible to sync
7304  *   them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
7305  *   method is used for tracking changes, and the equality check is based on object identity.
7306  *   However, if an object literal or an array literal is passed as the binding expression, the
7307  *   equality check is done by value (using the {@link angular.equals} function). It's also possible
7308  *   to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
7309  *   `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
7310  *
7311   * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
7312  *   expression passed via the attribute `attr`. The expression is evaluated in the context of the
7313  *   parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
7314  *   local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
7315  *
7316  *   For example, given `<my-component my-attr="parentModel">` and directive definition of
7317  *   `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
7318  *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
7319  *   in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
7320  *   two caveats:
7321  *     1. one-way binding does not copy the value from the parent to the isolate scope, it simply
7322  *     sets the same value. That means if your bound value is an object, changes to its properties
7323  *     in the isolated scope will be reflected in the parent scope (because both reference the same object).
7324  *     2. one-way binding watches changes to the **identity** of the parent value. That means the
7325  *     {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
7326  *     to the value has changed. In most cases, this should not be of concern, but can be important
7327  *     to know if you one-way bind to an object, and then replace that object in the isolated scope.
7328  *     If you now change a property of the object in your parent scope, the change will not be
7329  *     propagated to the isolated scope, because the identity of the object on the parent scope
7330  *     has not changed. Instead you must assign a new object.
7331  *
7332  *   One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
7333  *   back to the parent. However, it does not make this completely impossible.
7334  *
7335  * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
7336  *   no `attr` name is specified then the attribute name is assumed to be the same as the local name.
7337  *   Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
7338  *   localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
7339  *   the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
7340  *   via an expression to the parent scope. This can be done by passing a map of local variable names
7341  *   and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
7342  *   then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
7343  *
7344  * In general it's possible to apply more than one directive to one element, but there might be limitations
7345  * depending on the type of scope required by the directives. The following points will help explain these limitations.
7346  * For simplicity only two directives are taken into account, but it is also applicable for several directives:
7347  *
7348  * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
7349  * * **child scope** + **no scope** =>  Both directives will share one single child scope
7350  * * **child scope** + **child scope** =>  Both directives will share one single child scope
7351  * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
7352  * its parent's scope
7353  * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
7354  * be applied to the same element.
7355  * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
7356  * cannot be applied to the same element.
7357  *
7358  *
7359  * #### `bindToController`
7360  * This property is used to bind scope properties directly to the controller. It can be either
7361  * `true` or an object hash with the same format as the `scope` property.
7362  *
7363  * When an isolate scope is used for a directive (see above), `bindToController: true` will
7364  * allow a component to have its properties bound to the controller, rather than to scope.
7365  *
7366  * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
7367  * properties. You can access these bindings once they have been initialized by providing a controller method called
7368  * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
7369  * initialized.
7370  *
7371  * <div class="alert alert-warning">
7372  * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
7373  * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
7374  * code that relies upon bindings inside a `$onInit` method on the controller, instead.
7375  * </div>
7376  *
7377  * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
7378  * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
7379  * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
7380  * scope (useful for component directives).
7381  *
7382  * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
7383  *
7384  *
7385  * #### `controller`
7386  * Controller constructor function. The controller is instantiated before the
7387  * pre-linking phase and can be accessed by other directives (see
7388  * `require` attribute). This allows the directives to communicate with each other and augment
7389  * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
7390  *
7391  * * `$scope` - Current scope associated with the element
7392  * * `$element` - Current element
7393  * * `$attrs` - Current attributes object for the element
7394  * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
7395  *   `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
7396  *    * `scope`: (optional) override the scope.
7397  *    * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
7398  *    * `futureParentElement` (optional):
7399  *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
7400  *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
7401  *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
7402  *          and when the `cloneLinkingFn` is passed,
7403  *          as those elements need to created and cloned in a special way when they are defined outside their
7404  *          usual containers (e.g. like `<svg>`).
7405  *        * See also the `directive.templateNamespace` property.
7406  *    * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
7407  *      then the default transclusion is provided.
7408  *    The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
7409  *    `true` if the specified slot contains content (i.e. one or more DOM nodes).
7410  *
7411  * #### `require`
7412  * Require another directive and inject its controller as the fourth argument to the linking function. The
7413  * `require` property can be a string, an array or an object:
7414  * * a **string** containing the name of the directive to pass to the linking function
7415  * * an **array** containing the names of directives to pass to the linking function. The argument passed to the
7416  * linking function will be an array of controllers in the same order as the names in the `require` property
7417  * * an **object** whose property values are the names of the directives to pass to the linking function. The argument
7418  * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
7419  * controllers.
7420  *
7421  * If the `require` property is an object and `bindToController` is truthy, then the required controllers are
7422  * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
7423  * have been constructed but before `$onInit` is called.
7424  * If the name of the required controller is the same as the local name (the key), the name can be
7425  * omitted. For example, `{parentDir: '^^'}` is equivalent to `{parentDir: '^^parentDir'}`.
7426  * See the {@link $compileProvider#component} helper for an example of how this can be used.
7427  * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
7428  * raised (unless no link function is specified and the required controllers are not being bound to the directive
7429  * controller, in which case error checking is skipped). The name can be prefixed with:
7430  *
7431  * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
7432  * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
7433  * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
7434  * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
7435  * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
7436  *   `null` to the `link` fn if not found.
7437  * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
7438  *   `null` to the `link` fn if not found.
7439  *
7440  *
7441  * #### `controllerAs`
7442  * Identifier name for a reference to the controller in the directive's scope.
7443  * This allows the controller to be referenced from the directive template. This is especially
7444  * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
7445  * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
7446  * `controllerAs` reference might overwrite a property that already exists on the parent scope.
7447  *
7448  *
7449  * #### `restrict`
7450  * String of subset of `EACM` which restricts the directive to a specific directive
7451  * declaration style. If omitted, the defaults (elements and attributes) are used.
7452  *
7453  * * `E` - Element name (default): `<my-directive></my-directive>`
7454  * * `A` - Attribute (default): `<div my-directive="exp"></div>`
7455  * * `C` - Class: `<div class="my-directive: exp;"></div>`
7456  * * `M` - Comment: `<!-- directive: my-directive exp -->`
7457  *
7458  *
7459  * #### `templateNamespace`
7460  * String representing the document type used by the markup in the template.
7461  * AngularJS needs this information as those elements need to be created and cloned
7462  * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
7463  *
7464  * * `html` - All root nodes in the template are HTML. Root nodes may also be
7465  *   top-level elements such as `<svg>` or `<math>`.
7466  * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
7467  * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
7468  *
7469  * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
7470  *
7471  * #### `template`
7472  * HTML markup that may:
7473  * * Replace the contents of the directive's element (default).
7474  * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
7475  * * Wrap the contents of the directive's element (if `transclude` is true).
7476  *
7477  * Value may be:
7478  *
7479  * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
7480  * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
7481  *   function api below) and returns a string value.
7482  *
7483  *
7484  * #### `templateUrl`
7485  * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
7486  *
7487  * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
7488  * for later when the template has been resolved.  In the meantime it will continue to compile and link
7489  * sibling and parent elements as though this element had not contained any directives.
7490  *
7491  * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
7492  * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
7493  * case when only one deeply nested directive has `templateUrl`.
7494  *
7495  * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
7496  *
7497  * You can specify `templateUrl` as a string representing the URL or as a function which takes two
7498  * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
7499  * a string value representing the url.  In either case, the template URL is passed through {@link
7500  * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
7501  *
7502  *
7503  * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
7504  * specify what the template should replace. Defaults to `false`.
7505  *
7506  * * `true` - the template will replace the directive's element.
7507  * * `false` - the template will replace the contents of the directive's element.
7508  *
7509  * The replacement process migrates all of the attributes / classes from the old element to the new
7510  * one. See the {@link guide/directive#template-expanding-directive
7511  * Directives Guide} for an example.
7512  *
7513  * There are very few scenarios where element replacement is required for the application function,
7514  * the main one being reusable custom components that are used within SVG contexts
7515  * (because SVG doesn't work with custom elements in the DOM tree).
7516  *
7517  * #### `transclude`
7518  * Extract the contents of the element where the directive appears and make it available to the directive.
7519  * The contents are compiled and provided to the directive as a **transclusion function**. See the
7520  * {@link $compile#transclusion Transclusion} section below.
7521  *
7522  *
7523  * #### `compile`
7524  *
7525  * ```js
7526  *   function compile(tElement, tAttrs, transclude) { ... }
7527  * ```
7528  *
7529  * The compile function deals with transforming the template DOM. Since most directives do not do
7530  * template transformation, it is not used often. The compile function takes the following arguments:
7531  *
7532  *   * `tElement` - template element - The element where the directive has been declared. It is
7533  *     safe to do template transformation on the element and child elements only.
7534  *
7535  *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
7536  *     between all directive compile functions.
7537  *
7538  *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
7539  *
7540  * <div class="alert alert-warning">
7541  * **Note:** The template instance and the link instance may be different objects if the template has
7542  * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
7543  * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
7544  * should be done in a linking function rather than in a compile function.
7545  * </div>
7546
7547  * <div class="alert alert-warning">
7548  * **Note:** The compile function cannot handle directives that recursively use themselves in their
7549  * own templates or compile functions. Compiling these directives results in an infinite loop and
7550  * stack overflow errors.
7551  *
7552  * This can be avoided by manually using $compile in the postLink function to imperatively compile
7553  * a directive's template instead of relying on automatic template compilation via `template` or
7554  * `templateUrl` declaration or manual compilation inside the compile function.
7555  * </div>
7556  *
7557  * <div class="alert alert-danger">
7558  * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
7559  *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
7560  *   to the link function instead.
7561  * </div>
7562
7563  * A compile function can have a return value which can be either a function or an object.
7564  *
7565  * * returning a (post-link) function - is equivalent to registering the linking function via the
7566  *   `link` property of the config object when the compile function is empty.
7567  *
7568  * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
7569  *   control when a linking function should be called during the linking phase. See info about
7570  *   pre-linking and post-linking functions below.
7571  *
7572  *
7573  * #### `link`
7574  * This property is used only if the `compile` property is not defined.
7575  *
7576  * ```js
7577  *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
7578  * ```
7579  *
7580  * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
7581  * executed after the template has been cloned. This is where most of the directive logic will be
7582  * put.
7583  *
7584  *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
7585  *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
7586  *
7587  *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
7588  *     manipulate the children of the element only in `postLink` function since the children have
7589  *     already been linked.
7590  *
7591  *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
7592  *     between all directive linking functions.
7593  *
7594  *   * `controller` - the directive's required controller instance(s) - Instances are shared
7595  *     among all directives, which allows the directives to use the controllers as a communication
7596  *     channel. The exact value depends on the directive's `require` property:
7597  *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
7598  *       * `string`: the controller instance
7599  *       * `array`: array of controller instances
7600  *
7601  *     If a required controller cannot be found, and it is optional, the instance is `null`,
7602  *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
7603  *
7604  *     Note that you can also require the directive's own controller - it will be made available like
7605  *     any other controller.
7606  *
7607  *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
7608  *     This is the same as the `$transclude` parameter of directive controllers,
7609  *     see {@link ng.$compile#-controller- the controller section for details}.
7610  *     `function([scope], cloneLinkingFn, futureParentElement)`.
7611  *
7612  * #### Pre-linking function
7613  *
7614  * Executed before the child elements are linked. Not safe to do DOM transformation since the
7615  * compiler linking function will fail to locate the correct elements for linking.
7616  *
7617  * #### Post-linking function
7618  *
7619  * Executed after the child elements are linked.
7620  *
7621  * Note that child elements that contain `templateUrl` directives will not have been compiled
7622  * and linked since they are waiting for their template to load asynchronously and their own
7623  * compilation and linking has been suspended until that occurs.
7624  *
7625  * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
7626  * for their async templates to be resolved.
7627  *
7628  *
7629  * ### Transclusion
7630  *
7631  * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
7632  * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
7633  * scope from where they were taken.
7634  *
7635  * Transclusion is used (often with {@link ngTransclude}) to insert the
7636  * original contents of a directive's element into a specified place in the template of the directive.
7637  * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
7638  * content has access to the properties on the scope from which it was taken, even if the directive
7639  * has isolated scope.
7640  * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
7641  *
7642  * This makes it possible for the widget to have private state for its template, while the transcluded
7643  * content has access to its originating scope.
7644  *
7645  * <div class="alert alert-warning">
7646  * **Note:** When testing an element transclude directive you must not place the directive at the root of the
7647  * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
7648  * Testing Transclusion Directives}.
7649  * </div>
7650  *
7651  * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
7652  * directive's element, the entire element or multiple parts of the element contents:
7653  *
7654  * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
7655  * * `'element'` - transclude the whole of the directive's element including any directives on this
7656  *   element that defined at a lower priority than this directive. When used, the `template`
7657  *   property is ignored.
7658  * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
7659  *
7660  * **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
7661  *
7662  * This object is a map where the keys are the name of the slot to fill and the value is an element selector
7663  * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
7664  * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
7665  *
7666  * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7667  *
7668  * If the element selector is prefixed with a `?` then that slot is optional.
7669  *
7670  * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
7671  * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
7672  *
7673  * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
7674  * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
7675  * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
7676  * injectable into the directive's controller.
7677  *
7678  *
7679  * #### Transclusion Functions
7680  *
7681  * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
7682  * function** to the directive's `link` function and `controller`. This transclusion function is a special
7683  * **linking function** that will return the compiled contents linked to a new transclusion scope.
7684  *
7685  * <div class="alert alert-info">
7686  * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
7687  * ngTransclude will deal with it for us.
7688  * </div>
7689  *
7690  * If you want to manually control the insertion and removal of the transcluded content in your directive
7691  * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
7692  * object that contains the compiled DOM, which is linked to the correct transclusion scope.
7693  *
7694  * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
7695  * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
7696  * content and the `scope` is the newly created transclusion scope, which the clone will be linked to.
7697  *
7698  * <div class="alert alert-info">
7699  * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
7700  * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
7701  * </div>
7702  *
7703  * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
7704  * attach function**:
7705  *
7706  * ```js
7707  * var transcludedContent, transclusionScope;
7708  *
7709  * $transclude(function(clone, scope) {
7710  *   element.append(clone);
7711  *   transcludedContent = clone;
7712  *   transclusionScope = scope;
7713  * });
7714  * ```
7715  *
7716  * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
7717  * associated transclusion scope:
7718  *
7719  * ```js
7720  * transcludedContent.remove();
7721  * transclusionScope.$destroy();
7722  * ```
7723  *
7724  * <div class="alert alert-info">
7725  * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
7726  * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
7727  * then you are also responsible for calling `$destroy` on the transclusion scope.
7728  * </div>
7729  *
7730  * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
7731  * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
7732  * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
7733  *
7734  *
7735  * #### Transclusion Scopes
7736  *
7737  * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
7738  * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
7739  * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
7740  * was taken.
7741  *
7742  * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
7743  * like this:
7744  *
7745  * ```html
7746  * <div ng-app>
7747  *   <div isolate>
7748  *     <div transclusion>
7749  *     </div>
7750  *   </div>
7751  * </div>
7752  * ```
7753  *
7754  * The `$parent` scope hierarchy will look like this:
7755  *
7756    ```
7757    - $rootScope
7758      - isolate
7759        - transclusion
7760    ```
7761  *
7762  * but the scopes will inherit prototypically from different scopes to their `$parent`.
7763  *
7764    ```
7765    - $rootScope
7766      - transclusion
7767    - isolate
7768    ```
7769  *
7770  *
7771  * ### Attributes
7772  *
7773  * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
7774  * `link()` or `compile()` functions. It has a variety of uses.
7775  *
7776  * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
7777  *   'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
7778  *   to the attributes.
7779  *
7780  * * *Directive inter-communication:* All directives share the same instance of the attributes
7781  *   object which allows the directives to use the attributes object as inter directive
7782  *   communication.
7783  *
7784  * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
7785  *   allowing other directives to read the interpolated value.
7786  *
7787  * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
7788  *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
7789  *   the only way to easily get the actual value because during the linking phase the interpolation
7790  *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
7791  *
7792  * ```js
7793  * function linkingFn(scope, elm, attrs, ctrl) {
7794  *   // get the attribute value
7795  *   console.log(attrs.ngModel);
7796  *
7797  *   // change the attribute
7798  *   attrs.$set('ngModel', 'new value');
7799  *
7800  *   // observe changes to interpolated attribute
7801  *   attrs.$observe('ngModel', function(value) {
7802  *     console.log('ngModel has changed value to ' + value);
7803  *   });
7804  * }
7805  * ```
7806  *
7807  * ## Example
7808  *
7809  * <div class="alert alert-warning">
7810  * **Note**: Typically directives are registered with `module.directive`. The example below is
7811  * to illustrate how `$compile` works.
7812  * </div>
7813  *
7814  <example module="compileExample" name="compile">
7815    <file name="index.html">
7816     <script>
7817       angular.module('compileExample', [], function($compileProvider) {
7818         // configure new 'compile' directive by passing a directive
7819         // factory function. The factory function injects the '$compile'
7820         $compileProvider.directive('compile', function($compile) {
7821           // directive factory creates a link function
7822           return function(scope, element, attrs) {
7823             scope.$watch(
7824               function(scope) {
7825                  // watch the 'compile' expression for changes
7826                 return scope.$eval(attrs.compile);
7827               },
7828               function(value) {
7829                 // when the 'compile' expression changes
7830                 // assign it into the current DOM
7831                 element.html(value);
7832
7833                 // compile the new DOM and link it to the current
7834                 // scope.
7835                 // NOTE: we only compile .childNodes so that
7836                 // we don't get into infinite loop compiling ourselves
7837                 $compile(element.contents())(scope);
7838               }
7839             );
7840           };
7841         });
7842       })
7843       .controller('GreeterController', ['$scope', function($scope) {
7844         $scope.name = 'Angular';
7845         $scope.html = 'Hello {{name}}';
7846       }]);
7847     </script>
7848     <div ng-controller="GreeterController">
7849       <input ng-model="name"> <br/>
7850       <textarea ng-model="html"></textarea> <br/>
7851       <div compile="html"></div>
7852     </div>
7853    </file>
7854    <file name="protractor.js" type="protractor">
7855      it('should auto compile', function() {
7856        var textarea = $('textarea');
7857        var output = $('div[compile]');
7858        // The initial state reads 'Hello Angular'.
7859        expect(output.getText()).toBe('Hello Angular');
7860        textarea.clear();
7861        textarea.sendKeys('{{name}}!');
7862        expect(output.getText()).toBe('Angular!');
7863      });
7864    </file>
7865  </example>
7866
7867  *
7868  *
7869  * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7870  * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7871  *
7872  * <div class="alert alert-danger">
7873  * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7874  *   e.g. will not use the right outer scope. Please pass the transclude function as a
7875  *   `parentBoundTranscludeFn` to the link function instead.
7876  * </div>
7877  *
7878  * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7879  *                 root element(s), not their children)
7880  * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7881  * (a DOM element/tree) to a scope. Where:
7882  *
7883  *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7884  *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7885  *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
7886  *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7887  *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7888  *
7889  *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
7890  *      * `scope` - is the current scope with which the linking function is working with.
7891  *
7892  *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
7893  *  keys may be used to control linking behavior:
7894  *
7895  *      * `parentBoundTranscludeFn` - the transclude function made available to
7896  *        directives; if given, it will be passed through to the link functions of
7897  *        directives found in `element` during compilation.
7898  *      * `transcludeControllers` - an object hash with keys that map controller names
7899  *        to a hash with the key `instance`, which maps to the controller instance;
7900  *        if given, it will make the controllers available to directives on the compileNode:
7901  *        ```
7902  *        {
7903  *          parent: {
7904  *            instance: parentControllerInstance
7905  *          }
7906  *        }
7907  *        ```
7908  *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7909  *        the cloned elements; only needed for transcludes that are allowed to contain non html
7910  *        elements (e.g. SVG elements). See also the directive.controller property.
7911  *
7912  * Calling the linking function returns the element of the template. It is either the original
7913  * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7914  *
7915  * After linking the view is not updated until after a call to $digest which typically is done by
7916  * Angular automatically.
7917  *
7918  * If you need access to the bound view, there are two ways to do it:
7919  *
7920  * - If you are not asking the linking function to clone the template, create the DOM element(s)
7921  *   before you send them to the compiler and keep this reference around.
7922  *   ```js
7923  *     var element = $compile('<p>{{total}}</p>')(scope);
7924  *   ```
7925  *
7926  * - if on the other hand, you need the element to be cloned, the view reference from the original
7927  *   example would not point to the clone, but rather to the original template that was cloned. In
7928  *   this case, you can access the clone via the cloneAttachFn:
7929  *   ```js
7930  *     var templateElement = angular.element('<p>{{total}}</p>'),
7931  *         scope = ....;
7932  *
7933  *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7934  *       //attach the clone to DOM document at the right place
7935  *     });
7936  *
7937  *     //now we have reference to the cloned DOM via `clonedElement`
7938  *   ```
7939  *
7940  *
7941  * For information on how the compiler works, see the
7942  * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7943  *
7944  * @knownIssue
7945  *
7946  * ### Double Compilation
7947  *
7948    Double compilation occurs when an already compiled part of the DOM gets
7949    compiled again. This is an undesired effect and can lead to misbehaving directives, performance issues,
7950    and memory leaks. Refer to the Compiler Guide {@link guide/compiler#double-compilation-and-how-to-avoid-it
7951    section on double compilation} for an in-depth explanation and ways to avoid it.
7952  *
7953  */
7954
7955 var $compileMinErr = minErr('$compile');
7956
7957 function UNINITIALIZED_VALUE() {}
7958 var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
7959
7960 /**
7961  * @ngdoc provider
7962  * @name $compileProvider
7963  *
7964  * @description
7965  */
7966 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7967 /** @this */
7968 function $CompileProvider($provide, $$sanitizeUriProvider) {
7969   var hasDirectives = {},
7970       Suffix = 'Directive',
7971       COMMENT_DIRECTIVE_REGEXP = /^\s*directive:\s*([\w-]+)\s+(.*)$/,
7972       CLASS_DIRECTIVE_REGEXP = /(([\w-]+)(?::([^;]+))?;?)/,
7973       ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7974       REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7975
7976   // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7977   // The assumption is that future DOM event attribute names will begin with
7978   // 'on' and be composed of only English letters.
7979   var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7980   var bindingCache = createMap();
7981
7982   function parseIsolateBindings(scope, directiveName, isController) {
7983     var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/;
7984
7985     var bindings = createMap();
7986
7987     forEach(scope, function(definition, scopeName) {
7988       if (definition in bindingCache) {
7989         bindings[scopeName] = bindingCache[definition];
7990         return;
7991       }
7992       var match = definition.match(LOCAL_REGEXP);
7993
7994       if (!match) {
7995         throw $compileMinErr('iscp',
7996             'Invalid {3} for directive \'{0}\'.' +
7997             ' Definition: {... {1}: \'{2}\' ...}',
7998             directiveName, scopeName, definition,
7999             (isController ? 'controller bindings definition' :
8000             'isolate scope definition'));
8001       }
8002
8003       bindings[scopeName] = {
8004         mode: match[1][0],
8005         collection: match[2] === '*',
8006         optional: match[3] === '?',
8007         attrName: match[4] || scopeName
8008       };
8009       if (match[4]) {
8010         bindingCache[definition] = bindings[scopeName];
8011       }
8012     });
8013
8014     return bindings;
8015   }
8016
8017   function parseDirectiveBindings(directive, directiveName) {
8018     var bindings = {
8019       isolateScope: null,
8020       bindToController: null
8021     };
8022     if (isObject(directive.scope)) {
8023       if (directive.bindToController === true) {
8024         bindings.bindToController = parseIsolateBindings(directive.scope,
8025                                                          directiveName, true);
8026         bindings.isolateScope = {};
8027       } else {
8028         bindings.isolateScope = parseIsolateBindings(directive.scope,
8029                                                      directiveName, false);
8030       }
8031     }
8032     if (isObject(directive.bindToController)) {
8033       bindings.bindToController =
8034           parseIsolateBindings(directive.bindToController, directiveName, true);
8035     }
8036     if (bindings.bindToController && !directive.controller) {
8037       // There is no controller
8038       throw $compileMinErr('noctrl',
8039             'Cannot bind to controller without directive \'{0}\'s controller.',
8040             directiveName);
8041     }
8042     return bindings;
8043   }
8044
8045   function assertValidDirectiveName(name) {
8046     var letter = name.charAt(0);
8047     if (!letter || letter !== lowercase(letter)) {
8048       throw $compileMinErr('baddir', 'Directive/Component name \'{0}\' is invalid. The first character must be a lowercase letter', name);
8049     }
8050     if (name !== name.trim()) {
8051       throw $compileMinErr('baddir',
8052             'Directive/Component name \'{0}\' is invalid. The name should not contain leading or trailing whitespaces',
8053             name);
8054     }
8055   }
8056
8057   function getDirectiveRequire(directive) {
8058     var require = directive.require || (directive.controller && directive.name);
8059
8060     if (!isArray(require) && isObject(require)) {
8061       forEach(require, function(value, key) {
8062         var match = value.match(REQUIRE_PREFIX_REGEXP);
8063         var name = value.substring(match[0].length);
8064         if (!name) require[key] = match[0] + key;
8065       });
8066     }
8067
8068     return require;
8069   }
8070
8071   function getDirectiveRestrict(restrict, name) {
8072     if (restrict && !(isString(restrict) && /[EACM]/.test(restrict))) {
8073       throw $compileMinErr('badrestrict',
8074           'Restrict property \'{0}\' of directive \'{1}\' is invalid',
8075           restrict,
8076           name);
8077     }
8078
8079     return restrict || 'EA';
8080   }
8081
8082   /**
8083    * @ngdoc method
8084    * @name $compileProvider#directive
8085    * @kind function
8086    *
8087    * @description
8088    * Register a new directive with the compiler.
8089    *
8090    * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
8091    *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
8092    *    names and the values are the factories.
8093    * @param {Function|Array} directiveFactory An injectable directive factory function. See the
8094    *    {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
8095    * @returns {ng.$compileProvider} Self for chaining.
8096    */
8097   this.directive = function registerDirective(name, directiveFactory) {
8098     assertArg(name, 'name');
8099     assertNotHasOwnProperty(name, 'directive');
8100     if (isString(name)) {
8101       assertValidDirectiveName(name);
8102       assertArg(directiveFactory, 'directiveFactory');
8103       if (!hasDirectives.hasOwnProperty(name)) {
8104         hasDirectives[name] = [];
8105         $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
8106           function($injector, $exceptionHandler) {
8107             var directives = [];
8108             forEach(hasDirectives[name], function(directiveFactory, index) {
8109               try {
8110                 var directive = $injector.invoke(directiveFactory);
8111                 if (isFunction(directive)) {
8112                   directive = { compile: valueFn(directive) };
8113                 } else if (!directive.compile && directive.link) {
8114                   directive.compile = valueFn(directive.link);
8115                 }
8116                 directive.priority = directive.priority || 0;
8117                 directive.index = index;
8118                 directive.name = directive.name || name;
8119                 directive.require = getDirectiveRequire(directive);
8120                 directive.restrict = getDirectiveRestrict(directive.restrict, name);
8121                 directive.$$moduleName = directiveFactory.$$moduleName;
8122                 directives.push(directive);
8123               } catch (e) {
8124                 $exceptionHandler(e);
8125               }
8126             });
8127             return directives;
8128           }]);
8129       }
8130       hasDirectives[name].push(directiveFactory);
8131     } else {
8132       forEach(name, reverseParams(registerDirective));
8133     }
8134     return this;
8135   };
8136
8137   /**
8138    * @ngdoc method
8139    * @name $compileProvider#component
8140    * @module ng
8141    * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
8142    * @param {Object} options Component definition object (a simplified
8143    *    {@link ng.$compile#directive-definition-object directive definition object}),
8144    *    with the following properties (all optional):
8145    *
8146    *    - `controller` â€“ `{(string|function()=}` â€“ controller constructor function that should be
8147    *      associated with newly created scope or the name of a {@link ng.$compile#-controller-
8148    *      registered controller} if passed as a string. An empty `noop` function by default.
8149    *    - `controllerAs` â€“ `{string=}` â€“ identifier name for to reference the controller in the component's scope.
8150    *      If present, the controller will be published to scope under the `controllerAs` name.
8151    *      If not present, this will default to be `$ctrl`.
8152    *    - `template` â€“ `{string=|function()=}` â€“ html template as a string or a function that
8153    *      returns an html template as a string which should be used as the contents of this component.
8154    *      Empty string by default.
8155    *
8156    *      If `template` is a function, then it is {@link auto.$injector#invoke injected} with
8157    *      the following locals:
8158    *
8159    *      - `$element` - Current element
8160    *      - `$attrs` - Current attributes object for the element
8161    *
8162    *    - `templateUrl` â€“ `{string=|function()=}` â€“ path or function that returns a path to an html
8163    *      template that should be used  as the contents of this component.
8164    *
8165    *      If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
8166    *      the following locals:
8167    *
8168    *      - `$element` - Current element
8169    *      - `$attrs` - Current attributes object for the element
8170    *
8171    *    - `bindings` â€“ `{object=}` â€“ defines bindings between DOM attributes and component properties.
8172    *      Component properties are always bound to the component controller and not to the scope.
8173    *      See {@link ng.$compile#-bindtocontroller- `bindToController`}.
8174    *    - `transclude` â€“ `{boolean=}` â€“ whether {@link $compile#transclusion content transclusion} is enabled.
8175    *      Disabled by default.
8176    *    - `require` - `{Object<string, string>=}` - requires the controllers of other directives and binds them to
8177    *      this component's controller. The object keys specify the property names under which the required
8178    *      controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}.
8179    *    - `$...` â€“ additional properties to attach to the directive factory function and the controller
8180    *      constructor function. (This is used by the component router to annotate)
8181    *
8182    * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
8183    * @description
8184    * Register a **component definition** with the compiler. This is a shorthand for registering a special
8185    * type of directive, which represents a self-contained UI component in your application. Such components
8186    * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
8187    *
8188    * Component definitions are very simple and do not require as much configuration as defining general
8189    * directives. Component definitions usually consist only of a template and a controller backing it.
8190    *
8191    * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
8192    * `bindToController`. They always have **isolate scope** and are restricted to elements.
8193    *
8194    * Here are a few examples of how you would usually define components:
8195    *
8196    * ```js
8197    *   var myMod = angular.module(...);
8198    *   myMod.component('myComp', {
8199    *     template: '<div>My name is {{$ctrl.name}}</div>',
8200    *     controller: function() {
8201    *       this.name = 'shahar';
8202    *     }
8203    *   });
8204    *
8205    *   myMod.component('myComp', {
8206    *     template: '<div>My name is {{$ctrl.name}}</div>',
8207    *     bindings: {name: '@'}
8208    *   });
8209    *
8210    *   myMod.component('myComp', {
8211    *     templateUrl: 'views/my-comp.html',
8212    *     controller: 'MyCtrl',
8213    *     controllerAs: 'ctrl',
8214    *     bindings: {name: '@'}
8215    *   });
8216    *
8217    * ```
8218    * For more examples, and an in-depth guide, see the {@link guide/component component guide}.
8219    *
8220    * <br />
8221    * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
8222    */
8223   this.component = function registerComponent(name, options) {
8224     var controller = options.controller || function() {};
8225
8226     function factory($injector) {
8227       function makeInjectable(fn) {
8228         if (isFunction(fn) || isArray(fn)) {
8229           return /** @this */ function(tElement, tAttrs) {
8230             return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
8231           };
8232         } else {
8233           return fn;
8234         }
8235       }
8236
8237       var template = (!options.template && !options.templateUrl ? '' : options.template);
8238       var ddo = {
8239         controller: controller,
8240         controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
8241         template: makeInjectable(template),
8242         templateUrl: makeInjectable(options.templateUrl),
8243         transclude: options.transclude,
8244         scope: {},
8245         bindToController: options.bindings || {},
8246         restrict: 'E',
8247         require: options.require
8248       };
8249
8250       // Copy annotations (starting with $) over to the DDO
8251       forEach(options, function(val, key) {
8252         if (key.charAt(0) === '$') ddo[key] = val;
8253       });
8254
8255       return ddo;
8256     }
8257
8258     // TODO(pete) remove the following `forEach` before we release 1.6.0
8259     // The component-router@0.2.0 looks for the annotations on the controller constructor
8260     // Nothing in Angular looks for annotations on the factory function but we can't remove
8261     // it from 1.5.x yet.
8262
8263     // Copy any annotation properties (starting with $) over to the factory and controller constructor functions
8264     // These could be used by libraries such as the new component router
8265     forEach(options, function(val, key) {
8266       if (key.charAt(0) === '$') {
8267         factory[key] = val;
8268         // Don't try to copy over annotations to named controller
8269         if (isFunction(controller)) controller[key] = val;
8270       }
8271     });
8272
8273     factory.$inject = ['$injector'];
8274
8275     return this.directive(name, factory);
8276   };
8277
8278
8279   /**
8280    * @ngdoc method
8281    * @name $compileProvider#aHrefSanitizationWhitelist
8282    * @kind function
8283    *
8284    * @description
8285    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
8286    * urls during a[href] sanitization.
8287    *
8288    * The sanitization is a security measure aimed at preventing XSS attacks via html links.
8289    *
8290    * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
8291    * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
8292    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
8293    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
8294    *
8295    * @param {RegExp=} regexp New regexp to whitelist urls with.
8296    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
8297    *    chaining otherwise.
8298    */
8299   this.aHrefSanitizationWhitelist = function(regexp) {
8300     if (isDefined(regexp)) {
8301       $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
8302       return this;
8303     } else {
8304       return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
8305     }
8306   };
8307
8308
8309   /**
8310    * @ngdoc method
8311    * @name $compileProvider#imgSrcSanitizationWhitelist
8312    * @kind function
8313    *
8314    * @description
8315    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
8316    * urls during img[src] sanitization.
8317    *
8318    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
8319    *
8320    * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
8321    * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
8322    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
8323    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
8324    *
8325    * @param {RegExp=} regexp New regexp to whitelist urls with.
8326    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
8327    *    chaining otherwise.
8328    */
8329   this.imgSrcSanitizationWhitelist = function(regexp) {
8330     if (isDefined(regexp)) {
8331       $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
8332       return this;
8333     } else {
8334       return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
8335     }
8336   };
8337
8338   /**
8339    * @ngdoc method
8340    * @name  $compileProvider#debugInfoEnabled
8341    *
8342    * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
8343    * current debugInfoEnabled state
8344    * @returns {*} current value if used as getter or itself (chaining) if used as setter
8345    *
8346    * @kind function
8347    *
8348    * @description
8349    * Call this method to enable/disable various debug runtime information in the compiler such as adding
8350    * binding information and a reference to the current scope on to DOM elements.
8351    * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
8352    * * `ng-binding` CSS class
8353    * * `$binding` data property containing an array of the binding expressions
8354    *
8355    * You may want to disable this in production for a significant performance boost. See
8356    * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
8357    *
8358    * The default value is true.
8359    */
8360   var debugInfoEnabled = true;
8361   this.debugInfoEnabled = function(enabled) {
8362     if (isDefined(enabled)) {
8363       debugInfoEnabled = enabled;
8364       return this;
8365     }
8366     return debugInfoEnabled;
8367   };
8368
8369   /**
8370    * @ngdoc method
8371    * @name  $compileProvider#preAssignBindingsEnabled
8372    *
8373    * @param {boolean=} enabled update the preAssignBindingsEnabled state if provided, otherwise just return the
8374    * current preAssignBindingsEnabled state
8375    * @returns {*} current value if used as getter or itself (chaining) if used as setter
8376    *
8377    * @kind function
8378    *
8379    * @description
8380    * Call this method to enable/disable whether directive controllers are assigned bindings before
8381    * calling the controller's constructor.
8382    * If enabled (true), the compiler assigns the value of each of the bindings to the
8383    * properties of the controller object before the constructor of this object is called.
8384    *
8385    * If disabled (false), the compiler calls the constructor first before assigning bindings.
8386    *
8387    * The default value is true in Angular 1.5.x but will switch to false in Angular 1.6.x.
8388    */
8389   var preAssignBindingsEnabled = false;
8390   this.preAssignBindingsEnabled = function(enabled) {
8391     if (isDefined(enabled)) {
8392       preAssignBindingsEnabled = enabled;
8393       return this;
8394     }
8395     return preAssignBindingsEnabled;
8396   };
8397
8398
8399   var TTL = 10;
8400   /**
8401    * @ngdoc method
8402    * @name $compileProvider#onChangesTtl
8403    * @description
8404    *
8405    * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
8406    * assuming that the model is unstable.
8407    *
8408    * The current default is 10 iterations.
8409    *
8410    * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
8411    * in several iterations of calls to these hooks. However if an application needs more than the default 10
8412    * iterations to stabilize then you should investigate what is causing the model to continuously change during
8413    * the `$onChanges` hook execution.
8414    *
8415    * Increasing the TTL could have performance implications, so you should not change it without proper justification.
8416    *
8417    * @param {number} limit The number of `$onChanges` hook iterations.
8418    * @returns {number|object} the current limit (or `this` if called as a setter for chaining)
8419    */
8420   this.onChangesTtl = function(value) {
8421     if (arguments.length) {
8422       TTL = value;
8423       return this;
8424     }
8425     return TTL;
8426   };
8427
8428   var commentDirectivesEnabledConfig = true;
8429   /**
8430    * @ngdoc method
8431    * @name $compileProvider#commentDirectivesEnabled
8432    * @description
8433    *
8434    * It indicates to the compiler
8435    * whether or not directives on comments should be compiled.
8436    * Defaults to `true`.
8437    *
8438    * Calling this function with false disables the compilation of directives
8439    * on comments for the whole application.
8440    * This results in a compilation performance gain,
8441    * as the compiler doesn't have to check comments when looking for directives.
8442    * This should however only be used if you are sure that no comment directives are used in
8443    * the application (including any 3rd party directives).
8444    *
8445    * @param {boolean} enabled `false` if the compiler may ignore directives on comments
8446    * @returns {boolean|object} the current value (or `this` if called as a setter for chaining)
8447    */
8448   this.commentDirectivesEnabled = function(value) {
8449     if (arguments.length) {
8450       commentDirectivesEnabledConfig = value;
8451       return this;
8452     }
8453     return commentDirectivesEnabledConfig;
8454   };
8455
8456
8457   var cssClassDirectivesEnabledConfig = true;
8458   /**
8459    * @ngdoc method
8460    * @name $compileProvider#cssClassDirectivesEnabled
8461    * @description
8462    *
8463    * It indicates to the compiler
8464    * whether or not directives on element classes should be compiled.
8465    * Defaults to `true`.
8466    *
8467    * Calling this function with false disables the compilation of directives
8468    * on element classes for the whole application.
8469    * This results in a compilation performance gain,
8470    * as the compiler doesn't have to check element classes when looking for directives.
8471    * This should however only be used if you are sure that no class directives are used in
8472    * the application (including any 3rd party directives).
8473    *
8474    * @param {boolean} enabled `false` if the compiler may ignore directives on element classes
8475    * @returns {boolean|object} the current value (or `this` if called as a setter for chaining)
8476    */
8477   this.cssClassDirectivesEnabled = function(value) {
8478     if (arguments.length) {
8479       cssClassDirectivesEnabledConfig = value;
8480       return this;
8481     }
8482     return cssClassDirectivesEnabledConfig;
8483   };
8484
8485   this.$get = [
8486             '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
8487             '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
8488     function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
8489              $controller,   $rootScope,   $sce,   $animate,   $$sanitizeUri) {
8490
8491     var SIMPLE_ATTR_NAME = /^\w/;
8492     var specialAttrHolder = window.document.createElement('div');
8493
8494
8495     var commentDirectivesEnabled = commentDirectivesEnabledConfig;
8496     var cssClassDirectivesEnabled = cssClassDirectivesEnabledConfig;
8497
8498
8499     var onChangesTtl = TTL;
8500     // The onChanges hooks should all be run together in a single digest
8501     // When changes occur, the call to trigger their hooks will be added to this queue
8502     var onChangesQueue;
8503
8504     // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
8505     function flushOnChangesQueue() {
8506       try {
8507         if (!(--onChangesTtl)) {
8508           // We have hit the TTL limit so reset everything
8509           onChangesQueue = undefined;
8510           throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
8511         }
8512         // We must run this hook in an apply since the $$postDigest runs outside apply
8513         $rootScope.$apply(function() {
8514           var errors = [];
8515           for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
8516             try {
8517               onChangesQueue[i]();
8518             } catch (e) {
8519               errors.push(e);
8520             }
8521           }
8522           // Reset the queue to trigger a new schedule next time there is a change
8523           onChangesQueue = undefined;
8524           if (errors.length) {
8525             throw errors;
8526           }
8527         });
8528       } finally {
8529         onChangesTtl++;
8530       }
8531     }
8532
8533
8534     function Attributes(element, attributesToCopy) {
8535       if (attributesToCopy) {
8536         var keys = Object.keys(attributesToCopy);
8537         var i, l, key;
8538
8539         for (i = 0, l = keys.length; i < l; i++) {
8540           key = keys[i];
8541           this[key] = attributesToCopy[key];
8542         }
8543       } else {
8544         this.$attr = {};
8545       }
8546
8547       this.$$element = element;
8548     }
8549
8550     Attributes.prototype = {
8551       /**
8552        * @ngdoc method
8553        * @name $compile.directive.Attributes#$normalize
8554        * @kind function
8555        *
8556        * @description
8557        * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
8558        * `data-`) to its normalized, camelCase form.
8559        *
8560        * Also there is special case for Moz prefix starting with upper case letter.
8561        *
8562        * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
8563        *
8564        * @param {string} name Name to normalize
8565        */
8566       $normalize: directiveNormalize,
8567
8568
8569       /**
8570        * @ngdoc method
8571        * @name $compile.directive.Attributes#$addClass
8572        * @kind function
8573        *
8574        * @description
8575        * Adds the CSS class value specified by the classVal parameter to the element. If animations
8576        * are enabled then an animation will be triggered for the class addition.
8577        *
8578        * @param {string} classVal The className value that will be added to the element
8579        */
8580       $addClass: function(classVal) {
8581         if (classVal && classVal.length > 0) {
8582           $animate.addClass(this.$$element, classVal);
8583         }
8584       },
8585
8586       /**
8587        * @ngdoc method
8588        * @name $compile.directive.Attributes#$removeClass
8589        * @kind function
8590        *
8591        * @description
8592        * Removes the CSS class value specified by the classVal parameter from the element. If
8593        * animations are enabled then an animation will be triggered for the class removal.
8594        *
8595        * @param {string} classVal The className value that will be removed from the element
8596        */
8597       $removeClass: function(classVal) {
8598         if (classVal && classVal.length > 0) {
8599           $animate.removeClass(this.$$element, classVal);
8600         }
8601       },
8602
8603       /**
8604        * @ngdoc method
8605        * @name $compile.directive.Attributes#$updateClass
8606        * @kind function
8607        *
8608        * @description
8609        * Adds and removes the appropriate CSS class values to the element based on the difference
8610        * between the new and old CSS class values (specified as newClasses and oldClasses).
8611        *
8612        * @param {string} newClasses The current CSS className value
8613        * @param {string} oldClasses The former CSS className value
8614        */
8615       $updateClass: function(newClasses, oldClasses) {
8616         var toAdd = tokenDifference(newClasses, oldClasses);
8617         if (toAdd && toAdd.length) {
8618           $animate.addClass(this.$$element, toAdd);
8619         }
8620
8621         var toRemove = tokenDifference(oldClasses, newClasses);
8622         if (toRemove && toRemove.length) {
8623           $animate.removeClass(this.$$element, toRemove);
8624         }
8625       },
8626
8627       /**
8628        * Set a normalized attribute on the element in a way such that all directives
8629        * can share the attribute. This function properly handles boolean attributes.
8630        * @param {string} key Normalized key. (ie ngAttribute)
8631        * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
8632        * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
8633        *     Defaults to true.
8634        * @param {string=} attrName Optional none normalized name. Defaults to key.
8635        */
8636       $set: function(key, value, writeAttr, attrName) {
8637         // TODO: decide whether or not to throw an error if "class"
8638         //is set through this function since it may cause $updateClass to
8639         //become unstable.
8640
8641         var node = this.$$element[0],
8642             booleanKey = getBooleanAttrName(node, key),
8643             aliasedKey = getAliasedAttrName(key),
8644             observer = key,
8645             nodeName;
8646
8647         if (booleanKey) {
8648           this.$$element.prop(key, value);
8649           attrName = booleanKey;
8650         } else if (aliasedKey) {
8651           this[aliasedKey] = value;
8652           observer = aliasedKey;
8653         }
8654
8655         this[key] = value;
8656
8657         // translate normalized key to actual key
8658         if (attrName) {
8659           this.$attr[key] = attrName;
8660         } else {
8661           attrName = this.$attr[key];
8662           if (!attrName) {
8663             this.$attr[key] = attrName = snake_case(key, '-');
8664           }
8665         }
8666
8667         nodeName = nodeName_(this.$$element);
8668
8669         if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
8670             (nodeName === 'img' && key === 'src')) {
8671           // sanitize a[href] and img[src] values
8672           this[key] = value = $$sanitizeUri(value, key === 'src');
8673         } else if (nodeName === 'img' && key === 'srcset' && isDefined(value)) {
8674           // sanitize img[srcset] values
8675           var result = '';
8676
8677           // first check if there are spaces because it's not the same pattern
8678           var trimmedSrcset = trim(value);
8679           //                (   999x   ,|   999w   ,|   ,|,   )
8680           var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
8681           var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
8682
8683           // split srcset into tuple of uri and descriptor except for the last item
8684           var rawUris = trimmedSrcset.split(pattern);
8685
8686           // for each tuples
8687           var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
8688           for (var i = 0; i < nbrUrisWith2parts; i++) {
8689             var innerIdx = i * 2;
8690             // sanitize the uri
8691             result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
8692             // add the descriptor
8693             result += (' ' + trim(rawUris[innerIdx + 1]));
8694           }
8695
8696           // split the last item into uri and descriptor
8697           var lastTuple = trim(rawUris[i * 2]).split(/\s/);
8698
8699           // sanitize the last uri
8700           result += $$sanitizeUri(trim(lastTuple[0]), true);
8701
8702           // and add the last descriptor if any
8703           if (lastTuple.length === 2) {
8704             result += (' ' + trim(lastTuple[1]));
8705           }
8706           this[key] = value = result;
8707         }
8708
8709         if (writeAttr !== false) {
8710           if (value === null || isUndefined(value)) {
8711             this.$$element.removeAttr(attrName);
8712           } else {
8713             if (SIMPLE_ATTR_NAME.test(attrName)) {
8714               this.$$element.attr(attrName, value);
8715             } else {
8716               setSpecialAttr(this.$$element[0], attrName, value);
8717             }
8718           }
8719         }
8720
8721         // fire observers
8722         var $$observers = this.$$observers;
8723         if ($$observers) {
8724           forEach($$observers[observer], function(fn) {
8725             try {
8726               fn(value);
8727             } catch (e) {
8728               $exceptionHandler(e);
8729             }
8730           });
8731         }
8732       },
8733
8734
8735       /**
8736        * @ngdoc method
8737        * @name $compile.directive.Attributes#$observe
8738        * @kind function
8739        *
8740        * @description
8741        * Observes an interpolated attribute.
8742        *
8743        * The observer function will be invoked once during the next `$digest` following
8744        * compilation. The observer is then invoked whenever the interpolated value
8745        * changes.
8746        *
8747        * @param {string} key Normalized key. (ie ngAttribute) .
8748        * @param {function(interpolatedValue)} fn Function that will be called whenever
8749                 the interpolated value of the attribute changes.
8750        *        See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
8751        *        guide} for more info.
8752        * @returns {function()} Returns a deregistration function for this observer.
8753        */
8754       $observe: function(key, fn) {
8755         var attrs = this,
8756             $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
8757             listeners = ($$observers[key] || ($$observers[key] = []));
8758
8759         listeners.push(fn);
8760         $rootScope.$evalAsync(function() {
8761           if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
8762             // no one registered attribute interpolation function, so lets call it manually
8763             fn(attrs[key]);
8764           }
8765         });
8766
8767         return function() {
8768           arrayRemove(listeners, fn);
8769         };
8770       }
8771     };
8772
8773     function setSpecialAttr(element, attrName, value) {
8774       // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
8775       // so we have to jump through some hoops to get such an attribute
8776       // https://github.com/angular/angular.js/pull/13318
8777       specialAttrHolder.innerHTML = '<span ' + attrName + '>';
8778       var attributes = specialAttrHolder.firstChild.attributes;
8779       var attribute = attributes[0];
8780       // We have to remove the attribute from its container element before we can add it to the destination element
8781       attributes.removeNamedItem(attribute.name);
8782       attribute.value = value;
8783       element.attributes.setNamedItem(attribute);
8784     }
8785
8786     function safeAddClass($element, className) {
8787       try {
8788         $element.addClass(className);
8789       } catch (e) {
8790         // ignore, since it means that we are trying to set class on
8791         // SVG element, where class name is read-only.
8792       }
8793     }
8794
8795
8796     var startSymbol = $interpolate.startSymbol(),
8797         endSymbol = $interpolate.endSymbol(),
8798         denormalizeTemplate = (startSymbol === '{{' && endSymbol  === '}}')
8799             ? identity
8800             : function denormalizeTemplate(template) {
8801               return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
8802         },
8803         NG_ATTR_BINDING = /^ngAttr[A-Z]/;
8804     var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
8805
8806     compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
8807       var bindings = $element.data('$binding') || [];
8808
8809       if (isArray(binding)) {
8810         bindings = bindings.concat(binding);
8811       } else {
8812         bindings.push(binding);
8813       }
8814
8815       $element.data('$binding', bindings);
8816     } : noop;
8817
8818     compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
8819       safeAddClass($element, 'ng-binding');
8820     } : noop;
8821
8822     compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
8823       var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
8824       $element.data(dataName, scope);
8825     } : noop;
8826
8827     compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
8828       safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
8829     } : noop;
8830
8831     compile.$$createComment = function(directiveName, comment) {
8832       var content = '';
8833       if (debugInfoEnabled) {
8834         content = ' ' + (directiveName || '') + ': ';
8835         if (comment) content += comment + ' ';
8836       }
8837       return window.document.createComment(content);
8838     };
8839
8840     return compile;
8841
8842     //================================
8843
8844     function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
8845                         previousCompileContext) {
8846       if (!($compileNodes instanceof jqLite)) {
8847         // jquery always rewraps, whereas we need to preserve the original selector so that we can
8848         // modify it.
8849         $compileNodes = jqLite($compileNodes);
8850       }
8851       var compositeLinkFn =
8852               compileNodes($compileNodes, transcludeFn, $compileNodes,
8853                            maxPriority, ignoreDirective, previousCompileContext);
8854       compile.$$addScopeClass($compileNodes);
8855       var namespace = null;
8856       return function publicLinkFn(scope, cloneConnectFn, options) {
8857         if (!$compileNodes) {
8858           throw $compileMinErr('multilink', 'This element has already been linked.');
8859         }
8860         assertArg(scope, 'scope');
8861
8862         if (previousCompileContext && previousCompileContext.needsNewScope) {
8863           // A parent directive did a replace and a directive on this element asked
8864           // for transclusion, which caused us to lose a layer of element on which
8865           // we could hold the new transclusion scope, so we will create it manually
8866           // here.
8867           scope = scope.$parent.$new();
8868         }
8869
8870         options = options || {};
8871         var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
8872           transcludeControllers = options.transcludeControllers,
8873           futureParentElement = options.futureParentElement;
8874
8875         // When `parentBoundTranscludeFn` is passed, it is a
8876         // `controllersBoundTransclude` function (it was previously passed
8877         // as `transclude` to directive.link) so we must unwrap it to get
8878         // its `boundTranscludeFn`
8879         if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
8880           parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
8881         }
8882
8883         if (!namespace) {
8884           namespace = detectNamespaceForChildElements(futureParentElement);
8885         }
8886         var $linkNode;
8887         if (namespace !== 'html') {
8888           // When using a directive with replace:true and templateUrl the $compileNodes
8889           // (or a child element inside of them)
8890           // might change, so we need to recreate the namespace adapted compileNodes
8891           // for call to the link function.
8892           // Note: This will already clone the nodes...
8893           $linkNode = jqLite(
8894             wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
8895           );
8896         } else if (cloneConnectFn) {
8897           // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
8898           // and sometimes changes the structure of the DOM.
8899           $linkNode = JQLitePrototype.clone.call($compileNodes);
8900         } else {
8901           $linkNode = $compileNodes;
8902         }
8903
8904         if (transcludeControllers) {
8905           for (var controllerName in transcludeControllers) {
8906             $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
8907           }
8908         }
8909
8910         compile.$$addScopeInfo($linkNode, scope);
8911
8912         if (cloneConnectFn) cloneConnectFn($linkNode, scope);
8913         if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
8914
8915         if (!cloneConnectFn) {
8916           $compileNodes = compositeLinkFn = null;
8917         }
8918         return $linkNode;
8919       };
8920     }
8921
8922     function detectNamespaceForChildElements(parentElement) {
8923       // TODO: Make this detect MathML as well...
8924       var node = parentElement && parentElement[0];
8925       if (!node) {
8926         return 'html';
8927       } else {
8928         return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
8929       }
8930     }
8931
8932     /**
8933      * Compile function matches each node in nodeList against the directives. Once all directives
8934      * for a particular node are collected their compile functions are executed. The compile
8935      * functions return values - the linking functions - are combined into a composite linking
8936      * function, which is the a linking function for the node.
8937      *
8938      * @param {NodeList} nodeList an array of nodes or NodeList to compile
8939      * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8940      *        scope argument is auto-generated to the new child of the transcluded parent scope.
8941      * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
8942      *        the rootElement must be set the jqLite collection of the compile root. This is
8943      *        needed so that the jqLite collection items can be replaced with widgets.
8944      * @param {number=} maxPriority Max directive priority.
8945      * @returns {Function} A composite linking function of all of the matched directives or null.
8946      */
8947     function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
8948                             previousCompileContext) {
8949       var linkFns = [],
8950           // `nodeList` can be either an element's `.childNodes` (live NodeList)
8951           // or a jqLite/jQuery collection or an array
8952           notLiveList = isArray(nodeList) || (nodeList instanceof jqLite),
8953           attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
8954
8955
8956       for (var i = 0; i < nodeList.length; i++) {
8957         attrs = new Attributes();
8958
8959         // Support: IE 11 only
8960         // Workaround for #11781 and #14924
8961         if (msie === 11) {
8962           mergeConsecutiveTextNodes(nodeList, i, notLiveList);
8963         }
8964
8965         // We must always refer to `nodeList[i]` hereafter,
8966         // since the nodes can be replaced underneath us.
8967         directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
8968                                         ignoreDirective);
8969
8970         nodeLinkFn = (directives.length)
8971             ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
8972                                       null, [], [], previousCompileContext)
8973             : null;
8974
8975         if (nodeLinkFn && nodeLinkFn.scope) {
8976           compile.$$addScopeClass(attrs.$$element);
8977         }
8978
8979         childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
8980                       !(childNodes = nodeList[i].childNodes) ||
8981                       !childNodes.length)
8982             ? null
8983             : compileNodes(childNodes,
8984                  nodeLinkFn ? (
8985                   (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
8986                      && nodeLinkFn.transclude) : transcludeFn);
8987
8988         if (nodeLinkFn || childLinkFn) {
8989           linkFns.push(i, nodeLinkFn, childLinkFn);
8990           linkFnFound = true;
8991           nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
8992         }
8993
8994         //use the previous context only for the first element in the virtual group
8995         previousCompileContext = null;
8996       }
8997
8998       // return a linking function if we have found anything, null otherwise
8999       return linkFnFound ? compositeLinkFn : null;
9000
9001       function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
9002         var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
9003         var stableNodeList;
9004
9005
9006         if (nodeLinkFnFound) {
9007           // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
9008           // offsets don't get screwed up
9009           var nodeListLength = nodeList.length;
9010           stableNodeList = new Array(nodeListLength);
9011
9012           // create a sparse array by only copying the elements which have a linkFn
9013           for (i = 0; i < linkFns.length; i += 3) {
9014             idx = linkFns[i];
9015             stableNodeList[idx] = nodeList[idx];
9016           }
9017         } else {
9018           stableNodeList = nodeList;
9019         }
9020
9021         for (i = 0, ii = linkFns.length; i < ii;) {
9022           node = stableNodeList[linkFns[i++]];
9023           nodeLinkFn = linkFns[i++];
9024           childLinkFn = linkFns[i++];
9025
9026           if (nodeLinkFn) {
9027             if (nodeLinkFn.scope) {
9028               childScope = scope.$new();
9029               compile.$$addScopeInfo(jqLite(node), childScope);
9030             } else {
9031               childScope = scope;
9032             }
9033
9034             if (nodeLinkFn.transcludeOnThisElement) {
9035               childBoundTranscludeFn = createBoundTranscludeFn(
9036                   scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
9037
9038             } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
9039               childBoundTranscludeFn = parentBoundTranscludeFn;
9040
9041             } else if (!parentBoundTranscludeFn && transcludeFn) {
9042               childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
9043
9044             } else {
9045               childBoundTranscludeFn = null;
9046             }
9047
9048             nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
9049
9050           } else if (childLinkFn) {
9051             childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
9052           }
9053         }
9054       }
9055     }
9056
9057     function mergeConsecutiveTextNodes(nodeList, idx, notLiveList) {
9058       var node = nodeList[idx];
9059       var parent = node.parentNode;
9060       var sibling;
9061
9062       if (node.nodeType !== NODE_TYPE_TEXT) {
9063         return;
9064       }
9065
9066       while (true) {
9067         sibling = parent ? node.nextSibling : nodeList[idx + 1];
9068         if (!sibling || sibling.nodeType !== NODE_TYPE_TEXT) {
9069           break;
9070         }
9071
9072         node.nodeValue = node.nodeValue + sibling.nodeValue;
9073
9074         if (sibling.parentNode) {
9075           sibling.parentNode.removeChild(sibling);
9076         }
9077         if (notLiveList && sibling === nodeList[idx + 1]) {
9078           nodeList.splice(idx + 1, 1);
9079         }
9080       }
9081     }
9082
9083     function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
9084       function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
9085
9086         if (!transcludedScope) {
9087           transcludedScope = scope.$new(false, containingScope);
9088           transcludedScope.$$transcluded = true;
9089         }
9090
9091         return transcludeFn(transcludedScope, cloneFn, {
9092           parentBoundTranscludeFn: previousBoundTranscludeFn,
9093           transcludeControllers: controllers,
9094           futureParentElement: futureParentElement
9095         });
9096       }
9097
9098       // We need  to attach the transclusion slots onto the `boundTranscludeFn`
9099       // so that they are available inside the `controllersBoundTransclude` function
9100       var boundSlots = boundTranscludeFn.$$slots = createMap();
9101       for (var slotName in transcludeFn.$$slots) {
9102         if (transcludeFn.$$slots[slotName]) {
9103           boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
9104         } else {
9105           boundSlots[slotName] = null;
9106         }
9107       }
9108
9109       return boundTranscludeFn;
9110     }
9111
9112     /**
9113      * Looks for directives on the given node and adds them to the directive collection which is
9114      * sorted.
9115      *
9116      * @param node Node to search.
9117      * @param directives An array to which the directives are added to. This array is sorted before
9118      *        the function returns.
9119      * @param attrs The shared attrs object which is used to populate the normalized attributes.
9120      * @param {number=} maxPriority Max directive priority.
9121      */
9122     function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
9123       var nodeType = node.nodeType,
9124           attrsMap = attrs.$attr,
9125           match,
9126           nodeName,
9127           className;
9128
9129       switch (nodeType) {
9130         case NODE_TYPE_ELEMENT: /* Element */
9131
9132           nodeName = nodeName_(node);
9133
9134           // use the node name: <directive>
9135           addDirective(directives,
9136               directiveNormalize(nodeName), 'E', maxPriority, ignoreDirective);
9137
9138           // iterate over the attributes
9139           for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
9140                    j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
9141             var attrStartName = false;
9142             var attrEndName = false;
9143
9144             attr = nAttrs[j];
9145             name = attr.name;
9146             value = attr.value;
9147
9148             // support ngAttr attribute binding
9149             ngAttrName = directiveNormalize(name);
9150             isNgAttr = NG_ATTR_BINDING.test(ngAttrName);
9151             if (isNgAttr) {
9152               name = name.replace(PREFIX_REGEXP, '')
9153                 .substr(8).replace(/_(.)/g, function(match, letter) {
9154                   return letter.toUpperCase();
9155                 });
9156             }
9157
9158             var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
9159             if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
9160               attrStartName = name;
9161               attrEndName = name.substr(0, name.length - 5) + 'end';
9162               name = name.substr(0, name.length - 6);
9163             }
9164
9165             nName = directiveNormalize(name.toLowerCase());
9166             attrsMap[nName] = name;
9167             if (isNgAttr || !attrs.hasOwnProperty(nName)) {
9168                 attrs[nName] = value;
9169                 if (getBooleanAttrName(node, nName)) {
9170                   attrs[nName] = true; // presence means true
9171                 }
9172             }
9173             addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
9174             addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
9175                           attrEndName);
9176           }
9177
9178           if (nodeName === 'input' && node.getAttribute('type') === 'hidden') {
9179             // Hidden input elements can have strange behaviour when navigating back to the page
9180             // This tells the browser not to try to cache and reinstate previous values
9181             node.setAttribute('autocomplete', 'off');
9182           }
9183
9184           // use class as directive
9185           if (!cssClassDirectivesEnabled) break;
9186           className = node.className;
9187           if (isObject(className)) {
9188               // Maybe SVGAnimatedString
9189               className = className.animVal;
9190           }
9191           if (isString(className) && className !== '') {
9192             while ((match = CLASS_DIRECTIVE_REGEXP.exec(className))) {
9193               nName = directiveNormalize(match[2]);
9194               if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
9195                 attrs[nName] = trim(match[3]);
9196               }
9197               className = className.substr(match.index + match[0].length);
9198             }
9199           }
9200           break;
9201         case NODE_TYPE_TEXT: /* Text Node */
9202           addTextInterpolateDirective(directives, node.nodeValue);
9203           break;
9204         case NODE_TYPE_COMMENT: /* Comment */
9205           if (!commentDirectivesEnabled) break;
9206           collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective);
9207           break;
9208       }
9209
9210       directives.sort(byPriority);
9211       return directives;
9212     }
9213
9214     function collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
9215       // function created because of performance, try/catch disables
9216       // the optimization of the whole function #14848
9217       try {
9218         var match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
9219         if (match) {
9220           var nName = directiveNormalize(match[1]);
9221           if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
9222             attrs[nName] = trim(match[2]);
9223           }
9224         }
9225       } catch (e) {
9226         // turns out that under some circumstances IE9 throws errors when one attempts to read
9227         // comment's node value.
9228         // Just ignore it and continue. (Can't seem to reproduce in test case.)
9229       }
9230     }
9231
9232     /**
9233      * Given a node with a directive-start it collects all of the siblings until it finds
9234      * directive-end.
9235      * @param node
9236      * @param attrStart
9237      * @param attrEnd
9238      * @returns {*}
9239      */
9240     function groupScan(node, attrStart, attrEnd) {
9241       var nodes = [];
9242       var depth = 0;
9243       if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
9244         do {
9245           if (!node) {
9246             throw $compileMinErr('uterdir',
9247                       'Unterminated attribute, found \'{0}\' but no matching \'{1}\' found.',
9248                       attrStart, attrEnd);
9249           }
9250           if (node.nodeType === NODE_TYPE_ELEMENT) {
9251             if (node.hasAttribute(attrStart)) depth++;
9252             if (node.hasAttribute(attrEnd)) depth--;
9253           }
9254           nodes.push(node);
9255           node = node.nextSibling;
9256         } while (depth > 0);
9257       } else {
9258         nodes.push(node);
9259       }
9260
9261       return jqLite(nodes);
9262     }
9263
9264     /**
9265      * Wrapper for linking function which converts normal linking function into a grouped
9266      * linking function.
9267      * @param linkFn
9268      * @param attrStart
9269      * @param attrEnd
9270      * @returns {Function}
9271      */
9272     function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
9273       return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
9274         element = groupScan(element[0], attrStart, attrEnd);
9275         return linkFn(scope, element, attrs, controllers, transcludeFn);
9276       };
9277     }
9278
9279     /**
9280      * A function generator that is used to support both eager and lazy compilation
9281      * linking function.
9282      * @param eager
9283      * @param $compileNodes
9284      * @param transcludeFn
9285      * @param maxPriority
9286      * @param ignoreDirective
9287      * @param previousCompileContext
9288      * @returns {Function}
9289      */
9290     function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
9291       var compiled;
9292
9293       if (eager) {
9294         return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
9295       }
9296       return /** @this */ function lazyCompilation() {
9297         if (!compiled) {
9298           compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
9299
9300           // Null out all of these references in order to make them eligible for garbage collection
9301           // since this is a potentially long lived closure
9302           $compileNodes = transcludeFn = previousCompileContext = null;
9303         }
9304         return compiled.apply(this, arguments);
9305       };
9306     }
9307
9308     /**
9309      * Once the directives have been collected, their compile functions are executed. This method
9310      * is responsible for inlining directive templates as well as terminating the application
9311      * of the directives if the terminal directive has been reached.
9312      *
9313      * @param {Array} directives Array of collected directives to execute their compile function.
9314      *        this needs to be pre-sorted by priority order.
9315      * @param {Node} compileNode The raw DOM node to apply the compile functions to
9316      * @param {Object} templateAttrs The shared attribute function
9317      * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
9318      *                                                  scope argument is auto-generated to the new
9319      *                                                  child of the transcluded parent scope.
9320      * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
9321      *                              argument has the root jqLite array so that we can replace nodes
9322      *                              on it.
9323      * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
9324      *                                           compiling the transclusion.
9325      * @param {Array.<Function>} preLinkFns
9326      * @param {Array.<Function>} postLinkFns
9327      * @param {Object} previousCompileContext Context used for previous compilation of the current
9328      *                                        node
9329      * @returns {Function} linkFn
9330      */
9331     function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
9332                                    jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
9333                                    previousCompileContext) {
9334       previousCompileContext = previousCompileContext || {};
9335
9336       var terminalPriority = -Number.MAX_VALUE,
9337           newScopeDirective = previousCompileContext.newScopeDirective,
9338           controllerDirectives = previousCompileContext.controllerDirectives,
9339           newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
9340           templateDirective = previousCompileContext.templateDirective,
9341           nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
9342           hasTranscludeDirective = false,
9343           hasTemplate = false,
9344           hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
9345           $compileNode = templateAttrs.$$element = jqLite(compileNode),
9346           directive,
9347           directiveName,
9348           $template,
9349           replaceDirective = originalReplaceDirective,
9350           childTranscludeFn = transcludeFn,
9351           linkFn,
9352           didScanForMultipleTransclusion = false,
9353           mightHaveMultipleTransclusionError = false,
9354           directiveValue;
9355
9356       // executes all directives on the current element
9357       for (var i = 0, ii = directives.length; i < ii; i++) {
9358         directive = directives[i];
9359         var attrStart = directive.$$start;
9360         var attrEnd = directive.$$end;
9361
9362         // collect multiblock sections
9363         if (attrStart) {
9364           $compileNode = groupScan(compileNode, attrStart, attrEnd);
9365         }
9366         $template = undefined;
9367
9368         if (terminalPriority > directive.priority) {
9369           break; // prevent further processing of directives
9370         }
9371
9372         directiveValue = directive.scope;
9373
9374         if (directiveValue) {
9375
9376           // skip the check for directives with async templates, we'll check the derived sync
9377           // directive when the template arrives
9378           if (!directive.templateUrl) {
9379             if (isObject(directiveValue)) {
9380               // This directive is trying to add an isolated scope.
9381               // Check that there is no scope of any kind already
9382               assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
9383                                 directive, $compileNode);
9384               newIsolateScopeDirective = directive;
9385             } else {
9386               // This directive is trying to add a child scope.
9387               // Check that there is no isolated scope already
9388               assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
9389                                 $compileNode);
9390             }
9391           }
9392
9393           newScopeDirective = newScopeDirective || directive;
9394         }
9395
9396         directiveName = directive.name;
9397
9398         // If we encounter a condition that can result in transclusion on the directive,
9399         // then scan ahead in the remaining directives for others that may cause a multiple
9400         // transclusion error to be thrown during the compilation process.  If a matching directive
9401         // is found, then we know that when we encounter a transcluded directive, we need to eagerly
9402         // compile the `transclude` function rather than doing it lazily in order to throw
9403         // exceptions at the correct time
9404         if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
9405             || (directive.transclude && !directive.$$tlb))) {
9406                 var candidateDirective;
9407
9408                 for (var scanningIndex = i + 1; (candidateDirective = directives[scanningIndex++]);) {
9409                     if ((candidateDirective.transclude && !candidateDirective.$$tlb)
9410                         || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
9411                         mightHaveMultipleTransclusionError = true;
9412                         break;
9413                     }
9414                 }
9415
9416                 didScanForMultipleTransclusion = true;
9417         }
9418
9419         if (!directive.templateUrl && directive.controller) {
9420           controllerDirectives = controllerDirectives || createMap();
9421           assertNoDuplicate('\'' + directiveName + '\' controller',
9422               controllerDirectives[directiveName], directive, $compileNode);
9423           controllerDirectives[directiveName] = directive;
9424         }
9425
9426         directiveValue = directive.transclude;
9427
9428         if (directiveValue) {
9429           hasTranscludeDirective = true;
9430
9431           // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
9432           // This option should only be used by directives that know how to safely handle element transclusion,
9433           // where the transcluded nodes are added or replaced after linking.
9434           if (!directive.$$tlb) {
9435             assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
9436             nonTlbTranscludeDirective = directive;
9437           }
9438
9439           if (directiveValue === 'element') {
9440             hasElementTranscludeDirective = true;
9441             terminalPriority = directive.priority;
9442             $template = $compileNode;
9443             $compileNode = templateAttrs.$$element =
9444                 jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
9445             compileNode = $compileNode[0];
9446             replaceWith(jqCollection, sliceArgs($template), compileNode);
9447
9448             // Support: Chrome < 50
9449             // https://github.com/angular/angular.js/issues/14041
9450
9451             // In the versions of V8 prior to Chrome 50, the document fragment that is created
9452             // in the `replaceWith` function is improperly garbage collected despite still
9453             // being referenced by the `parentNode` property of all of the child nodes.  By adding
9454             // a reference to the fragment via a different property, we can avoid that incorrect
9455             // behavior.
9456             // TODO: remove this line after Chrome 50 has been released
9457             $template[0].$$parentNode = $template[0].parentNode;
9458
9459             childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
9460                                         replaceDirective && replaceDirective.name, {
9461                                           // Don't pass in:
9462                                           // - controllerDirectives - otherwise we'll create duplicates controllers
9463                                           // - newIsolateScopeDirective or templateDirective - combining templates with
9464                                           //   element transclusion doesn't make sense.
9465                                           //
9466                                           // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
9467                                           // on the same element more than once.
9468                                           nonTlbTranscludeDirective: nonTlbTranscludeDirective
9469                                         });
9470           } else {
9471
9472             var slots = createMap();
9473
9474             if (!isObject(directiveValue)) {
9475               $template = jqLite(jqLiteClone(compileNode)).contents();
9476             } else {
9477
9478               // We have transclusion slots,
9479               // collect them up, compile them and store their transclusion functions
9480               $template = [];
9481
9482               var slotMap = createMap();
9483               var filledSlots = createMap();
9484
9485               // Parse the element selectors
9486               forEach(directiveValue, function(elementSelector, slotName) {
9487                 // If an element selector starts with a ? then it is optional
9488                 var optional = (elementSelector.charAt(0) === '?');
9489                 elementSelector = optional ? elementSelector.substring(1) : elementSelector;
9490
9491                 slotMap[elementSelector] = slotName;
9492
9493                 // We explicitly assign `null` since this implies that a slot was defined but not filled.
9494                 // Later when calling boundTransclusion functions with a slot name we only error if the
9495                 // slot is `undefined`
9496                 slots[slotName] = null;
9497
9498                 // filledSlots contains `true` for all slots that are either optional or have been
9499                 // filled. This is used to check that we have not missed any required slots
9500                 filledSlots[slotName] = optional;
9501               });
9502
9503               // Add the matching elements into their slot
9504               forEach($compileNode.contents(), function(node) {
9505                 var slotName = slotMap[directiveNormalize(nodeName_(node))];
9506                 if (slotName) {
9507                   filledSlots[slotName] = true;
9508                   slots[slotName] = slots[slotName] || [];
9509                   slots[slotName].push(node);
9510                 } else {
9511                   $template.push(node);
9512                 }
9513               });
9514
9515               // Check for required slots that were not filled
9516               forEach(filledSlots, function(filled, slotName) {
9517                 if (!filled) {
9518                   throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
9519                 }
9520               });
9521
9522               for (var slotName in slots) {
9523                 if (slots[slotName]) {
9524                   // Only define a transclusion function if the slot was filled
9525                   slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
9526                 }
9527               }
9528             }
9529
9530             $compileNode.empty(); // clear contents
9531             childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
9532                 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
9533             childTranscludeFn.$$slots = slots;
9534           }
9535         }
9536
9537         if (directive.template) {
9538           hasTemplate = true;
9539           assertNoDuplicate('template', templateDirective, directive, $compileNode);
9540           templateDirective = directive;
9541
9542           directiveValue = (isFunction(directive.template))
9543               ? directive.template($compileNode, templateAttrs)
9544               : directive.template;
9545
9546           directiveValue = denormalizeTemplate(directiveValue);
9547
9548           if (directive.replace) {
9549             replaceDirective = directive;
9550             if (jqLiteIsTextNode(directiveValue)) {
9551               $template = [];
9552             } else {
9553               $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
9554             }
9555             compileNode = $template[0];
9556
9557             if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
9558               throw $compileMinErr('tplrt',
9559                   'Template for directive \'{0}\' must have exactly one root element. {1}',
9560                   directiveName, '');
9561             }
9562
9563             replaceWith(jqCollection, $compileNode, compileNode);
9564
9565             var newTemplateAttrs = {$attr: {}};
9566
9567             // combine directives from the original node and from the template:
9568             // - take the array of directives for this element
9569             // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
9570             // - collect directives from the template and sort them by priority
9571             // - combine directives as: processed + template + unprocessed
9572             var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
9573             var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
9574
9575             if (newIsolateScopeDirective || newScopeDirective) {
9576               // The original directive caused the current element to be replaced but this element
9577               // also needs to have a new scope, so we need to tell the template directives
9578               // that they would need to get their scope from further up, if they require transclusion
9579               markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
9580             }
9581             directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
9582             mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
9583
9584             ii = directives.length;
9585           } else {
9586             $compileNode.html(directiveValue);
9587           }
9588         }
9589
9590         if (directive.templateUrl) {
9591           hasTemplate = true;
9592           assertNoDuplicate('template', templateDirective, directive, $compileNode);
9593           templateDirective = directive;
9594
9595           if (directive.replace) {
9596             replaceDirective = directive;
9597           }
9598
9599           // eslint-disable-next-line no-func-assign
9600           nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
9601               templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
9602                 controllerDirectives: controllerDirectives,
9603                 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
9604                 newIsolateScopeDirective: newIsolateScopeDirective,
9605                 templateDirective: templateDirective,
9606                 nonTlbTranscludeDirective: nonTlbTranscludeDirective
9607               });
9608           ii = directives.length;
9609         } else if (directive.compile) {
9610           try {
9611             linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
9612             var context = directive.$$originalDirective || directive;
9613             if (isFunction(linkFn)) {
9614               addLinkFns(null, bind(context, linkFn), attrStart, attrEnd);
9615             } else if (linkFn) {
9616               addLinkFns(bind(context, linkFn.pre), bind(context, linkFn.post), attrStart, attrEnd);
9617             }
9618           } catch (e) {
9619             $exceptionHandler(e, startingTag($compileNode));
9620           }
9621         }
9622
9623         if (directive.terminal) {
9624           nodeLinkFn.terminal = true;
9625           terminalPriority = Math.max(terminalPriority, directive.priority);
9626         }
9627
9628       }
9629
9630       nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
9631       nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
9632       nodeLinkFn.templateOnThisElement = hasTemplate;
9633       nodeLinkFn.transclude = childTranscludeFn;
9634
9635       previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
9636
9637       // might be normal or delayed nodeLinkFn depending on if templateUrl is present
9638       return nodeLinkFn;
9639
9640       ////////////////////
9641
9642       function addLinkFns(pre, post, attrStart, attrEnd) {
9643         if (pre) {
9644           if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
9645           pre.require = directive.require;
9646           pre.directiveName = directiveName;
9647           if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
9648             pre = cloneAndAnnotateFn(pre, {isolateScope: true});
9649           }
9650           preLinkFns.push(pre);
9651         }
9652         if (post) {
9653           if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
9654           post.require = directive.require;
9655           post.directiveName = directiveName;
9656           if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
9657             post = cloneAndAnnotateFn(post, {isolateScope: true});
9658           }
9659           postLinkFns.push(post);
9660         }
9661       }
9662
9663       function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
9664         var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
9665             attrs, scopeBindingInfo;
9666
9667         if (compileNode === linkNode) {
9668           attrs = templateAttrs;
9669           $element = templateAttrs.$$element;
9670         } else {
9671           $element = jqLite(linkNode);
9672           attrs = new Attributes($element, templateAttrs);
9673         }
9674
9675         controllerScope = scope;
9676         if (newIsolateScopeDirective) {
9677           isolateScope = scope.$new(true);
9678         } else if (newScopeDirective) {
9679           controllerScope = scope.$parent;
9680         }
9681
9682         if (boundTranscludeFn) {
9683           // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
9684           // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
9685           transcludeFn = controllersBoundTransclude;
9686           transcludeFn.$$boundTransclude = boundTranscludeFn;
9687           // expose the slots on the `$transclude` function
9688           transcludeFn.isSlotFilled = function(slotName) {
9689             return !!boundTranscludeFn.$$slots[slotName];
9690           };
9691         }
9692
9693         if (controllerDirectives) {
9694           elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
9695         }
9696
9697         if (newIsolateScopeDirective) {
9698           // Initialize isolate scope bindings for new isolate scope directive.
9699           compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
9700               templateDirective === newIsolateScopeDirective.$$originalDirective)));
9701           compile.$$addScopeClass($element, true);
9702           isolateScope.$$isolateBindings =
9703               newIsolateScopeDirective.$$isolateBindings;
9704           scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
9705                                         isolateScope.$$isolateBindings,
9706                                         newIsolateScopeDirective);
9707           if (scopeBindingInfo.removeWatches) {
9708             isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
9709           }
9710         }
9711
9712         // Initialize bindToController bindings
9713         for (var name in elementControllers) {
9714           var controllerDirective = controllerDirectives[name];
9715           var controller = elementControllers[name];
9716           var bindings = controllerDirective.$$bindings.bindToController;
9717
9718           if (preAssignBindingsEnabled) {
9719             if (bindings) {
9720               controller.bindingInfo =
9721                 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
9722             } else {
9723               controller.bindingInfo = {};
9724             }
9725
9726             var controllerResult = controller();
9727             if (controllerResult !== controller.instance) {
9728               // If the controller constructor has a return value, overwrite the instance
9729               // from setupControllers
9730               controller.instance = controllerResult;
9731               $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
9732               if (controller.bindingInfo.removeWatches) {
9733                 controller.bindingInfo.removeWatches();
9734               }
9735               controller.bindingInfo =
9736                 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
9737             }
9738           } else {
9739             controller.instance = controller();
9740             $element.data('$' + controllerDirective.name + 'Controller', controller.instance);
9741             controller.bindingInfo =
9742               initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
9743           }
9744         }
9745
9746         // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
9747         forEach(controllerDirectives, function(controllerDirective, name) {
9748           var require = controllerDirective.require;
9749           if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
9750             extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
9751           }
9752         });
9753
9754         // Handle the init and destroy lifecycle hooks on all controllers that have them
9755         forEach(elementControllers, function(controller) {
9756           var controllerInstance = controller.instance;
9757           if (isFunction(controllerInstance.$onChanges)) {
9758             try {
9759               controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
9760             } catch (e) {
9761               $exceptionHandler(e);
9762             }
9763           }
9764           if (isFunction(controllerInstance.$onInit)) {
9765             try {
9766               controllerInstance.$onInit();
9767             } catch (e) {
9768               $exceptionHandler(e);
9769             }
9770           }
9771           if (isFunction(controllerInstance.$doCheck)) {
9772             controllerScope.$watch(function() { controllerInstance.$doCheck(); });
9773             controllerInstance.$doCheck();
9774           }
9775           if (isFunction(controllerInstance.$onDestroy)) {
9776             controllerScope.$on('$destroy', function callOnDestroyHook() {
9777               controllerInstance.$onDestroy();
9778             });
9779           }
9780         });
9781
9782         // PRELINKING
9783         for (i = 0, ii = preLinkFns.length; i < ii; i++) {
9784           linkFn = preLinkFns[i];
9785           invokeLinkFn(linkFn,
9786               linkFn.isolateScope ? isolateScope : scope,
9787               $element,
9788               attrs,
9789               linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
9790               transcludeFn
9791           );
9792         }
9793
9794         // RECURSION
9795         // We only pass the isolate scope, if the isolate directive has a template,
9796         // otherwise the child elements do not belong to the isolate directive.
9797         var scopeToChild = scope;
9798         if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
9799           scopeToChild = isolateScope;
9800         }
9801         if (childLinkFn) {
9802           childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
9803         }
9804
9805         // POSTLINKING
9806         for (i = postLinkFns.length - 1; i >= 0; i--) {
9807           linkFn = postLinkFns[i];
9808           invokeLinkFn(linkFn,
9809               linkFn.isolateScope ? isolateScope : scope,
9810               $element,
9811               attrs,
9812               linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
9813               transcludeFn
9814           );
9815         }
9816
9817         // Trigger $postLink lifecycle hooks
9818         forEach(elementControllers, function(controller) {
9819           var controllerInstance = controller.instance;
9820           if (isFunction(controllerInstance.$postLink)) {
9821             controllerInstance.$postLink();
9822           }
9823         });
9824
9825         // This is the function that is injected as `$transclude`.
9826         // Note: all arguments are optional!
9827         function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
9828           var transcludeControllers;
9829           // No scope passed in:
9830           if (!isScope(scope)) {
9831             slotName = futureParentElement;
9832             futureParentElement = cloneAttachFn;
9833             cloneAttachFn = scope;
9834             scope = undefined;
9835           }
9836
9837           if (hasElementTranscludeDirective) {
9838             transcludeControllers = elementControllers;
9839           }
9840           if (!futureParentElement) {
9841             futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
9842           }
9843           if (slotName) {
9844             // slotTranscludeFn can be one of three things:
9845             //  * a transclude function - a filled slot
9846             //  * `null` - an optional slot that was not filled
9847             //  * `undefined` - a slot that was not declared (i.e. invalid)
9848             var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
9849             if (slotTranscludeFn) {
9850               return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
9851             } else if (isUndefined(slotTranscludeFn)) {
9852               throw $compileMinErr('noslot',
9853                'No parent directive that requires a transclusion with slot name "{0}". ' +
9854                'Element: {1}',
9855                slotName, startingTag($element));
9856             }
9857           } else {
9858             return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
9859           }
9860         }
9861       }
9862     }
9863
9864     function getControllers(directiveName, require, $element, elementControllers) {
9865       var value;
9866
9867       if (isString(require)) {
9868         var match = require.match(REQUIRE_PREFIX_REGEXP);
9869         var name = require.substring(match[0].length);
9870         var inheritType = match[1] || match[3];
9871         var optional = match[2] === '?';
9872
9873         //If only parents then start at the parent element
9874         if (inheritType === '^^') {
9875           $element = $element.parent();
9876         //Otherwise attempt getting the controller from elementControllers in case
9877         //the element is transcluded (and has no data) and to avoid .data if possible
9878         } else {
9879           value = elementControllers && elementControllers[name];
9880           value = value && value.instance;
9881         }
9882
9883         if (!value) {
9884           var dataName = '$' + name + 'Controller';
9885           value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
9886         }
9887
9888         if (!value && !optional) {
9889           throw $compileMinErr('ctreq',
9890               'Controller \'{0}\', required by directive \'{1}\', can\'t be found!',
9891               name, directiveName);
9892         }
9893       } else if (isArray(require)) {
9894         value = [];
9895         for (var i = 0, ii = require.length; i < ii; i++) {
9896           value[i] = getControllers(directiveName, require[i], $element, elementControllers);
9897         }
9898       } else if (isObject(require)) {
9899         value = {};
9900         forEach(require, function(controller, property) {
9901           value[property] = getControllers(directiveName, controller, $element, elementControllers);
9902         });
9903       }
9904
9905       return value || null;
9906     }
9907
9908     function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
9909       var elementControllers = createMap();
9910       for (var controllerKey in controllerDirectives) {
9911         var directive = controllerDirectives[controllerKey];
9912         var locals = {
9913           $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
9914           $element: $element,
9915           $attrs: attrs,
9916           $transclude: transcludeFn
9917         };
9918
9919         var controller = directive.controller;
9920         if (controller === '@') {
9921           controller = attrs[directive.name];
9922         }
9923
9924         var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
9925
9926         // For directives with element transclusion the element is a comment.
9927         // In this case .data will not attach any data.
9928         // Instead, we save the controllers for the element in a local hash and attach to .data
9929         // later, once we have the actual element.
9930         elementControllers[directive.name] = controllerInstance;
9931         $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
9932       }
9933       return elementControllers;
9934     }
9935
9936     // Depending upon the context in which a directive finds itself it might need to have a new isolated
9937     // or child scope created. For instance:
9938     // * if the directive has been pulled into a template because another directive with a higher priority
9939     // asked for element transclusion
9940     // * if the directive itself asks for transclusion but it is at the root of a template and the original
9941     // element was replaced. See https://github.com/angular/angular.js/issues/12936
9942     function markDirectiveScope(directives, isolateScope, newScope) {
9943       for (var j = 0, jj = directives.length; j < jj; j++) {
9944         directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
9945       }
9946     }
9947
9948     /**
9949      * looks up the directive and decorates it with exception handling and proper parameters. We
9950      * call this the boundDirective.
9951      *
9952      * @param {string} name name of the directive to look up.
9953      * @param {string} location The directive must be found in specific format.
9954      *   String containing any of theses characters:
9955      *
9956      *   * `E`: element name
9957      *   * `A': attribute
9958      *   * `C`: class
9959      *   * `M`: comment
9960      * @returns {boolean} true if directive was added.
9961      */
9962     function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
9963                           endAttrName) {
9964       if (name === ignoreDirective) return null;
9965       var match = null;
9966       if (hasDirectives.hasOwnProperty(name)) {
9967         for (var directive, directives = $injector.get(name + Suffix),
9968             i = 0, ii = directives.length; i < ii; i++) {
9969           directive = directives[i];
9970           if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
9971                directive.restrict.indexOf(location) !== -1) {
9972             if (startAttrName) {
9973               directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
9974             }
9975             if (!directive.$$bindings) {
9976               var bindings = directive.$$bindings =
9977                   parseDirectiveBindings(directive, directive.name);
9978               if (isObject(bindings.isolateScope)) {
9979                 directive.$$isolateBindings = bindings.isolateScope;
9980               }
9981             }
9982             tDirectives.push(directive);
9983             match = directive;
9984           }
9985         }
9986       }
9987       return match;
9988     }
9989
9990
9991     /**
9992      * looks up the directive and returns true if it is a multi-element directive,
9993      * and therefore requires DOM nodes between -start and -end markers to be grouped
9994      * together.
9995      *
9996      * @param {string} name name of the directive to look up.
9997      * @returns true if directive was registered as multi-element.
9998      */
9999     function directiveIsMultiElement(name) {
10000       if (hasDirectives.hasOwnProperty(name)) {
10001         for (var directive, directives = $injector.get(name + Suffix),
10002             i = 0, ii = directives.length; i < ii; i++) {
10003           directive = directives[i];
10004           if (directive.multiElement) {
10005             return true;
10006           }
10007         }
10008       }
10009       return false;
10010     }
10011
10012     /**
10013      * When the element is replaced with HTML template then the new attributes
10014      * on the template need to be merged with the existing attributes in the DOM.
10015      * The desired effect is to have both of the attributes present.
10016      *
10017      * @param {object} dst destination attributes (original DOM)
10018      * @param {object} src source attributes (from the directive template)
10019      */
10020     function mergeTemplateAttributes(dst, src) {
10021       var srcAttr = src.$attr,
10022           dstAttr = dst.$attr;
10023
10024       // reapply the old attributes to the new element
10025       forEach(dst, function(value, key) {
10026         if (key.charAt(0) !== '$') {
10027           if (src[key] && src[key] !== value) {
10028             if (value.length) {
10029               value += (key === 'style' ? ';' : ' ') + src[key];
10030             } else {
10031               value = src[key];
10032             }
10033           }
10034           dst.$set(key, value, true, srcAttr[key]);
10035         }
10036       });
10037
10038       // copy the new attributes on the old attrs object
10039       forEach(src, function(value, key) {
10040         // Check if we already set this attribute in the loop above.
10041         // `dst` will never contain hasOwnProperty as DOM parser won't let it.
10042         // You will get an "InvalidCharacterError: DOM Exception 5" error if you
10043         // have an attribute like "has-own-property" or "data-has-own-property", etc.
10044         if (!dst.hasOwnProperty(key) && key.charAt(0) !== '$') {
10045           dst[key] = value;
10046
10047           if (key !== 'class' && key !== 'style') {
10048             dstAttr[key] = srcAttr[key];
10049           }
10050         }
10051       });
10052     }
10053
10054
10055     function compileTemplateUrl(directives, $compileNode, tAttrs,
10056         $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
10057       var linkQueue = [],
10058           afterTemplateNodeLinkFn,
10059           afterTemplateChildLinkFn,
10060           beforeTemplateCompileNode = $compileNode[0],
10061           origAsyncDirective = directives.shift(),
10062           derivedSyncDirective = inherit(origAsyncDirective, {
10063             templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
10064           }),
10065           templateUrl = (isFunction(origAsyncDirective.templateUrl))
10066               ? origAsyncDirective.templateUrl($compileNode, tAttrs)
10067               : origAsyncDirective.templateUrl,
10068           templateNamespace = origAsyncDirective.templateNamespace;
10069
10070       $compileNode.empty();
10071
10072       $templateRequest(templateUrl)
10073         .then(function(content) {
10074           var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
10075
10076           content = denormalizeTemplate(content);
10077
10078           if (origAsyncDirective.replace) {
10079             if (jqLiteIsTextNode(content)) {
10080               $template = [];
10081             } else {
10082               $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
10083             }
10084             compileNode = $template[0];
10085
10086             if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
10087               throw $compileMinErr('tplrt',
10088                   'Template for directive \'{0}\' must have exactly one root element. {1}',
10089                   origAsyncDirective.name, templateUrl);
10090             }
10091
10092             tempTemplateAttrs = {$attr: {}};
10093             replaceWith($rootElement, $compileNode, compileNode);
10094             var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
10095
10096             if (isObject(origAsyncDirective.scope)) {
10097               // the original directive that caused the template to be loaded async required
10098               // an isolate scope
10099               markDirectiveScope(templateDirectives, true);
10100             }
10101             directives = templateDirectives.concat(directives);
10102             mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
10103           } else {
10104             compileNode = beforeTemplateCompileNode;
10105             $compileNode.html(content);
10106           }
10107
10108           directives.unshift(derivedSyncDirective);
10109
10110           afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
10111               childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
10112               previousCompileContext);
10113           forEach($rootElement, function(node, i) {
10114             if (node === compileNode) {
10115               $rootElement[i] = $compileNode[0];
10116             }
10117           });
10118           afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
10119
10120           while (linkQueue.length) {
10121             var scope = linkQueue.shift(),
10122                 beforeTemplateLinkNode = linkQueue.shift(),
10123                 linkRootElement = linkQueue.shift(),
10124                 boundTranscludeFn = linkQueue.shift(),
10125                 linkNode = $compileNode[0];
10126
10127             if (scope.$$destroyed) continue;
10128
10129             if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
10130               var oldClasses = beforeTemplateLinkNode.className;
10131
10132               if (!(previousCompileContext.hasElementTranscludeDirective &&
10133                   origAsyncDirective.replace)) {
10134                 // it was cloned therefore we have to clone as well.
10135                 linkNode = jqLiteClone(compileNode);
10136               }
10137               replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
10138
10139               // Copy in CSS classes from original node
10140               safeAddClass(jqLite(linkNode), oldClasses);
10141             }
10142             if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
10143               childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
10144             } else {
10145               childBoundTranscludeFn = boundTranscludeFn;
10146             }
10147             afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
10148               childBoundTranscludeFn);
10149           }
10150           linkQueue = null;
10151         }).catch(function(error) {
10152           if (error instanceof Error) {
10153             $exceptionHandler(error);
10154           }
10155         });
10156
10157       return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
10158         var childBoundTranscludeFn = boundTranscludeFn;
10159         if (scope.$$destroyed) return;
10160         if (linkQueue) {
10161           linkQueue.push(scope,
10162                          node,
10163                          rootElement,
10164                          childBoundTranscludeFn);
10165         } else {
10166           if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
10167             childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
10168           }
10169           afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
10170         }
10171       };
10172     }
10173
10174
10175     /**
10176      * Sorting function for bound directives.
10177      */
10178     function byPriority(a, b) {
10179       var diff = b.priority - a.priority;
10180       if (diff !== 0) return diff;
10181       if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
10182       return a.index - b.index;
10183     }
10184
10185     function assertNoDuplicate(what, previousDirective, directive, element) {
10186
10187       function wrapModuleNameIfDefined(moduleName) {
10188         return moduleName ?
10189           (' (module: ' + moduleName + ')') :
10190           '';
10191       }
10192
10193       if (previousDirective) {
10194         throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
10195             previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
10196             directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
10197       }
10198     }
10199
10200
10201     function addTextInterpolateDirective(directives, text) {
10202       var interpolateFn = $interpolate(text, true);
10203       if (interpolateFn) {
10204         directives.push({
10205           priority: 0,
10206           compile: function textInterpolateCompileFn(templateNode) {
10207             var templateNodeParent = templateNode.parent(),
10208                 hasCompileParent = !!templateNodeParent.length;
10209
10210             // When transcluding a template that has bindings in the root
10211             // we don't have a parent and thus need to add the class during linking fn.
10212             if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
10213
10214             return function textInterpolateLinkFn(scope, node) {
10215               var parent = node.parent();
10216               if (!hasCompileParent) compile.$$addBindingClass(parent);
10217               compile.$$addBindingInfo(parent, interpolateFn.expressions);
10218               scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
10219                 node[0].nodeValue = value;
10220               });
10221             };
10222           }
10223         });
10224       }
10225     }
10226
10227
10228     function wrapTemplate(type, template) {
10229       type = lowercase(type || 'html');
10230       switch (type) {
10231       case 'svg':
10232       case 'math':
10233         var wrapper = window.document.createElement('div');
10234         wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
10235         return wrapper.childNodes[0].childNodes;
10236       default:
10237         return template;
10238       }
10239     }
10240
10241
10242     function getTrustedContext(node, attrNormalizedName) {
10243       if (attrNormalizedName === 'srcdoc') {
10244         return $sce.HTML;
10245       }
10246       var tag = nodeName_(node);
10247       // All tags with src attributes require a RESOURCE_URL value, except for
10248       // img and various html5 media tags.
10249       if (attrNormalizedName === 'src' || attrNormalizedName === 'ngSrc') {
10250         if (['img', 'video', 'audio', 'source', 'track'].indexOf(tag) === -1) {
10251           return $sce.RESOURCE_URL;
10252         }
10253       // maction[xlink:href] can source SVG.  It's not limited to <maction>.
10254       } else if (attrNormalizedName === 'xlinkHref' ||
10255           (tag === 'form' && attrNormalizedName === 'action') ||
10256           // links can be stylesheets or imports, which can run script in the current origin
10257           (tag === 'link' && attrNormalizedName === 'href')
10258       ) {
10259         return $sce.RESOURCE_URL;
10260       }
10261     }
10262
10263
10264     function addAttrInterpolateDirective(node, directives, value, name, isNgAttr) {
10265       var trustedContext = getTrustedContext(node, name);
10266       var mustHaveExpression = !isNgAttr;
10267       var allOrNothing = ALL_OR_NOTHING_ATTRS[name] || isNgAttr;
10268
10269       var interpolateFn = $interpolate(value, mustHaveExpression, trustedContext, allOrNothing);
10270
10271       // no interpolation found -> ignore
10272       if (!interpolateFn) return;
10273
10274       if (name === 'multiple' && nodeName_(node) === 'select') {
10275         throw $compileMinErr('selmulti',
10276             'Binding to the \'multiple\' attribute is not supported. Element: {0}',
10277             startingTag(node));
10278       }
10279
10280       if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
10281         throw $compileMinErr('nodomevents',
10282             'Interpolations for HTML DOM event attributes are disallowed.  Please use the ' +
10283                 'ng- versions (such as ng-click instead of onclick) instead.');
10284       }
10285
10286       directives.push({
10287         priority: 100,
10288         compile: function() {
10289             return {
10290               pre: function attrInterpolatePreLinkFn(scope, element, attr) {
10291                 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
10292
10293                 // If the attribute has changed since last $interpolate()ed
10294                 var newValue = attr[name];
10295                 if (newValue !== value) {
10296                   // we need to interpolate again since the attribute value has been updated
10297                   // (e.g. by another directive's compile function)
10298                   // ensure unset/empty values make interpolateFn falsy
10299                   interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
10300                   value = newValue;
10301                 }
10302
10303                 // if attribute was updated so that there is no interpolation going on we don't want to
10304                 // register any observers
10305                 if (!interpolateFn) return;
10306
10307                 // initialize attr object so that it's ready in case we need the value for isolate
10308                 // scope initialization, otherwise the value would not be available from isolate
10309                 // directive's linking fn during linking phase
10310                 attr[name] = interpolateFn(scope);
10311
10312                 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
10313                 (attr.$$observers && attr.$$observers[name].$$scope || scope).
10314                   $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
10315                     //special case for class attribute addition + removal
10316                     //so that class changes can tap into the animation
10317                     //hooks provided by the $animate service. Be sure to
10318                     //skip animations when the first digest occurs (when
10319                     //both the new and the old values are the same) since
10320                     //the CSS classes are the non-interpolated values
10321                     if (name === 'class' && newValue !== oldValue) {
10322                       attr.$updateClass(newValue, oldValue);
10323                     } else {
10324                       attr.$set(name, newValue);
10325                     }
10326                   });
10327               }
10328             };
10329           }
10330       });
10331     }
10332
10333
10334     /**
10335      * This is a special jqLite.replaceWith, which can replace items which
10336      * have no parents, provided that the containing jqLite collection is provided.
10337      *
10338      * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
10339      *                               in the root of the tree.
10340      * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
10341      *                                  the shell, but replace its DOM node reference.
10342      * @param {Node} newNode The new DOM node.
10343      */
10344     function replaceWith($rootElement, elementsToRemove, newNode) {
10345       var firstElementToRemove = elementsToRemove[0],
10346           removeCount = elementsToRemove.length,
10347           parent = firstElementToRemove.parentNode,
10348           i, ii;
10349
10350       if ($rootElement) {
10351         for (i = 0, ii = $rootElement.length; i < ii; i++) {
10352           if ($rootElement[i] === firstElementToRemove) {
10353             $rootElement[i++] = newNode;
10354             for (var j = i, j2 = j + removeCount - 1,
10355                      jj = $rootElement.length;
10356                  j < jj; j++, j2++) {
10357               if (j2 < jj) {
10358                 $rootElement[j] = $rootElement[j2];
10359               } else {
10360                 delete $rootElement[j];
10361               }
10362             }
10363             $rootElement.length -= removeCount - 1;
10364
10365             // If the replaced element is also the jQuery .context then replace it
10366             // .context is a deprecated jQuery api, so we should set it only when jQuery set it
10367             // http://api.jquery.com/context/
10368             if ($rootElement.context === firstElementToRemove) {
10369               $rootElement.context = newNode;
10370             }
10371             break;
10372           }
10373         }
10374       }
10375
10376       if (parent) {
10377         parent.replaceChild(newNode, firstElementToRemove);
10378       }
10379
10380       // Append all the `elementsToRemove` to a fragment. This will...
10381       // - remove them from the DOM
10382       // - allow them to still be traversed with .nextSibling
10383       // - allow a single fragment.qSA to fetch all elements being removed
10384       var fragment = window.document.createDocumentFragment();
10385       for (i = 0; i < removeCount; i++) {
10386         fragment.appendChild(elementsToRemove[i]);
10387       }
10388
10389       if (jqLite.hasData(firstElementToRemove)) {
10390         // Copy over user data (that includes Angular's $scope etc.). Don't copy private
10391         // data here because there's no public interface in jQuery to do that and copying over
10392         // event listeners (which is the main use of private data) wouldn't work anyway.
10393         jqLite.data(newNode, jqLite.data(firstElementToRemove));
10394
10395         // Remove $destroy event listeners from `firstElementToRemove`
10396         jqLite(firstElementToRemove).off('$destroy');
10397       }
10398
10399       // Cleanup any data/listeners on the elements and children.
10400       // This includes invoking the $destroy event on any elements with listeners.
10401       jqLite.cleanData(fragment.querySelectorAll('*'));
10402
10403       // Update the jqLite collection to only contain the `newNode`
10404       for (i = 1; i < removeCount; i++) {
10405         delete elementsToRemove[i];
10406       }
10407       elementsToRemove[0] = newNode;
10408       elementsToRemove.length = 1;
10409     }
10410
10411
10412     function cloneAndAnnotateFn(fn, annotation) {
10413       return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
10414     }
10415
10416
10417     function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
10418       try {
10419         linkFn(scope, $element, attrs, controllers, transcludeFn);
10420       } catch (e) {
10421         $exceptionHandler(e, startingTag($element));
10422       }
10423     }
10424
10425
10426     // Set up $watches for isolate scope and controller bindings.
10427     function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
10428       var removeWatchCollection = [];
10429       var initialChanges = {};
10430       var changes;
10431       forEach(bindings, function initializeBinding(definition, scopeName) {
10432         var attrName = definition.attrName,
10433         optional = definition.optional,
10434         mode = definition.mode, // @, =, <, or &
10435         lastValue,
10436         parentGet, parentSet, compare, removeWatch;
10437
10438         switch (mode) {
10439
10440           case '@':
10441             if (!optional && !hasOwnProperty.call(attrs, attrName)) {
10442               destination[scopeName] = attrs[attrName] = undefined;
10443             }
10444             removeWatch = attrs.$observe(attrName, function(value) {
10445               if (isString(value) || isBoolean(value)) {
10446                 var oldValue = destination[scopeName];
10447                 recordChanges(scopeName, value, oldValue);
10448                 destination[scopeName] = value;
10449               }
10450             });
10451             attrs.$$observers[attrName].$$scope = scope;
10452             lastValue = attrs[attrName];
10453             if (isString(lastValue)) {
10454               // If the attribute has been provided then we trigger an interpolation to ensure
10455               // the value is there for use in the link fn
10456               destination[scopeName] = $interpolate(lastValue)(scope);
10457             } else if (isBoolean(lastValue)) {
10458               // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
10459               // the value to boolean rather than a string, so we special case this situation
10460               destination[scopeName] = lastValue;
10461             }
10462             initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
10463             removeWatchCollection.push(removeWatch);
10464             break;
10465
10466           case '=':
10467             if (!hasOwnProperty.call(attrs, attrName)) {
10468               if (optional) break;
10469               attrs[attrName] = undefined;
10470             }
10471             if (optional && !attrs[attrName]) break;
10472
10473             parentGet = $parse(attrs[attrName]);
10474             if (parentGet.literal) {
10475               compare = equals;
10476             } else {
10477               // eslint-disable-next-line no-self-compare
10478               compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
10479             }
10480             parentSet = parentGet.assign || function() {
10481               // reset the change, or we will throw this exception on every $digest
10482               lastValue = destination[scopeName] = parentGet(scope);
10483               throw $compileMinErr('nonassign',
10484                   'Expression \'{0}\' in attribute \'{1}\' used with directive \'{2}\' is non-assignable!',
10485                   attrs[attrName], attrName, directive.name);
10486             };
10487             lastValue = destination[scopeName] = parentGet(scope);
10488             var parentValueWatch = function parentValueWatch(parentValue) {
10489               if (!compare(parentValue, destination[scopeName])) {
10490                 // we are out of sync and need to copy
10491                 if (!compare(parentValue, lastValue)) {
10492                   // parent changed and it has precedence
10493                   destination[scopeName] = parentValue;
10494                 } else {
10495                   // if the parent can be assigned then do so
10496                   parentSet(scope, parentValue = destination[scopeName]);
10497                 }
10498               }
10499               lastValue = parentValue;
10500               return lastValue;
10501             };
10502             parentValueWatch.$stateful = true;
10503             if (definition.collection) {
10504               removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
10505             } else {
10506               removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
10507             }
10508             removeWatchCollection.push(removeWatch);
10509             break;
10510
10511           case '<':
10512             if (!hasOwnProperty.call(attrs, attrName)) {
10513               if (optional) break;
10514               attrs[attrName] = undefined;
10515             }
10516             if (optional && !attrs[attrName]) break;
10517
10518             parentGet = $parse(attrs[attrName]);
10519             var deepWatch = parentGet.literal;
10520
10521             var initialValue = destination[scopeName] = parentGet(scope);
10522             initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
10523
10524             removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
10525               if (oldValue === newValue) {
10526                 if (oldValue === initialValue || (deepWatch && equals(oldValue, initialValue))) {
10527                   return;
10528                 }
10529                 oldValue = initialValue;
10530               }
10531               recordChanges(scopeName, newValue, oldValue);
10532               destination[scopeName] = newValue;
10533             }, deepWatch);
10534
10535             removeWatchCollection.push(removeWatch);
10536             break;
10537
10538           case '&':
10539             // Don't assign Object.prototype method to scope
10540             parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
10541
10542             // Don't assign noop to destination if expression is not valid
10543             if (parentGet === noop && optional) break;
10544
10545             destination[scopeName] = function(locals) {
10546               return parentGet(scope, locals);
10547             };
10548             break;
10549         }
10550       });
10551
10552       function recordChanges(key, currentValue, previousValue) {
10553         if (isFunction(destination.$onChanges) && currentValue !== previousValue &&
10554             // eslint-disable-next-line no-self-compare
10555             (currentValue === currentValue || previousValue === previousValue)) {
10556           // If we have not already scheduled the top level onChangesQueue handler then do so now
10557           if (!onChangesQueue) {
10558             scope.$$postDigest(flushOnChangesQueue);
10559             onChangesQueue = [];
10560           }
10561           // If we have not already queued a trigger of onChanges for this controller then do so now
10562           if (!changes) {
10563             changes = {};
10564             onChangesQueue.push(triggerOnChangesHook);
10565           }
10566           // If the has been a change on this property already then we need to reuse the previous value
10567           if (changes[key]) {
10568             previousValue = changes[key].previousValue;
10569           }
10570           // Store this change
10571           changes[key] = new SimpleChange(previousValue, currentValue);
10572         }
10573       }
10574
10575       function triggerOnChangesHook() {
10576         destination.$onChanges(changes);
10577         // Now clear the changes so that we schedule onChanges when more changes arrive
10578         changes = undefined;
10579       }
10580
10581       return {
10582         initialChanges: initialChanges,
10583         removeWatches: removeWatchCollection.length && function removeWatches() {
10584           for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
10585             removeWatchCollection[i]();
10586           }
10587         }
10588       };
10589     }
10590   }];
10591 }
10592
10593 function SimpleChange(previous, current) {
10594   this.previousValue = previous;
10595   this.currentValue = current;
10596 }
10597 SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
10598
10599
10600 var PREFIX_REGEXP = /^((?:x|data)[:\-_])/i;
10601 var SPECIAL_CHARS_REGEXP = /[:\-_]+(.)/g;
10602
10603 /**
10604  * Converts all accepted directives format into proper directive name.
10605  * @param name Name to normalize
10606  */
10607 function directiveNormalize(name) {
10608   return name
10609     .replace(PREFIX_REGEXP, '')
10610     .replace(SPECIAL_CHARS_REGEXP, fnCamelCaseReplace);
10611 }
10612
10613 /**
10614  * @ngdoc type
10615  * @name $compile.directive.Attributes
10616  *
10617  * @description
10618  * A shared object between directive compile / linking functions which contains normalized DOM
10619  * element attributes. The values reflect current binding state `{{ }}`. The normalization is
10620  * needed since all of these are treated as equivalent in Angular:
10621  *
10622  * ```
10623  *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
10624  * ```
10625  */
10626
10627 /**
10628  * @ngdoc property
10629  * @name $compile.directive.Attributes#$attr
10630  *
10631  * @description
10632  * A map of DOM element attribute names to the normalized name. This is
10633  * needed to do reverse lookup from normalized name back to actual name.
10634  */
10635
10636
10637 /**
10638  * @ngdoc method
10639  * @name $compile.directive.Attributes#$set
10640  * @kind function
10641  *
10642  * @description
10643  * Set DOM element attribute value.
10644  *
10645  *
10646  * @param {string} name Normalized element attribute name of the property to modify. The name is
10647  *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
10648  *          property to the original name.
10649  * @param {string} value Value to set the attribute to. The value can be an interpolated string.
10650  */
10651
10652
10653
10654 /**
10655  * Closure compiler type information
10656  */
10657
10658 function nodesetLinkingFn(
10659   /* angular.Scope */ scope,
10660   /* NodeList */ nodeList,
10661   /* Element */ rootElement,
10662   /* function(Function) */ boundTranscludeFn
10663 ) {}
10664
10665 function directiveLinkingFn(
10666   /* nodesetLinkingFn */ nodesetLinkingFn,
10667   /* angular.Scope */ scope,
10668   /* Node */ node,
10669   /* Element */ rootElement,
10670   /* function(Function) */ boundTranscludeFn
10671 ) {}
10672
10673 function tokenDifference(str1, str2) {
10674   var values = '',
10675       tokens1 = str1.split(/\s+/),
10676       tokens2 = str2.split(/\s+/);
10677
10678   outer:
10679   for (var i = 0; i < tokens1.length; i++) {
10680     var token = tokens1[i];
10681     for (var j = 0; j < tokens2.length; j++) {
10682       if (token === tokens2[j]) continue outer;
10683     }
10684     values += (values.length > 0 ? ' ' : '') + token;
10685   }
10686   return values;
10687 }
10688
10689 function removeComments(jqNodes) {
10690   jqNodes = jqLite(jqNodes);
10691   var i = jqNodes.length;
10692
10693   if (i <= 1) {
10694     return jqNodes;
10695   }
10696
10697   while (i--) {
10698     var node = jqNodes[i];
10699     if (node.nodeType === NODE_TYPE_COMMENT ||
10700        (node.nodeType === NODE_TYPE_TEXT && node.nodeValue.trim() === '')) {
10701          splice.call(jqNodes, i, 1);
10702     }
10703   }
10704   return jqNodes;
10705 }
10706
10707 var $controllerMinErr = minErr('$controller');
10708
10709
10710 var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
10711 function identifierForController(controller, ident) {
10712   if (ident && isString(ident)) return ident;
10713   if (isString(controller)) {
10714     var match = CNTRL_REG.exec(controller);
10715     if (match) return match[3];
10716   }
10717 }
10718
10719
10720 /**
10721  * @ngdoc provider
10722  * @name $controllerProvider
10723  * @this
10724  *
10725  * @description
10726  * The {@link ng.$controller $controller service} is used by Angular to create new
10727  * controllers.
10728  *
10729  * This provider allows controller registration via the
10730  * {@link ng.$controllerProvider#register register} method.
10731  */
10732 function $ControllerProvider() {
10733   var controllers = {},
10734       globals = false;
10735
10736   /**
10737    * @ngdoc method
10738    * @name $controllerProvider#has
10739    * @param {string} name Controller name to check.
10740    */
10741   this.has = function(name) {
10742     return controllers.hasOwnProperty(name);
10743   };
10744
10745   /**
10746    * @ngdoc method
10747    * @name $controllerProvider#register
10748    * @param {string|Object} name Controller name, or an object map of controllers where the keys are
10749    *    the names and the values are the constructors.
10750    * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
10751    *    annotations in the array notation).
10752    */
10753   this.register = function(name, constructor) {
10754     assertNotHasOwnProperty(name, 'controller');
10755     if (isObject(name)) {
10756       extend(controllers, name);
10757     } else {
10758       controllers[name] = constructor;
10759     }
10760   };
10761
10762   /**
10763    * @ngdoc method
10764    * @name $controllerProvider#allowGlobals
10765    * @description If called, allows `$controller` to find controller constructors on `window`
10766    *
10767    * @deprecated
10768    * sinceVersion="v1.3.0"
10769    * removeVersion="v1.7.0"
10770    * This method of finding controllers has been deprecated.
10771    */
10772   this.allowGlobals = function() {
10773     globals = true;
10774   };
10775
10776
10777   this.$get = ['$injector', '$window', function($injector, $window) {
10778
10779     /**
10780      * @ngdoc service
10781      * @name $controller
10782      * @requires $injector
10783      *
10784      * @param {Function|string} constructor If called with a function then it's considered to be the
10785      *    controller constructor function. Otherwise it's considered to be a string which is used
10786      *    to retrieve the controller constructor using the following steps:
10787      *
10788      *    * check if a controller with given name is registered via `$controllerProvider`
10789      *    * check if evaluating the string on the current scope returns a constructor
10790      *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
10791      *      `window` object (deprecated, not recommended)
10792      *
10793      *    The string can use the `controller as property` syntax, where the controller instance is published
10794      *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
10795      *    to work correctly.
10796      *
10797      * @param {Object} locals Injection locals for Controller.
10798      * @return {Object} Instance of given controller.
10799      *
10800      * @description
10801      * `$controller` service is responsible for instantiating controllers.
10802      *
10803      * It's just a simple call to {@link auto.$injector $injector}, but extracted into
10804      * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
10805      */
10806     return function $controller(expression, locals, later, ident) {
10807       // PRIVATE API:
10808       //   param `later` --- indicates that the controller's constructor is invoked at a later time.
10809       //                     If true, $controller will allocate the object with the correct
10810       //                     prototype chain, but will not invoke the controller until a returned
10811       //                     callback is invoked.
10812       //   param `ident` --- An optional label which overrides the label parsed from the controller
10813       //                     expression, if any.
10814       var instance, match, constructor, identifier;
10815       later = later === true;
10816       if (ident && isString(ident)) {
10817         identifier = ident;
10818       }
10819
10820       if (isString(expression)) {
10821         match = expression.match(CNTRL_REG);
10822         if (!match) {
10823           throw $controllerMinErr('ctrlfmt',
10824             'Badly formed controller string \'{0}\'. ' +
10825             'Must match `__name__ as __id__` or `__name__`.', expression);
10826         }
10827         constructor = match[1];
10828         identifier = identifier || match[3];
10829         expression = controllers.hasOwnProperty(constructor)
10830             ? controllers[constructor]
10831             : getter(locals.$scope, constructor, true) ||
10832                 (globals ? getter($window, constructor, true) : undefined);
10833
10834         if (!expression) {
10835           throw $controllerMinErr('ctrlreg',
10836             'The controller with the name \'{0}\' is not registered.', constructor);
10837         }
10838
10839         assertArgFn(expression, constructor, true);
10840       }
10841
10842       if (later) {
10843         // Instantiate controller later:
10844         // This machinery is used to create an instance of the object before calling the
10845         // controller's constructor itself.
10846         //
10847         // This allows properties to be added to the controller before the constructor is
10848         // invoked. Primarily, this is used for isolate scope bindings in $compile.
10849         //
10850         // This feature is not intended for use by applications, and is thus not documented
10851         // publicly.
10852         // Object creation: http://jsperf.com/create-constructor/2
10853         var controllerPrototype = (isArray(expression) ?
10854           expression[expression.length - 1] : expression).prototype;
10855         instance = Object.create(controllerPrototype || null);
10856
10857         if (identifier) {
10858           addIdentifier(locals, identifier, instance, constructor || expression.name);
10859         }
10860
10861         return extend(function $controllerInit() {
10862           var result = $injector.invoke(expression, instance, locals, constructor);
10863           if (result !== instance && (isObject(result) || isFunction(result))) {
10864             instance = result;
10865             if (identifier) {
10866               // If result changed, re-assign controllerAs value to scope.
10867               addIdentifier(locals, identifier, instance, constructor || expression.name);
10868             }
10869           }
10870           return instance;
10871         }, {
10872           instance: instance,
10873           identifier: identifier
10874         });
10875       }
10876
10877       instance = $injector.instantiate(expression, locals, constructor);
10878
10879       if (identifier) {
10880         addIdentifier(locals, identifier, instance, constructor || expression.name);
10881       }
10882
10883       return instance;
10884     };
10885
10886     function addIdentifier(locals, identifier, instance, name) {
10887       if (!(locals && isObject(locals.$scope))) {
10888         throw minErr('$controller')('noscp',
10889           'Cannot export controller \'{0}\' as \'{1}\'! No $scope object provided via `locals`.',
10890           name, identifier);
10891       }
10892
10893       locals.$scope[identifier] = instance;
10894     }
10895   }];
10896 }
10897
10898 /**
10899  * @ngdoc service
10900  * @name $document
10901  * @requires $window
10902  * @this
10903  *
10904  * @description
10905  * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
10906  *
10907  * @example
10908    <example module="documentExample" name="document">
10909      <file name="index.html">
10910        <div ng-controller="ExampleController">
10911          <p>$document title: <b ng-bind="title"></b></p>
10912          <p>window.document title: <b ng-bind="windowTitle"></b></p>
10913        </div>
10914      </file>
10915      <file name="script.js">
10916        angular.module('documentExample', [])
10917          .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
10918            $scope.title = $document[0].title;
10919            $scope.windowTitle = angular.element(window.document)[0].title;
10920          }]);
10921      </file>
10922    </example>
10923  */
10924 function $DocumentProvider() {
10925   this.$get = ['$window', function(window) {
10926     return jqLite(window.document);
10927   }];
10928 }
10929
10930
10931 /**
10932  * @private
10933  * @this
10934  * Listens for document visibility change and makes the current status accessible.
10935  */
10936 function $$IsDocumentHiddenProvider() {
10937   this.$get = ['$document', '$rootScope', function($document, $rootScope) {
10938     var doc = $document[0];
10939     var hidden = doc && doc.hidden;
10940
10941     $document.on('visibilitychange', changeListener);
10942
10943     $rootScope.$on('$destroy', function() {
10944       $document.off('visibilitychange', changeListener);
10945     });
10946
10947     function changeListener() {
10948       hidden = doc.hidden;
10949     }
10950
10951     return function() {
10952       return hidden;
10953     };
10954   }];
10955 }
10956
10957 /**
10958  * @ngdoc service
10959  * @name $exceptionHandler
10960  * @requires ng.$log
10961  * @this
10962  *
10963  * @description
10964  * Any uncaught exception in angular expressions is delegated to this service.
10965  * The default implementation simply delegates to `$log.error` which logs it into
10966  * the browser console.
10967  *
10968  * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
10969  * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
10970  *
10971  * ## Example:
10972  *
10973  * The example below will overwrite the default `$exceptionHandler` in order to (a) log uncaught
10974  * errors to the backend for later inspection by the developers and (b) to use `$log.warn()` instead
10975  * of `$log.error()`.
10976  *
10977  * ```js
10978  *   angular.
10979  *     module('exceptionOverwrite', []).
10980  *     factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) {
10981  *       return function myExceptionHandler(exception, cause) {
10982  *         logErrorsToBackend(exception, cause);
10983  *         $log.warn(exception, cause);
10984  *       };
10985  *     }]);
10986  * ```
10987  *
10988  * <hr />
10989  * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
10990  * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
10991  * (unless executed during a digest).
10992  *
10993  * If you wish, you can manually delegate exceptions, e.g.
10994  * `try { ... } catch(e) { $exceptionHandler(e); }`
10995  *
10996  * @param {Error} exception Exception associated with the error.
10997  * @param {string=} cause Optional information about the context in which
10998  *       the error was thrown.
10999  *
11000  */
11001 function $ExceptionHandlerProvider() {
11002   this.$get = ['$log', function($log) {
11003     return function(exception, cause) {
11004       $log.error.apply($log, arguments);
11005     };
11006   }];
11007 }
11008
11009 var $$ForceReflowProvider = /** @this */ function() {
11010   this.$get = ['$document', function($document) {
11011     return function(domNode) {
11012       //the line below will force the browser to perform a repaint so
11013       //that all the animated elements within the animation frame will
11014       //be properly updated and drawn on screen. This is required to
11015       //ensure that the preparation animation is properly flushed so that
11016       //the active state picks up from there. DO NOT REMOVE THIS LINE.
11017       //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
11018       //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
11019       //WILL TAKE YEARS AWAY FROM YOUR LIFE.
11020       if (domNode) {
11021         if (!domNode.nodeType && domNode instanceof jqLite) {
11022           domNode = domNode[0];
11023         }
11024       } else {
11025         domNode = $document[0].body;
11026       }
11027       return domNode.offsetWidth + 1;
11028     };
11029   }];
11030 };
11031
11032 var APPLICATION_JSON = 'application/json';
11033 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
11034 var JSON_START = /^\[|^\{(?!\{)/;
11035 var JSON_ENDS = {
11036   '[': /]$/,
11037   '{': /}$/
11038 };
11039 var JSON_PROTECTION_PREFIX = /^\)]\}',?\n/;
11040 var $httpMinErr = minErr('$http');
11041
11042 function serializeValue(v) {
11043   if (isObject(v)) {
11044     return isDate(v) ? v.toISOString() : toJson(v);
11045   }
11046   return v;
11047 }
11048
11049
11050 /** @this */
11051 function $HttpParamSerializerProvider() {
11052   /**
11053    * @ngdoc service
11054    * @name $httpParamSerializer
11055    * @description
11056    *
11057    * Default {@link $http `$http`} params serializer that converts objects to strings
11058    * according to the following rules:
11059    *
11060    * * `{'foo': 'bar'}` results in `foo=bar`
11061    * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
11062    * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
11063    * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D` (stringified and encoded representation of an object)
11064    *
11065    * Note that serializer will sort the request parameters alphabetically.
11066    * */
11067
11068   this.$get = function() {
11069     return function ngParamSerializer(params) {
11070       if (!params) return '';
11071       var parts = [];
11072       forEachSorted(params, function(value, key) {
11073         if (value === null || isUndefined(value)) return;
11074         if (isArray(value)) {
11075           forEach(value, function(v) {
11076             parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
11077           });
11078         } else {
11079           parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
11080         }
11081       });
11082
11083       return parts.join('&');
11084     };
11085   };
11086 }
11087
11088 /** @this */
11089 function $HttpParamSerializerJQLikeProvider() {
11090   /**
11091    * @ngdoc service
11092    * @name $httpParamSerializerJQLike
11093    *
11094    * @description
11095    *
11096    * Alternative {@link $http `$http`} params serializer that follows
11097    * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
11098    * The serializer will also sort the params alphabetically.
11099    *
11100    * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
11101    *
11102    * ```js
11103    * $http({
11104    *   url: myUrl,
11105    *   method: 'GET',
11106    *   params: myParams,
11107    *   paramSerializer: '$httpParamSerializerJQLike'
11108    * });
11109    * ```
11110    *
11111    * It is also possible to set it as the default `paramSerializer` in the
11112    * {@link $httpProvider#defaults `$httpProvider`}.
11113    *
11114    * Additionally, you can inject the serializer and use it explicitly, for example to serialize
11115    * form data for submission:
11116    *
11117    * ```js
11118    * .controller(function($http, $httpParamSerializerJQLike) {
11119    *   //...
11120    *
11121    *   $http({
11122    *     url: myUrl,
11123    *     method: 'POST',
11124    *     data: $httpParamSerializerJQLike(myData),
11125    *     headers: {
11126    *       'Content-Type': 'application/x-www-form-urlencoded'
11127    *     }
11128    *   });
11129    *
11130    * });
11131    * ```
11132    *
11133    * */
11134   this.$get = function() {
11135     return function jQueryLikeParamSerializer(params) {
11136       if (!params) return '';
11137       var parts = [];
11138       serialize(params, '', true);
11139       return parts.join('&');
11140
11141       function serialize(toSerialize, prefix, topLevel) {
11142         if (toSerialize === null || isUndefined(toSerialize)) return;
11143         if (isArray(toSerialize)) {
11144           forEach(toSerialize, function(value, index) {
11145             serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
11146           });
11147         } else if (isObject(toSerialize) && !isDate(toSerialize)) {
11148           forEachSorted(toSerialize, function(value, key) {
11149             serialize(value, prefix +
11150                 (topLevel ? '' : '[') +
11151                 key +
11152                 (topLevel ? '' : ']'));
11153           });
11154         } else {
11155           parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
11156         }
11157       }
11158     };
11159   };
11160 }
11161
11162 function defaultHttpResponseTransform(data, headers) {
11163   if (isString(data)) {
11164     // Strip json vulnerability protection prefix and trim whitespace
11165     var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
11166
11167     if (tempData) {
11168       var contentType = headers('Content-Type');
11169       if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
11170         data = fromJson(tempData);
11171       }
11172     }
11173   }
11174
11175   return data;
11176 }
11177
11178 function isJsonLike(str) {
11179     var jsonStart = str.match(JSON_START);
11180     return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
11181 }
11182
11183 /**
11184  * Parse headers into key value object
11185  *
11186  * @param {string} headers Raw headers as a string
11187  * @returns {Object} Parsed headers as key value object
11188  */
11189 function parseHeaders(headers) {
11190   var parsed = createMap(), i;
11191
11192   function fillInParsed(key, val) {
11193     if (key) {
11194       parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
11195     }
11196   }
11197
11198   if (isString(headers)) {
11199     forEach(headers.split('\n'), function(line) {
11200       i = line.indexOf(':');
11201       fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
11202     });
11203   } else if (isObject(headers)) {
11204     forEach(headers, function(headerVal, headerKey) {
11205       fillInParsed(lowercase(headerKey), trim(headerVal));
11206     });
11207   }
11208
11209   return parsed;
11210 }
11211
11212
11213 /**
11214  * Returns a function that provides access to parsed headers.
11215  *
11216  * Headers are lazy parsed when first requested.
11217  * @see parseHeaders
11218  *
11219  * @param {(string|Object)} headers Headers to provide access to.
11220  * @returns {function(string=)} Returns a getter function which if called with:
11221  *
11222  *   - if called with an argument returns a single header value or null
11223  *   - if called with no arguments returns an object containing all headers.
11224  */
11225 function headersGetter(headers) {
11226   var headersObj;
11227
11228   return function(name) {
11229     if (!headersObj) headersObj =  parseHeaders(headers);
11230
11231     if (name) {
11232       var value = headersObj[lowercase(name)];
11233       if (value === undefined) {
11234         value = null;
11235       }
11236       return value;
11237     }
11238
11239     return headersObj;
11240   };
11241 }
11242
11243
11244 /**
11245  * Chain all given functions
11246  *
11247  * This function is used for both request and response transforming
11248  *
11249  * @param {*} data Data to transform.
11250  * @param {function(string=)} headers HTTP headers getter fn.
11251  * @param {number} status HTTP status code of the response.
11252  * @param {(Function|Array.<Function>)} fns Function or an array of functions.
11253  * @returns {*} Transformed data.
11254  */
11255 function transformData(data, headers, status, fns) {
11256   if (isFunction(fns)) {
11257     return fns(data, headers, status);
11258   }
11259
11260   forEach(fns, function(fn) {
11261     data = fn(data, headers, status);
11262   });
11263
11264   return data;
11265 }
11266
11267
11268 function isSuccess(status) {
11269   return 200 <= status && status < 300;
11270 }
11271
11272
11273 /**
11274  * @ngdoc provider
11275  * @name $httpProvider
11276  * @this
11277  *
11278  * @description
11279  * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
11280  * */
11281 function $HttpProvider() {
11282   /**
11283    * @ngdoc property
11284    * @name $httpProvider#defaults
11285    * @description
11286    *
11287    * Object containing default values for all {@link ng.$http $http} requests.
11288    *
11289    * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
11290    * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
11291    * by default. See {@link $http#caching $http Caching} for more information.
11292    *
11293    * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
11294    * Defaults value is `'XSRF-TOKEN'`.
11295    *
11296    * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
11297    * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
11298    *
11299    * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
11300    * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
11301    * setting default headers.
11302    *     - **`defaults.headers.common`**
11303    *     - **`defaults.headers.post`**
11304    *     - **`defaults.headers.put`**
11305    *     - **`defaults.headers.patch`**
11306    *
11307    *
11308    * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
11309    *  used to the prepare string representation of request parameters (specified as an object).
11310    *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
11311    *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
11312    *
11313    * - **`defaults.jsonpCallbackParam`** - `{string}` - the name of the query parameter that passes the name of the
11314    * callback in a JSONP request. The value of this parameter will be replaced with the expression generated by the
11315    * {@link $jsonpCallbacks} service. Defaults to `'callback'`.
11316    *
11317    **/
11318   var defaults = this.defaults = {
11319     // transform incoming response data
11320     transformResponse: [defaultHttpResponseTransform],
11321
11322     // transform outgoing request data
11323     transformRequest: [function(d) {
11324       return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
11325     }],
11326
11327     // default headers
11328     headers: {
11329       common: {
11330         'Accept': 'application/json, text/plain, */*'
11331       },
11332       post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
11333       put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
11334       patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
11335     },
11336
11337     xsrfCookieName: 'XSRF-TOKEN',
11338     xsrfHeaderName: 'X-XSRF-TOKEN',
11339
11340     paramSerializer: '$httpParamSerializer',
11341
11342     jsonpCallbackParam: 'callback'
11343   };
11344
11345   var useApplyAsync = false;
11346   /**
11347    * @ngdoc method
11348    * @name $httpProvider#useApplyAsync
11349    * @description
11350    *
11351    * Configure $http service to combine processing of multiple http responses received at around
11352    * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
11353    * significant performance improvement for bigger applications that make many HTTP requests
11354    * concurrently (common during application bootstrap).
11355    *
11356    * Defaults to false. If no value is specified, returns the current configured value.
11357    *
11358    * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
11359    *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
11360    *    to load and share the same digest cycle.
11361    *
11362    * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
11363    *    otherwise, returns the current configured value.
11364    **/
11365   this.useApplyAsync = function(value) {
11366     if (isDefined(value)) {
11367       useApplyAsync = !!value;
11368       return this;
11369     }
11370     return useApplyAsync;
11371   };
11372
11373   /**
11374    * @ngdoc property
11375    * @name $httpProvider#interceptors
11376    * @description
11377    *
11378    * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
11379    * pre-processing of request or postprocessing of responses.
11380    *
11381    * These service factories are ordered by request, i.e. they are applied in the same order as the
11382    * array, on request, but reverse order, on response.
11383    *
11384    * {@link ng.$http#interceptors Interceptors detailed info}
11385    **/
11386   var interceptorFactories = this.interceptors = [];
11387
11388   this.$get = ['$browser', '$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector', '$sce',
11389       function($browser, $httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector, $sce) {
11390
11391     var defaultCache = $cacheFactory('$http');
11392
11393     /**
11394      * Make sure that default param serializer is exposed as a function
11395      */
11396     defaults.paramSerializer = isString(defaults.paramSerializer) ?
11397       $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
11398
11399     /**
11400      * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
11401      * The reversal is needed so that we can build up the interception chain around the
11402      * server request.
11403      */
11404     var reversedInterceptors = [];
11405
11406     forEach(interceptorFactories, function(interceptorFactory) {
11407       reversedInterceptors.unshift(isString(interceptorFactory)
11408           ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
11409     });
11410
11411     /**
11412      * @ngdoc service
11413      * @kind function
11414      * @name $http
11415      * @requires ng.$httpBackend
11416      * @requires $cacheFactory
11417      * @requires $rootScope
11418      * @requires $q
11419      * @requires $injector
11420      *
11421      * @description
11422      * The `$http` service is a core Angular service that facilitates communication with the remote
11423      * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
11424      * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
11425      *
11426      * For unit testing applications that use `$http` service, see
11427      * {@link ngMock.$httpBackend $httpBackend mock}.
11428      *
11429      * For a higher level of abstraction, please check out the {@link ngResource.$resource
11430      * $resource} service.
11431      *
11432      * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
11433      * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
11434      * it is important to familiarize yourself with these APIs and the guarantees they provide.
11435      *
11436      *
11437      * ## General usage
11438      * The `$http` service is a function which takes a single argument â€” a {@link $http#usage configuration object} â€”
11439      * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
11440      *
11441      * ```js
11442      *   // Simple GET request example:
11443      *   $http({
11444      *     method: 'GET',
11445      *     url: '/someUrl'
11446      *   }).then(function successCallback(response) {
11447      *       // this callback will be called asynchronously
11448      *       // when the response is available
11449      *     }, function errorCallback(response) {
11450      *       // called asynchronously if an error occurs
11451      *       // or server returns response with an error status.
11452      *     });
11453      * ```
11454      *
11455      * The response object has these properties:
11456      *
11457      *   - **data** â€“ `{string|Object}` â€“ The response body transformed with the transform
11458      *     functions.
11459      *   - **status** â€“ `{number}` â€“ HTTP status code of the response.
11460      *   - **headers** â€“ `{function([headerName])}` â€“ Header getter function.
11461      *   - **config** â€“ `{Object}` â€“ The configuration object that was used to generate the request.
11462      *   - **statusText** â€“ `{string}` â€“ HTTP status text of the response.
11463      *
11464      * A response status code between 200 and 299 is considered a success status and will result in
11465      * the success callback being called. Any response status code outside of that range is
11466      * considered an error status and will result in the error callback being called.
11467      * Also, status codes less than -1 are normalized to zero. -1 usually means the request was
11468      * aborted, e.g. using a `config.timeout`.
11469      * Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning
11470      * that the outcome (success or error) will be determined by the final response status code.
11471      *
11472      *
11473      * ## Shortcut methods
11474      *
11475      * Shortcut methods are also available. All shortcut methods require passing in the URL, and
11476      * request data must be passed in for POST/PUT requests. An optional config can be passed as the
11477      * last argument.
11478      *
11479      * ```js
11480      *   $http.get('/someUrl', config).then(successCallback, errorCallback);
11481      *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
11482      * ```
11483      *
11484      * Complete list of shortcut methods:
11485      *
11486      * - {@link ng.$http#get $http.get}
11487      * - {@link ng.$http#head $http.head}
11488      * - {@link ng.$http#post $http.post}
11489      * - {@link ng.$http#put $http.put}
11490      * - {@link ng.$http#delete $http.delete}
11491      * - {@link ng.$http#jsonp $http.jsonp}
11492      * - {@link ng.$http#patch $http.patch}
11493      *
11494      *
11495      * ## Writing Unit Tests that use $http
11496      * When unit testing (using {@link ngMock ngMock}), it is necessary to call
11497      * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
11498      * request using trained responses.
11499      *
11500      * ```
11501      * $httpBackend.expectGET(...);
11502      * $http.get(...);
11503      * $httpBackend.flush();
11504      * ```
11505      *
11506      * ## Setting HTTP Headers
11507      *
11508      * The $http service will automatically add certain HTTP headers to all requests. These defaults
11509      * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
11510      * object, which currently contains this default configuration:
11511      *
11512      * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
11513      *   - <code>Accept: application/json, text/plain, \*&#65279;/&#65279;\*</code>
11514      * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
11515      *   - `Content-Type: application/json`
11516      * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
11517      *   - `Content-Type: application/json`
11518      *
11519      * To add or overwrite these defaults, simply add or remove a property from these configuration
11520      * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
11521      * with the lowercased HTTP method name as the key, e.g.
11522      * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
11523      *
11524      * The defaults can also be set at runtime via the `$http.defaults` object in the same
11525      * fashion. For example:
11526      *
11527      * ```
11528      * module.run(function($http) {
11529      *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
11530      * });
11531      * ```
11532      *
11533      * In addition, you can supply a `headers` property in the config object passed when
11534      * calling `$http(config)`, which overrides the defaults without changing them globally.
11535      *
11536      * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
11537      * Use the `headers` property, setting the desired header to `undefined`. For example:
11538      *
11539      * ```js
11540      * var req = {
11541      *  method: 'POST',
11542      *  url: 'http://example.com',
11543      *  headers: {
11544      *    'Content-Type': undefined
11545      *  },
11546      *  data: { test: 'test' }
11547      * }
11548      *
11549      * $http(req).then(function(){...}, function(){...});
11550      * ```
11551      *
11552      * ## Transforming Requests and Responses
11553      *
11554      * Both requests and responses can be transformed using transformation functions: `transformRequest`
11555      * and `transformResponse`. These properties can be a single function that returns
11556      * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
11557      * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
11558      *
11559      * <div class="alert alert-warning">
11560      * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
11561      * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
11562      * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
11563      * function will be reflected on the scope and in any templates where the object is data-bound.
11564      * To prevent this, transform functions should have no side-effects.
11565      * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
11566      * </div>
11567      *
11568      * ### Default Transformations
11569      *
11570      * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
11571      * `defaults.transformResponse` properties. If a request does not provide its own transformations
11572      * then these will be applied.
11573      *
11574      * You can augment or replace the default transformations by modifying these properties by adding to or
11575      * replacing the array.
11576      *
11577      * Angular provides the following default transformations:
11578      *
11579      * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
11580      *
11581      * - If the `data` property of the request configuration object contains an object, serialize it
11582      *   into JSON format.
11583      *
11584      * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
11585      *
11586      *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
11587      *  - If JSON response is detected, deserialize it using a JSON parser.
11588      *
11589      *
11590      * ### Overriding the Default Transformations Per Request
11591      *
11592      * If you wish to override the request/response transformations only for a single request then provide
11593      * `transformRequest` and/or `transformResponse` properties on the configuration object passed
11594      * into `$http`.
11595      *
11596      * Note that if you provide these properties on the config object the default transformations will be
11597      * overwritten. If you wish to augment the default transformations then you must include them in your
11598      * local transformation array.
11599      *
11600      * The following code demonstrates adding a new response transformation to be run after the default response
11601      * transformations have been run.
11602      *
11603      * ```js
11604      * function appendTransform(defaults, transform) {
11605      *
11606      *   // We can't guarantee that the default transformation is an array
11607      *   defaults = angular.isArray(defaults) ? defaults : [defaults];
11608      *
11609      *   // Append the new transformation to the defaults
11610      *   return defaults.concat(transform);
11611      * }
11612      *
11613      * $http({
11614      *   url: '...',
11615      *   method: 'GET',
11616      *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
11617      *     return doTransform(value);
11618      *   })
11619      * });
11620      * ```
11621      *
11622      *
11623      * ## Caching
11624      *
11625      * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
11626      * set the config.cache value or the default cache value to TRUE or to a cache object (created
11627      * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
11628      * precedence over the default cache value.
11629      *
11630      * In order to:
11631      *   * cache all responses - set the default cache value to TRUE or to a cache object
11632      *   * cache a specific response - set config.cache value to TRUE or to a cache object
11633      *
11634      * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
11635      * then the default `$cacheFactory("$http")` object is used.
11636      *
11637      * The default cache value can be set by updating the
11638      * {@link ng.$http#defaults `$http.defaults.cache`} property or the
11639      * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
11640      *
11641      * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
11642      * the relevant cache object. The next time the same request is made, the response is returned
11643      * from the cache without sending a request to the server.
11644      *
11645      * Take note that:
11646      *
11647      *   * Only GET and JSONP requests are cached.
11648      *   * The cache key is the request URL including search parameters; headers are not considered.
11649      *   * Cached responses are returned asynchronously, in the same way as responses from the server.
11650      *   * If multiple identical requests are made using the same cache, which is not yet populated,
11651      *     one request will be made to the server and remaining requests will return the same response.
11652      *   * A cache-control header on the response does not affect if or how responses are cached.
11653      *
11654      *
11655      * ## Interceptors
11656      *
11657      * Before you start creating interceptors, be sure to understand the
11658      * {@link ng.$q $q and deferred/promise APIs}.
11659      *
11660      * For purposes of global error handling, authentication, or any kind of synchronous or
11661      * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
11662      * able to intercept requests before they are handed to the server and
11663      * responses before they are handed over to the application code that
11664      * initiated these requests. The interceptors leverage the {@link ng.$q
11665      * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
11666      *
11667      * The interceptors are service factories that are registered with the `$httpProvider` by
11668      * adding them to the `$httpProvider.interceptors` array. The factory is called and
11669      * injected with dependencies (if specified) and returns the interceptor.
11670      *
11671      * There are two kinds of interceptors (and two kinds of rejection interceptors):
11672      *
11673      *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
11674      *     modify the `config` object or create a new one. The function needs to return the `config`
11675      *     object directly, or a promise containing the `config` or a new `config` object.
11676      *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
11677      *     resolved with a rejection.
11678      *   * `response`: interceptors get called with http `response` object. The function is free to
11679      *     modify the `response` object or create a new one. The function needs to return the `response`
11680      *     object directly, or as a promise containing the `response` or a new `response` object.
11681      *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
11682      *     resolved with a rejection.
11683      *
11684      *
11685      * ```js
11686      *   // register the interceptor as a service
11687      *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
11688      *     return {
11689      *       // optional method
11690      *       'request': function(config) {
11691      *         // do something on success
11692      *         return config;
11693      *       },
11694      *
11695      *       // optional method
11696      *      'requestError': function(rejection) {
11697      *         // do something on error
11698      *         if (canRecover(rejection)) {
11699      *           return responseOrNewPromise
11700      *         }
11701      *         return $q.reject(rejection);
11702      *       },
11703      *
11704      *
11705      *
11706      *       // optional method
11707      *       'response': function(response) {
11708      *         // do something on success
11709      *         return response;
11710      *       },
11711      *
11712      *       // optional method
11713      *      'responseError': function(rejection) {
11714      *         // do something on error
11715      *         if (canRecover(rejection)) {
11716      *           return responseOrNewPromise
11717      *         }
11718      *         return $q.reject(rejection);
11719      *       }
11720      *     };
11721      *   });
11722      *
11723      *   $httpProvider.interceptors.push('myHttpInterceptor');
11724      *
11725      *
11726      *   // alternatively, register the interceptor via an anonymous factory
11727      *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
11728      *     return {
11729      *      'request': function(config) {
11730      *          // same as above
11731      *       },
11732      *
11733      *       'response': function(response) {
11734      *          // same as above
11735      *       }
11736      *     };
11737      *   });
11738      * ```
11739      *
11740      * ## Security Considerations
11741      *
11742      * When designing web applications, consider security threats from:
11743      *
11744      * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
11745      * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
11746      *
11747      * Both server and the client must cooperate in order to eliminate these threats. Angular comes
11748      * pre-configured with strategies that address these issues, but for this to work backend server
11749      * cooperation is required.
11750      *
11751      * ### JSON Vulnerability Protection
11752      *
11753      * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
11754      * allows third party website to turn your JSON resource URL into
11755      * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
11756      * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
11757      * Angular will automatically strip the prefix before processing it as JSON.
11758      *
11759      * For example if your server needs to return:
11760      * ```js
11761      * ['one','two']
11762      * ```
11763      *
11764      * which is vulnerable to attack, your server can return:
11765      * ```js
11766      * )]}',
11767      * ['one','two']
11768      * ```
11769      *
11770      * Angular will strip the prefix, before processing the JSON.
11771      *
11772      *
11773      * ### Cross Site Request Forgery (XSRF) Protection
11774      *
11775      * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
11776      * which the attacker can trick an authenticated user into unknowingly executing actions on your
11777      * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
11778      * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
11779      * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
11780      * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
11781      * The header will not be set for cross-domain requests.
11782      *
11783      * To take advantage of this, your server needs to set a token in a JavaScript readable session
11784      * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
11785      * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
11786      * that only JavaScript running on your domain could have sent the request. The token must be
11787      * unique for each user and must be verifiable by the server (to prevent the JavaScript from
11788      * making up its own tokens). We recommend that the token is a digest of your site's
11789      * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
11790      * for added security.
11791      *
11792      * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
11793      * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
11794      * or the per-request config object.
11795      *
11796      * In order to prevent collisions in environments where multiple Angular apps share the
11797      * same domain or subdomain, we recommend that each application uses unique cookie name.
11798      *
11799      * @param {object} config Object describing the request to be made and how it should be
11800      *    processed. The object has following properties:
11801      *
11802      *    - **method** â€“ `{string}` â€“ HTTP method (e.g. 'GET', 'POST', etc)
11803      *    - **url** â€“ `{string|TrustedObject}` â€“ Absolute or relative URL of the resource that is being requested;
11804      *      or an object created by a call to `$sce.trustAsResourceUrl(url)`.
11805      *    - **params** â€“ `{Object.<string|Object>}` â€“ Map of strings or objects which will be serialized
11806      *      with the `paramSerializer` and appended as GET parameters.
11807      *    - **data** â€“ `{string|Object}` â€“ Data to be sent as the request message data.
11808      *    - **headers** â€“ `{Object}` â€“ Map of strings or functions which return strings representing
11809      *      HTTP headers to send to the server. If the return value of a function is null, the
11810      *      header will not be sent. Functions accept a config object as an argument.
11811      *    - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
11812      *      To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
11813      *      The handler will be called in the context of a `$apply` block.
11814      *    - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
11815      *      object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
11816      *      The handler will be called in the context of a `$apply` block.
11817      *    - **xsrfHeaderName** â€“ `{string}` â€“ Name of HTTP header to populate with the XSRF token.
11818      *    - **xsrfCookieName** â€“ `{string}` â€“ Name of cookie containing the XSRF token.
11819      *    - **transformRequest** â€“
11820      *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` â€“
11821      *      transform function or an array of such functions. The transform function takes the http
11822      *      request body and headers and returns its transformed (typically serialized) version.
11823      *      See {@link ng.$http#overriding-the-default-transformations-per-request
11824      *      Overriding the Default Transformations}
11825      *    - **transformResponse** â€“
11826      *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` â€“
11827      *      transform function or an array of such functions. The transform function takes the http
11828      *      response body, headers and status and returns its transformed (typically deserialized) version.
11829      *      See {@link ng.$http#overriding-the-default-transformations-per-request
11830      *      Overriding the Default Transformations}
11831      *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
11832      *      prepare the string representation of request parameters (specified as an object).
11833      *      If specified as string, it is interpreted as function registered with the
11834      *      {@link $injector $injector}, which means you can create your own serializer
11835      *      by registering it as a {@link auto.$provide#service service}.
11836      *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
11837      *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
11838      *    - **cache** â€“ `{boolean|Object}` â€“ A boolean value or object created with
11839      *      {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
11840      *      See {@link $http#caching $http Caching} for more information.
11841      *    - **timeout** â€“ `{number|Promise}` â€“ timeout in milliseconds, or {@link ng.$q promise}
11842      *      that should abort the request when resolved.
11843      *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
11844      *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
11845      *      for more information.
11846      *    - **responseType** - `{string}` - see
11847      *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
11848      *
11849      * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
11850      *                        when the request succeeds or fails.
11851      *
11852      *
11853      * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
11854      *   requests. This is primarily meant to be used for debugging purposes.
11855      *
11856      *
11857      * @example
11858 <example module="httpExample" name="http-service">
11859 <file name="index.html">
11860   <div ng-controller="FetchController">
11861     <select ng-model="method" aria-label="Request method">
11862       <option>GET</option>
11863       <option>JSONP</option>
11864     </select>
11865     <input type="text" ng-model="url" size="80" aria-label="URL" />
11866     <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
11867     <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
11868     <button id="samplejsonpbtn"
11869       ng-click="updateModel('JSONP',
11870                     'https://angularjs.org/greet.php?name=Super%20Hero')">
11871       Sample JSONP
11872     </button>
11873     <button id="invalidjsonpbtn"
11874       ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist')">
11875         Invalid JSONP
11876       </button>
11877     <pre>http status code: {{status}}</pre>
11878     <pre>http response data: {{data}}</pre>
11879   </div>
11880 </file>
11881 <file name="script.js">
11882   angular.module('httpExample', [])
11883     .config(['$sceDelegateProvider', function($sceDelegateProvider) {
11884       // We must whitelist the JSONP endpoint that we are using to show that we trust it
11885       $sceDelegateProvider.resourceUrlWhitelist([
11886         'self',
11887         'https://angularjs.org/**'
11888       ]);
11889     }])
11890     .controller('FetchController', ['$scope', '$http', '$templateCache',
11891       function($scope, $http, $templateCache) {
11892         $scope.method = 'GET';
11893         $scope.url = 'http-hello.html';
11894
11895         $scope.fetch = function() {
11896           $scope.code = null;
11897           $scope.response = null;
11898
11899           $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
11900             then(function(response) {
11901               $scope.status = response.status;
11902               $scope.data = response.data;
11903             }, function(response) {
11904               $scope.data = response.data || 'Request failed';
11905               $scope.status = response.status;
11906           });
11907         };
11908
11909         $scope.updateModel = function(method, url) {
11910           $scope.method = method;
11911           $scope.url = url;
11912         };
11913       }]);
11914 </file>
11915 <file name="http-hello.html">
11916   Hello, $http!
11917 </file>
11918 <file name="protractor.js" type="protractor">
11919   var status = element(by.binding('status'));
11920   var data = element(by.binding('data'));
11921   var fetchBtn = element(by.id('fetchbtn'));
11922   var sampleGetBtn = element(by.id('samplegetbtn'));
11923   var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
11924
11925   it('should make an xhr GET request', function() {
11926     sampleGetBtn.click();
11927     fetchBtn.click();
11928     expect(status.getText()).toMatch('200');
11929     expect(data.getText()).toMatch(/Hello, \$http!/);
11930   });
11931
11932 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
11933 // it('should make a JSONP request to angularjs.org', function() {
11934 //   var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
11935 //   sampleJsonpBtn.click();
11936 //   fetchBtn.click();
11937 //   expect(status.getText()).toMatch('200');
11938 //   expect(data.getText()).toMatch(/Super Hero!/);
11939 // });
11940
11941   it('should make JSONP request to invalid URL and invoke the error handler',
11942       function() {
11943     invalidJsonpBtn.click();
11944     fetchBtn.click();
11945     expect(status.getText()).toMatch('0');
11946     expect(data.getText()).toMatch('Request failed');
11947   });
11948 </file>
11949 </example>
11950      */
11951     function $http(requestConfig) {
11952
11953       if (!isObject(requestConfig)) {
11954         throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
11955       }
11956
11957       if (!isString($sce.valueOf(requestConfig.url))) {
11958         throw minErr('$http')('badreq', 'Http request configuration url must be a string or a $sce trusted object.  Received: {0}', requestConfig.url);
11959       }
11960
11961       var config = extend({
11962         method: 'get',
11963         transformRequest: defaults.transformRequest,
11964         transformResponse: defaults.transformResponse,
11965         paramSerializer: defaults.paramSerializer,
11966         jsonpCallbackParam: defaults.jsonpCallbackParam
11967       }, requestConfig);
11968
11969       config.headers = mergeHeaders(requestConfig);
11970       config.method = uppercase(config.method);
11971       config.paramSerializer = isString(config.paramSerializer) ?
11972           $injector.get(config.paramSerializer) : config.paramSerializer;
11973
11974       $browser.$$incOutstandingRequestCount();
11975
11976       var requestInterceptors = [];
11977       var responseInterceptors = [];
11978       var promise = $q.resolve(config);
11979
11980       // apply interceptors
11981       forEach(reversedInterceptors, function(interceptor) {
11982         if (interceptor.request || interceptor.requestError) {
11983           requestInterceptors.unshift(interceptor.request, interceptor.requestError);
11984         }
11985         if (interceptor.response || interceptor.responseError) {
11986           responseInterceptors.push(interceptor.response, interceptor.responseError);
11987         }
11988       });
11989
11990       promise = chainInterceptors(promise, requestInterceptors);
11991       promise = promise.then(serverRequest);
11992       promise = chainInterceptors(promise, responseInterceptors);
11993       promise = promise.finally(completeOutstandingRequest);
11994
11995       return promise;
11996
11997
11998       function chainInterceptors(promise, interceptors) {
11999         for (var i = 0, ii = interceptors.length; i < ii;) {
12000           var thenFn = interceptors[i++];
12001           var rejectFn = interceptors[i++];
12002
12003           promise = promise.then(thenFn, rejectFn);
12004         }
12005
12006         interceptors.length = 0;
12007
12008         return promise;
12009       }
12010
12011       function completeOutstandingRequest() {
12012         $browser.$$completeOutstandingRequest(noop);
12013       }
12014
12015       function executeHeaderFns(headers, config) {
12016         var headerContent, processedHeaders = {};
12017
12018         forEach(headers, function(headerFn, header) {
12019           if (isFunction(headerFn)) {
12020             headerContent = headerFn(config);
12021             if (headerContent != null) {
12022               processedHeaders[header] = headerContent;
12023             }
12024           } else {
12025             processedHeaders[header] = headerFn;
12026           }
12027         });
12028
12029         return processedHeaders;
12030       }
12031
12032       function mergeHeaders(config) {
12033         var defHeaders = defaults.headers,
12034             reqHeaders = extend({}, config.headers),
12035             defHeaderName, lowercaseDefHeaderName, reqHeaderName;
12036
12037         defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
12038
12039         // using for-in instead of forEach to avoid unnecessary iteration after header has been found
12040         defaultHeadersIteration:
12041         for (defHeaderName in defHeaders) {
12042           lowercaseDefHeaderName = lowercase(defHeaderName);
12043
12044           for (reqHeaderName in reqHeaders) {
12045             if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
12046               continue defaultHeadersIteration;
12047             }
12048           }
12049
12050           reqHeaders[defHeaderName] = defHeaders[defHeaderName];
12051         }
12052
12053         // execute if header value is a function for merged headers
12054         return executeHeaderFns(reqHeaders, shallowCopy(config));
12055       }
12056
12057       function serverRequest(config) {
12058         var headers = config.headers;
12059         var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
12060
12061         // strip content-type if data is undefined
12062         if (isUndefined(reqData)) {
12063           forEach(headers, function(value, header) {
12064             if (lowercase(header) === 'content-type') {
12065               delete headers[header];
12066             }
12067           });
12068         }
12069
12070         if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
12071           config.withCredentials = defaults.withCredentials;
12072         }
12073
12074         // send request
12075         return sendReq(config, reqData).then(transformResponse, transformResponse);
12076       }
12077
12078       function transformResponse(response) {
12079         // make a copy since the response must be cacheable
12080         var resp = extend({}, response);
12081         resp.data = transformData(response.data, response.headers, response.status,
12082                                   config.transformResponse);
12083         return (isSuccess(response.status))
12084           ? resp
12085           : $q.reject(resp);
12086       }
12087     }
12088
12089     $http.pendingRequests = [];
12090
12091     /**
12092      * @ngdoc method
12093      * @name $http#get
12094      *
12095      * @description
12096      * Shortcut method to perform `GET` request.
12097      *
12098      * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
12099      *                                   or an object created by a call to `$sce.trustAsResourceUrl(url)`.
12100      * @param {Object=} config Optional configuration object
12101      * @returns {HttpPromise} Future object
12102      */
12103
12104     /**
12105      * @ngdoc method
12106      * @name $http#delete
12107      *
12108      * @description
12109      * Shortcut method to perform `DELETE` request.
12110      *
12111      * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
12112      *                                   or an object created by a call to `$sce.trustAsResourceUrl(url)`.
12113      * @param {Object=} config Optional configuration object
12114      * @returns {HttpPromise} Future object
12115      */
12116
12117     /**
12118      * @ngdoc method
12119      * @name $http#head
12120      *
12121      * @description
12122      * Shortcut method to perform `HEAD` request.
12123      *
12124      * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
12125      *                                   or an object created by a call to `$sce.trustAsResourceUrl(url)`.
12126      * @param {Object=} config Optional configuration object
12127      * @returns {HttpPromise} Future object
12128      */
12129
12130     /**
12131      * @ngdoc method
12132      * @name $http#jsonp
12133      *
12134      * @description
12135      * Shortcut method to perform `JSONP` request.
12136      *
12137      * Note that, since JSONP requests are sensitive because the response is given full access to the browser,
12138      * the url must be declared, via {@link $sce} as a trusted resource URL.
12139      * You can trust a URL by adding it to the whitelist via
12140      * {@link $sceDelegateProvider#resourceUrlWhitelist  `$sceDelegateProvider.resourceUrlWhitelist`} or
12141      * by explicitly trusting the URL via {@link $sce#trustAsResourceUrl `$sce.trustAsResourceUrl(url)`}.
12142      *
12143      * JSONP requests must specify a callback to be used in the response from the server. This callback
12144      * is passed as a query parameter in the request. You must specify the name of this parameter by
12145      * setting the `jsonpCallbackParam` property on the request config object.
12146      *
12147      * ```
12148      * $http.jsonp('some/trusted/url', {jsonpCallbackParam: 'callback'})
12149      * ```
12150      *
12151      * You can also specify a default callback parameter name in `$http.defaults.jsonpCallbackParam`.
12152      * Initially this is set to `'callback'`.
12153      *
12154      * <div class="alert alert-danger">
12155      * You can no longer use the `JSON_CALLBACK` string as a placeholder for specifying where the callback
12156      * parameter value should go.
12157      * </div>
12158      *
12159      * If you would like to customise where and how the callbacks are stored then try overriding
12160      * or decorating the {@link $jsonpCallbacks} service.
12161      *
12162      * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
12163      *                                   or an object created by a call to `$sce.trustAsResourceUrl(url)`.
12164      * @param {Object=} config Optional configuration object
12165      * @returns {HttpPromise} Future object
12166      */
12167     createShortMethods('get', 'delete', 'head', 'jsonp');
12168
12169     /**
12170      * @ngdoc method
12171      * @name $http#post
12172      *
12173      * @description
12174      * Shortcut method to perform `POST` request.
12175      *
12176      * @param {string} url Relative or absolute URL specifying the destination of the request
12177      * @param {*} data Request content
12178      * @param {Object=} config Optional configuration object
12179      * @returns {HttpPromise} Future object
12180      */
12181
12182     /**
12183      * @ngdoc method
12184      * @name $http#put
12185      *
12186      * @description
12187      * Shortcut method to perform `PUT` request.
12188      *
12189      * @param {string} url Relative or absolute URL specifying the destination of the request
12190      * @param {*} data Request content
12191      * @param {Object=} config Optional configuration object
12192      * @returns {HttpPromise} Future object
12193      */
12194
12195      /**
12196       * @ngdoc method
12197       * @name $http#patch
12198       *
12199       * @description
12200       * Shortcut method to perform `PATCH` request.
12201       *
12202       * @param {string} url Relative or absolute URL specifying the destination of the request
12203       * @param {*} data Request content
12204       * @param {Object=} config Optional configuration object
12205       * @returns {HttpPromise} Future object
12206       */
12207     createShortMethodsWithData('post', 'put', 'patch');
12208
12209         /**
12210          * @ngdoc property
12211          * @name $http#defaults
12212          *
12213          * @description
12214          * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
12215          * default headers, withCredentials as well as request and response transformations.
12216          *
12217          * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
12218          */
12219     $http.defaults = defaults;
12220
12221
12222     return $http;
12223
12224
12225     function createShortMethods(names) {
12226       forEach(arguments, function(name) {
12227         $http[name] = function(url, config) {
12228           return $http(extend({}, config || {}, {
12229             method: name,
12230             url: url
12231           }));
12232         };
12233       });
12234     }
12235
12236
12237     function createShortMethodsWithData(name) {
12238       forEach(arguments, function(name) {
12239         $http[name] = function(url, data, config) {
12240           return $http(extend({}, config || {}, {
12241             method: name,
12242             url: url,
12243             data: data
12244           }));
12245         };
12246       });
12247     }
12248
12249
12250     /**
12251      * Makes the request.
12252      *
12253      * !!! ACCESSES CLOSURE VARS:
12254      * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
12255      */
12256     function sendReq(config, reqData) {
12257       var deferred = $q.defer(),
12258           promise = deferred.promise,
12259           cache,
12260           cachedResp,
12261           reqHeaders = config.headers,
12262           isJsonp = lowercase(config.method) === 'jsonp',
12263           url = config.url;
12264
12265       if (isJsonp) {
12266         // JSONP is a pretty sensitive operation where we're allowing a script to have full access to
12267         // our DOM and JS space.  So we require that the URL satisfies SCE.RESOURCE_URL.
12268         url = $sce.getTrustedResourceUrl(url);
12269       } else if (!isString(url)) {
12270         // If it is not a string then the URL must be a $sce trusted object
12271         url = $sce.valueOf(url);
12272       }
12273
12274       url = buildUrl(url, config.paramSerializer(config.params));
12275
12276       if (isJsonp) {
12277         // Check the url and add the JSONP callback placeholder
12278         url = sanitizeJsonpCallbackParam(url, config.jsonpCallbackParam);
12279       }
12280
12281       $http.pendingRequests.push(config);
12282       promise.then(removePendingReq, removePendingReq);
12283
12284       if ((config.cache || defaults.cache) && config.cache !== false &&
12285           (config.method === 'GET' || config.method === 'JSONP')) {
12286         cache = isObject(config.cache) ? config.cache
12287             : isObject(/** @type {?} */ (defaults).cache)
12288               ? /** @type {?} */ (defaults).cache
12289               : defaultCache;
12290       }
12291
12292       if (cache) {
12293         cachedResp = cache.get(url);
12294         if (isDefined(cachedResp)) {
12295           if (isPromiseLike(cachedResp)) {
12296             // cached request has already been sent, but there is no response yet
12297             cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
12298           } else {
12299             // serving from cache
12300             if (isArray(cachedResp)) {
12301               resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
12302             } else {
12303               resolvePromise(cachedResp, 200, {}, 'OK');
12304             }
12305           }
12306         } else {
12307           // put the promise for the non-transformed response into cache as a placeholder
12308           cache.put(url, promise);
12309         }
12310       }
12311
12312
12313       // if we won't have the response in cache, set the xsrf headers and
12314       // send the request to the backend
12315       if (isUndefined(cachedResp)) {
12316         var xsrfValue = urlIsSameOrigin(config.url)
12317             ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
12318             : undefined;
12319         if (xsrfValue) {
12320           reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
12321         }
12322
12323         $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
12324             config.withCredentials, config.responseType,
12325             createApplyHandlers(config.eventHandlers),
12326             createApplyHandlers(config.uploadEventHandlers));
12327       }
12328
12329       return promise;
12330
12331       function createApplyHandlers(eventHandlers) {
12332         if (eventHandlers) {
12333           var applyHandlers = {};
12334           forEach(eventHandlers, function(eventHandler, key) {
12335             applyHandlers[key] = function(event) {
12336               if (useApplyAsync) {
12337                 $rootScope.$applyAsync(callEventHandler);
12338               } else if ($rootScope.$$phase) {
12339                 callEventHandler();
12340               } else {
12341                 $rootScope.$apply(callEventHandler);
12342               }
12343
12344               function callEventHandler() {
12345                 eventHandler(event);
12346               }
12347             };
12348           });
12349           return applyHandlers;
12350         }
12351       }
12352
12353
12354       /**
12355        * Callback registered to $httpBackend():
12356        *  - caches the response if desired
12357        *  - resolves the raw $http promise
12358        *  - calls $apply
12359        */
12360       function done(status, response, headersString, statusText) {
12361         if (cache) {
12362           if (isSuccess(status)) {
12363             cache.put(url, [status, response, parseHeaders(headersString), statusText]);
12364           } else {
12365             // remove promise from the cache
12366             cache.remove(url);
12367           }
12368         }
12369
12370         function resolveHttpPromise() {
12371           resolvePromise(response, status, headersString, statusText);
12372         }
12373
12374         if (useApplyAsync) {
12375           $rootScope.$applyAsync(resolveHttpPromise);
12376         } else {
12377           resolveHttpPromise();
12378           if (!$rootScope.$$phase) $rootScope.$apply();
12379         }
12380       }
12381
12382
12383       /**
12384        * Resolves the raw $http promise.
12385        */
12386       function resolvePromise(response, status, headers, statusText) {
12387         //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
12388         status = status >= -1 ? status : 0;
12389
12390         (isSuccess(status) ? deferred.resolve : deferred.reject)({
12391           data: response,
12392           status: status,
12393           headers: headersGetter(headers),
12394           config: config,
12395           statusText: statusText
12396         });
12397       }
12398
12399       function resolvePromiseWithResult(result) {
12400         resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
12401       }
12402
12403       function removePendingReq() {
12404         var idx = $http.pendingRequests.indexOf(config);
12405         if (idx !== -1) $http.pendingRequests.splice(idx, 1);
12406       }
12407     }
12408
12409
12410     function buildUrl(url, serializedParams) {
12411       if (serializedParams.length > 0) {
12412         url += ((url.indexOf('?') === -1) ? '?' : '&') + serializedParams;
12413       }
12414       return url;
12415     }
12416
12417     function sanitizeJsonpCallbackParam(url, key) {
12418       if (/[&?][^=]+=JSON_CALLBACK/.test(url)) {
12419         // Throw if the url already contains a reference to JSON_CALLBACK
12420         throw $httpMinErr('badjsonp', 'Illegal use of JSON_CALLBACK in url, "{0}"', url);
12421       }
12422
12423       var callbackParamRegex = new RegExp('[&?]' + key + '=');
12424       if (callbackParamRegex.test(url)) {
12425         // Throw if the callback param was already provided
12426         throw $httpMinErr('badjsonp', 'Illegal use of callback param, "{0}", in url, "{1}"', key, url);
12427       }
12428
12429       // Add in the JSON_CALLBACK callback param value
12430       url += ((url.indexOf('?') === -1) ? '?' : '&') + key + '=JSON_CALLBACK';
12431
12432       return url;
12433     }
12434   }];
12435 }
12436
12437 /**
12438  * @ngdoc service
12439  * @name $xhrFactory
12440  * @this
12441  *
12442  * @description
12443  * Factory function used to create XMLHttpRequest objects.
12444  *
12445  * Replace or decorate this service to create your own custom XMLHttpRequest objects.
12446  *
12447  * ```
12448  * angular.module('myApp', [])
12449  * .factory('$xhrFactory', function() {
12450  *   return function createXhr(method, url) {
12451  *     return new window.XMLHttpRequest({mozSystem: true});
12452  *   };
12453  * });
12454  * ```
12455  *
12456  * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
12457  * @param {string} url URL of the request.
12458  */
12459 function $xhrFactoryProvider() {
12460   this.$get = function() {
12461     return function createXhr() {
12462       return new window.XMLHttpRequest();
12463     };
12464   };
12465 }
12466
12467 /**
12468  * @ngdoc service
12469  * @name $httpBackend
12470  * @requires $jsonpCallbacks
12471  * @requires $document
12472  * @requires $xhrFactory
12473  * @this
12474  *
12475  * @description
12476  * HTTP backend used by the {@link ng.$http service} that delegates to
12477  * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
12478  *
12479  * You should never need to use this service directly, instead use the higher-level abstractions:
12480  * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
12481  *
12482  * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
12483  * $httpBackend} which can be trained with responses.
12484  */
12485 function $HttpBackendProvider() {
12486   this.$get = ['$browser', '$jsonpCallbacks', '$document', '$xhrFactory', function($browser, $jsonpCallbacks, $document, $xhrFactory) {
12487     return createHttpBackend($browser, $xhrFactory, $browser.defer, $jsonpCallbacks, $document[0]);
12488   }];
12489 }
12490
12491 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
12492   // TODO(vojta): fix the signature
12493   return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
12494     url = url || $browser.url();
12495
12496     if (lowercase(method) === 'jsonp') {
12497       var callbackPath = callbacks.createCallback(url);
12498       var jsonpDone = jsonpReq(url, callbackPath, function(status, text) {
12499         // jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING)
12500         var response = (status === 200) && callbacks.getResponse(callbackPath);
12501         completeRequest(callback, status, response, '', text);
12502         callbacks.removeCallback(callbackPath);
12503       });
12504     } else {
12505
12506       var xhr = createXhr(method, url);
12507
12508       xhr.open(method, url, true);
12509       forEach(headers, function(value, key) {
12510         if (isDefined(value)) {
12511             xhr.setRequestHeader(key, value);
12512         }
12513       });
12514
12515       xhr.onload = function requestLoaded() {
12516         var statusText = xhr.statusText || '';
12517
12518         // responseText is the old-school way of retrieving response (supported by IE9)
12519         // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
12520         var response = ('response' in xhr) ? xhr.response : xhr.responseText;
12521
12522         // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
12523         var status = xhr.status === 1223 ? 204 : xhr.status;
12524
12525         // fix status code when it is 0 (0 status is undocumented).
12526         // Occurs when accessing file resources or on Android 4.1 stock browser
12527         // while retrieving files from application cache.
12528         if (status === 0) {
12529           status = response ? 200 : urlResolve(url).protocol === 'file' ? 404 : 0;
12530         }
12531
12532         completeRequest(callback,
12533             status,
12534             response,
12535             xhr.getAllResponseHeaders(),
12536             statusText);
12537       };
12538
12539       var requestError = function() {
12540         // The response is always empty
12541         // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
12542         completeRequest(callback, -1, null, null, '');
12543       };
12544
12545       xhr.onerror = requestError;
12546       xhr.onabort = requestError;
12547       xhr.ontimeout = requestError;
12548
12549       forEach(eventHandlers, function(value, key) {
12550           xhr.addEventListener(key, value);
12551       });
12552
12553       forEach(uploadEventHandlers, function(value, key) {
12554         xhr.upload.addEventListener(key, value);
12555       });
12556
12557       if (withCredentials) {
12558         xhr.withCredentials = true;
12559       }
12560
12561       if (responseType) {
12562         try {
12563           xhr.responseType = responseType;
12564         } catch (e) {
12565           // WebKit added support for the json responseType value on 09/03/2013
12566           // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
12567           // known to throw when setting the value "json" as the response type. Other older
12568           // browsers implementing the responseType
12569           //
12570           // The json response type can be ignored if not supported, because JSON payloads are
12571           // parsed on the client-side regardless.
12572           if (responseType !== 'json') {
12573             throw e;
12574           }
12575         }
12576       }
12577
12578       xhr.send(isUndefined(post) ? null : post);
12579     }
12580
12581     if (timeout > 0) {
12582       var timeoutId = $browserDefer(timeoutRequest, timeout);
12583     } else if (isPromiseLike(timeout)) {
12584       timeout.then(timeoutRequest);
12585     }
12586
12587
12588     function timeoutRequest() {
12589       if (jsonpDone) {
12590         jsonpDone();
12591       }
12592       if (xhr) {
12593         xhr.abort();
12594       }
12595     }
12596
12597     function completeRequest(callback, status, response, headersString, statusText) {
12598       // cancel timeout and subsequent timeout promise resolution
12599       if (isDefined(timeoutId)) {
12600         $browserDefer.cancel(timeoutId);
12601       }
12602       jsonpDone = xhr = null;
12603
12604       callback(status, response, headersString, statusText);
12605     }
12606   };
12607
12608   function jsonpReq(url, callbackPath, done) {
12609     url = url.replace('JSON_CALLBACK', callbackPath);
12610     // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
12611     // - fetches local scripts via XHR and evals them
12612     // - adds and immediately removes script elements from the document
12613     var script = rawDocument.createElement('script'), callback = null;
12614     script.type = 'text/javascript';
12615     script.src = url;
12616     script.async = true;
12617
12618     callback = function(event) {
12619       script.removeEventListener('load', callback);
12620       script.removeEventListener('error', callback);
12621       rawDocument.body.removeChild(script);
12622       script = null;
12623       var status = -1;
12624       var text = 'unknown';
12625
12626       if (event) {
12627         if (event.type === 'load' && !callbacks.wasCalled(callbackPath)) {
12628           event = { type: 'error' };
12629         }
12630         text = event.type;
12631         status = event.type === 'error' ? 404 : 200;
12632       }
12633
12634       if (done) {
12635         done(status, text);
12636       }
12637     };
12638
12639     script.addEventListener('load', callback);
12640     script.addEventListener('error', callback);
12641     rawDocument.body.appendChild(script);
12642     return callback;
12643   }
12644 }
12645
12646 var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
12647 $interpolateMinErr.throwNoconcat = function(text) {
12648   throw $interpolateMinErr('noconcat',
12649       'Error while interpolating: {0}\nStrict Contextual Escaping disallows ' +
12650       'interpolations that concatenate multiple expressions when a trusted value is ' +
12651       'required.  See http://docs.angularjs.org/api/ng.$sce', text);
12652 };
12653
12654 $interpolateMinErr.interr = function(text, err) {
12655   return $interpolateMinErr('interr', 'Can\'t interpolate: {0}\n{1}', text, err.toString());
12656 };
12657
12658 /**
12659  * @ngdoc provider
12660  * @name $interpolateProvider
12661  * @this
12662  *
12663  * @description
12664  *
12665  * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
12666  *
12667  * <div class="alert alert-danger">
12668  * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
12669  * template within a Python Jinja template (or any other template language). Mixing templating
12670  * languages is **very dangerous**. The embedding template language will not safely escape Angular
12671  * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
12672  * security bugs!
12673  * </div>
12674  *
12675  * @example
12676 <example name="custom-interpolation-markup" module="customInterpolationApp">
12677 <file name="index.html">
12678 <script>
12679   var customInterpolationApp = angular.module('customInterpolationApp', []);
12680
12681   customInterpolationApp.config(function($interpolateProvider) {
12682     $interpolateProvider.startSymbol('//');
12683     $interpolateProvider.endSymbol('//');
12684   });
12685
12686
12687   customInterpolationApp.controller('DemoController', function() {
12688       this.label = "This binding is brought you by // interpolation symbols.";
12689   });
12690 </script>
12691 <div ng-controller="DemoController as demo">
12692     //demo.label//
12693 </div>
12694 </file>
12695 <file name="protractor.js" type="protractor">
12696   it('should interpolate binding with custom symbols', function() {
12697     expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
12698   });
12699 </file>
12700 </example>
12701  */
12702 function $InterpolateProvider() {
12703   var startSymbol = '{{';
12704   var endSymbol = '}}';
12705
12706   /**
12707    * @ngdoc method
12708    * @name $interpolateProvider#startSymbol
12709    * @description
12710    * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
12711    *
12712    * @param {string=} value new value to set the starting symbol to.
12713    * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
12714    */
12715   this.startSymbol = function(value) {
12716     if (value) {
12717       startSymbol = value;
12718       return this;
12719     } else {
12720       return startSymbol;
12721     }
12722   };
12723
12724   /**
12725    * @ngdoc method
12726    * @name $interpolateProvider#endSymbol
12727    * @description
12728    * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
12729    *
12730    * @param {string=} value new value to set the ending symbol to.
12731    * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
12732    */
12733   this.endSymbol = function(value) {
12734     if (value) {
12735       endSymbol = value;
12736       return this;
12737     } else {
12738       return endSymbol;
12739     }
12740   };
12741
12742
12743   this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
12744     var startSymbolLength = startSymbol.length,
12745         endSymbolLength = endSymbol.length,
12746         escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
12747         escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
12748
12749     function escape(ch) {
12750       return '\\\\\\' + ch;
12751     }
12752
12753     function unescapeText(text) {
12754       return text.replace(escapedStartRegexp, startSymbol).
12755         replace(escapedEndRegexp, endSymbol);
12756     }
12757
12758     // TODO: this is the same as the constantWatchDelegate in parse.js
12759     function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
12760       var unwatch = scope.$watch(function constantInterpolateWatch(scope) {
12761         unwatch();
12762         return constantInterp(scope);
12763       }, listener, objectEquality);
12764       return unwatch;
12765     }
12766
12767     /**
12768      * @ngdoc service
12769      * @name $interpolate
12770      * @kind function
12771      *
12772      * @requires $parse
12773      * @requires $sce
12774      *
12775      * @description
12776      *
12777      * Compiles a string with markup into an interpolation function. This service is used by the
12778      * HTML {@link ng.$compile $compile} service for data binding. See
12779      * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
12780      * interpolation markup.
12781      *
12782      *
12783      * ```js
12784      *   var $interpolate = ...; // injected
12785      *   var exp = $interpolate('Hello {{name | uppercase}}!');
12786      *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
12787      * ```
12788      *
12789      * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
12790      * `true`, the interpolation function will return `undefined` unless all embedded expressions
12791      * evaluate to a value other than `undefined`.
12792      *
12793      * ```js
12794      *   var $interpolate = ...; // injected
12795      *   var context = {greeting: 'Hello', name: undefined };
12796      *
12797      *   // default "forgiving" mode
12798      *   var exp = $interpolate('{{greeting}} {{name}}!');
12799      *   expect(exp(context)).toEqual('Hello !');
12800      *
12801      *   // "allOrNothing" mode
12802      *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
12803      *   expect(exp(context)).toBeUndefined();
12804      *   context.name = 'Angular';
12805      *   expect(exp(context)).toEqual('Hello Angular!');
12806      * ```
12807      *
12808      * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
12809      *
12810      * #### Escaped Interpolation
12811      * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
12812      * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
12813      * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
12814      * or binding.
12815      *
12816      * This enables web-servers to prevent script injection attacks and defacing attacks, to some
12817      * degree, while also enabling code examples to work without relying on the
12818      * {@link ng.directive:ngNonBindable ngNonBindable} directive.
12819      *
12820      * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
12821      * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
12822      * interpolation start/end markers with their escaped counterparts.**
12823      *
12824      * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
12825      * output when the $interpolate service processes the text. So, for HTML elements interpolated
12826      * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
12827      * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
12828      * this is typically useful only when user-data is used in rendering a template from the server, or
12829      * when otherwise untrusted data is used by a directive.
12830      *
12831      * <example name="interpolation">
12832      *  <file name="index.html">
12833      *    <div ng-init="username='A user'">
12834      *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
12835      *        </p>
12836      *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
12837      *        application, but fails to accomplish their task, because the server has correctly
12838      *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
12839      *        characters.</p>
12840      *      <p>Instead, the result of the attempted script injection is visible, and can be removed
12841      *        from the database by an administrator.</p>
12842      *    </div>
12843      *  </file>
12844      * </example>
12845      *
12846      * @knownIssue
12847      * It is currently not possible for an interpolated expression to contain the interpolation end
12848      * symbol. For example, `{{ '}}' }}` will be incorrectly interpreted as `{{ ' }}` + `' }}`, i.e.
12849      * an interpolated expression consisting of a single-quote (`'`) and the `' }}` string.
12850      *
12851      * @knownIssue
12852      * All directives and components must use the standard `{{` `}}` interpolation symbols
12853      * in their templates. If you change the application interpolation symbols the {@link $compile}
12854      * service will attempt to denormalize the standard symbols to the custom symbols.
12855      * The denormalization process is not clever enough to know not to replace instances of the standard
12856      * symbols where they would not normally be treated as interpolation symbols. For example in the following
12857      * code snippet the closing braces of the literal object will get incorrectly denormalized:
12858      *
12859      * ```
12860      * <div data-context='{"context":{"id":3,"type":"page"}}">
12861      * ```
12862      *
12863      * The workaround is to ensure that such instances are separated by whitespace:
12864      * ```
12865      * <div data-context='{"context":{"id":3,"type":"page"} }">
12866      * ```
12867      *
12868      * See https://github.com/angular/angular.js/pull/14610#issuecomment-219401099 for more information.
12869      *
12870      * @param {string} text The text with markup to interpolate.
12871      * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
12872      *    embedded expression in order to return an interpolation function. Strings with no
12873      *    embedded expression will return null for the interpolation function.
12874      * @param {string=} trustedContext when provided, the returned function passes the interpolated
12875      *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
12876      *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
12877      *    provides Strict Contextual Escaping for details.
12878      * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
12879      *    unless all embedded expressions evaluate to a value other than `undefined`.
12880      * @returns {function(context)} an interpolation function which is used to compute the
12881      *    interpolated string. The function has these parameters:
12882      *
12883      * - `context`: evaluation context for all expressions embedded in the interpolated text
12884      */
12885     function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
12886       // Provide a quick exit and simplified result function for text with no interpolation
12887       if (!text.length || text.indexOf(startSymbol) === -1) {
12888         var constantInterp;
12889         if (!mustHaveExpression) {
12890           var unescapedText = unescapeText(text);
12891           constantInterp = valueFn(unescapedText);
12892           constantInterp.exp = text;
12893           constantInterp.expressions = [];
12894           constantInterp.$$watchDelegate = constantWatchDelegate;
12895         }
12896         return constantInterp;
12897       }
12898
12899       allOrNothing = !!allOrNothing;
12900       var startIndex,
12901           endIndex,
12902           index = 0,
12903           expressions = [],
12904           parseFns = [],
12905           textLength = text.length,
12906           exp,
12907           concat = [],
12908           expressionPositions = [];
12909
12910       while (index < textLength) {
12911         if (((startIndex = text.indexOf(startSymbol, index)) !== -1) &&
12912              ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) !== -1)) {
12913           if (index !== startIndex) {
12914             concat.push(unescapeText(text.substring(index, startIndex)));
12915           }
12916           exp = text.substring(startIndex + startSymbolLength, endIndex);
12917           expressions.push(exp);
12918           parseFns.push($parse(exp, parseStringifyInterceptor));
12919           index = endIndex + endSymbolLength;
12920           expressionPositions.push(concat.length);
12921           concat.push('');
12922         } else {
12923           // we did not find an interpolation, so we have to add the remainder to the separators array
12924           if (index !== textLength) {
12925             concat.push(unescapeText(text.substring(index)));
12926           }
12927           break;
12928         }
12929       }
12930
12931       // Concatenating expressions makes it hard to reason about whether some combination of
12932       // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
12933       // single expression be used for iframe[src], object[src], etc., we ensure that the value
12934       // that's used is assigned or constructed by some JS code somewhere that is more testable or
12935       // make it obvious that you bound the value to some user controlled value.  This helps reduce
12936       // the load when auditing for XSS issues.
12937       if (trustedContext && concat.length > 1) {
12938           $interpolateMinErr.throwNoconcat(text);
12939       }
12940
12941       if (!mustHaveExpression || expressions.length) {
12942         var compute = function(values) {
12943           for (var i = 0, ii = expressions.length; i < ii; i++) {
12944             if (allOrNothing && isUndefined(values[i])) return;
12945             concat[expressionPositions[i]] = values[i];
12946           }
12947           return concat.join('');
12948         };
12949
12950         var getValue = function(value) {
12951           return trustedContext ?
12952             $sce.getTrusted(trustedContext, value) :
12953             $sce.valueOf(value);
12954         };
12955
12956         return extend(function interpolationFn(context) {
12957             var i = 0;
12958             var ii = expressions.length;
12959             var values = new Array(ii);
12960
12961             try {
12962               for (; i < ii; i++) {
12963                 values[i] = parseFns[i](context);
12964               }
12965
12966               return compute(values);
12967             } catch (err) {
12968               $exceptionHandler($interpolateMinErr.interr(text, err));
12969             }
12970
12971           }, {
12972           // all of these properties are undocumented for now
12973           exp: text, //just for compatibility with regular watchers created via $watch
12974           expressions: expressions,
12975           $$watchDelegate: function(scope, listener) {
12976             var lastValue;
12977             return scope.$watchGroup(parseFns, /** @this */ function interpolateFnWatcher(values, oldValues) {
12978               var currValue = compute(values);
12979               if (isFunction(listener)) {
12980                 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
12981               }
12982               lastValue = currValue;
12983             });
12984           }
12985         });
12986       }
12987
12988       function parseStringifyInterceptor(value) {
12989         try {
12990           value = getValue(value);
12991           return allOrNothing && !isDefined(value) ? value : stringify(value);
12992         } catch (err) {
12993           $exceptionHandler($interpolateMinErr.interr(text, err));
12994         }
12995       }
12996     }
12997
12998
12999     /**
13000      * @ngdoc method
13001      * @name $interpolate#startSymbol
13002      * @description
13003      * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
13004      *
13005      * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
13006      * the symbol.
13007      *
13008      * @returns {string} start symbol.
13009      */
13010     $interpolate.startSymbol = function() {
13011       return startSymbol;
13012     };
13013
13014
13015     /**
13016      * @ngdoc method
13017      * @name $interpolate#endSymbol
13018      * @description
13019      * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
13020      *
13021      * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
13022      * the symbol.
13023      *
13024      * @returns {string} end symbol.
13025      */
13026     $interpolate.endSymbol = function() {
13027       return endSymbol;
13028     };
13029
13030     return $interpolate;
13031   }];
13032 }
13033
13034 /** @this */
13035 function $IntervalProvider() {
13036   this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
13037        function($rootScope,   $window,   $q,   $$q,   $browser) {
13038     var intervals = {};
13039
13040
13041      /**
13042       * @ngdoc service
13043       * @name $interval
13044       *
13045       * @description
13046       * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
13047       * milliseconds.
13048       *
13049       * The return value of registering an interval function is a promise. This promise will be
13050       * notified upon each tick of the interval, and will be resolved after `count` iterations, or
13051       * run indefinitely if `count` is not defined. The value of the notification will be the
13052       * number of iterations that have run.
13053       * To cancel an interval, call `$interval.cancel(promise)`.
13054       *
13055       * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
13056       * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
13057       * time.
13058       *
13059       * <div class="alert alert-warning">
13060       * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
13061       * with them.  In particular they are not automatically destroyed when a controller's scope or a
13062       * directive's element are destroyed.
13063       * You should take this into consideration and make sure to always cancel the interval at the
13064       * appropriate moment.  See the example below for more details on how and when to do this.
13065       * </div>
13066       *
13067       * @param {function()} fn A function that should be called repeatedly. If no additional arguments
13068       *   are passed (see below), the function is called with the current iteration count.
13069       * @param {number} delay Number of milliseconds between each function call.
13070       * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
13071       *   indefinitely.
13072       * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
13073       *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
13074       * @param {...*=} Pass additional parameters to the executed function.
13075       * @returns {promise} A promise which will be notified on each iteration.
13076       *
13077       * @example
13078       * <example module="intervalExample" name="interval-service">
13079       * <file name="index.html">
13080       *   <script>
13081       *     angular.module('intervalExample', [])
13082       *       .controller('ExampleController', ['$scope', '$interval',
13083       *         function($scope, $interval) {
13084       *           $scope.format = 'M/d/yy h:mm:ss a';
13085       *           $scope.blood_1 = 100;
13086       *           $scope.blood_2 = 120;
13087       *
13088       *           var stop;
13089       *           $scope.fight = function() {
13090       *             // Don't start a new fight if we are already fighting
13091       *             if ( angular.isDefined(stop) ) return;
13092       *
13093       *             stop = $interval(function() {
13094       *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
13095       *                 $scope.blood_1 = $scope.blood_1 - 3;
13096       *                 $scope.blood_2 = $scope.blood_2 - 4;
13097       *               } else {
13098       *                 $scope.stopFight();
13099       *               }
13100       *             }, 100);
13101       *           };
13102       *
13103       *           $scope.stopFight = function() {
13104       *             if (angular.isDefined(stop)) {
13105       *               $interval.cancel(stop);
13106       *               stop = undefined;
13107       *             }
13108       *           };
13109       *
13110       *           $scope.resetFight = function() {
13111       *             $scope.blood_1 = 100;
13112       *             $scope.blood_2 = 120;
13113       *           };
13114       *
13115       *           $scope.$on('$destroy', function() {
13116       *             // Make sure that the interval is destroyed too
13117       *             $scope.stopFight();
13118       *           });
13119       *         }])
13120       *       // Register the 'myCurrentTime' directive factory method.
13121       *       // We inject $interval and dateFilter service since the factory method is DI.
13122       *       .directive('myCurrentTime', ['$interval', 'dateFilter',
13123       *         function($interval, dateFilter) {
13124       *           // return the directive link function. (compile function not needed)
13125       *           return function(scope, element, attrs) {
13126       *             var format,  // date format
13127       *                 stopTime; // so that we can cancel the time updates
13128       *
13129       *             // used to update the UI
13130       *             function updateTime() {
13131       *               element.text(dateFilter(new Date(), format));
13132       *             }
13133       *
13134       *             // watch the expression, and update the UI on change.
13135       *             scope.$watch(attrs.myCurrentTime, function(value) {
13136       *               format = value;
13137       *               updateTime();
13138       *             });
13139       *
13140       *             stopTime = $interval(updateTime, 1000);
13141       *
13142       *             // listen on DOM destroy (removal) event, and cancel the next UI update
13143       *             // to prevent updating time after the DOM element was removed.
13144       *             element.on('$destroy', function() {
13145       *               $interval.cancel(stopTime);
13146       *             });
13147       *           }
13148       *         }]);
13149       *   </script>
13150       *
13151       *   <div>
13152       *     <div ng-controller="ExampleController">
13153       *       <label>Date format: <input ng-model="format"></label> <hr/>
13154       *       Current time is: <span my-current-time="format"></span>
13155       *       <hr/>
13156       *       Blood 1 : <font color='red'>{{blood_1}}</font>
13157       *       Blood 2 : <font color='red'>{{blood_2}}</font>
13158       *       <button type="button" data-ng-click="fight()">Fight</button>
13159       *       <button type="button" data-ng-click="stopFight()">StopFight</button>
13160       *       <button type="button" data-ng-click="resetFight()">resetFight</button>
13161       *     </div>
13162       *   </div>
13163       *
13164       * </file>
13165       * </example>
13166       */
13167     function interval(fn, delay, count, invokeApply) {
13168       var hasParams = arguments.length > 4,
13169           args = hasParams ? sliceArgs(arguments, 4) : [],
13170           setInterval = $window.setInterval,
13171           clearInterval = $window.clearInterval,
13172           iteration = 0,
13173           skipApply = (isDefined(invokeApply) && !invokeApply),
13174           deferred = (skipApply ? $$q : $q).defer(),
13175           promise = deferred.promise;
13176
13177       count = isDefined(count) ? count : 0;
13178
13179       promise.$$intervalId = setInterval(function tick() {
13180         if (skipApply) {
13181           $browser.defer(callback);
13182         } else {
13183           $rootScope.$evalAsync(callback);
13184         }
13185         deferred.notify(iteration++);
13186
13187         if (count > 0 && iteration >= count) {
13188           deferred.resolve(iteration);
13189           clearInterval(promise.$$intervalId);
13190           delete intervals[promise.$$intervalId];
13191         }
13192
13193         if (!skipApply) $rootScope.$apply();
13194
13195       }, delay);
13196
13197       intervals[promise.$$intervalId] = deferred;
13198
13199       return promise;
13200
13201       function callback() {
13202         if (!hasParams) {
13203           fn(iteration);
13204         } else {
13205           fn.apply(null, args);
13206         }
13207       }
13208     }
13209
13210
13211      /**
13212       * @ngdoc method
13213       * @name $interval#cancel
13214       *
13215       * @description
13216       * Cancels a task associated with the `promise`.
13217       *
13218       * @param {Promise=} promise returned by the `$interval` function.
13219       * @returns {boolean} Returns `true` if the task was successfully canceled.
13220       */
13221     interval.cancel = function(promise) {
13222       if (promise && promise.$$intervalId in intervals) {
13223         // Interval cancels should not report as unhandled promise.
13224         intervals[promise.$$intervalId].promise.catch(noop);
13225         intervals[promise.$$intervalId].reject('canceled');
13226         $window.clearInterval(promise.$$intervalId);
13227         delete intervals[promise.$$intervalId];
13228         return true;
13229       }
13230       return false;
13231     };
13232
13233     return interval;
13234   }];
13235 }
13236
13237 /**
13238  * @ngdoc service
13239  * @name $jsonpCallbacks
13240  * @requires $window
13241  * @description
13242  * This service handles the lifecycle of callbacks to handle JSONP requests.
13243  * Override this service if you wish to customise where the callbacks are stored and
13244  * how they vary compared to the requested url.
13245  */
13246 var $jsonpCallbacksProvider = /** @this */ function() {
13247   this.$get = function() {
13248     var callbacks = angular.callbacks;
13249     var callbackMap = {};
13250
13251     function createCallback(callbackId) {
13252       var callback = function(data) {
13253         callback.data = data;
13254         callback.called = true;
13255       };
13256       callback.id = callbackId;
13257       return callback;
13258     }
13259
13260     return {
13261       /**
13262        * @ngdoc method
13263        * @name $jsonpCallbacks#createCallback
13264        * @param {string} url the url of the JSONP request
13265        * @returns {string} the callback path to send to the server as part of the JSONP request
13266        * @description
13267        * {@link $httpBackend} calls this method to create a callback and get hold of the path to the callback
13268        * to pass to the server, which will be used to call the callback with its payload in the JSONP response.
13269        */
13270       createCallback: function(url) {
13271         var callbackId = '_' + (callbacks.$$counter++).toString(36);
13272         var callbackPath = 'angular.callbacks.' + callbackId;
13273         var callback = createCallback(callbackId);
13274         callbackMap[callbackPath] = callbacks[callbackId] = callback;
13275         return callbackPath;
13276       },
13277       /**
13278        * @ngdoc method
13279        * @name $jsonpCallbacks#wasCalled
13280        * @param {string} callbackPath the path to the callback that was sent in the JSONP request
13281        * @returns {boolean} whether the callback has been called, as a result of the JSONP response
13282        * @description
13283        * {@link $httpBackend} calls this method to find out whether the JSONP response actually called the
13284        * callback that was passed in the request.
13285        */
13286       wasCalled: function(callbackPath) {
13287         return callbackMap[callbackPath].called;
13288       },
13289       /**
13290        * @ngdoc method
13291        * @name $jsonpCallbacks#getResponse
13292        * @param {string} callbackPath the path to the callback that was sent in the JSONP request
13293        * @returns {*} the data received from the response via the registered callback
13294        * @description
13295        * {@link $httpBackend} calls this method to get hold of the data that was provided to the callback
13296        * in the JSONP response.
13297        */
13298       getResponse: function(callbackPath) {
13299         return callbackMap[callbackPath].data;
13300       },
13301       /**
13302        * @ngdoc method
13303        * @name $jsonpCallbacks#removeCallback
13304        * @param {string} callbackPath the path to the callback that was sent in the JSONP request
13305        * @description
13306        * {@link $httpBackend} calls this method to remove the callback after the JSONP request has
13307        * completed or timed-out.
13308        */
13309       removeCallback: function(callbackPath) {
13310         var callback = callbackMap[callbackPath];
13311         delete callbacks[callback.id];
13312         delete callbackMap[callbackPath];
13313       }
13314     };
13315   };
13316 };
13317
13318 /**
13319  * @ngdoc service
13320  * @name $locale
13321  *
13322  * @description
13323  * $locale service provides localization rules for various Angular components. As of right now the
13324  * only public api is:
13325  *
13326  * * `id` â€“ `{string}` â€“ locale id formatted as `languageId-countryId` (e.g. `en-us`)
13327  */
13328
13329 var PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/,
13330     DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
13331 var $locationMinErr = minErr('$location');
13332
13333
13334 /**
13335  * Encode path using encodeUriSegment, ignoring forward slashes
13336  *
13337  * @param {string} path Path to encode
13338  * @returns {string}
13339  */
13340 function encodePath(path) {
13341   var segments = path.split('/'),
13342       i = segments.length;
13343
13344   while (i--) {
13345     segments[i] = encodeUriSegment(segments[i]);
13346   }
13347
13348   return segments.join('/');
13349 }
13350
13351 function parseAbsoluteUrl(absoluteUrl, locationObj) {
13352   var parsedUrl = urlResolve(absoluteUrl);
13353
13354   locationObj.$$protocol = parsedUrl.protocol;
13355   locationObj.$$host = parsedUrl.hostname;
13356   locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
13357 }
13358
13359 var DOUBLE_SLASH_REGEX = /^\s*[\\/]{2,}/;
13360 function parseAppUrl(url, locationObj) {
13361
13362   if (DOUBLE_SLASH_REGEX.test(url)) {
13363     throw $locationMinErr('badpath', 'Invalid url "{0}".', url);
13364   }
13365
13366   var prefixed = (url.charAt(0) !== '/');
13367   if (prefixed) {
13368     url = '/' + url;
13369   }
13370   var match = urlResolve(url);
13371   locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
13372       match.pathname.substring(1) : match.pathname);
13373   locationObj.$$search = parseKeyValue(match.search);
13374   locationObj.$$hash = decodeURIComponent(match.hash);
13375
13376   // make sure path starts with '/';
13377   if (locationObj.$$path && locationObj.$$path.charAt(0) !== '/') {
13378     locationObj.$$path = '/' + locationObj.$$path;
13379   }
13380 }
13381
13382 function startsWith(str, search) {
13383   return str.slice(0, search.length) === search;
13384 }
13385
13386 /**
13387  *
13388  * @param {string} base
13389  * @param {string} url
13390  * @returns {string} returns text from `url` after `base` or `undefined` if it does not begin with
13391  *                   the expected string.
13392  */
13393 function stripBaseUrl(base, url) {
13394   if (startsWith(url, base)) {
13395     return url.substr(base.length);
13396   }
13397 }
13398
13399
13400 function stripHash(url) {
13401   var index = url.indexOf('#');
13402   return index === -1 ? url : url.substr(0, index);
13403 }
13404
13405 function trimEmptyHash(url) {
13406   return url.replace(/(#.+)|#$/, '$1');
13407 }
13408
13409
13410 function stripFile(url) {
13411   return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
13412 }
13413
13414 /* return the server only (scheme://host:port) */
13415 function serverBase(url) {
13416   return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
13417 }
13418
13419
13420 /**
13421  * LocationHtml5Url represents a URL
13422  * This object is exposed as $location service when HTML5 mode is enabled and supported
13423  *
13424  * @constructor
13425  * @param {string} appBase application base URL
13426  * @param {string} appBaseNoFile application base URL stripped of any filename
13427  * @param {string} basePrefix URL path prefix
13428  */
13429 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
13430   this.$$html5 = true;
13431   basePrefix = basePrefix || '';
13432   parseAbsoluteUrl(appBase, this);
13433
13434
13435   /**
13436    * Parse given HTML5 (regular) URL string into properties
13437    * @param {string} url HTML5 URL
13438    * @private
13439    */
13440   this.$$parse = function(url) {
13441     var pathUrl = stripBaseUrl(appBaseNoFile, url);
13442     if (!isString(pathUrl)) {
13443       throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
13444           appBaseNoFile);
13445     }
13446
13447     parseAppUrl(pathUrl, this);
13448
13449     if (!this.$$path) {
13450       this.$$path = '/';
13451     }
13452
13453     this.$$compose();
13454   };
13455
13456   /**
13457    * Compose url and update `absUrl` property
13458    * @private
13459    */
13460   this.$$compose = function() {
13461     var search = toKeyValue(this.$$search),
13462         hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
13463
13464     this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
13465     this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
13466
13467     this.$$urlUpdatedByLocation = true;
13468   };
13469
13470   this.$$parseLinkUrl = function(url, relHref) {
13471     if (relHref && relHref[0] === '#') {
13472       // special case for links to hash fragments:
13473       // keep the old url and only replace the hash fragment
13474       this.hash(relHref.slice(1));
13475       return true;
13476     }
13477     var appUrl, prevAppUrl;
13478     var rewrittenUrl;
13479
13480
13481     if (isDefined(appUrl = stripBaseUrl(appBase, url))) {
13482       prevAppUrl = appUrl;
13483       if (basePrefix && isDefined(appUrl = stripBaseUrl(basePrefix, appUrl))) {
13484         rewrittenUrl = appBaseNoFile + (stripBaseUrl('/', appUrl) || appUrl);
13485       } else {
13486         rewrittenUrl = appBase + prevAppUrl;
13487       }
13488     } else if (isDefined(appUrl = stripBaseUrl(appBaseNoFile, url))) {
13489       rewrittenUrl = appBaseNoFile + appUrl;
13490     } else if (appBaseNoFile === url + '/') {
13491       rewrittenUrl = appBaseNoFile;
13492     }
13493     if (rewrittenUrl) {
13494       this.$$parse(rewrittenUrl);
13495     }
13496     return !!rewrittenUrl;
13497   };
13498 }
13499
13500
13501 /**
13502  * LocationHashbangUrl represents URL
13503  * This object is exposed as $location service when developer doesn't opt into html5 mode.
13504  * It also serves as the base class for html5 mode fallback on legacy browsers.
13505  *
13506  * @constructor
13507  * @param {string} appBase application base URL
13508  * @param {string} appBaseNoFile application base URL stripped of any filename
13509  * @param {string} hashPrefix hashbang prefix
13510  */
13511 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
13512
13513   parseAbsoluteUrl(appBase, this);
13514
13515
13516   /**
13517    * Parse given hashbang URL into properties
13518    * @param {string} url Hashbang URL
13519    * @private
13520    */
13521   this.$$parse = function(url) {
13522     var withoutBaseUrl = stripBaseUrl(appBase, url) || stripBaseUrl(appBaseNoFile, url);
13523     var withoutHashUrl;
13524
13525     if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
13526
13527       // The rest of the URL starts with a hash so we have
13528       // got either a hashbang path or a plain hash fragment
13529       withoutHashUrl = stripBaseUrl(hashPrefix, withoutBaseUrl);
13530       if (isUndefined(withoutHashUrl)) {
13531         // There was no hashbang prefix so we just have a hash fragment
13532         withoutHashUrl = withoutBaseUrl;
13533       }
13534
13535     } else {
13536       // There was no hashbang path nor hash fragment:
13537       // If we are in HTML5 mode we use what is left as the path;
13538       // Otherwise we ignore what is left
13539       if (this.$$html5) {
13540         withoutHashUrl = withoutBaseUrl;
13541       } else {
13542         withoutHashUrl = '';
13543         if (isUndefined(withoutBaseUrl)) {
13544           appBase = url;
13545           /** @type {?} */ (this).replace();
13546         }
13547       }
13548     }
13549
13550     parseAppUrl(withoutHashUrl, this);
13551
13552     this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
13553
13554     this.$$compose();
13555
13556     /*
13557      * In Windows, on an anchor node on documents loaded from
13558      * the filesystem, the browser will return a pathname
13559      * prefixed with the drive name ('/C:/path') when a
13560      * pathname without a drive is set:
13561      *  * a.setAttribute('href', '/foo')
13562      *   * a.pathname === '/C:/foo' //true
13563      *
13564      * Inside of Angular, we're always using pathnames that
13565      * do not include drive names for routing.
13566      */
13567     function removeWindowsDriveName(path, url, base) {
13568       /*
13569       Matches paths for file protocol on windows,
13570       such as /C:/foo/bar, and captures only /foo/bar.
13571       */
13572       var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
13573
13574       var firstPathSegmentMatch;
13575
13576       //Get the relative path from the input URL.
13577       if (startsWith(url, base)) {
13578         url = url.replace(base, '');
13579       }
13580
13581       // The input URL intentionally contains a first path segment that ends with a colon.
13582       if (windowsFilePathExp.exec(url)) {
13583         return path;
13584       }
13585
13586       firstPathSegmentMatch = windowsFilePathExp.exec(path);
13587       return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
13588     }
13589   };
13590
13591   /**
13592    * Compose hashbang URL and update `absUrl` property
13593    * @private
13594    */
13595   this.$$compose = function() {
13596     var search = toKeyValue(this.$$search),
13597         hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
13598
13599     this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
13600     this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
13601
13602     this.$$urlUpdatedByLocation = true;
13603   };
13604
13605   this.$$parseLinkUrl = function(url, relHref) {
13606     if (stripHash(appBase) === stripHash(url)) {
13607       this.$$parse(url);
13608       return true;
13609     }
13610     return false;
13611   };
13612 }
13613
13614
13615 /**
13616  * LocationHashbangUrl represents URL
13617  * This object is exposed as $location service when html5 history api is enabled but the browser
13618  * does not support it.
13619  *
13620  * @constructor
13621  * @param {string} appBase application base URL
13622  * @param {string} appBaseNoFile application base URL stripped of any filename
13623  * @param {string} hashPrefix hashbang prefix
13624  */
13625 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
13626   this.$$html5 = true;
13627   LocationHashbangUrl.apply(this, arguments);
13628
13629   this.$$parseLinkUrl = function(url, relHref) {
13630     if (relHref && relHref[0] === '#') {
13631       // special case for links to hash fragments:
13632       // keep the old url and only replace the hash fragment
13633       this.hash(relHref.slice(1));
13634       return true;
13635     }
13636
13637     var rewrittenUrl;
13638     var appUrl;
13639
13640     if (appBase === stripHash(url)) {
13641       rewrittenUrl = url;
13642     } else if ((appUrl = stripBaseUrl(appBaseNoFile, url))) {
13643       rewrittenUrl = appBase + hashPrefix + appUrl;
13644     } else if (appBaseNoFile === url + '/') {
13645       rewrittenUrl = appBaseNoFile;
13646     }
13647     if (rewrittenUrl) {
13648       this.$$parse(rewrittenUrl);
13649     }
13650     return !!rewrittenUrl;
13651   };
13652
13653   this.$$compose = function() {
13654     var search = toKeyValue(this.$$search),
13655         hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
13656
13657     this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
13658     // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
13659     this.$$absUrl = appBase + hashPrefix + this.$$url;
13660
13661     this.$$urlUpdatedByLocation = true;
13662   };
13663
13664 }
13665
13666
13667 var locationPrototype = {
13668
13669   /**
13670    * Ensure absolute URL is initialized.
13671    * @private
13672    */
13673   $$absUrl:'',
13674
13675   /**
13676    * Are we in html5 mode?
13677    * @private
13678    */
13679   $$html5: false,
13680
13681   /**
13682    * Has any change been replacing?
13683    * @private
13684    */
13685   $$replace: false,
13686
13687   /**
13688    * @ngdoc method
13689    * @name $location#absUrl
13690    *
13691    * @description
13692    * This method is getter only.
13693    *
13694    * Return full URL representation with all segments encoded according to rules specified in
13695    * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
13696    *
13697    *
13698    * ```js
13699    * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13700    * var absUrl = $location.absUrl();
13701    * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
13702    * ```
13703    *
13704    * @return {string} full URL
13705    */
13706   absUrl: locationGetter('$$absUrl'),
13707
13708   /**
13709    * @ngdoc method
13710    * @name $location#url
13711    *
13712    * @description
13713    * This method is getter / setter.
13714    *
13715    * Return URL (e.g. `/path?a=b#hash`) when called without any parameter.
13716    *
13717    * Change path, search and hash, when called with parameter and return `$location`.
13718    *
13719    *
13720    * ```js
13721    * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13722    * var url = $location.url();
13723    * // => "/some/path?foo=bar&baz=xoxo"
13724    * ```
13725    *
13726    * @param {string=} url New URL without base prefix (e.g. `/path?a=b#hash`)
13727    * @return {string} url
13728    */
13729   url: function(url) {
13730     if (isUndefined(url)) {
13731       return this.$$url;
13732     }
13733
13734     var match = PATH_MATCH.exec(url);
13735     if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
13736     if (match[2] || match[1] || url === '') this.search(match[3] || '');
13737     this.hash(match[5] || '');
13738
13739     return this;
13740   },
13741
13742   /**
13743    * @ngdoc method
13744    * @name $location#protocol
13745    *
13746    * @description
13747    * This method is getter only.
13748    *
13749    * Return protocol of current URL.
13750    *
13751    *
13752    * ```js
13753    * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13754    * var protocol = $location.protocol();
13755    * // => "http"
13756    * ```
13757    *
13758    * @return {string} protocol of current URL
13759    */
13760   protocol: locationGetter('$$protocol'),
13761
13762   /**
13763    * @ngdoc method
13764    * @name $location#host
13765    *
13766    * @description
13767    * This method is getter only.
13768    *
13769    * Return host of current URL.
13770    *
13771    * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
13772    *
13773    *
13774    * ```js
13775    * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13776    * var host = $location.host();
13777    * // => "example.com"
13778    *
13779    * // given URL http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
13780    * host = $location.host();
13781    * // => "example.com"
13782    * host = location.host;
13783    * // => "example.com:8080"
13784    * ```
13785    *
13786    * @return {string} host of current URL.
13787    */
13788   host: locationGetter('$$host'),
13789
13790   /**
13791    * @ngdoc method
13792    * @name $location#port
13793    *
13794    * @description
13795    * This method is getter only.
13796    *
13797    * Return port of current URL.
13798    *
13799    *
13800    * ```js
13801    * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13802    * var port = $location.port();
13803    * // => 80
13804    * ```
13805    *
13806    * @return {Number} port
13807    */
13808   port: locationGetter('$$port'),
13809
13810   /**
13811    * @ngdoc method
13812    * @name $location#path
13813    *
13814    * @description
13815    * This method is getter / setter.
13816    *
13817    * Return path of current URL when called without any parameter.
13818    *
13819    * Change path when called with parameter and return `$location`.
13820    *
13821    * Note: Path should always begin with forward slash (/), this method will add the forward slash
13822    * if it is missing.
13823    *
13824    *
13825    * ```js
13826    * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13827    * var path = $location.path();
13828    * // => "/some/path"
13829    * ```
13830    *
13831    * @param {(string|number)=} path New path
13832    * @return {(string|object)} path if called with no parameters, or `$location` if called with a parameter
13833    */
13834   path: locationGetterSetter('$$path', function(path) {
13835     path = path !== null ? path.toString() : '';
13836     return path.charAt(0) === '/' ? path : '/' + path;
13837   }),
13838
13839   /**
13840    * @ngdoc method
13841    * @name $location#search
13842    *
13843    * @description
13844    * This method is getter / setter.
13845    *
13846    * Return search part (as object) of current URL when called without any parameter.
13847    *
13848    * Change search part when called with parameter and return `$location`.
13849    *
13850    *
13851    * ```js
13852    * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13853    * var searchObject = $location.search();
13854    * // => {foo: 'bar', baz: 'xoxo'}
13855    *
13856    * // set foo to 'yipee'
13857    * $location.search('foo', 'yipee');
13858    * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
13859    * ```
13860    *
13861    * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
13862    * hash object.
13863    *
13864    * When called with a single argument the method acts as a setter, setting the `search` component
13865    * of `$location` to the specified value.
13866    *
13867    * If the argument is a hash object containing an array of values, these values will be encoded
13868    * as duplicate search parameters in the URL.
13869    *
13870    * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
13871    * will override only a single search property.
13872    *
13873    * If `paramValue` is an array, it will override the property of the `search` component of
13874    * `$location` specified via the first argument.
13875    *
13876    * If `paramValue` is `null`, the property specified via the first argument will be deleted.
13877    *
13878    * If `paramValue` is `true`, the property specified via the first argument will be added with no
13879    * value nor trailing equal sign.
13880    *
13881    * @return {Object} If called with no arguments returns the parsed `search` object. If called with
13882    * one or more arguments returns `$location` object itself.
13883    */
13884   search: function(search, paramValue) {
13885     switch (arguments.length) {
13886       case 0:
13887         return this.$$search;
13888       case 1:
13889         if (isString(search) || isNumber(search)) {
13890           search = search.toString();
13891           this.$$search = parseKeyValue(search);
13892         } else if (isObject(search)) {
13893           search = copy(search, {});
13894           // remove object undefined or null properties
13895           forEach(search, function(value, key) {
13896             if (value == null) delete search[key];
13897           });
13898
13899           this.$$search = search;
13900         } else {
13901           throw $locationMinErr('isrcharg',
13902               'The first argument of the `$location#search()` call must be a string or an object.');
13903         }
13904         break;
13905       default:
13906         if (isUndefined(paramValue) || paramValue === null) {
13907           delete this.$$search[search];
13908         } else {
13909           this.$$search[search] = paramValue;
13910         }
13911     }
13912
13913     this.$$compose();
13914     return this;
13915   },
13916
13917   /**
13918    * @ngdoc method
13919    * @name $location#hash
13920    *
13921    * @description
13922    * This method is getter / setter.
13923    *
13924    * Returns the hash fragment when called without any parameters.
13925    *
13926    * Changes the hash fragment when called with a parameter and returns `$location`.
13927    *
13928    *
13929    * ```js
13930    * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
13931    * var hash = $location.hash();
13932    * // => "hashValue"
13933    * ```
13934    *
13935    * @param {(string|number)=} hash New hash fragment
13936    * @return {string} hash
13937    */
13938   hash: locationGetterSetter('$$hash', function(hash) {
13939     return hash !== null ? hash.toString() : '';
13940   }),
13941
13942   /**
13943    * @ngdoc method
13944    * @name $location#replace
13945    *
13946    * @description
13947    * If called, all changes to $location during the current `$digest` will replace the current history
13948    * record, instead of adding a new one.
13949    */
13950   replace: function() {
13951     this.$$replace = true;
13952     return this;
13953   }
13954 };
13955
13956 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
13957   Location.prototype = Object.create(locationPrototype);
13958
13959   /**
13960    * @ngdoc method
13961    * @name $location#state
13962    *
13963    * @description
13964    * This method is getter / setter.
13965    *
13966    * Return the history state object when called without any parameter.
13967    *
13968    * Change the history state object when called with one parameter and return `$location`.
13969    * The state object is later passed to `pushState` or `replaceState`.
13970    *
13971    * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
13972    * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
13973    * older browsers (like IE9 or Android < 4.0), don't use this method.
13974    *
13975    * @param {object=} state State object for pushState or replaceState
13976    * @return {object} state
13977    */
13978   Location.prototype.state = function(state) {
13979     if (!arguments.length) {
13980       return this.$$state;
13981     }
13982
13983     if (Location !== LocationHtml5Url || !this.$$html5) {
13984       throw $locationMinErr('nostate', 'History API state support is available only ' +
13985         'in HTML5 mode and only in browsers supporting HTML5 History API');
13986     }
13987     // The user might modify `stateObject` after invoking `$location.state(stateObject)`
13988     // but we're changing the $$state reference to $browser.state() during the $digest
13989     // so the modification window is narrow.
13990     this.$$state = isUndefined(state) ? null : state;
13991     this.$$urlUpdatedByLocation = true;
13992
13993     return this;
13994   };
13995 });
13996
13997
13998 function locationGetter(property) {
13999   return /** @this */ function() {
14000     return this[property];
14001   };
14002 }
14003
14004
14005 function locationGetterSetter(property, preprocess) {
14006   return /** @this */ function(value) {
14007     if (isUndefined(value)) {
14008       return this[property];
14009     }
14010
14011     this[property] = preprocess(value);
14012     this.$$compose();
14013
14014     return this;
14015   };
14016 }
14017
14018
14019 /**
14020  * @ngdoc service
14021  * @name $location
14022  *
14023  * @requires $rootElement
14024  *
14025  * @description
14026  * The $location service parses the URL in the browser address bar (based on the
14027  * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
14028  * available to your application. Changes to the URL in the address bar are reflected into
14029  * $location service and changes to $location are reflected into the browser address bar.
14030  *
14031  * **The $location service:**
14032  *
14033  * - Exposes the current URL in the browser address bar, so you can
14034  *   - Watch and observe the URL.
14035  *   - Change the URL.
14036  * - Synchronizes the URL with the browser when the user
14037  *   - Changes the address bar.
14038  *   - Clicks the back or forward button (or clicks a History link).
14039  *   - Clicks on a link.
14040  * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
14041  *
14042  * For more information see {@link guide/$location Developer Guide: Using $location}
14043  */
14044
14045 /**
14046  * @ngdoc provider
14047  * @name $locationProvider
14048  * @this
14049  *
14050  * @description
14051  * Use the `$locationProvider` to configure how the application deep linking paths are stored.
14052  */
14053 function $LocationProvider() {
14054   var hashPrefix = '!',
14055       html5Mode = {
14056         enabled: false,
14057         requireBase: true,
14058         rewriteLinks: true
14059       };
14060
14061   /**
14062    * @ngdoc method
14063    * @name $locationProvider#hashPrefix
14064    * @description
14065    * The default value for the prefix is `'!'`.
14066    * @param {string=} prefix Prefix for hash part (containing path and search)
14067    * @returns {*} current value if used as getter or itself (chaining) if used as setter
14068    */
14069   this.hashPrefix = function(prefix) {
14070     if (isDefined(prefix)) {
14071       hashPrefix = prefix;
14072       return this;
14073     } else {
14074       return hashPrefix;
14075     }
14076   };
14077
14078   /**
14079    * @ngdoc method
14080    * @name $locationProvider#html5Mode
14081    * @description
14082    * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
14083    *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
14084    *   properties:
14085    *   - **enabled** â€“ `{boolean}` â€“ (default: false) If true, will rely on `history.pushState` to
14086    *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
14087    *     support `pushState`.
14088    *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
14089    *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
14090    *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
14091    *     See the {@link guide/$location $location guide for more information}
14092    *   - **rewriteLinks** - `{boolean|string}` - (default: `true`) When html5Mode is enabled,
14093    *     enables/disables URL rewriting for relative links. If set to a string, URL rewriting will
14094    *     only happen on links with an attribute that matches the given string. For example, if set
14095    *     to `'internal-link'`, then the URL will only be rewritten for `<a internal-link>` links.
14096    *     Note that [attribute name normalization](guide/directive#normalization) does not apply
14097    *     here, so `'internalLink'` will **not** match `'internal-link'`.
14098    *
14099    * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
14100    */
14101   this.html5Mode = function(mode) {
14102     if (isBoolean(mode)) {
14103       html5Mode.enabled = mode;
14104       return this;
14105     } else if (isObject(mode)) {
14106
14107       if (isBoolean(mode.enabled)) {
14108         html5Mode.enabled = mode.enabled;
14109       }
14110
14111       if (isBoolean(mode.requireBase)) {
14112         html5Mode.requireBase = mode.requireBase;
14113       }
14114
14115       if (isBoolean(mode.rewriteLinks) || isString(mode.rewriteLinks)) {
14116         html5Mode.rewriteLinks = mode.rewriteLinks;
14117       }
14118
14119       return this;
14120     } else {
14121       return html5Mode;
14122     }
14123   };
14124
14125   /**
14126    * @ngdoc event
14127    * @name $location#$locationChangeStart
14128    * @eventType broadcast on root scope
14129    * @description
14130    * Broadcasted before a URL will change.
14131    *
14132    * This change can be prevented by calling
14133    * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
14134    * details about event object. Upon successful change
14135    * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
14136    *
14137    * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
14138    * the browser supports the HTML5 History API.
14139    *
14140    * @param {Object} angularEvent Synthetic event object.
14141    * @param {string} newUrl New URL
14142    * @param {string=} oldUrl URL that was before it was changed.
14143    * @param {string=} newState New history state object
14144    * @param {string=} oldState History state object that was before it was changed.
14145    */
14146
14147   /**
14148    * @ngdoc event
14149    * @name $location#$locationChangeSuccess
14150    * @eventType broadcast on root scope
14151    * @description
14152    * Broadcasted after a URL was changed.
14153    *
14154    * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
14155    * the browser supports the HTML5 History API.
14156    *
14157    * @param {Object} angularEvent Synthetic event object.
14158    * @param {string} newUrl New URL
14159    * @param {string=} oldUrl URL that was before it was changed.
14160    * @param {string=} newState New history state object
14161    * @param {string=} oldState History state object that was before it was changed.
14162    */
14163
14164   this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
14165       function($rootScope, $browser, $sniffer, $rootElement, $window) {
14166     var $location,
14167         LocationMode,
14168         baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
14169         initialUrl = $browser.url(),
14170         appBase;
14171
14172     if (html5Mode.enabled) {
14173       if (!baseHref && html5Mode.requireBase) {
14174         throw $locationMinErr('nobase',
14175           '$location in HTML5 mode requires a <base> tag to be present!');
14176       }
14177       appBase = serverBase(initialUrl) + (baseHref || '/');
14178       LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
14179     } else {
14180       appBase = stripHash(initialUrl);
14181       LocationMode = LocationHashbangUrl;
14182     }
14183     var appBaseNoFile = stripFile(appBase);
14184
14185     $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
14186     $location.$$parseLinkUrl(initialUrl, initialUrl);
14187
14188     $location.$$state = $browser.state();
14189
14190     var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
14191
14192     function setBrowserUrlWithFallback(url, replace, state) {
14193       var oldUrl = $location.url();
14194       var oldState = $location.$$state;
14195       try {
14196         $browser.url(url, replace, state);
14197
14198         // Make sure $location.state() returns referentially identical (not just deeply equal)
14199         // state object; this makes possible quick checking if the state changed in the digest
14200         // loop. Checking deep equality would be too expensive.
14201         $location.$$state = $browser.state();
14202       } catch (e) {
14203         // Restore old values if pushState fails
14204         $location.url(oldUrl);
14205         $location.$$state = oldState;
14206
14207         throw e;
14208       }
14209     }
14210
14211     $rootElement.on('click', function(event) {
14212       var rewriteLinks = html5Mode.rewriteLinks;
14213       // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
14214       // currently we open nice url link and redirect then
14215
14216       if (!rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which === 2 || event.button === 2) return;
14217
14218       var elm = jqLite(event.target);
14219
14220       // traverse the DOM up to find first A tag
14221       while (nodeName_(elm[0]) !== 'a') {
14222         // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
14223         if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
14224       }
14225
14226       if (isString(rewriteLinks) && isUndefined(elm.attr(rewriteLinks))) return;
14227
14228       var absHref = elm.prop('href');
14229       // get the actual href attribute - see
14230       // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
14231       var relHref = elm.attr('href') || elm.attr('xlink:href');
14232
14233       if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
14234         // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
14235         // an animation.
14236         absHref = urlResolve(absHref.animVal).href;
14237       }
14238
14239       // Ignore when url is started with javascript: or mailto:
14240       if (IGNORE_URI_REGEXP.test(absHref)) return;
14241
14242       if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
14243         if ($location.$$parseLinkUrl(absHref, relHref)) {
14244           // We do a preventDefault for all urls that are part of the angular application,
14245           // in html5mode and also without, so that we are able to abort navigation without
14246           // getting double entries in the location history.
14247           event.preventDefault();
14248           // update location manually
14249           if ($location.absUrl() !== $browser.url()) {
14250             $rootScope.$apply();
14251             // hack to work around FF6 bug 684208 when scenario runner clicks on links
14252             $window.angular['ff-684208-preventDefault'] = true;
14253           }
14254         }
14255       }
14256     });
14257
14258
14259     // rewrite hashbang url <> html5 url
14260     if (trimEmptyHash($location.absUrl()) !== trimEmptyHash(initialUrl)) {
14261       $browser.url($location.absUrl(), true);
14262     }
14263
14264     var initializing = true;
14265
14266     // update $location when $browser url changes
14267     $browser.onUrlChange(function(newUrl, newState) {
14268
14269       if (!startsWith(newUrl, appBaseNoFile)) {
14270         // If we are navigating outside of the app then force a reload
14271         $window.location.href = newUrl;
14272         return;
14273       }
14274
14275       $rootScope.$evalAsync(function() {
14276         var oldUrl = $location.absUrl();
14277         var oldState = $location.$$state;
14278         var defaultPrevented;
14279         newUrl = trimEmptyHash(newUrl);
14280         $location.$$parse(newUrl);
14281         $location.$$state = newState;
14282
14283         defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
14284             newState, oldState).defaultPrevented;
14285
14286         // if the location was changed by a `$locationChangeStart` handler then stop
14287         // processing this location change
14288         if ($location.absUrl() !== newUrl) return;
14289
14290         if (defaultPrevented) {
14291           $location.$$parse(oldUrl);
14292           $location.$$state = oldState;
14293           setBrowserUrlWithFallback(oldUrl, false, oldState);
14294         } else {
14295           initializing = false;
14296           afterLocationChange(oldUrl, oldState);
14297         }
14298       });
14299       if (!$rootScope.$$phase) $rootScope.$digest();
14300     });
14301
14302     // update browser
14303     $rootScope.$watch(function $locationWatch() {
14304       if (initializing || $location.$$urlUpdatedByLocation) {
14305         $location.$$urlUpdatedByLocation = false;
14306
14307         var oldUrl = trimEmptyHash($browser.url());
14308         var newUrl = trimEmptyHash($location.absUrl());
14309         var oldState = $browser.state();
14310         var currentReplace = $location.$$replace;
14311         var urlOrStateChanged = oldUrl !== newUrl ||
14312           ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
14313
14314         if (initializing || urlOrStateChanged) {
14315           initializing = false;
14316
14317           $rootScope.$evalAsync(function() {
14318             var newUrl = $location.absUrl();
14319             var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
14320                 $location.$$state, oldState).defaultPrevented;
14321
14322             // if the location was changed by a `$locationChangeStart` handler then stop
14323             // processing this location change
14324             if ($location.absUrl() !== newUrl) return;
14325
14326             if (defaultPrevented) {
14327               $location.$$parse(oldUrl);
14328               $location.$$state = oldState;
14329             } else {
14330               if (urlOrStateChanged) {
14331                 setBrowserUrlWithFallback(newUrl, currentReplace,
14332                                           oldState === $location.$$state ? null : $location.$$state);
14333               }
14334               afterLocationChange(oldUrl, oldState);
14335             }
14336           });
14337         }
14338       }
14339
14340       $location.$$replace = false;
14341
14342       // we don't need to return anything because $evalAsync will make the digest loop dirty when
14343       // there is a change
14344     });
14345
14346     return $location;
14347
14348     function afterLocationChange(oldUrl, oldState) {
14349       $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
14350         $location.$$state, oldState);
14351     }
14352 }];
14353 }
14354
14355 /**
14356  * @ngdoc service
14357  * @name $log
14358  * @requires $window
14359  *
14360  * @description
14361  * Simple service for logging. Default implementation safely writes the message
14362  * into the browser's console (if present).
14363  *
14364  * The main purpose of this service is to simplify debugging and troubleshooting.
14365  *
14366  * The default is to log `debug` messages. You can use
14367  * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
14368  *
14369  * @example
14370    <example module="logExample" name="log-service">
14371      <file name="script.js">
14372        angular.module('logExample', [])
14373          .controller('LogController', ['$scope', '$log', function($scope, $log) {
14374            $scope.$log = $log;
14375            $scope.message = 'Hello World!';
14376          }]);
14377      </file>
14378      <file name="index.html">
14379        <div ng-controller="LogController">
14380          <p>Reload this page with open console, enter text and hit the log button...</p>
14381          <label>Message:
14382          <input type="text" ng-model="message" /></label>
14383          <button ng-click="$log.log(message)">log</button>
14384          <button ng-click="$log.warn(message)">warn</button>
14385          <button ng-click="$log.info(message)">info</button>
14386          <button ng-click="$log.error(message)">error</button>
14387          <button ng-click="$log.debug(message)">debug</button>
14388        </div>
14389      </file>
14390    </example>
14391  */
14392
14393 /**
14394  * @ngdoc provider
14395  * @name $logProvider
14396  * @this
14397  *
14398  * @description
14399  * Use the `$logProvider` to configure how the application logs messages
14400  */
14401 function $LogProvider() {
14402   var debug = true,
14403       self = this;
14404
14405   /**
14406    * @ngdoc method
14407    * @name $logProvider#debugEnabled
14408    * @description
14409    * @param {boolean=} flag enable or disable debug level messages
14410    * @returns {*} current value if used as getter or itself (chaining) if used as setter
14411    */
14412   this.debugEnabled = function(flag) {
14413     if (isDefined(flag)) {
14414       debug = flag;
14415       return this;
14416     } else {
14417       return debug;
14418     }
14419   };
14420
14421   this.$get = ['$window', function($window) {
14422     // Support: IE 9-11, Edge 12-14+
14423     // IE/Edge display errors in such a way that it requires the user to click in 4 places
14424     // to see the stack trace. There is no way to feature-detect it so there's a chance
14425     // of the user agent sniffing to go wrong but since it's only about logging, this shouldn't
14426     // break apps. Other browsers display errors in a sensible way and some of them map stack
14427     // traces along source maps if available so it makes sense to let browsers display it
14428     // as they want.
14429     var formatStackTrace = msie || /\bEdge\//.test($window.navigator && $window.navigator.userAgent);
14430
14431     return {
14432       /**
14433        * @ngdoc method
14434        * @name $log#log
14435        *
14436        * @description
14437        * Write a log message
14438        */
14439       log: consoleLog('log'),
14440
14441       /**
14442        * @ngdoc method
14443        * @name $log#info
14444        *
14445        * @description
14446        * Write an information message
14447        */
14448       info: consoleLog('info'),
14449
14450       /**
14451        * @ngdoc method
14452        * @name $log#warn
14453        *
14454        * @description
14455        * Write a warning message
14456        */
14457       warn: consoleLog('warn'),
14458
14459       /**
14460        * @ngdoc method
14461        * @name $log#error
14462        *
14463        * @description
14464        * Write an error message
14465        */
14466       error: consoleLog('error'),
14467
14468       /**
14469        * @ngdoc method
14470        * @name $log#debug
14471        *
14472        * @description
14473        * Write a debug message
14474        */
14475       debug: (function() {
14476         var fn = consoleLog('debug');
14477
14478         return function() {
14479           if (debug) {
14480             fn.apply(self, arguments);
14481           }
14482         };
14483       })()
14484     };
14485
14486     function formatError(arg) {
14487       if (arg instanceof Error) {
14488         if (arg.stack && formatStackTrace) {
14489           arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
14490               ? 'Error: ' + arg.message + '\n' + arg.stack
14491               : arg.stack;
14492         } else if (arg.sourceURL) {
14493           arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
14494         }
14495       }
14496       return arg;
14497     }
14498
14499     function consoleLog(type) {
14500       var console = $window.console || {},
14501           logFn = console[type] || console.log || noop,
14502           hasApply = false;
14503
14504       // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
14505       // The reason behind this is that console.log has type "object" in IE8...
14506       try {
14507         hasApply = !!logFn.apply;
14508       } catch (e) { /* empty */ }
14509
14510       if (hasApply) {
14511         return function() {
14512           var args = [];
14513           forEach(arguments, function(arg) {
14514             args.push(formatError(arg));
14515           });
14516           return logFn.apply(console, args);
14517         };
14518       }
14519
14520       // we are IE which either doesn't have window.console => this is noop and we do nothing,
14521       // or we are IE where console.log doesn't have apply so we log at least first 2 args
14522       return function(arg1, arg2) {
14523         logFn(arg1, arg2 == null ? '' : arg2);
14524       };
14525     }
14526   }];
14527 }
14528
14529 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14530  *     Any commits to this file should be reviewed with security in mind.  *
14531  *   Changes to this file can potentially create security vulnerabilities. *
14532  *          An approval from 2 Core members with history of modifying      *
14533  *                         this file is required.                          *
14534  *                                                                         *
14535  *  Does the change somehow allow for arbitrary javascript to be executed? *
14536  *    Or allows for someone to change the prototype of built-in objects?   *
14537  *     Or gives undesired access to variables likes document or window?    *
14538  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
14539
14540 var $parseMinErr = minErr('$parse');
14541
14542 var objectValueOf = {}.constructor.prototype.valueOf;
14543
14544 // Sandboxing Angular Expressions
14545 // ------------------------------
14546 // Angular expressions are no longer sandboxed. So it is now even easier to access arbitrary JS code by
14547 // various means such as obtaining a reference to native JS functions like the Function constructor.
14548 //
14549 // As an example, consider the following Angular expression:
14550 //
14551 //   {}.toString.constructor('alert("evil JS code")')
14552 //
14553 // It is important to realize that if you create an expression from a string that contains user provided
14554 // content then it is possible that your application contains a security vulnerability to an XSS style attack.
14555 //
14556 // See https://docs.angularjs.org/guide/security
14557
14558
14559 function getStringValue(name) {
14560   // Property names must be strings. This means that non-string objects cannot be used
14561   // as keys in an object. Any non-string object, including a number, is typecasted
14562   // into a string via the toString method.
14563   // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
14564   //
14565   // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
14566   // to a string. It's not always possible. If `name` is an object and its `toString` method is
14567   // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
14568   //
14569   // TypeError: Cannot convert object to primitive value
14570   //
14571   // For performance reasons, we don't catch this error here and allow it to propagate up the call
14572   // stack. Note that you'll get the same error in JavaScript if you try to access a property using
14573   // such a 'broken' object as a key.
14574   return name + '';
14575 }
14576
14577
14578 var OPERATORS = createMap();
14579 forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
14580 var ESCAPE = {'n':'\n', 'f':'\f', 'r':'\r', 't':'\t', 'v':'\v', '\'':'\'', '"':'"'};
14581
14582
14583 /////////////////////////////////////////
14584
14585
14586 /**
14587  * @constructor
14588  */
14589 var Lexer = function Lexer(options) {
14590   this.options = options;
14591 };
14592
14593 Lexer.prototype = {
14594   constructor: Lexer,
14595
14596   lex: function(text) {
14597     this.text = text;
14598     this.index = 0;
14599     this.tokens = [];
14600
14601     while (this.index < this.text.length) {
14602       var ch = this.text.charAt(this.index);
14603       if (ch === '"' || ch === '\'') {
14604         this.readString(ch);
14605       } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
14606         this.readNumber();
14607       } else if (this.isIdentifierStart(this.peekMultichar())) {
14608         this.readIdent();
14609       } else if (this.is(ch, '(){}[].,;:?')) {
14610         this.tokens.push({index: this.index, text: ch});
14611         this.index++;
14612       } else if (this.isWhitespace(ch)) {
14613         this.index++;
14614       } else {
14615         var ch2 = ch + this.peek();
14616         var ch3 = ch2 + this.peek(2);
14617         var op1 = OPERATORS[ch];
14618         var op2 = OPERATORS[ch2];
14619         var op3 = OPERATORS[ch3];
14620         if (op1 || op2 || op3) {
14621           var token = op3 ? ch3 : (op2 ? ch2 : ch);
14622           this.tokens.push({index: this.index, text: token, operator: true});
14623           this.index += token.length;
14624         } else {
14625           this.throwError('Unexpected next character ', this.index, this.index + 1);
14626         }
14627       }
14628     }
14629     return this.tokens;
14630   },
14631
14632   is: function(ch, chars) {
14633     return chars.indexOf(ch) !== -1;
14634   },
14635
14636   peek: function(i) {
14637     var num = i || 1;
14638     return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
14639   },
14640
14641   isNumber: function(ch) {
14642     return ('0' <= ch && ch <= '9') && typeof ch === 'string';
14643   },
14644
14645   isWhitespace: function(ch) {
14646     // IE treats non-breaking space as \u00A0
14647     return (ch === ' ' || ch === '\r' || ch === '\t' ||
14648             ch === '\n' || ch === '\v' || ch === '\u00A0');
14649   },
14650
14651   isIdentifierStart: function(ch) {
14652     return this.options.isIdentifierStart ?
14653         this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
14654         this.isValidIdentifierStart(ch);
14655   },
14656
14657   isValidIdentifierStart: function(ch) {
14658     return ('a' <= ch && ch <= 'z' ||
14659             'A' <= ch && ch <= 'Z' ||
14660             '_' === ch || ch === '$');
14661   },
14662
14663   isIdentifierContinue: function(ch) {
14664     return this.options.isIdentifierContinue ?
14665         this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
14666         this.isValidIdentifierContinue(ch);
14667   },
14668
14669   isValidIdentifierContinue: function(ch, cp) {
14670     return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
14671   },
14672
14673   codePointAt: function(ch) {
14674     if (ch.length === 1) return ch.charCodeAt(0);
14675     // eslint-disable-next-line no-bitwise
14676     return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
14677   },
14678
14679   peekMultichar: function() {
14680     var ch = this.text.charAt(this.index);
14681     var peek = this.peek();
14682     if (!peek) {
14683       return ch;
14684     }
14685     var cp1 = ch.charCodeAt(0);
14686     var cp2 = peek.charCodeAt(0);
14687     if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
14688       return ch + peek;
14689     }
14690     return ch;
14691   },
14692
14693   isExpOperator: function(ch) {
14694     return (ch === '-' || ch === '+' || this.isNumber(ch));
14695   },
14696
14697   throwError: function(error, start, end) {
14698     end = end || this.index;
14699     var colStr = (isDefined(start)
14700             ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
14701             : ' ' + end);
14702     throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
14703         error, colStr, this.text);
14704   },
14705
14706   readNumber: function() {
14707     var number = '';
14708     var start = this.index;
14709     while (this.index < this.text.length) {
14710       var ch = lowercase(this.text.charAt(this.index));
14711       if (ch === '.' || this.isNumber(ch)) {
14712         number += ch;
14713       } else {
14714         var peekCh = this.peek();
14715         if (ch === 'e' && this.isExpOperator(peekCh)) {
14716           number += ch;
14717         } else if (this.isExpOperator(ch) &&
14718             peekCh && this.isNumber(peekCh) &&
14719             number.charAt(number.length - 1) === 'e') {
14720           number += ch;
14721         } else if (this.isExpOperator(ch) &&
14722             (!peekCh || !this.isNumber(peekCh)) &&
14723             number.charAt(number.length - 1) === 'e') {
14724           this.throwError('Invalid exponent');
14725         } else {
14726           break;
14727         }
14728       }
14729       this.index++;
14730     }
14731     this.tokens.push({
14732       index: start,
14733       text: number,
14734       constant: true,
14735       value: Number(number)
14736     });
14737   },
14738
14739   readIdent: function() {
14740     var start = this.index;
14741     this.index += this.peekMultichar().length;
14742     while (this.index < this.text.length) {
14743       var ch = this.peekMultichar();
14744       if (!this.isIdentifierContinue(ch)) {
14745         break;
14746       }
14747       this.index += ch.length;
14748     }
14749     this.tokens.push({
14750       index: start,
14751       text: this.text.slice(start, this.index),
14752       identifier: true
14753     });
14754   },
14755
14756   readString: function(quote) {
14757     var start = this.index;
14758     this.index++;
14759     var string = '';
14760     var rawString = quote;
14761     var escape = false;
14762     while (this.index < this.text.length) {
14763       var ch = this.text.charAt(this.index);
14764       rawString += ch;
14765       if (escape) {
14766         if (ch === 'u') {
14767           var hex = this.text.substring(this.index + 1, this.index + 5);
14768           if (!hex.match(/[\da-f]{4}/i)) {
14769             this.throwError('Invalid unicode escape [\\u' + hex + ']');
14770           }
14771           this.index += 4;
14772           string += String.fromCharCode(parseInt(hex, 16));
14773         } else {
14774           var rep = ESCAPE[ch];
14775           string = string + (rep || ch);
14776         }
14777         escape = false;
14778       } else if (ch === '\\') {
14779         escape = true;
14780       } else if (ch === quote) {
14781         this.index++;
14782         this.tokens.push({
14783           index: start,
14784           text: rawString,
14785           constant: true,
14786           value: string
14787         });
14788         return;
14789       } else {
14790         string += ch;
14791       }
14792       this.index++;
14793     }
14794     this.throwError('Unterminated quote', start);
14795   }
14796 };
14797
14798 var AST = function AST(lexer, options) {
14799   this.lexer = lexer;
14800   this.options = options;
14801 };
14802
14803 AST.Program = 'Program';
14804 AST.ExpressionStatement = 'ExpressionStatement';
14805 AST.AssignmentExpression = 'AssignmentExpression';
14806 AST.ConditionalExpression = 'ConditionalExpression';
14807 AST.LogicalExpression = 'LogicalExpression';
14808 AST.BinaryExpression = 'BinaryExpression';
14809 AST.UnaryExpression = 'UnaryExpression';
14810 AST.CallExpression = 'CallExpression';
14811 AST.MemberExpression = 'MemberExpression';
14812 AST.Identifier = 'Identifier';
14813 AST.Literal = 'Literal';
14814 AST.ArrayExpression = 'ArrayExpression';
14815 AST.Property = 'Property';
14816 AST.ObjectExpression = 'ObjectExpression';
14817 AST.ThisExpression = 'ThisExpression';
14818 AST.LocalsExpression = 'LocalsExpression';
14819
14820 // Internal use only
14821 AST.NGValueParameter = 'NGValueParameter';
14822
14823 AST.prototype = {
14824   ast: function(text) {
14825     this.text = text;
14826     this.tokens = this.lexer.lex(text);
14827
14828     var value = this.program();
14829
14830     if (this.tokens.length !== 0) {
14831       this.throwError('is an unexpected token', this.tokens[0]);
14832     }
14833
14834     return value;
14835   },
14836
14837   program: function() {
14838     var body = [];
14839     while (true) {
14840       if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
14841         body.push(this.expressionStatement());
14842       if (!this.expect(';')) {
14843         return { type: AST.Program, body: body};
14844       }
14845     }
14846   },
14847
14848   expressionStatement: function() {
14849     return { type: AST.ExpressionStatement, expression: this.filterChain() };
14850   },
14851
14852   filterChain: function() {
14853     var left = this.expression();
14854     while (this.expect('|')) {
14855       left = this.filter(left);
14856     }
14857     return left;
14858   },
14859
14860   expression: function() {
14861     return this.assignment();
14862   },
14863
14864   assignment: function() {
14865     var result = this.ternary();
14866     if (this.expect('=')) {
14867       if (!isAssignable(result)) {
14868         throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
14869       }
14870
14871       result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
14872     }
14873     return result;
14874   },
14875
14876   ternary: function() {
14877     var test = this.logicalOR();
14878     var alternate;
14879     var consequent;
14880     if (this.expect('?')) {
14881       alternate = this.expression();
14882       if (this.consume(':')) {
14883         consequent = this.expression();
14884         return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
14885       }
14886     }
14887     return test;
14888   },
14889
14890   logicalOR: function() {
14891     var left = this.logicalAND();
14892     while (this.expect('||')) {
14893       left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
14894     }
14895     return left;
14896   },
14897
14898   logicalAND: function() {
14899     var left = this.equality();
14900     while (this.expect('&&')) {
14901       left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
14902     }
14903     return left;
14904   },
14905
14906   equality: function() {
14907     var left = this.relational();
14908     var token;
14909     while ((token = this.expect('==','!=','===','!=='))) {
14910       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
14911     }
14912     return left;
14913   },
14914
14915   relational: function() {
14916     var left = this.additive();
14917     var token;
14918     while ((token = this.expect('<', '>', '<=', '>='))) {
14919       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
14920     }
14921     return left;
14922   },
14923
14924   additive: function() {
14925     var left = this.multiplicative();
14926     var token;
14927     while ((token = this.expect('+','-'))) {
14928       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
14929     }
14930     return left;
14931   },
14932
14933   multiplicative: function() {
14934     var left = this.unary();
14935     var token;
14936     while ((token = this.expect('*','/','%'))) {
14937       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
14938     }
14939     return left;
14940   },
14941
14942   unary: function() {
14943     var token;
14944     if ((token = this.expect('+', '-', '!'))) {
14945       return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
14946     } else {
14947       return this.primary();
14948     }
14949   },
14950
14951   primary: function() {
14952     var primary;
14953     if (this.expect('(')) {
14954       primary = this.filterChain();
14955       this.consume(')');
14956     } else if (this.expect('[')) {
14957       primary = this.arrayDeclaration();
14958     } else if (this.expect('{')) {
14959       primary = this.object();
14960     } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
14961       primary = copy(this.selfReferential[this.consume().text]);
14962     } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
14963       primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
14964     } else if (this.peek().identifier) {
14965       primary = this.identifier();
14966     } else if (this.peek().constant) {
14967       primary = this.constant();
14968     } else {
14969       this.throwError('not a primary expression', this.peek());
14970     }
14971
14972     var next;
14973     while ((next = this.expect('(', '[', '.'))) {
14974       if (next.text === '(') {
14975         primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
14976         this.consume(')');
14977       } else if (next.text === '[') {
14978         primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
14979         this.consume(']');
14980       } else if (next.text === '.') {
14981         primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
14982       } else {
14983         this.throwError('IMPOSSIBLE');
14984       }
14985     }
14986     return primary;
14987   },
14988
14989   filter: function(baseExpression) {
14990     var args = [baseExpression];
14991     var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
14992
14993     while (this.expect(':')) {
14994       args.push(this.expression());
14995     }
14996
14997     return result;
14998   },
14999
15000   parseArguments: function() {
15001     var args = [];
15002     if (this.peekToken().text !== ')') {
15003       do {
15004         args.push(this.filterChain());
15005       } while (this.expect(','));
15006     }
15007     return args;
15008   },
15009
15010   identifier: function() {
15011     var token = this.consume();
15012     if (!token.identifier) {
15013       this.throwError('is not a valid identifier', token);
15014     }
15015     return { type: AST.Identifier, name: token.text };
15016   },
15017
15018   constant: function() {
15019     // TODO check that it is a constant
15020     return { type: AST.Literal, value: this.consume().value };
15021   },
15022
15023   arrayDeclaration: function() {
15024     var elements = [];
15025     if (this.peekToken().text !== ']') {
15026       do {
15027         if (this.peek(']')) {
15028           // Support trailing commas per ES5.1.
15029           break;
15030         }
15031         elements.push(this.expression());
15032       } while (this.expect(','));
15033     }
15034     this.consume(']');
15035
15036     return { type: AST.ArrayExpression, elements: elements };
15037   },
15038
15039   object: function() {
15040     var properties = [], property;
15041     if (this.peekToken().text !== '}') {
15042       do {
15043         if (this.peek('}')) {
15044           // Support trailing commas per ES5.1.
15045           break;
15046         }
15047         property = {type: AST.Property, kind: 'init'};
15048         if (this.peek().constant) {
15049           property.key = this.constant();
15050           property.computed = false;
15051           this.consume(':');
15052           property.value = this.expression();
15053         } else if (this.peek().identifier) {
15054           property.key = this.identifier();
15055           property.computed = false;
15056           if (this.peek(':')) {
15057             this.consume(':');
15058             property.value = this.expression();
15059           } else {
15060             property.value = property.key;
15061           }
15062         } else if (this.peek('[')) {
15063           this.consume('[');
15064           property.key = this.expression();
15065           this.consume(']');
15066           property.computed = true;
15067           this.consume(':');
15068           property.value = this.expression();
15069         } else {
15070           this.throwError('invalid key', this.peek());
15071         }
15072         properties.push(property);
15073       } while (this.expect(','));
15074     }
15075     this.consume('}');
15076
15077     return {type: AST.ObjectExpression, properties: properties };
15078   },
15079
15080   throwError: function(msg, token) {
15081     throw $parseMinErr('syntax',
15082         'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
15083           token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
15084   },
15085
15086   consume: function(e1) {
15087     if (this.tokens.length === 0) {
15088       throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
15089     }
15090
15091     var token = this.expect(e1);
15092     if (!token) {
15093       this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
15094     }
15095     return token;
15096   },
15097
15098   peekToken: function() {
15099     if (this.tokens.length === 0) {
15100       throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
15101     }
15102     return this.tokens[0];
15103   },
15104
15105   peek: function(e1, e2, e3, e4) {
15106     return this.peekAhead(0, e1, e2, e3, e4);
15107   },
15108
15109   peekAhead: function(i, e1, e2, e3, e4) {
15110     if (this.tokens.length > i) {
15111       var token = this.tokens[i];
15112       var t = token.text;
15113       if (t === e1 || t === e2 || t === e3 || t === e4 ||
15114           (!e1 && !e2 && !e3 && !e4)) {
15115         return token;
15116       }
15117     }
15118     return false;
15119   },
15120
15121   expect: function(e1, e2, e3, e4) {
15122     var token = this.peek(e1, e2, e3, e4);
15123     if (token) {
15124       this.tokens.shift();
15125       return token;
15126     }
15127     return false;
15128   },
15129
15130   selfReferential: {
15131     'this': {type: AST.ThisExpression },
15132     '$locals': {type: AST.LocalsExpression }
15133   }
15134 };
15135
15136 function ifDefined(v, d) {
15137   return typeof v !== 'undefined' ? v : d;
15138 }
15139
15140 function plusFn(l, r) {
15141   if (typeof l === 'undefined') return r;
15142   if (typeof r === 'undefined') return l;
15143   return l + r;
15144 }
15145
15146 function isStateless($filter, filterName) {
15147   var fn = $filter(filterName);
15148   return !fn.$stateful;
15149 }
15150
15151 function findConstantAndWatchExpressions(ast, $filter) {
15152   var allConstants;
15153   var argsToWatch;
15154   var isStatelessFilter;
15155   switch (ast.type) {
15156   case AST.Program:
15157     allConstants = true;
15158     forEach(ast.body, function(expr) {
15159       findConstantAndWatchExpressions(expr.expression, $filter);
15160       allConstants = allConstants && expr.expression.constant;
15161     });
15162     ast.constant = allConstants;
15163     break;
15164   case AST.Literal:
15165     ast.constant = true;
15166     ast.toWatch = [];
15167     break;
15168   case AST.UnaryExpression:
15169     findConstantAndWatchExpressions(ast.argument, $filter);
15170     ast.constant = ast.argument.constant;
15171     ast.toWatch = ast.argument.toWatch;
15172     break;
15173   case AST.BinaryExpression:
15174     findConstantAndWatchExpressions(ast.left, $filter);
15175     findConstantAndWatchExpressions(ast.right, $filter);
15176     ast.constant = ast.left.constant && ast.right.constant;
15177     ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
15178     break;
15179   case AST.LogicalExpression:
15180     findConstantAndWatchExpressions(ast.left, $filter);
15181     findConstantAndWatchExpressions(ast.right, $filter);
15182     ast.constant = ast.left.constant && ast.right.constant;
15183     ast.toWatch = ast.constant ? [] : [ast];
15184     break;
15185   case AST.ConditionalExpression:
15186     findConstantAndWatchExpressions(ast.test, $filter);
15187     findConstantAndWatchExpressions(ast.alternate, $filter);
15188     findConstantAndWatchExpressions(ast.consequent, $filter);
15189     ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
15190     ast.toWatch = ast.constant ? [] : [ast];
15191     break;
15192   case AST.Identifier:
15193     ast.constant = false;
15194     ast.toWatch = [ast];
15195     break;
15196   case AST.MemberExpression:
15197     findConstantAndWatchExpressions(ast.object, $filter);
15198     if (ast.computed) {
15199       findConstantAndWatchExpressions(ast.property, $filter);
15200     }
15201     ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
15202     ast.toWatch = [ast];
15203     break;
15204   case AST.CallExpression:
15205     isStatelessFilter = ast.filter ? isStateless($filter, ast.callee.name) : false;
15206     allConstants = isStatelessFilter;
15207     argsToWatch = [];
15208     forEach(ast.arguments, function(expr) {
15209       findConstantAndWatchExpressions(expr, $filter);
15210       allConstants = allConstants && expr.constant;
15211       if (!expr.constant) {
15212         argsToWatch.push.apply(argsToWatch, expr.toWatch);
15213       }
15214     });
15215     ast.constant = allConstants;
15216     ast.toWatch = isStatelessFilter ? argsToWatch : [ast];
15217     break;
15218   case AST.AssignmentExpression:
15219     findConstantAndWatchExpressions(ast.left, $filter);
15220     findConstantAndWatchExpressions(ast.right, $filter);
15221     ast.constant = ast.left.constant && ast.right.constant;
15222     ast.toWatch = [ast];
15223     break;
15224   case AST.ArrayExpression:
15225     allConstants = true;
15226     argsToWatch = [];
15227     forEach(ast.elements, function(expr) {
15228       findConstantAndWatchExpressions(expr, $filter);
15229       allConstants = allConstants && expr.constant;
15230       if (!expr.constant) {
15231         argsToWatch.push.apply(argsToWatch, expr.toWatch);
15232       }
15233     });
15234     ast.constant = allConstants;
15235     ast.toWatch = argsToWatch;
15236     break;
15237   case AST.ObjectExpression:
15238     allConstants = true;
15239     argsToWatch = [];
15240     forEach(ast.properties, function(property) {
15241       findConstantAndWatchExpressions(property.value, $filter);
15242       allConstants = allConstants && property.value.constant && !property.computed;
15243       if (!property.value.constant) {
15244         argsToWatch.push.apply(argsToWatch, property.value.toWatch);
15245       }
15246       if (property.computed) {
15247         findConstantAndWatchExpressions(property.key, $filter);
15248         if (!property.key.constant) {
15249           argsToWatch.push.apply(argsToWatch, property.key.toWatch);
15250         }
15251       }
15252
15253     });
15254     ast.constant = allConstants;
15255     ast.toWatch = argsToWatch;
15256     break;
15257   case AST.ThisExpression:
15258     ast.constant = false;
15259     ast.toWatch = [];
15260     break;
15261   case AST.LocalsExpression:
15262     ast.constant = false;
15263     ast.toWatch = [];
15264     break;
15265   }
15266 }
15267
15268 function getInputs(body) {
15269   if (body.length !== 1) return;
15270   var lastExpression = body[0].expression;
15271   var candidate = lastExpression.toWatch;
15272   if (candidate.length !== 1) return candidate;
15273   return candidate[0] !== lastExpression ? candidate : undefined;
15274 }
15275
15276 function isAssignable(ast) {
15277   return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
15278 }
15279
15280 function assignableAST(ast) {
15281   if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
15282     return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
15283   }
15284 }
15285
15286 function isLiteral(ast) {
15287   return ast.body.length === 0 ||
15288       ast.body.length === 1 && (
15289       ast.body[0].expression.type === AST.Literal ||
15290       ast.body[0].expression.type === AST.ArrayExpression ||
15291       ast.body[0].expression.type === AST.ObjectExpression);
15292 }
15293
15294 function isConstant(ast) {
15295   return ast.constant;
15296 }
15297
15298 function ASTCompiler(astBuilder, $filter) {
15299   this.astBuilder = astBuilder;
15300   this.$filter = $filter;
15301 }
15302
15303 ASTCompiler.prototype = {
15304   compile: function(expression) {
15305     var self = this;
15306     var ast = this.astBuilder.ast(expression);
15307     this.state = {
15308       nextId: 0,
15309       filters: {},
15310       fn: {vars: [], body: [], own: {}},
15311       assign: {vars: [], body: [], own: {}},
15312       inputs: []
15313     };
15314     findConstantAndWatchExpressions(ast, self.$filter);
15315     var extra = '';
15316     var assignable;
15317     this.stage = 'assign';
15318     if ((assignable = assignableAST(ast))) {
15319       this.state.computing = 'assign';
15320       var result = this.nextId();
15321       this.recurse(assignable, result);
15322       this.return_(result);
15323       extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
15324     }
15325     var toWatch = getInputs(ast.body);
15326     self.stage = 'inputs';
15327     forEach(toWatch, function(watch, key) {
15328       var fnKey = 'fn' + key;
15329       self.state[fnKey] = {vars: [], body: [], own: {}};
15330       self.state.computing = fnKey;
15331       var intoId = self.nextId();
15332       self.recurse(watch, intoId);
15333       self.return_(intoId);
15334       self.state.inputs.push(fnKey);
15335       watch.watchId = key;
15336     });
15337     this.state.computing = 'fn';
15338     this.stage = 'main';
15339     this.recurse(ast);
15340     var fnString =
15341       // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
15342       // This is a workaround for this until we do a better job at only removing the prefix only when we should.
15343       '"' + this.USE + ' ' + this.STRICT + '";\n' +
15344       this.filterPrefix() +
15345       'var fn=' + this.generateFunction('fn', 's,l,a,i') +
15346       extra +
15347       this.watchFns() +
15348       'return fn;';
15349
15350     // eslint-disable-next-line no-new-func
15351     var fn = (new Function('$filter',
15352         'getStringValue',
15353         'ifDefined',
15354         'plus',
15355         fnString))(
15356           this.$filter,
15357           getStringValue,
15358           ifDefined,
15359           plusFn);
15360     this.state = this.stage = undefined;
15361     fn.literal = isLiteral(ast);
15362     fn.constant = isConstant(ast);
15363     return fn;
15364   },
15365
15366   USE: 'use',
15367
15368   STRICT: 'strict',
15369
15370   watchFns: function() {
15371     var result = [];
15372     var fns = this.state.inputs;
15373     var self = this;
15374     forEach(fns, function(name) {
15375       result.push('var ' + name + '=' + self.generateFunction(name, 's'));
15376     });
15377     if (fns.length) {
15378       result.push('fn.inputs=[' + fns.join(',') + '];');
15379     }
15380     return result.join('');
15381   },
15382
15383   generateFunction: function(name, params) {
15384     return 'function(' + params + '){' +
15385         this.varsPrefix(name) +
15386         this.body(name) +
15387         '};';
15388   },
15389
15390   filterPrefix: function() {
15391     var parts = [];
15392     var self = this;
15393     forEach(this.state.filters, function(id, filter) {
15394       parts.push(id + '=$filter(' + self.escape(filter) + ')');
15395     });
15396     if (parts.length) return 'var ' + parts.join(',') + ';';
15397     return '';
15398   },
15399
15400   varsPrefix: function(section) {
15401     return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
15402   },
15403
15404   body: function(section) {
15405     return this.state[section].body.join('');
15406   },
15407
15408   recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
15409     var left, right, self = this, args, expression, computed;
15410     recursionFn = recursionFn || noop;
15411     if (!skipWatchIdCheck && isDefined(ast.watchId)) {
15412       intoId = intoId || this.nextId();
15413       this.if_('i',
15414         this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
15415         this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
15416       );
15417       return;
15418     }
15419     switch (ast.type) {
15420     case AST.Program:
15421       forEach(ast.body, function(expression, pos) {
15422         self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
15423         if (pos !== ast.body.length - 1) {
15424           self.current().body.push(right, ';');
15425         } else {
15426           self.return_(right);
15427         }
15428       });
15429       break;
15430     case AST.Literal:
15431       expression = this.escape(ast.value);
15432       this.assign(intoId, expression);
15433       recursionFn(intoId || expression);
15434       break;
15435     case AST.UnaryExpression:
15436       this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
15437       expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
15438       this.assign(intoId, expression);
15439       recursionFn(expression);
15440       break;
15441     case AST.BinaryExpression:
15442       this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
15443       this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
15444       if (ast.operator === '+') {
15445         expression = this.plus(left, right);
15446       } else if (ast.operator === '-') {
15447         expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
15448       } else {
15449         expression = '(' + left + ')' + ast.operator + '(' + right + ')';
15450       }
15451       this.assign(intoId, expression);
15452       recursionFn(expression);
15453       break;
15454     case AST.LogicalExpression:
15455       intoId = intoId || this.nextId();
15456       self.recurse(ast.left, intoId);
15457       self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
15458       recursionFn(intoId);
15459       break;
15460     case AST.ConditionalExpression:
15461       intoId = intoId || this.nextId();
15462       self.recurse(ast.test, intoId);
15463       self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
15464       recursionFn(intoId);
15465       break;
15466     case AST.Identifier:
15467       intoId = intoId || this.nextId();
15468       if (nameId) {
15469         nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
15470         nameId.computed = false;
15471         nameId.name = ast.name;
15472       }
15473       self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
15474         function() {
15475           self.if_(self.stage === 'inputs' || 's', function() {
15476             if (create && create !== 1) {
15477               self.if_(
15478                 self.isNull(self.nonComputedMember('s', ast.name)),
15479                 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
15480             }
15481             self.assign(intoId, self.nonComputedMember('s', ast.name));
15482           });
15483         }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
15484         );
15485       recursionFn(intoId);
15486       break;
15487     case AST.MemberExpression:
15488       left = nameId && (nameId.context = this.nextId()) || this.nextId();
15489       intoId = intoId || this.nextId();
15490       self.recurse(ast.object, left, undefined, function() {
15491         self.if_(self.notNull(left), function() {
15492           if (ast.computed) {
15493             right = self.nextId();
15494             self.recurse(ast.property, right);
15495             self.getStringValue(right);
15496             if (create && create !== 1) {
15497               self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
15498             }
15499             expression = self.computedMember(left, right);
15500             self.assign(intoId, expression);
15501             if (nameId) {
15502               nameId.computed = true;
15503               nameId.name = right;
15504             }
15505           } else {
15506             if (create && create !== 1) {
15507               self.if_(self.isNull(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
15508             }
15509             expression = self.nonComputedMember(left, ast.property.name);
15510             self.assign(intoId, expression);
15511             if (nameId) {
15512               nameId.computed = false;
15513               nameId.name = ast.property.name;
15514             }
15515           }
15516         }, function() {
15517           self.assign(intoId, 'undefined');
15518         });
15519         recursionFn(intoId);
15520       }, !!create);
15521       break;
15522     case AST.CallExpression:
15523       intoId = intoId || this.nextId();
15524       if (ast.filter) {
15525         right = self.filter(ast.callee.name);
15526         args = [];
15527         forEach(ast.arguments, function(expr) {
15528           var argument = self.nextId();
15529           self.recurse(expr, argument);
15530           args.push(argument);
15531         });
15532         expression = right + '(' + args.join(',') + ')';
15533         self.assign(intoId, expression);
15534         recursionFn(intoId);
15535       } else {
15536         right = self.nextId();
15537         left = {};
15538         args = [];
15539         self.recurse(ast.callee, right, left, function() {
15540           self.if_(self.notNull(right), function() {
15541             forEach(ast.arguments, function(expr) {
15542               self.recurse(expr, ast.constant ? undefined : self.nextId(), undefined, function(argument) {
15543                 args.push(argument);
15544               });
15545             });
15546             if (left.name) {
15547               expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
15548             } else {
15549               expression = right + '(' + args.join(',') + ')';
15550             }
15551             self.assign(intoId, expression);
15552           }, function() {
15553             self.assign(intoId, 'undefined');
15554           });
15555           recursionFn(intoId);
15556         });
15557       }
15558       break;
15559     case AST.AssignmentExpression:
15560       right = this.nextId();
15561       left = {};
15562       this.recurse(ast.left, undefined, left, function() {
15563         self.if_(self.notNull(left.context), function() {
15564           self.recurse(ast.right, right);
15565           expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
15566           self.assign(intoId, expression);
15567           recursionFn(intoId || expression);
15568         });
15569       }, 1);
15570       break;
15571     case AST.ArrayExpression:
15572       args = [];
15573       forEach(ast.elements, function(expr) {
15574         self.recurse(expr, ast.constant ? undefined : self.nextId(), undefined, function(argument) {
15575           args.push(argument);
15576         });
15577       });
15578       expression = '[' + args.join(',') + ']';
15579       this.assign(intoId, expression);
15580       recursionFn(intoId || expression);
15581       break;
15582     case AST.ObjectExpression:
15583       args = [];
15584       computed = false;
15585       forEach(ast.properties, function(property) {
15586         if (property.computed) {
15587           computed = true;
15588         }
15589       });
15590       if (computed) {
15591         intoId = intoId || this.nextId();
15592         this.assign(intoId, '{}');
15593         forEach(ast.properties, function(property) {
15594           if (property.computed) {
15595             left = self.nextId();
15596             self.recurse(property.key, left);
15597           } else {
15598             left = property.key.type === AST.Identifier ?
15599                        property.key.name :
15600                        ('' + property.key.value);
15601           }
15602           right = self.nextId();
15603           self.recurse(property.value, right);
15604           self.assign(self.member(intoId, left, property.computed), right);
15605         });
15606       } else {
15607         forEach(ast.properties, function(property) {
15608           self.recurse(property.value, ast.constant ? undefined : self.nextId(), undefined, function(expr) {
15609             args.push(self.escape(
15610                 property.key.type === AST.Identifier ? property.key.name :
15611                   ('' + property.key.value)) +
15612                 ':' + expr);
15613           });
15614         });
15615         expression = '{' + args.join(',') + '}';
15616         this.assign(intoId, expression);
15617       }
15618       recursionFn(intoId || expression);
15619       break;
15620     case AST.ThisExpression:
15621       this.assign(intoId, 's');
15622       recursionFn(intoId || 's');
15623       break;
15624     case AST.LocalsExpression:
15625       this.assign(intoId, 'l');
15626       recursionFn(intoId || 'l');
15627       break;
15628     case AST.NGValueParameter:
15629       this.assign(intoId, 'v');
15630       recursionFn(intoId || 'v');
15631       break;
15632     }
15633   },
15634
15635   getHasOwnProperty: function(element, property) {
15636     var key = element + '.' + property;
15637     var own = this.current().own;
15638     if (!own.hasOwnProperty(key)) {
15639       own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
15640     }
15641     return own[key];
15642   },
15643
15644   assign: function(id, value) {
15645     if (!id) return;
15646     this.current().body.push(id, '=', value, ';');
15647     return id;
15648   },
15649
15650   filter: function(filterName) {
15651     if (!this.state.filters.hasOwnProperty(filterName)) {
15652       this.state.filters[filterName] = this.nextId(true);
15653     }
15654     return this.state.filters[filterName];
15655   },
15656
15657   ifDefined: function(id, defaultValue) {
15658     return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
15659   },
15660
15661   plus: function(left, right) {
15662     return 'plus(' + left + ',' + right + ')';
15663   },
15664
15665   return_: function(id) {
15666     this.current().body.push('return ', id, ';');
15667   },
15668
15669   if_: function(test, alternate, consequent) {
15670     if (test === true) {
15671       alternate();
15672     } else {
15673       var body = this.current().body;
15674       body.push('if(', test, '){');
15675       alternate();
15676       body.push('}');
15677       if (consequent) {
15678         body.push('else{');
15679         consequent();
15680         body.push('}');
15681       }
15682     }
15683   },
15684
15685   not: function(expression) {
15686     return '!(' + expression + ')';
15687   },
15688
15689   isNull: function(expression) {
15690     return expression + '==null';
15691   },
15692
15693   notNull: function(expression) {
15694     return expression + '!=null';
15695   },
15696
15697   nonComputedMember: function(left, right) {
15698     var SAFE_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
15699     var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
15700     if (SAFE_IDENTIFIER.test(right)) {
15701       return left + '.' + right;
15702     } else {
15703       return left  + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
15704     }
15705   },
15706
15707   computedMember: function(left, right) {
15708     return left + '[' + right + ']';
15709   },
15710
15711   member: function(left, right, computed) {
15712     if (computed) return this.computedMember(left, right);
15713     return this.nonComputedMember(left, right);
15714   },
15715
15716   getStringValue: function(item) {
15717     this.assign(item, 'getStringValue(' + item + ')');
15718   },
15719
15720   lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
15721     var self = this;
15722     return function() {
15723       self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
15724     };
15725   },
15726
15727   lazyAssign: function(id, value) {
15728     var self = this;
15729     return function() {
15730       self.assign(id, value);
15731     };
15732   },
15733
15734   stringEscapeRegex: /[^ a-zA-Z0-9]/g,
15735
15736   stringEscapeFn: function(c) {
15737     return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
15738   },
15739
15740   escape: function(value) {
15741     if (isString(value)) return '\'' + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + '\'';
15742     if (isNumber(value)) return value.toString();
15743     if (value === true) return 'true';
15744     if (value === false) return 'false';
15745     if (value === null) return 'null';
15746     if (typeof value === 'undefined') return 'undefined';
15747
15748     throw $parseMinErr('esc', 'IMPOSSIBLE');
15749   },
15750
15751   nextId: function(skip, init) {
15752     var id = 'v' + (this.state.nextId++);
15753     if (!skip) {
15754       this.current().vars.push(id + (init ? '=' + init : ''));
15755     }
15756     return id;
15757   },
15758
15759   current: function() {
15760     return this.state[this.state.computing];
15761   }
15762 };
15763
15764
15765 function ASTInterpreter(astBuilder, $filter) {
15766   this.astBuilder = astBuilder;
15767   this.$filter = $filter;
15768 }
15769
15770 ASTInterpreter.prototype = {
15771   compile: function(expression) {
15772     var self = this;
15773     var ast = this.astBuilder.ast(expression);
15774     findConstantAndWatchExpressions(ast, self.$filter);
15775     var assignable;
15776     var assign;
15777     if ((assignable = assignableAST(ast))) {
15778       assign = this.recurse(assignable);
15779     }
15780     var toWatch = getInputs(ast.body);
15781     var inputs;
15782     if (toWatch) {
15783       inputs = [];
15784       forEach(toWatch, function(watch, key) {
15785         var input = self.recurse(watch);
15786         watch.input = input;
15787         inputs.push(input);
15788         watch.watchId = key;
15789       });
15790     }
15791     var expressions = [];
15792     forEach(ast.body, function(expression) {
15793       expressions.push(self.recurse(expression.expression));
15794     });
15795     var fn = ast.body.length === 0 ? noop :
15796              ast.body.length === 1 ? expressions[0] :
15797              function(scope, locals) {
15798                var lastValue;
15799                forEach(expressions, function(exp) {
15800                  lastValue = exp(scope, locals);
15801                });
15802                return lastValue;
15803              };
15804     if (assign) {
15805       fn.assign = function(scope, value, locals) {
15806         return assign(scope, locals, value);
15807       };
15808     }
15809     if (inputs) {
15810       fn.inputs = inputs;
15811     }
15812     fn.literal = isLiteral(ast);
15813     fn.constant = isConstant(ast);
15814     return fn;
15815   },
15816
15817   recurse: function(ast, context, create) {
15818     var left, right, self = this, args;
15819     if (ast.input) {
15820       return this.inputs(ast.input, ast.watchId);
15821     }
15822     switch (ast.type) {
15823     case AST.Literal:
15824       return this.value(ast.value, context);
15825     case AST.UnaryExpression:
15826       right = this.recurse(ast.argument);
15827       return this['unary' + ast.operator](right, context);
15828     case AST.BinaryExpression:
15829       left = this.recurse(ast.left);
15830       right = this.recurse(ast.right);
15831       return this['binary' + ast.operator](left, right, context);
15832     case AST.LogicalExpression:
15833       left = this.recurse(ast.left);
15834       right = this.recurse(ast.right);
15835       return this['binary' + ast.operator](left, right, context);
15836     case AST.ConditionalExpression:
15837       return this['ternary?:'](
15838         this.recurse(ast.test),
15839         this.recurse(ast.alternate),
15840         this.recurse(ast.consequent),
15841         context
15842       );
15843     case AST.Identifier:
15844       return self.identifier(ast.name, context, create);
15845     case AST.MemberExpression:
15846       left = this.recurse(ast.object, false, !!create);
15847       if (!ast.computed) {
15848         right = ast.property.name;
15849       }
15850       if (ast.computed) right = this.recurse(ast.property);
15851       return ast.computed ?
15852         this.computedMember(left, right, context, create) :
15853         this.nonComputedMember(left, right, context, create);
15854     case AST.CallExpression:
15855       args = [];
15856       forEach(ast.arguments, function(expr) {
15857         args.push(self.recurse(expr));
15858       });
15859       if (ast.filter) right = this.$filter(ast.callee.name);
15860       if (!ast.filter) right = this.recurse(ast.callee, true);
15861       return ast.filter ?
15862         function(scope, locals, assign, inputs) {
15863           var values = [];
15864           for (var i = 0; i < args.length; ++i) {
15865             values.push(args[i](scope, locals, assign, inputs));
15866           }
15867           var value = right.apply(undefined, values, inputs);
15868           return context ? {context: undefined, name: undefined, value: value} : value;
15869         } :
15870         function(scope, locals, assign, inputs) {
15871           var rhs = right(scope, locals, assign, inputs);
15872           var value;
15873           if (rhs.value != null) {
15874             var values = [];
15875             for (var i = 0; i < args.length; ++i) {
15876               values.push(args[i](scope, locals, assign, inputs));
15877             }
15878             value = rhs.value.apply(rhs.context, values);
15879           }
15880           return context ? {value: value} : value;
15881         };
15882     case AST.AssignmentExpression:
15883       left = this.recurse(ast.left, true, 1);
15884       right = this.recurse(ast.right);
15885       return function(scope, locals, assign, inputs) {
15886         var lhs = left(scope, locals, assign, inputs);
15887         var rhs = right(scope, locals, assign, inputs);
15888         lhs.context[lhs.name] = rhs;
15889         return context ? {value: rhs} : rhs;
15890       };
15891     case AST.ArrayExpression:
15892       args = [];
15893       forEach(ast.elements, function(expr) {
15894         args.push(self.recurse(expr));
15895       });
15896       return function(scope, locals, assign, inputs) {
15897         var value = [];
15898         for (var i = 0; i < args.length; ++i) {
15899           value.push(args[i](scope, locals, assign, inputs));
15900         }
15901         return context ? {value: value} : value;
15902       };
15903     case AST.ObjectExpression:
15904       args = [];
15905       forEach(ast.properties, function(property) {
15906         if (property.computed) {
15907           args.push({key: self.recurse(property.key),
15908                      computed: true,
15909                      value: self.recurse(property.value)
15910           });
15911         } else {
15912           args.push({key: property.key.type === AST.Identifier ?
15913                           property.key.name :
15914                           ('' + property.key.value),
15915                      computed: false,
15916                      value: self.recurse(property.value)
15917           });
15918         }
15919       });
15920       return function(scope, locals, assign, inputs) {
15921         var value = {};
15922         for (var i = 0; i < args.length; ++i) {
15923           if (args[i].computed) {
15924             value[args[i].key(scope, locals, assign, inputs)] = args[i].value(scope, locals, assign, inputs);
15925           } else {
15926             value[args[i].key] = args[i].value(scope, locals, assign, inputs);
15927           }
15928         }
15929         return context ? {value: value} : value;
15930       };
15931     case AST.ThisExpression:
15932       return function(scope) {
15933         return context ? {value: scope} : scope;
15934       };
15935     case AST.LocalsExpression:
15936       return function(scope, locals) {
15937         return context ? {value: locals} : locals;
15938       };
15939     case AST.NGValueParameter:
15940       return function(scope, locals, assign) {
15941         return context ? {value: assign} : assign;
15942       };
15943     }
15944   },
15945
15946   'unary+': function(argument, context) {
15947     return function(scope, locals, assign, inputs) {
15948       var arg = argument(scope, locals, assign, inputs);
15949       if (isDefined(arg)) {
15950         arg = +arg;
15951       } else {
15952         arg = 0;
15953       }
15954       return context ? {value: arg} : arg;
15955     };
15956   },
15957   'unary-': function(argument, context) {
15958     return function(scope, locals, assign, inputs) {
15959       var arg = argument(scope, locals, assign, inputs);
15960       if (isDefined(arg)) {
15961         arg = -arg;
15962       } else {
15963         arg = -0;
15964       }
15965       return context ? {value: arg} : arg;
15966     };
15967   },
15968   'unary!': function(argument, context) {
15969     return function(scope, locals, assign, inputs) {
15970       var arg = !argument(scope, locals, assign, inputs);
15971       return context ? {value: arg} : arg;
15972     };
15973   },
15974   'binary+': function(left, right, context) {
15975     return function(scope, locals, assign, inputs) {
15976       var lhs = left(scope, locals, assign, inputs);
15977       var rhs = right(scope, locals, assign, inputs);
15978       var arg = plusFn(lhs, rhs);
15979       return context ? {value: arg} : arg;
15980     };
15981   },
15982   'binary-': function(left, right, context) {
15983     return function(scope, locals, assign, inputs) {
15984       var lhs = left(scope, locals, assign, inputs);
15985       var rhs = right(scope, locals, assign, inputs);
15986       var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
15987       return context ? {value: arg} : arg;
15988     };
15989   },
15990   'binary*': function(left, right, context) {
15991     return function(scope, locals, assign, inputs) {
15992       var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
15993       return context ? {value: arg} : arg;
15994     };
15995   },
15996   'binary/': function(left, right, context) {
15997     return function(scope, locals, assign, inputs) {
15998       var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
15999       return context ? {value: arg} : arg;
16000     };
16001   },
16002   'binary%': function(left, right, context) {
16003     return function(scope, locals, assign, inputs) {
16004       var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
16005       return context ? {value: arg} : arg;
16006     };
16007   },
16008   'binary===': function(left, right, context) {
16009     return function(scope, locals, assign, inputs) {
16010       var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
16011       return context ? {value: arg} : arg;
16012     };
16013   },
16014   'binary!==': function(left, right, context) {
16015     return function(scope, locals, assign, inputs) {
16016       var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
16017       return context ? {value: arg} : arg;
16018     };
16019   },
16020   'binary==': function(left, right, context) {
16021     return function(scope, locals, assign, inputs) {
16022       // eslint-disable-next-line eqeqeq
16023       var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
16024       return context ? {value: arg} : arg;
16025     };
16026   },
16027   'binary!=': function(left, right, context) {
16028     return function(scope, locals, assign, inputs) {
16029       // eslint-disable-next-line eqeqeq
16030       var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
16031       return context ? {value: arg} : arg;
16032     };
16033   },
16034   'binary<': function(left, right, context) {
16035     return function(scope, locals, assign, inputs) {
16036       var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
16037       return context ? {value: arg} : arg;
16038     };
16039   },
16040   'binary>': function(left, right, context) {
16041     return function(scope, locals, assign, inputs) {
16042       var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
16043       return context ? {value: arg} : arg;
16044     };
16045   },
16046   'binary<=': function(left, right, context) {
16047     return function(scope, locals, assign, inputs) {
16048       var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
16049       return context ? {value: arg} : arg;
16050     };
16051   },
16052   'binary>=': function(left, right, context) {
16053     return function(scope, locals, assign, inputs) {
16054       var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
16055       return context ? {value: arg} : arg;
16056     };
16057   },
16058   'binary&&': function(left, right, context) {
16059     return function(scope, locals, assign, inputs) {
16060       var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
16061       return context ? {value: arg} : arg;
16062     };
16063   },
16064   'binary||': function(left, right, context) {
16065     return function(scope, locals, assign, inputs) {
16066       var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
16067       return context ? {value: arg} : arg;
16068     };
16069   },
16070   'ternary?:': function(test, alternate, consequent, context) {
16071     return function(scope, locals, assign, inputs) {
16072       var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
16073       return context ? {value: arg} : arg;
16074     };
16075   },
16076   value: function(value, context) {
16077     return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
16078   },
16079   identifier: function(name, context, create) {
16080     return function(scope, locals, assign, inputs) {
16081       var base = locals && (name in locals) ? locals : scope;
16082       if (create && create !== 1 && base && base[name] == null) {
16083         base[name] = {};
16084       }
16085       var value = base ? base[name] : undefined;
16086       if (context) {
16087         return {context: base, name: name, value: value};
16088       } else {
16089         return value;
16090       }
16091     };
16092   },
16093   computedMember: function(left, right, context, create) {
16094     return function(scope, locals, assign, inputs) {
16095       var lhs = left(scope, locals, assign, inputs);
16096       var rhs;
16097       var value;
16098       if (lhs != null) {
16099         rhs = right(scope, locals, assign, inputs);
16100         rhs = getStringValue(rhs);
16101         if (create && create !== 1) {
16102           if (lhs && !(lhs[rhs])) {
16103             lhs[rhs] = {};
16104           }
16105         }
16106         value = lhs[rhs];
16107       }
16108       if (context) {
16109         return {context: lhs, name: rhs, value: value};
16110       } else {
16111         return value;
16112       }
16113     };
16114   },
16115   nonComputedMember: function(left, right, context, create) {
16116     return function(scope, locals, assign, inputs) {
16117       var lhs = left(scope, locals, assign, inputs);
16118       if (create && create !== 1) {
16119         if (lhs && lhs[right] == null) {
16120           lhs[right] = {};
16121         }
16122       }
16123       var value = lhs != null ? lhs[right] : undefined;
16124       if (context) {
16125         return {context: lhs, name: right, value: value};
16126       } else {
16127         return value;
16128       }
16129     };
16130   },
16131   inputs: function(input, watchId) {
16132     return function(scope, value, locals, inputs) {
16133       if (inputs) return inputs[watchId];
16134       return input(scope, value, locals);
16135     };
16136   }
16137 };
16138
16139 /**
16140  * @constructor
16141  */
16142 var Parser = function Parser(lexer, $filter, options) {
16143   this.lexer = lexer;
16144   this.$filter = $filter;
16145   this.options = options;
16146   this.ast = new AST(lexer, options);
16147   this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
16148                                    new ASTCompiler(this.ast, $filter);
16149 };
16150
16151 Parser.prototype = {
16152   constructor: Parser,
16153
16154   parse: function(text) {
16155     return this.astCompiler.compile(text);
16156   }
16157 };
16158
16159 function getValueOf(value) {
16160   return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
16161 }
16162
16163 ///////////////////////////////////
16164
16165 /**
16166  * @ngdoc service
16167  * @name $parse
16168  * @kind function
16169  *
16170  * @description
16171  *
16172  * Converts Angular {@link guide/expression expression} into a function.
16173  *
16174  * ```js
16175  *   var getter = $parse('user.name');
16176  *   var setter = getter.assign;
16177  *   var context = {user:{name:'angular'}};
16178  *   var locals = {user:{name:'local'}};
16179  *
16180  *   expect(getter(context)).toEqual('angular');
16181  *   setter(context, 'newValue');
16182  *   expect(context.user.name).toEqual('newValue');
16183  *   expect(getter(context, locals)).toEqual('local');
16184  * ```
16185  *
16186  *
16187  * @param {string} expression String expression to compile.
16188  * @returns {function(context, locals)} a function which represents the compiled expression:
16189  *
16190  *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
16191  *      are evaluated against (typically a scope object).
16192  *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
16193  *      `context`.
16194  *
16195  *    The returned function also has the following properties:
16196  *      * `literal` â€“ `{boolean}` â€“ whether the expression's top-level node is a JavaScript
16197  *        literal.
16198  *      * `constant` â€“ `{boolean}` â€“ whether the expression is made entirely of JavaScript
16199  *        constant literals.
16200  *      * `assign` â€“ `{?function(context, value)}` â€“ if the expression is assignable, this will be
16201  *        set to a function to change its value on the given context.
16202  *
16203  */
16204
16205
16206 /**
16207  * @ngdoc provider
16208  * @name $parseProvider
16209  * @this
16210  *
16211  * @description
16212  * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
16213  *  service.
16214  */
16215 function $ParseProvider() {
16216   var cache = createMap();
16217   var literals = {
16218     'true': true,
16219     'false': false,
16220     'null': null,
16221     'undefined': undefined
16222   };
16223   var identStart, identContinue;
16224
16225   /**
16226    * @ngdoc method
16227    * @name $parseProvider#addLiteral
16228    * @description
16229    *
16230    * Configure $parse service to add literal values that will be present as literal at expressions.
16231    *
16232    * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
16233    * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
16234    *
16235    **/
16236   this.addLiteral = function(literalName, literalValue) {
16237     literals[literalName] = literalValue;
16238   };
16239
16240  /**
16241   * @ngdoc method
16242   * @name $parseProvider#setIdentifierFns
16243   *
16244   * @description
16245   *
16246   * Allows defining the set of characters that are allowed in Angular expressions. The function
16247   * `identifierStart` will get called to know if a given character is a valid character to be the
16248   * first character for an identifier. The function `identifierContinue` will get called to know if
16249   * a given character is a valid character to be a follow-up identifier character. The functions
16250   * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
16251   * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
16252   * mind that the `string` parameter can be two characters long depending on the character
16253   * representation. It is expected for the function to return `true` or `false`, whether that
16254   * character is allowed or not.
16255   *
16256   * Since this function will be called extensively, keep the implementation of these functions fast,
16257   * as the performance of these functions have a direct impact on the expressions parsing speed.
16258   *
16259   * @param {function=} identifierStart The function that will decide whether the given character is
16260   *   a valid identifier start character.
16261   * @param {function=} identifierContinue The function that will decide whether the given character is
16262   *   a valid identifier continue character.
16263   */
16264   this.setIdentifierFns = function(identifierStart, identifierContinue) {
16265     identStart = identifierStart;
16266     identContinue = identifierContinue;
16267     return this;
16268   };
16269
16270   this.$get = ['$filter', function($filter) {
16271     var noUnsafeEval = csp().noUnsafeEval;
16272     var $parseOptions = {
16273           csp: noUnsafeEval,
16274           literals: copy(literals),
16275           isIdentifierStart: isFunction(identStart) && identStart,
16276           isIdentifierContinue: isFunction(identContinue) && identContinue
16277         };
16278     return $parse;
16279
16280     function $parse(exp, interceptorFn) {
16281       var parsedExpression, oneTime, cacheKey;
16282
16283       switch (typeof exp) {
16284         case 'string':
16285           exp = exp.trim();
16286           cacheKey = exp;
16287
16288           parsedExpression = cache[cacheKey];
16289
16290           if (!parsedExpression) {
16291             if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
16292               oneTime = true;
16293               exp = exp.substring(2);
16294             }
16295             var lexer = new Lexer($parseOptions);
16296             var parser = new Parser(lexer, $filter, $parseOptions);
16297             parsedExpression = parser.parse(exp);
16298             if (parsedExpression.constant) {
16299               parsedExpression.$$watchDelegate = constantWatchDelegate;
16300             } else if (oneTime) {
16301               parsedExpression.$$watchDelegate = parsedExpression.literal ?
16302                   oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
16303             } else if (parsedExpression.inputs) {
16304               parsedExpression.$$watchDelegate = inputsWatchDelegate;
16305             }
16306             cache[cacheKey] = parsedExpression;
16307           }
16308           return addInterceptor(parsedExpression, interceptorFn);
16309
16310         case 'function':
16311           return addInterceptor(exp, interceptorFn);
16312
16313         default:
16314           return addInterceptor(noop, interceptorFn);
16315       }
16316     }
16317
16318     function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) {
16319
16320       if (newValue == null || oldValueOfValue == null) { // null/undefined
16321         return newValue === oldValueOfValue;
16322       }
16323
16324       if (typeof newValue === 'object' && !compareObjectIdentity) {
16325
16326         // attempt to convert the value to a primitive type
16327         // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
16328         //             be cheaply dirty-checked
16329         newValue = getValueOf(newValue);
16330
16331         if (typeof newValue === 'object') {
16332           // objects/arrays are not supported - deep-watching them would be too expensive
16333           return false;
16334         }
16335
16336         // fall-through to the primitive equality check
16337       }
16338
16339       //Primitive or NaN
16340       // eslint-disable-next-line no-self-compare
16341       return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
16342     }
16343
16344     function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
16345       var inputExpressions = parsedExpression.inputs;
16346       var lastResult;
16347
16348       if (inputExpressions.length === 1) {
16349         var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
16350         inputExpressions = inputExpressions[0];
16351         return scope.$watch(function expressionInputWatch(scope) {
16352           var newInputValue = inputExpressions(scope);
16353           if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, parsedExpression.literal)) {
16354             lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
16355             oldInputValueOf = newInputValue && getValueOf(newInputValue);
16356           }
16357           return lastResult;
16358         }, listener, objectEquality, prettyPrintExpression);
16359       }
16360
16361       var oldInputValueOfValues = [];
16362       var oldInputValues = [];
16363       for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
16364         oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
16365         oldInputValues[i] = null;
16366       }
16367
16368       return scope.$watch(function expressionInputsWatch(scope) {
16369         var changed = false;
16370
16371         for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
16372           var newInputValue = inputExpressions[i](scope);
16373           if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], parsedExpression.literal))) {
16374             oldInputValues[i] = newInputValue;
16375             oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
16376           }
16377         }
16378
16379         if (changed) {
16380           lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
16381         }
16382
16383         return lastResult;
16384       }, listener, objectEquality, prettyPrintExpression);
16385     }
16386
16387     function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
16388       var unwatch, lastValue;
16389       if (parsedExpression.inputs) {
16390         unwatch = inputsWatchDelegate(scope, oneTimeListener, objectEquality, parsedExpression, prettyPrintExpression);
16391       } else {
16392         unwatch = scope.$watch(oneTimeWatch, oneTimeListener, objectEquality);
16393       }
16394       return unwatch;
16395
16396       function oneTimeWatch(scope) {
16397         return parsedExpression(scope);
16398       }
16399       function oneTimeListener(value, old, scope) {
16400         lastValue = value;
16401         if (isFunction(listener)) {
16402           listener(value, old, scope);
16403         }
16404         if (isDefined(value)) {
16405           scope.$$postDigest(function() {
16406             if (isDefined(lastValue)) {
16407               unwatch();
16408             }
16409           });
16410         }
16411       }
16412     }
16413
16414     function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
16415       var unwatch, lastValue;
16416       unwatch = scope.$watch(function oneTimeWatch(scope) {
16417         return parsedExpression(scope);
16418       }, function oneTimeListener(value, old, scope) {
16419         lastValue = value;
16420         if (isFunction(listener)) {
16421           listener(value, old, scope);
16422         }
16423         if (isAllDefined(value)) {
16424           scope.$$postDigest(function() {
16425             if (isAllDefined(lastValue)) unwatch();
16426           });
16427         }
16428       }, objectEquality);
16429
16430       return unwatch;
16431
16432       function isAllDefined(value) {
16433         var allDefined = true;
16434         forEach(value, function(val) {
16435           if (!isDefined(val)) allDefined = false;
16436         });
16437         return allDefined;
16438       }
16439     }
16440
16441     function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
16442       var unwatch = scope.$watch(function constantWatch(scope) {
16443         unwatch();
16444         return parsedExpression(scope);
16445       }, listener, objectEquality);
16446       return unwatch;
16447     }
16448
16449     function addInterceptor(parsedExpression, interceptorFn) {
16450       if (!interceptorFn) return parsedExpression;
16451       var watchDelegate = parsedExpression.$$watchDelegate;
16452       var useInputs = false;
16453
16454       var regularWatch =
16455           watchDelegate !== oneTimeLiteralWatchDelegate &&
16456           watchDelegate !== oneTimeWatchDelegate;
16457
16458       var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
16459         var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
16460         return interceptorFn(value, scope, locals);
16461       } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
16462         var value = parsedExpression(scope, locals, assign, inputs);
16463         var result = interceptorFn(value, scope, locals);
16464         // we only return the interceptor's result if the
16465         // initial value is defined (for bind-once)
16466         return isDefined(value) ? result : value;
16467       };
16468
16469       // Propagate $$watchDelegates other then inputsWatchDelegate
16470       useInputs = !parsedExpression.inputs;
16471       if (parsedExpression.$$watchDelegate &&
16472           parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
16473         fn.$$watchDelegate = parsedExpression.$$watchDelegate;
16474         fn.inputs = parsedExpression.inputs;
16475       } else if (!interceptorFn.$stateful) {
16476         // If there is an interceptor, but no watchDelegate then treat the interceptor like
16477         // we treat filters - it is assumed to be a pure function unless flagged with $stateful
16478         fn.$$watchDelegate = inputsWatchDelegate;
16479         fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
16480       }
16481
16482       return fn;
16483     }
16484   }];
16485 }
16486
16487 /**
16488  * @ngdoc service
16489  * @name $q
16490  * @requires $rootScope
16491  *
16492  * @description
16493  * A service that helps you run functions asynchronously, and use their return values (or exceptions)
16494  * when they are done processing.
16495  *
16496  * This is a [Promises/A+](https://promisesaplus.com/)-compliant implementation of promises/deferred
16497  * objects inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
16498  *
16499  * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
16500  * implementations, and the other which resembles ES6 (ES2015) promises to some degree.
16501  *
16502  * # $q constructor
16503  *
16504  * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
16505  * function as the first argument. This is similar to the native Promise implementation from ES6,
16506  * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
16507  *
16508  * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
16509  * available yet.
16510  *
16511  * It can be used like so:
16512  *
16513  * ```js
16514  *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
16515  *   // are available in the current lexical scope (they could have been injected or passed in).
16516  *
16517  *   function asyncGreet(name) {
16518  *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
16519  *     return $q(function(resolve, reject) {
16520  *       setTimeout(function() {
16521  *         if (okToGreet(name)) {
16522  *           resolve('Hello, ' + name + '!');
16523  *         } else {
16524  *           reject('Greeting ' + name + ' is not allowed.');
16525  *         }
16526  *       }, 1000);
16527  *     });
16528  *   }
16529  *
16530  *   var promise = asyncGreet('Robin Hood');
16531  *   promise.then(function(greeting) {
16532  *     alert('Success: ' + greeting);
16533  *   }, function(reason) {
16534  *     alert('Failed: ' + reason);
16535  *   });
16536  * ```
16537  *
16538  * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
16539  *
16540  * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
16541  *
16542  * However, the more traditional CommonJS-style usage is still available, and documented below.
16543  *
16544  * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
16545  * interface for interacting with an object that represents the result of an action that is
16546  * performed asynchronously, and may or may not be finished at any given point in time.
16547  *
16548  * From the perspective of dealing with error handling, deferred and promise APIs are to
16549  * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
16550  *
16551  * ```js
16552  *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
16553  *   // are available in the current lexical scope (they could have been injected or passed in).
16554  *
16555  *   function asyncGreet(name) {
16556  *     var deferred = $q.defer();
16557  *
16558  *     setTimeout(function() {
16559  *       deferred.notify('About to greet ' + name + '.');
16560  *
16561  *       if (okToGreet(name)) {
16562  *         deferred.resolve('Hello, ' + name + '!');
16563  *       } else {
16564  *         deferred.reject('Greeting ' + name + ' is not allowed.');
16565  *       }
16566  *     }, 1000);
16567  *
16568  *     return deferred.promise;
16569  *   }
16570  *
16571  *   var promise = asyncGreet('Robin Hood');
16572  *   promise.then(function(greeting) {
16573  *     alert('Success: ' + greeting);
16574  *   }, function(reason) {
16575  *     alert('Failed: ' + reason);
16576  *   }, function(update) {
16577  *     alert('Got notification: ' + update);
16578  *   });
16579  * ```
16580  *
16581  * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
16582  * comes in the way of guarantees that promise and deferred APIs make, see
16583  * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
16584  *
16585  * Additionally the promise api allows for composition that is very hard to do with the
16586  * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
16587  * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
16588  * section on serial or parallel joining of promises.
16589  *
16590  * # The Deferred API
16591  *
16592  * A new instance of deferred is constructed by calling `$q.defer()`.
16593  *
16594  * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
16595  * that can be used for signaling the successful or unsuccessful completion, as well as the status
16596  * of the task.
16597  *
16598  * **Methods**
16599  *
16600  * - `resolve(value)` â€“ resolves the derived promise with the `value`. If the value is a rejection
16601  *   constructed via `$q.reject`, the promise will be rejected instead.
16602  * - `reject(reason)` â€“ rejects the derived promise with the `reason`. This is equivalent to
16603  *   resolving it with a rejection constructed via `$q.reject`.
16604  * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
16605  *   multiple times before the promise is either resolved or rejected.
16606  *
16607  * **Properties**
16608  *
16609  * - promise â€“ `{Promise}` â€“ promise object associated with this deferred.
16610  *
16611  *
16612  * # The Promise API
16613  *
16614  * A new promise instance is created when a deferred instance is created and can be retrieved by
16615  * calling `deferred.promise`.
16616  *
16617  * The purpose of the promise object is to allow for interested parties to get access to the result
16618  * of the deferred task when it completes.
16619  *
16620  * **Methods**
16621  *
16622  * - `then(successCallback, [errorCallback], [notifyCallback])` â€“ regardless of when the promise was or
16623  *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
16624  *   as soon as the result is available. The callbacks are called with a single argument: the result
16625  *   or rejection reason. Additionally, the notify callback may be called zero or more times to
16626  *   provide a progress indication, before the promise is resolved or rejected.
16627  *
16628  *   This method *returns a new promise* which is resolved or rejected via the return value of the
16629  *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
16630  *   with the value which is resolved in that promise using
16631  *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
16632  *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
16633  *   resolved or rejected from the notifyCallback method. The errorCallback and notifyCallback
16634  *   arguments are optional.
16635  *
16636  * - `catch(errorCallback)` â€“ shorthand for `promise.then(null, errorCallback)`
16637  *
16638  * - `finally(callback, notifyCallback)` â€“ allows you to observe either the fulfillment or rejection of a promise,
16639  *   but to do so without modifying the final value. This is useful to release resources or do some
16640  *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
16641  *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
16642  *   more information.
16643  *
16644  * # Chaining promises
16645  *
16646  * Because calling the `then` method of a promise returns a new derived promise, it is easily
16647  * possible to create a chain of promises:
16648  *
16649  * ```js
16650  *   promiseB = promiseA.then(function(result) {
16651  *     return result + 1;
16652  *   });
16653  *
16654  *   // promiseB will be resolved immediately after promiseA is resolved and its value
16655  *   // will be the result of promiseA incremented by 1
16656  * ```
16657  *
16658  * It is possible to create chains of any length and since a promise can be resolved with another
16659  * promise (which will defer its resolution further), it is possible to pause/defer resolution of
16660  * the promises at any point in the chain. This makes it possible to implement powerful APIs like
16661  * $http's response interceptors.
16662  *
16663  *
16664  * # Differences between Kris Kowal's Q and $q
16665  *
16666  *  There are two main differences:
16667  *
16668  * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
16669  *   mechanism in angular, which means faster propagation of resolution or rejection into your
16670  *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
16671  * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
16672  *   all the important functionality needed for common async tasks.
16673  *
16674  * # Testing
16675  *
16676  *  ```js
16677  *    it('should simulate promise', inject(function($q, $rootScope) {
16678  *      var deferred = $q.defer();
16679  *      var promise = deferred.promise;
16680  *      var resolvedValue;
16681  *
16682  *      promise.then(function(value) { resolvedValue = value; });
16683  *      expect(resolvedValue).toBeUndefined();
16684  *
16685  *      // Simulate resolving of promise
16686  *      deferred.resolve(123);
16687  *      // Note that the 'then' function does not get called synchronously.
16688  *      // This is because we want the promise API to always be async, whether or not
16689  *      // it got called synchronously or asynchronously.
16690  *      expect(resolvedValue).toBeUndefined();
16691  *
16692  *      // Propagate promise resolution to 'then' functions using $apply().
16693  *      $rootScope.$apply();
16694  *      expect(resolvedValue).toEqual(123);
16695  *    }));
16696  *  ```
16697  *
16698  * @param {function(function, function)} resolver Function which is responsible for resolving or
16699  *   rejecting the newly created promise. The first parameter is a function which resolves the
16700  *   promise, the second parameter is a function which rejects the promise.
16701  *
16702  * @returns {Promise} The newly created promise.
16703  */
16704 /**
16705  * @ngdoc provider
16706  * @name $qProvider
16707  * @this
16708  *
16709  * @description
16710  */
16711 function $QProvider() {
16712   var errorOnUnhandledRejections = true;
16713   this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
16714     return qFactory(function(callback) {
16715       $rootScope.$evalAsync(callback);
16716     }, $exceptionHandler, errorOnUnhandledRejections);
16717   }];
16718
16719   /**
16720    * @ngdoc method
16721    * @name $qProvider#errorOnUnhandledRejections
16722    * @kind function
16723    *
16724    * @description
16725    * Retrieves or overrides whether to generate an error when a rejected promise is not handled.
16726    * This feature is enabled by default.
16727    *
16728    * @param {boolean=} value Whether to generate an error when a rejected promise is not handled.
16729    * @returns {boolean|ng.$qProvider} Current value when called without a new value or self for
16730    *    chaining otherwise.
16731    */
16732   this.errorOnUnhandledRejections = function(value) {
16733     if (isDefined(value)) {
16734       errorOnUnhandledRejections = value;
16735       return this;
16736     } else {
16737       return errorOnUnhandledRejections;
16738     }
16739   };
16740 }
16741
16742 /** @this */
16743 function $$QProvider() {
16744   var errorOnUnhandledRejections = true;
16745   this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
16746     return qFactory(function(callback) {
16747       $browser.defer(callback);
16748     }, $exceptionHandler, errorOnUnhandledRejections);
16749   }];
16750
16751   this.errorOnUnhandledRejections = function(value) {
16752     if (isDefined(value)) {
16753       errorOnUnhandledRejections = value;
16754       return this;
16755     } else {
16756       return errorOnUnhandledRejections;
16757     }
16758   };
16759 }
16760
16761 /**
16762  * Constructs a promise manager.
16763  *
16764  * @param {function(function)} nextTick Function for executing functions in the next turn.
16765  * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
16766  *     debugging purposes.
16767  @ param {=boolean} errorOnUnhandledRejections Whether an error should be generated on unhandled
16768  *     promises rejections.
16769  * @returns {object} Promise manager.
16770  */
16771 function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
16772   var $qMinErr = minErr('$q', TypeError);
16773   var queueSize = 0;
16774   var checkQueue = [];
16775
16776   /**
16777    * @ngdoc method
16778    * @name ng.$q#defer
16779    * @kind function
16780    *
16781    * @description
16782    * Creates a `Deferred` object which represents a task which will finish in the future.
16783    *
16784    * @returns {Deferred} Returns a new instance of deferred.
16785    */
16786   function defer() {
16787     return new Deferred();
16788   }
16789
16790   function Deferred() {
16791     var promise = this.promise = new Promise();
16792     //Non prototype methods necessary to support unbound execution :/
16793     this.resolve = function(val) { resolvePromise(promise, val); };
16794     this.reject = function(reason) { rejectPromise(promise, reason); };
16795     this.notify = function(progress) { notifyPromise(promise, progress); };
16796   }
16797
16798
16799   function Promise() {
16800     this.$$state = { status: 0 };
16801   }
16802
16803   extend(Promise.prototype, {
16804     then: function(onFulfilled, onRejected, progressBack) {
16805       if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
16806         return this;
16807       }
16808       var result = new Promise();
16809
16810       this.$$state.pending = this.$$state.pending || [];
16811       this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
16812       if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
16813
16814       return result;
16815     },
16816
16817     'catch': function(callback) {
16818       return this.then(null, callback);
16819     },
16820
16821     'finally': function(callback, progressBack) {
16822       return this.then(function(value) {
16823         return handleCallback(value, resolve, callback);
16824       }, function(error) {
16825         return handleCallback(error, reject, callback);
16826       }, progressBack);
16827     }
16828   });
16829
16830   function processQueue(state) {
16831     var fn, promise, pending;
16832
16833     pending = state.pending;
16834     state.processScheduled = false;
16835     state.pending = undefined;
16836     try {
16837       for (var i = 0, ii = pending.length; i < ii; ++i) {
16838         state.pur = true;
16839         promise = pending[i][0];
16840         fn = pending[i][state.status];
16841         try {
16842           if (isFunction(fn)) {
16843             resolvePromise(promise, fn(state.value));
16844           } else if (state.status === 1) {
16845             resolvePromise(promise, state.value);
16846           } else {
16847             rejectPromise(promise, state.value);
16848           }
16849         } catch (e) {
16850           rejectPromise(promise, e);
16851         }
16852       }
16853     } finally {
16854       --queueSize;
16855       if (errorOnUnhandledRejections && queueSize === 0) {
16856         nextTick(processChecks);
16857       }
16858     }
16859   }
16860
16861   function processChecks() {
16862     // eslint-disable-next-line no-unmodified-loop-condition
16863     while (!queueSize && checkQueue.length) {
16864       var toCheck = checkQueue.shift();
16865       if (!toCheck.pur) {
16866         toCheck.pur = true;
16867         var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value);
16868         if (toCheck.value instanceof Error) {
16869           exceptionHandler(toCheck.value, errorMessage);
16870         } else {
16871           exceptionHandler(errorMessage);
16872         }
16873       }
16874     }
16875   }
16876
16877   function scheduleProcessQueue(state) {
16878     if (errorOnUnhandledRejections && !state.pending && state.status === 2 && !state.pur) {
16879       if (queueSize === 0 && checkQueue.length === 0) {
16880         nextTick(processChecks);
16881       }
16882       checkQueue.push(state);
16883     }
16884     if (state.processScheduled || !state.pending) return;
16885     state.processScheduled = true;
16886     ++queueSize;
16887     nextTick(function() { processQueue(state); });
16888   }
16889
16890   function resolvePromise(promise, val) {
16891     if (promise.$$state.status) return;
16892     if (val === promise) {
16893       $$reject(promise, $qMinErr(
16894         'qcycle',
16895         'Expected promise to be resolved with value other than itself \'{0}\'',
16896         val));
16897     } else {
16898       $$resolve(promise, val);
16899     }
16900
16901   }
16902
16903   function $$resolve(promise, val) {
16904     var then;
16905     var done = false;
16906     try {
16907       if (isObject(val) || isFunction(val)) then = val.then;
16908       if (isFunction(then)) {
16909         promise.$$state.status = -1;
16910         then.call(val, doResolve, doReject, doNotify);
16911       } else {
16912         promise.$$state.value = val;
16913         promise.$$state.status = 1;
16914         scheduleProcessQueue(promise.$$state);
16915       }
16916     } catch (e) {
16917       doReject(e);
16918     }
16919
16920     function doResolve(val) {
16921       if (done) return;
16922       done = true;
16923       $$resolve(promise, val);
16924     }
16925     function doReject(val) {
16926       if (done) return;
16927       done = true;
16928       $$reject(promise, val);
16929     }
16930     function doNotify(progress) {
16931       notifyPromise(promise, progress);
16932     }
16933   }
16934
16935   function rejectPromise(promise, reason) {
16936     if (promise.$$state.status) return;
16937     $$reject(promise, reason);
16938   }
16939
16940   function $$reject(promise, reason) {
16941     promise.$$state.value = reason;
16942     promise.$$state.status = 2;
16943     scheduleProcessQueue(promise.$$state);
16944   }
16945
16946   function notifyPromise(promise, progress) {
16947     var callbacks = promise.$$state.pending;
16948
16949     if ((promise.$$state.status <= 0) && callbacks && callbacks.length) {
16950       nextTick(function() {
16951         var callback, result;
16952         for (var i = 0, ii = callbacks.length; i < ii; i++) {
16953           result = callbacks[i][0];
16954           callback = callbacks[i][3];
16955           try {
16956             notifyPromise(result, isFunction(callback) ? callback(progress) : progress);
16957           } catch (e) {
16958             exceptionHandler(e);
16959           }
16960         }
16961       });
16962     }
16963   }
16964
16965   /**
16966    * @ngdoc method
16967    * @name $q#reject
16968    * @kind function
16969    *
16970    * @description
16971    * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
16972    * used to forward rejection in a chain of promises. If you are dealing with the last promise in
16973    * a promise chain, you don't need to worry about it.
16974    *
16975    * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
16976    * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
16977    * a promise error callback and you want to forward the error to the promise derived from the
16978    * current promise, you have to "rethrow" the error by returning a rejection constructed via
16979    * `reject`.
16980    *
16981    * ```js
16982    *   promiseB = promiseA.then(function(result) {
16983    *     // success: do something and resolve promiseB
16984    *     //          with the old or a new result
16985    *     return result;
16986    *   }, function(reason) {
16987    *     // error: handle the error if possible and
16988    *     //        resolve promiseB with newPromiseOrValue,
16989    *     //        otherwise forward the rejection to promiseB
16990    *     if (canHandle(reason)) {
16991    *      // handle the error and recover
16992    *      return newPromiseOrValue;
16993    *     }
16994    *     return $q.reject(reason);
16995    *   });
16996    * ```
16997    *
16998    * @param {*} reason Constant, message, exception or an object representing the rejection reason.
16999    * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
17000    */
17001   function reject(reason) {
17002     var result = new Promise();
17003     rejectPromise(result, reason);
17004     return result;
17005   }
17006
17007   function handleCallback(value, resolver, callback) {
17008     var callbackOutput = null;
17009     try {
17010       if (isFunction(callback)) callbackOutput = callback();
17011     } catch (e) {
17012       return reject(e);
17013     }
17014     if (isPromiseLike(callbackOutput)) {
17015       return callbackOutput.then(function() {
17016         return resolver(value);
17017       }, reject);
17018     } else {
17019       return resolver(value);
17020     }
17021   }
17022
17023   /**
17024    * @ngdoc method
17025    * @name $q#when
17026    * @kind function
17027    *
17028    * @description
17029    * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
17030    * This is useful when you are dealing with an object that might or might not be a promise, or if
17031    * the promise comes from a source that can't be trusted.
17032    *
17033    * @param {*} value Value or a promise
17034    * @param {Function=} successCallback
17035    * @param {Function=} errorCallback
17036    * @param {Function=} progressCallback
17037    * @returns {Promise} Returns a promise of the passed value or promise
17038    */
17039
17040
17041   function when(value, callback, errback, progressBack) {
17042     var result = new Promise();
17043     resolvePromise(result, value);
17044     return result.then(callback, errback, progressBack);
17045   }
17046
17047   /**
17048    * @ngdoc method
17049    * @name $q#resolve
17050    * @kind function
17051    *
17052    * @description
17053    * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
17054    *
17055    * @param {*} value Value or a promise
17056    * @param {Function=} successCallback
17057    * @param {Function=} errorCallback
17058    * @param {Function=} progressCallback
17059    * @returns {Promise} Returns a promise of the passed value or promise
17060    */
17061   var resolve = when;
17062
17063   /**
17064    * @ngdoc method
17065    * @name $q#all
17066    * @kind function
17067    *
17068    * @description
17069    * Combines multiple promises into a single promise that is resolved when all of the input
17070    * promises are resolved.
17071    *
17072    * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
17073    * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
17074    *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
17075    *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
17076    *   with the same rejection value.
17077    */
17078
17079   function all(promises) {
17080     var result = new Promise(),
17081         counter = 0,
17082         results = isArray(promises) ? [] : {};
17083
17084     forEach(promises, function(promise, key) {
17085       counter++;
17086       when(promise).then(function(value) {
17087         results[key] = value;
17088         if (!(--counter)) resolvePromise(result, results);
17089       }, function(reason) {
17090         rejectPromise(result, reason);
17091       });
17092     });
17093
17094     if (counter === 0) {
17095       resolvePromise(result, results);
17096     }
17097
17098     return result;
17099   }
17100
17101   /**
17102    * @ngdoc method
17103    * @name $q#race
17104    * @kind function
17105    *
17106    * @description
17107    * Returns a promise that resolves or rejects as soon as one of those promises
17108    * resolves or rejects, with the value or reason from that promise.
17109    *
17110    * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
17111    * @returns {Promise} a promise that resolves or rejects as soon as one of the `promises`
17112    * resolves or rejects, with the value or reason from that promise.
17113    */
17114
17115   function race(promises) {
17116     var deferred = defer();
17117
17118     forEach(promises, function(promise) {
17119       when(promise).then(deferred.resolve, deferred.reject);
17120     });
17121
17122     return deferred.promise;
17123   }
17124
17125   function $Q(resolver) {
17126     if (!isFunction(resolver)) {
17127       throw $qMinErr('norslvr', 'Expected resolverFn, got \'{0}\'', resolver);
17128     }
17129
17130     var promise = new Promise();
17131
17132     function resolveFn(value) {
17133       resolvePromise(promise, value);
17134     }
17135
17136     function rejectFn(reason) {
17137       rejectPromise(promise, reason);
17138     }
17139
17140     resolver(resolveFn, rejectFn);
17141
17142     return promise;
17143   }
17144
17145   // Let's make the instanceof operator work for promises, so that
17146   // `new $q(fn) instanceof $q` would evaluate to true.
17147   $Q.prototype = Promise.prototype;
17148
17149   $Q.defer = defer;
17150   $Q.reject = reject;
17151   $Q.when = when;
17152   $Q.resolve = resolve;
17153   $Q.all = all;
17154   $Q.race = race;
17155
17156   return $Q;
17157 }
17158
17159 /** @this */
17160 function $$RAFProvider() { //rAF
17161   this.$get = ['$window', '$timeout', function($window, $timeout) {
17162     var requestAnimationFrame = $window.requestAnimationFrame ||
17163                                 $window.webkitRequestAnimationFrame;
17164
17165     var cancelAnimationFrame = $window.cancelAnimationFrame ||
17166                                $window.webkitCancelAnimationFrame ||
17167                                $window.webkitCancelRequestAnimationFrame;
17168
17169     var rafSupported = !!requestAnimationFrame;
17170     var raf = rafSupported
17171       ? function(fn) {
17172           var id = requestAnimationFrame(fn);
17173           return function() {
17174             cancelAnimationFrame(id);
17175           };
17176         }
17177       : function(fn) {
17178           var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
17179           return function() {
17180             $timeout.cancel(timer);
17181           };
17182         };
17183
17184     raf.supported = rafSupported;
17185
17186     return raf;
17187   }];
17188 }
17189
17190 /**
17191  * DESIGN NOTES
17192  *
17193  * The design decisions behind the scope are heavily favored for speed and memory consumption.
17194  *
17195  * The typical use of scope is to watch the expressions, which most of the time return the same
17196  * value as last time so we optimize the operation.
17197  *
17198  * Closures construction is expensive in terms of speed as well as memory:
17199  *   - No closures, instead use prototypical inheritance for API
17200  *   - Internal state needs to be stored on scope directly, which means that private state is
17201  *     exposed as $$____ properties
17202  *
17203  * Loop operations are optimized by using while(count--) { ... }
17204  *   - This means that in order to keep the same order of execution as addition we have to add
17205  *     items to the array at the beginning (unshift) instead of at the end (push)
17206  *
17207  * Child scopes are created and removed often
17208  *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
17209  *
17210  * There are fewer watches than observers. This is why you don't want the observer to be implemented
17211  * in the same way as watch. Watch requires return of the initialization function which is expensive
17212  * to construct.
17213  */
17214
17215
17216 /**
17217  * @ngdoc provider
17218  * @name $rootScopeProvider
17219  * @description
17220  *
17221  * Provider for the $rootScope service.
17222  */
17223
17224 /**
17225  * @ngdoc method
17226  * @name $rootScopeProvider#digestTtl
17227  * @description
17228  *
17229  * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
17230  * assuming that the model is unstable.
17231  *
17232  * The current default is 10 iterations.
17233  *
17234  * In complex applications it's possible that the dependencies between `$watch`s will result in
17235  * several digest iterations. However if an application needs more than the default 10 digest
17236  * iterations for its model to stabilize then you should investigate what is causing the model to
17237  * continuously change during the digest.
17238  *
17239  * Increasing the TTL could have performance implications, so you should not change it without
17240  * proper justification.
17241  *
17242  * @param {number} limit The number of digest iterations.
17243  */
17244
17245
17246 /**
17247  * @ngdoc service
17248  * @name $rootScope
17249  * @this
17250  *
17251  * @description
17252  *
17253  * Every application has a single root {@link ng.$rootScope.Scope scope}.
17254  * All other scopes are descendant scopes of the root scope. Scopes provide separation
17255  * between the model and the view, via a mechanism for watching the model for changes.
17256  * They also provide event emission/broadcast and subscription facility. See the
17257  * {@link guide/scope developer guide on scopes}.
17258  */
17259 function $RootScopeProvider() {
17260   var TTL = 10;
17261   var $rootScopeMinErr = minErr('$rootScope');
17262   var lastDirtyWatch = null;
17263   var applyAsyncId = null;
17264
17265   this.digestTtl = function(value) {
17266     if (arguments.length) {
17267       TTL = value;
17268     }
17269     return TTL;
17270   };
17271
17272   function createChildScopeClass(parent) {
17273     function ChildScope() {
17274       this.$$watchers = this.$$nextSibling =
17275           this.$$childHead = this.$$childTail = null;
17276       this.$$listeners = {};
17277       this.$$listenerCount = {};
17278       this.$$watchersCount = 0;
17279       this.$id = nextUid();
17280       this.$$ChildScope = null;
17281     }
17282     ChildScope.prototype = parent;
17283     return ChildScope;
17284   }
17285
17286   this.$get = ['$exceptionHandler', '$parse', '$browser',
17287       function($exceptionHandler, $parse, $browser) {
17288
17289     function destroyChildScope($event) {
17290         $event.currentScope.$$destroyed = true;
17291     }
17292
17293     function cleanUpScope($scope) {
17294
17295       // Support: IE 9 only
17296       if (msie === 9) {
17297         // There is a memory leak in IE9 if all child scopes are not disconnected
17298         // completely when a scope is destroyed. So this code will recurse up through
17299         // all this scopes children
17300         //
17301         // See issue https://github.com/angular/angular.js/issues/10706
17302         if ($scope.$$childHead) {
17303           cleanUpScope($scope.$$childHead);
17304         }
17305         if ($scope.$$nextSibling) {
17306           cleanUpScope($scope.$$nextSibling);
17307         }
17308       }
17309
17310       // The code below works around IE9 and V8's memory leaks
17311       //
17312       // See:
17313       // - https://code.google.com/p/v8/issues/detail?id=2073#c26
17314       // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
17315       // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
17316
17317       $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
17318           $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
17319     }
17320
17321     /**
17322      * @ngdoc type
17323      * @name $rootScope.Scope
17324      *
17325      * @description
17326      * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
17327      * {@link auto.$injector $injector}. Child scopes are created using the
17328      * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
17329      * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
17330      * an in-depth introduction and usage examples.
17331      *
17332      *
17333      * # Inheritance
17334      * A scope can inherit from a parent scope, as in this example:
17335      * ```js
17336          var parent = $rootScope;
17337          var child = parent.$new();
17338
17339          parent.salutation = "Hello";
17340          expect(child.salutation).toEqual('Hello');
17341
17342          child.salutation = "Welcome";
17343          expect(child.salutation).toEqual('Welcome');
17344          expect(parent.salutation).toEqual('Hello');
17345      * ```
17346      *
17347      * When interacting with `Scope` in tests, additional helper methods are available on the
17348      * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
17349      * details.
17350      *
17351      *
17352      * @param {Object.<string, function()>=} providers Map of service factory which need to be
17353      *                                       provided for the current scope. Defaults to {@link ng}.
17354      * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
17355      *                              append/override services provided by `providers`. This is handy
17356      *                              when unit-testing and having the need to override a default
17357      *                              service.
17358      * @returns {Object} Newly created scope.
17359      *
17360      */
17361     function Scope() {
17362       this.$id = nextUid();
17363       this.$$phase = this.$parent = this.$$watchers =
17364                      this.$$nextSibling = this.$$prevSibling =
17365                      this.$$childHead = this.$$childTail = null;
17366       this.$root = this;
17367       this.$$destroyed = false;
17368       this.$$listeners = {};
17369       this.$$listenerCount = {};
17370       this.$$watchersCount = 0;
17371       this.$$isolateBindings = null;
17372     }
17373
17374     /**
17375      * @ngdoc property
17376      * @name $rootScope.Scope#$id
17377      *
17378      * @description
17379      * Unique scope ID (monotonically increasing) useful for debugging.
17380      */
17381
17382      /**
17383       * @ngdoc property
17384       * @name $rootScope.Scope#$parent
17385       *
17386       * @description
17387       * Reference to the parent scope.
17388       */
17389
17390       /**
17391        * @ngdoc property
17392        * @name $rootScope.Scope#$root
17393        *
17394        * @description
17395        * Reference to the root scope.
17396        */
17397
17398     Scope.prototype = {
17399       constructor: Scope,
17400       /**
17401        * @ngdoc method
17402        * @name $rootScope.Scope#$new
17403        * @kind function
17404        *
17405        * @description
17406        * Creates a new child {@link ng.$rootScope.Scope scope}.
17407        *
17408        * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
17409        * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
17410        *
17411        * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
17412        * desired for the scope and its child scopes to be permanently detached from the parent and
17413        * thus stop participating in model change detection and listener notification by invoking.
17414        *
17415        * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
17416        *         parent scope. The scope is isolated, as it can not see parent scope properties.
17417        *         When creating widgets, it is useful for the widget to not accidentally read parent
17418        *         state.
17419        *
17420        * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
17421        *                              of the newly created scope. Defaults to `this` scope if not provided.
17422        *                              This is used when creating a transclude scope to correctly place it
17423        *                              in the scope hierarchy while maintaining the correct prototypical
17424        *                              inheritance.
17425        *
17426        * @returns {Object} The newly created child scope.
17427        *
17428        */
17429       $new: function(isolate, parent) {
17430         var child;
17431
17432         parent = parent || this;
17433
17434         if (isolate) {
17435           child = new Scope();
17436           child.$root = this.$root;
17437         } else {
17438           // Only create a child scope class if somebody asks for one,
17439           // but cache it to allow the VM to optimize lookups.
17440           if (!this.$$ChildScope) {
17441             this.$$ChildScope = createChildScopeClass(this);
17442           }
17443           child = new this.$$ChildScope();
17444         }
17445         child.$parent = parent;
17446         child.$$prevSibling = parent.$$childTail;
17447         if (parent.$$childHead) {
17448           parent.$$childTail.$$nextSibling = child;
17449           parent.$$childTail = child;
17450         } else {
17451           parent.$$childHead = parent.$$childTail = child;
17452         }
17453
17454         // When the new scope is not isolated or we inherit from `this`, and
17455         // the parent scope is destroyed, the property `$$destroyed` is inherited
17456         // prototypically. In all other cases, this property needs to be set
17457         // when the parent scope is destroyed.
17458         // The listener needs to be added after the parent is set
17459         if (isolate || parent !== this) child.$on('$destroy', destroyChildScope);
17460
17461         return child;
17462       },
17463
17464       /**
17465        * @ngdoc method
17466        * @name $rootScope.Scope#$watch
17467        * @kind function
17468        *
17469        * @description
17470        * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
17471        *
17472        * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
17473        *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
17474        *   its value when executed multiple times with the same input because it may be executed multiple
17475        *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
17476        *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).)
17477        * - The `listener` is called only when the value from the current `watchExpression` and the
17478        *   previous call to `watchExpression` are not equal (with the exception of the initial run,
17479        *   see below). Inequality is determined according to reference inequality,
17480        *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
17481        *    via the `!==` Javascript operator, unless `objectEquality == true`
17482        *   (see next point)
17483        * - When `objectEquality == true`, inequality of the `watchExpression` is determined
17484        *   according to the {@link angular.equals} function. To save the value of the object for
17485        *   later comparison, the {@link angular.copy} function is used. This therefore means that
17486        *   watching complex objects will have adverse memory and performance implications.
17487        * - This should not be used to watch for changes in objects that are
17488        *   or contain [File](https://developer.mozilla.org/docs/Web/API/File) objects due to limitations with {@link angular.copy `angular.copy`}.
17489        * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
17490        *   This is achieved by rerunning the watchers until no changes are detected. The rerun
17491        *   iteration limit is 10 to prevent an infinite loop deadlock.
17492        *
17493        *
17494        * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
17495        * you can register a `watchExpression` function with no `listener`. (Be prepared for
17496        * multiple calls to your `watchExpression` because it will execute multiple times in a
17497        * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
17498        *
17499        * After a watcher is registered with the scope, the `listener` fn is called asynchronously
17500        * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
17501        * watcher. In rare cases, this is undesirable because the listener is called when the result
17502        * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
17503        * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
17504        * listener was called due to initialization.
17505        *
17506        *
17507        *
17508        * # Example
17509        * ```js
17510            // let's assume that scope was dependency injected as the $rootScope
17511            var scope = $rootScope;
17512            scope.name = 'misko';
17513            scope.counter = 0;
17514
17515            expect(scope.counter).toEqual(0);
17516            scope.$watch('name', function(newValue, oldValue) {
17517              scope.counter = scope.counter + 1;
17518            });
17519            expect(scope.counter).toEqual(0);
17520
17521            scope.$digest();
17522            // the listener is always called during the first $digest loop after it was registered
17523            expect(scope.counter).toEqual(1);
17524
17525            scope.$digest();
17526            // but now it will not be called unless the value changes
17527            expect(scope.counter).toEqual(1);
17528
17529            scope.name = 'adam';
17530            scope.$digest();
17531            expect(scope.counter).toEqual(2);
17532
17533
17534
17535            // Using a function as a watchExpression
17536            var food;
17537            scope.foodCounter = 0;
17538            expect(scope.foodCounter).toEqual(0);
17539            scope.$watch(
17540              // This function returns the value being watched. It is called for each turn of the $digest loop
17541              function() { return food; },
17542              // This is the change listener, called when the value returned from the above function changes
17543              function(newValue, oldValue) {
17544                if ( newValue !== oldValue ) {
17545                  // Only increment the counter if the value changed
17546                  scope.foodCounter = scope.foodCounter + 1;
17547                }
17548              }
17549            );
17550            // No digest has been run so the counter will be zero
17551            expect(scope.foodCounter).toEqual(0);
17552
17553            // Run the digest but since food has not changed count will still be zero
17554            scope.$digest();
17555            expect(scope.foodCounter).toEqual(0);
17556
17557            // Update food and run digest.  Now the counter will increment
17558            food = 'cheeseburger';
17559            scope.$digest();
17560            expect(scope.foodCounter).toEqual(1);
17561
17562        * ```
17563        *
17564        *
17565        *
17566        * @param {(function()|string)} watchExpression Expression that is evaluated on each
17567        *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
17568        *    a call to the `listener`.
17569        *
17570        *    - `string`: Evaluated as {@link guide/expression expression}
17571        *    - `function(scope)`: called with current `scope` as a parameter.
17572        * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
17573        *    of `watchExpression` changes.
17574        *
17575        *    - `newVal` contains the current value of the `watchExpression`
17576        *    - `oldVal` contains the previous value of the `watchExpression`
17577        *    - `scope` refers to the current scope
17578        * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
17579        *     comparing for reference equality.
17580        * @returns {function()} Returns a deregistration function for this listener.
17581        */
17582       $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
17583         var get = $parse(watchExp);
17584
17585         if (get.$$watchDelegate) {
17586           return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
17587         }
17588         var scope = this,
17589             array = scope.$$watchers,
17590             watcher = {
17591               fn: listener,
17592               last: initWatchVal,
17593               get: get,
17594               exp: prettyPrintExpression || watchExp,
17595               eq: !!objectEquality
17596             };
17597
17598         lastDirtyWatch = null;
17599
17600         if (!isFunction(listener)) {
17601           watcher.fn = noop;
17602         }
17603
17604         if (!array) {
17605           array = scope.$$watchers = [];
17606           array.$$digestWatchIndex = -1;
17607         }
17608         // we use unshift since we use a while loop in $digest for speed.
17609         // the while loop reads in reverse order.
17610         array.unshift(watcher);
17611         array.$$digestWatchIndex++;
17612         incrementWatchersCount(this, 1);
17613
17614         return function deregisterWatch() {
17615           var index = arrayRemove(array, watcher);
17616           if (index >= 0) {
17617             incrementWatchersCount(scope, -1);
17618             if (index < array.$$digestWatchIndex) {
17619               array.$$digestWatchIndex--;
17620             }
17621           }
17622           lastDirtyWatch = null;
17623         };
17624       },
17625
17626       /**
17627        * @ngdoc method
17628        * @name $rootScope.Scope#$watchGroup
17629        * @kind function
17630        *
17631        * @description
17632        * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
17633        * If any one expression in the collection changes the `listener` is executed.
17634        *
17635        * - The items in the `watchExpressions` array are observed via the standard `$watch` operation. Their return
17636        *   values are examined for changes on every call to `$digest`.
17637        * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
17638        *
17639        * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
17640        * watched using {@link ng.$rootScope.Scope#$watch $watch()}
17641        *
17642        * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
17643        *    expression in `watchExpressions` changes
17644        *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
17645        *    those of `watchExpression`
17646        *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
17647        *    those of `watchExpression`
17648        *    The `scope` refers to the current scope.
17649        * @returns {function()} Returns a de-registration function for all listeners.
17650        */
17651       $watchGroup: function(watchExpressions, listener) {
17652         var oldValues = new Array(watchExpressions.length);
17653         var newValues = new Array(watchExpressions.length);
17654         var deregisterFns = [];
17655         var self = this;
17656         var changeReactionScheduled = false;
17657         var firstRun = true;
17658
17659         if (!watchExpressions.length) {
17660           // No expressions means we call the listener ASAP
17661           var shouldCall = true;
17662           self.$evalAsync(function() {
17663             if (shouldCall) listener(newValues, newValues, self);
17664           });
17665           return function deregisterWatchGroup() {
17666             shouldCall = false;
17667           };
17668         }
17669
17670         if (watchExpressions.length === 1) {
17671           // Special case size of one
17672           return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
17673             newValues[0] = value;
17674             oldValues[0] = oldValue;
17675             listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
17676           });
17677         }
17678
17679         forEach(watchExpressions, function(expr, i) {
17680           var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
17681             newValues[i] = value;
17682             oldValues[i] = oldValue;
17683             if (!changeReactionScheduled) {
17684               changeReactionScheduled = true;
17685               self.$evalAsync(watchGroupAction);
17686             }
17687           });
17688           deregisterFns.push(unwatchFn);
17689         });
17690
17691         function watchGroupAction() {
17692           changeReactionScheduled = false;
17693
17694           if (firstRun) {
17695             firstRun = false;
17696             listener(newValues, newValues, self);
17697           } else {
17698             listener(newValues, oldValues, self);
17699           }
17700         }
17701
17702         return function deregisterWatchGroup() {
17703           while (deregisterFns.length) {
17704             deregisterFns.shift()();
17705           }
17706         };
17707       },
17708
17709
17710       /**
17711        * @ngdoc method
17712        * @name $rootScope.Scope#$watchCollection
17713        * @kind function
17714        *
17715        * @description
17716        * Shallow watches the properties of an object and fires whenever any of the properties change
17717        * (for arrays, this implies watching the array items; for object maps, this implies watching
17718        * the properties). If a change is detected, the `listener` callback is fired.
17719        *
17720        * - The `obj` collection is observed via standard $watch operation and is examined on every
17721        *   call to $digest() to see if any items have been added, removed, or moved.
17722        * - The `listener` is called whenever anything within the `obj` has changed. Examples include
17723        *   adding, removing, and moving items belonging to an object or array.
17724        *
17725        *
17726        * # Example
17727        * ```js
17728           $scope.names = ['igor', 'matias', 'misko', 'james'];
17729           $scope.dataCount = 4;
17730
17731           $scope.$watchCollection('names', function(newNames, oldNames) {
17732             $scope.dataCount = newNames.length;
17733           });
17734
17735           expect($scope.dataCount).toEqual(4);
17736           $scope.$digest();
17737
17738           //still at 4 ... no changes
17739           expect($scope.dataCount).toEqual(4);
17740
17741           $scope.names.pop();
17742           $scope.$digest();
17743
17744           //now there's been a change
17745           expect($scope.dataCount).toEqual(3);
17746        * ```
17747        *
17748        *
17749        * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
17750        *    expression value should evaluate to an object or an array which is observed on each
17751        *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
17752        *    collection will trigger a call to the `listener`.
17753        *
17754        * @param {function(newCollection, oldCollection, scope)} listener a callback function called
17755        *    when a change is detected.
17756        *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
17757        *    - The `oldCollection` object is a copy of the former collection data.
17758        *      Due to performance considerations, the`oldCollection` value is computed only if the
17759        *      `listener` function declares two or more arguments.
17760        *    - The `scope` argument refers to the current scope.
17761        *
17762        * @returns {function()} Returns a de-registration function for this listener. When the
17763        *    de-registration function is executed, the internal watch operation is terminated.
17764        */
17765       $watchCollection: function(obj, listener) {
17766         $watchCollectionInterceptor.$stateful = true;
17767
17768         var self = this;
17769         // the current value, updated on each dirty-check run
17770         var newValue;
17771         // a shallow copy of the newValue from the last dirty-check run,
17772         // updated to match newValue during dirty-check run
17773         var oldValue;
17774         // a shallow copy of the newValue from when the last change happened
17775         var veryOldValue;
17776         // only track veryOldValue if the listener is asking for it
17777         var trackVeryOldValue = (listener.length > 1);
17778         var changeDetected = 0;
17779         var changeDetector = $parse(obj, $watchCollectionInterceptor);
17780         var internalArray = [];
17781         var internalObject = {};
17782         var initRun = true;
17783         var oldLength = 0;
17784
17785         function $watchCollectionInterceptor(_value) {
17786           newValue = _value;
17787           var newLength, key, bothNaN, newItem, oldItem;
17788
17789           // If the new value is undefined, then return undefined as the watch may be a one-time watch
17790           if (isUndefined(newValue)) return;
17791
17792           if (!isObject(newValue)) { // if primitive
17793             if (oldValue !== newValue) {
17794               oldValue = newValue;
17795               changeDetected++;
17796             }
17797           } else if (isArrayLike(newValue)) {
17798             if (oldValue !== internalArray) {
17799               // we are transitioning from something which was not an array into array.
17800               oldValue = internalArray;
17801               oldLength = oldValue.length = 0;
17802               changeDetected++;
17803             }
17804
17805             newLength = newValue.length;
17806
17807             if (oldLength !== newLength) {
17808               // if lengths do not match we need to trigger change notification
17809               changeDetected++;
17810               oldValue.length = oldLength = newLength;
17811             }
17812             // copy the items to oldValue and look for changes.
17813             for (var i = 0; i < newLength; i++) {
17814               oldItem = oldValue[i];
17815               newItem = newValue[i];
17816
17817               // eslint-disable-next-line no-self-compare
17818               bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
17819               if (!bothNaN && (oldItem !== newItem)) {
17820                 changeDetected++;
17821                 oldValue[i] = newItem;
17822               }
17823             }
17824           } else {
17825             if (oldValue !== internalObject) {
17826               // we are transitioning from something which was not an object into object.
17827               oldValue = internalObject = {};
17828               oldLength = 0;
17829               changeDetected++;
17830             }
17831             // copy the items to oldValue and look for changes.
17832             newLength = 0;
17833             for (key in newValue) {
17834               if (hasOwnProperty.call(newValue, key)) {
17835                 newLength++;
17836                 newItem = newValue[key];
17837                 oldItem = oldValue[key];
17838
17839                 if (key in oldValue) {
17840                   // eslint-disable-next-line no-self-compare
17841                   bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
17842                   if (!bothNaN && (oldItem !== newItem)) {
17843                     changeDetected++;
17844                     oldValue[key] = newItem;
17845                   }
17846                 } else {
17847                   oldLength++;
17848                   oldValue[key] = newItem;
17849                   changeDetected++;
17850                 }
17851               }
17852             }
17853             if (oldLength > newLength) {
17854               // we used to have more keys, need to find them and destroy them.
17855               changeDetected++;
17856               for (key in oldValue) {
17857                 if (!hasOwnProperty.call(newValue, key)) {
17858                   oldLength--;
17859                   delete oldValue[key];
17860                 }
17861               }
17862             }
17863           }
17864           return changeDetected;
17865         }
17866
17867         function $watchCollectionAction() {
17868           if (initRun) {
17869             initRun = false;
17870             listener(newValue, newValue, self);
17871           } else {
17872             listener(newValue, veryOldValue, self);
17873           }
17874
17875           // make a copy for the next time a collection is changed
17876           if (trackVeryOldValue) {
17877             if (!isObject(newValue)) {
17878               //primitive
17879               veryOldValue = newValue;
17880             } else if (isArrayLike(newValue)) {
17881               veryOldValue = new Array(newValue.length);
17882               for (var i = 0; i < newValue.length; i++) {
17883                 veryOldValue[i] = newValue[i];
17884               }
17885             } else { // if object
17886               veryOldValue = {};
17887               for (var key in newValue) {
17888                 if (hasOwnProperty.call(newValue, key)) {
17889                   veryOldValue[key] = newValue[key];
17890                 }
17891               }
17892             }
17893           }
17894         }
17895
17896         return this.$watch(changeDetector, $watchCollectionAction);
17897       },
17898
17899       /**
17900        * @ngdoc method
17901        * @name $rootScope.Scope#$digest
17902        * @kind function
17903        *
17904        * @description
17905        * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
17906        * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
17907        * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
17908        * until no more listeners are firing. This means that it is possible to get into an infinite
17909        * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
17910        * iterations exceeds 10.
17911        *
17912        * Usually, you don't call `$digest()` directly in
17913        * {@link ng.directive:ngController controllers} or in
17914        * {@link ng.$compileProvider#directive directives}.
17915        * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
17916        * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
17917        *
17918        * If you want to be notified whenever `$digest()` is called,
17919        * you can register a `watchExpression` function with
17920        * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
17921        *
17922        * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
17923        *
17924        * # Example
17925        * ```js
17926            var scope = ...;
17927            scope.name = 'misko';
17928            scope.counter = 0;
17929
17930            expect(scope.counter).toEqual(0);
17931            scope.$watch('name', function(newValue, oldValue) {
17932              scope.counter = scope.counter + 1;
17933            });
17934            expect(scope.counter).toEqual(0);
17935
17936            scope.$digest();
17937            // the listener is always called during the first $digest loop after it was registered
17938            expect(scope.counter).toEqual(1);
17939
17940            scope.$digest();
17941            // but now it will not be called unless the value changes
17942            expect(scope.counter).toEqual(1);
17943
17944            scope.name = 'adam';
17945            scope.$digest();
17946            expect(scope.counter).toEqual(2);
17947        * ```
17948        *
17949        */
17950       $digest: function() {
17951         var watch, value, last, fn, get,
17952             watchers,
17953             dirty, ttl = TTL,
17954             next, current, target = this,
17955             watchLog = [],
17956             logIdx, asyncTask;
17957
17958         beginPhase('$digest');
17959         // Check for changes to browser url that happened in sync before the call to $digest
17960         $browser.$$checkUrlChange();
17961
17962         if (this === $rootScope && applyAsyncId !== null) {
17963           // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
17964           // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
17965           $browser.defer.cancel(applyAsyncId);
17966           flushApplyAsync();
17967         }
17968
17969         lastDirtyWatch = null;
17970
17971         do { // "while dirty" loop
17972           dirty = false;
17973           current = target;
17974
17975           // It's safe for asyncQueuePosition to be a local variable here because this loop can't
17976           // be reentered recursively. Calling $digest from a function passed to $evalAsync would
17977           // lead to a '$digest already in progress' error.
17978           for (var asyncQueuePosition = 0; asyncQueuePosition < asyncQueue.length; asyncQueuePosition++) {
17979             try {
17980               asyncTask = asyncQueue[asyncQueuePosition];
17981               fn = asyncTask.fn;
17982               fn(asyncTask.scope, asyncTask.locals);
17983             } catch (e) {
17984               $exceptionHandler(e);
17985             }
17986             lastDirtyWatch = null;
17987           }
17988           asyncQueue.length = 0;
17989
17990           traverseScopesLoop:
17991           do { // "traverse the scopes" loop
17992             if ((watchers = current.$$watchers)) {
17993               // process our watches
17994               watchers.$$digestWatchIndex = watchers.length;
17995               while (watchers.$$digestWatchIndex--) {
17996                 try {
17997                   watch = watchers[watchers.$$digestWatchIndex];
17998                   // Most common watches are on primitives, in which case we can short
17999                   // circuit it with === operator, only when === fails do we use .equals
18000                   if (watch) {
18001                     get = watch.get;
18002                     if ((value = get(current)) !== (last = watch.last) &&
18003                         !(watch.eq
18004                             ? equals(value, last)
18005                             : (isNumberNaN(value) && isNumberNaN(last)))) {
18006                       dirty = true;
18007                       lastDirtyWatch = watch;
18008                       watch.last = watch.eq ? copy(value, null) : value;
18009                       fn = watch.fn;
18010                       fn(value, ((last === initWatchVal) ? value : last), current);
18011                       if (ttl < 5) {
18012                         logIdx = 4 - ttl;
18013                         if (!watchLog[logIdx]) watchLog[logIdx] = [];
18014                         watchLog[logIdx].push({
18015                           msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
18016                           newVal: value,
18017                           oldVal: last
18018                         });
18019                       }
18020                     } else if (watch === lastDirtyWatch) {
18021                       // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
18022                       // have already been tested.
18023                       dirty = false;
18024                       break traverseScopesLoop;
18025                     }
18026                   }
18027                 } catch (e) {
18028                   $exceptionHandler(e);
18029                 }
18030               }
18031             }
18032
18033             // Insanity Warning: scope depth-first traversal
18034             // yes, this code is a bit crazy, but it works and we have tests to prove it!
18035             // this piece should be kept in sync with the traversal in $broadcast
18036             if (!(next = ((current.$$watchersCount && current.$$childHead) ||
18037                 (current !== target && current.$$nextSibling)))) {
18038               while (current !== target && !(next = current.$$nextSibling)) {
18039                 current = current.$parent;
18040               }
18041             }
18042           } while ((current = next));
18043
18044           // `break traverseScopesLoop;` takes us to here
18045
18046           if ((dirty || asyncQueue.length) && !(ttl--)) {
18047             clearPhase();
18048             throw $rootScopeMinErr('infdig',
18049                 '{0} $digest() iterations reached. Aborting!\n' +
18050                 'Watchers fired in the last 5 iterations: {1}',
18051                 TTL, watchLog);
18052           }
18053
18054         } while (dirty || asyncQueue.length);
18055
18056         clearPhase();
18057
18058         // postDigestQueuePosition isn't local here because this loop can be reentered recursively.
18059         while (postDigestQueuePosition < postDigestQueue.length) {
18060           try {
18061             postDigestQueue[postDigestQueuePosition++]();
18062           } catch (e) {
18063             $exceptionHandler(e);
18064           }
18065         }
18066         postDigestQueue.length = postDigestQueuePosition = 0;
18067
18068         // Check for changes to browser url that happened during the $digest
18069         // (for which no event is fired; e.g. via `history.pushState()`)
18070         $browser.$$checkUrlChange();
18071       },
18072
18073
18074       /**
18075        * @ngdoc event
18076        * @name $rootScope.Scope#$destroy
18077        * @eventType broadcast on scope being destroyed
18078        *
18079        * @description
18080        * Broadcasted when a scope and its children are being destroyed.
18081        *
18082        * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
18083        * clean up DOM bindings before an element is removed from the DOM.
18084        */
18085
18086       /**
18087        * @ngdoc method
18088        * @name $rootScope.Scope#$destroy
18089        * @kind function
18090        *
18091        * @description
18092        * Removes the current scope (and all of its children) from the parent scope. Removal implies
18093        * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
18094        * propagate to the current scope and its children. Removal also implies that the current
18095        * scope is eligible for garbage collection.
18096        *
18097        * The `$destroy()` is usually used by directives such as
18098        * {@link ng.directive:ngRepeat ngRepeat} for managing the
18099        * unrolling of the loop.
18100        *
18101        * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
18102        * Application code can register a `$destroy` event handler that will give it a chance to
18103        * perform any necessary cleanup.
18104        *
18105        * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
18106        * clean up DOM bindings before an element is removed from the DOM.
18107        */
18108       $destroy: function() {
18109         // We can't destroy a scope that has been already destroyed.
18110         if (this.$$destroyed) return;
18111         var parent = this.$parent;
18112
18113         this.$broadcast('$destroy');
18114         this.$$destroyed = true;
18115
18116         if (this === $rootScope) {
18117           //Remove handlers attached to window when $rootScope is removed
18118           $browser.$$applicationDestroyed();
18119         }
18120
18121         incrementWatchersCount(this, -this.$$watchersCount);
18122         for (var eventName in this.$$listenerCount) {
18123           decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
18124         }
18125
18126         // sever all the references to parent scopes (after this cleanup, the current scope should
18127         // not be retained by any of our references and should be eligible for garbage collection)
18128         if (parent && parent.$$childHead === this) parent.$$childHead = this.$$nextSibling;
18129         if (parent && parent.$$childTail === this) parent.$$childTail = this.$$prevSibling;
18130         if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
18131         if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
18132
18133         // Disable listeners, watchers and apply/digest methods
18134         this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
18135         this.$on = this.$watch = this.$watchGroup = function() { return noop; };
18136         this.$$listeners = {};
18137
18138         // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
18139         this.$$nextSibling = null;
18140         cleanUpScope(this);
18141       },
18142
18143       /**
18144        * @ngdoc method
18145        * @name $rootScope.Scope#$eval
18146        * @kind function
18147        *
18148        * @description
18149        * Executes the `expression` on the current scope and returns the result. Any exceptions in
18150        * the expression are propagated (uncaught). This is useful when evaluating Angular
18151        * expressions.
18152        *
18153        * # Example
18154        * ```js
18155            var scope = ng.$rootScope.Scope();
18156            scope.a = 1;
18157            scope.b = 2;
18158
18159            expect(scope.$eval('a+b')).toEqual(3);
18160            expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
18161        * ```
18162        *
18163        * @param {(string|function())=} expression An angular expression to be executed.
18164        *
18165        *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
18166        *    - `function(scope)`: execute the function with the current `scope` parameter.
18167        *
18168        * @param {(object)=} locals Local variables object, useful for overriding values in scope.
18169        * @returns {*} The result of evaluating the expression.
18170        */
18171       $eval: function(expr, locals) {
18172         return $parse(expr)(this, locals);
18173       },
18174
18175       /**
18176        * @ngdoc method
18177        * @name $rootScope.Scope#$evalAsync
18178        * @kind function
18179        *
18180        * @description
18181        * Executes the expression on the current scope at a later point in time.
18182        *
18183        * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
18184        * that:
18185        *
18186        *   - it will execute after the function that scheduled the evaluation (preferably before DOM
18187        *     rendering).
18188        *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
18189        *     `expression` execution.
18190        *
18191        * Any exceptions from the execution of the expression are forwarded to the
18192        * {@link ng.$exceptionHandler $exceptionHandler} service.
18193        *
18194        * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
18195        * will be scheduled. However, it is encouraged to always call code that changes the model
18196        * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
18197        *
18198        * @param {(string|function())=} expression An angular expression to be executed.
18199        *
18200        *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
18201        *    - `function(scope)`: execute the function with the current `scope` parameter.
18202        *
18203        * @param {(object)=} locals Local variables object, useful for overriding values in scope.
18204        */
18205       $evalAsync: function(expr, locals) {
18206         // if we are outside of an $digest loop and this is the first time we are scheduling async
18207         // task also schedule async auto-flush
18208         if (!$rootScope.$$phase && !asyncQueue.length) {
18209           $browser.defer(function() {
18210             if (asyncQueue.length) {
18211               $rootScope.$digest();
18212             }
18213           });
18214         }
18215
18216         asyncQueue.push({scope: this, fn: $parse(expr), locals: locals});
18217       },
18218
18219       $$postDigest: function(fn) {
18220         postDigestQueue.push(fn);
18221       },
18222
18223       /**
18224        * @ngdoc method
18225        * @name $rootScope.Scope#$apply
18226        * @kind function
18227        *
18228        * @description
18229        * `$apply()` is used to execute an expression in angular from outside of the angular
18230        * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
18231        * Because we are calling into the angular framework we need to perform proper scope life
18232        * cycle of {@link ng.$exceptionHandler exception handling},
18233        * {@link ng.$rootScope.Scope#$digest executing watches}.
18234        *
18235        * ## Life cycle
18236        *
18237        * # Pseudo-Code of `$apply()`
18238        * ```js
18239            function $apply(expr) {
18240              try {
18241                return $eval(expr);
18242              } catch (e) {
18243                $exceptionHandler(e);
18244              } finally {
18245                $root.$digest();
18246              }
18247            }
18248        * ```
18249        *
18250        *
18251        * Scope's `$apply()` method transitions through the following stages:
18252        *
18253        * 1. The {@link guide/expression expression} is executed using the
18254        *    {@link ng.$rootScope.Scope#$eval $eval()} method.
18255        * 2. Any exceptions from the execution of the expression are forwarded to the
18256        *    {@link ng.$exceptionHandler $exceptionHandler} service.
18257        * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
18258        *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
18259        *
18260        *
18261        * @param {(string|function())=} exp An angular expression to be executed.
18262        *
18263        *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
18264        *    - `function(scope)`: execute the function with current `scope` parameter.
18265        *
18266        * @returns {*} The result of evaluating the expression.
18267        */
18268       $apply: function(expr) {
18269         try {
18270           beginPhase('$apply');
18271           try {
18272             return this.$eval(expr);
18273           } finally {
18274             clearPhase();
18275           }
18276         } catch (e) {
18277           $exceptionHandler(e);
18278         } finally {
18279           try {
18280             $rootScope.$digest();
18281           } catch (e) {
18282             $exceptionHandler(e);
18283             // eslint-disable-next-line no-unsafe-finally
18284             throw e;
18285           }
18286         }
18287       },
18288
18289       /**
18290        * @ngdoc method
18291        * @name $rootScope.Scope#$applyAsync
18292        * @kind function
18293        *
18294        * @description
18295        * Schedule the invocation of $apply to occur at a later time. The actual time difference
18296        * varies across browsers, but is typically around ~10 milliseconds.
18297        *
18298        * This can be used to queue up multiple expressions which need to be evaluated in the same
18299        * digest.
18300        *
18301        * @param {(string|function())=} exp An angular expression to be executed.
18302        *
18303        *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
18304        *    - `function(scope)`: execute the function with current `scope` parameter.
18305        */
18306       $applyAsync: function(expr) {
18307         var scope = this;
18308         if (expr) {
18309           applyAsyncQueue.push($applyAsyncExpression);
18310         }
18311         expr = $parse(expr);
18312         scheduleApplyAsync();
18313
18314         function $applyAsyncExpression() {
18315           scope.$eval(expr);
18316         }
18317       },
18318
18319       /**
18320        * @ngdoc method
18321        * @name $rootScope.Scope#$on
18322        * @kind function
18323        *
18324        * @description
18325        * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
18326        * discussion of event life cycle.
18327        *
18328        * The event listener function format is: `function(event, args...)`. The `event` object
18329        * passed into the listener has the following attributes:
18330        *
18331        *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
18332        *     `$broadcast`-ed.
18333        *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
18334        *     event propagates through the scope hierarchy, this property is set to null.
18335        *   - `name` - `{string}`: name of the event.
18336        *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
18337        *     further event propagation (available only for events that were `$emit`-ed).
18338        *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
18339        *     to true.
18340        *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
18341        *
18342        * @param {string} name Event name to listen on.
18343        * @param {function(event, ...args)} listener Function to call when the event is emitted.
18344        * @returns {function()} Returns a deregistration function for this listener.
18345        */
18346       $on: function(name, listener) {
18347         var namedListeners = this.$$listeners[name];
18348         if (!namedListeners) {
18349           this.$$listeners[name] = namedListeners = [];
18350         }
18351         namedListeners.push(listener);
18352
18353         var current = this;
18354         do {
18355           if (!current.$$listenerCount[name]) {
18356             current.$$listenerCount[name] = 0;
18357           }
18358           current.$$listenerCount[name]++;
18359         } while ((current = current.$parent));
18360
18361         var self = this;
18362         return function() {
18363           var indexOfListener = namedListeners.indexOf(listener);
18364           if (indexOfListener !== -1) {
18365             namedListeners[indexOfListener] = null;
18366             decrementListenerCount(self, 1, name);
18367           }
18368         };
18369       },
18370
18371
18372       /**
18373        * @ngdoc method
18374        * @name $rootScope.Scope#$emit
18375        * @kind function
18376        *
18377        * @description
18378        * Dispatches an event `name` upwards through the scope hierarchy notifying the
18379        * registered {@link ng.$rootScope.Scope#$on} listeners.
18380        *
18381        * The event life cycle starts at the scope on which `$emit` was called. All
18382        * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
18383        * notified. Afterwards, the event traverses upwards toward the root scope and calls all
18384        * registered listeners along the way. The event will stop propagating if one of the listeners
18385        * cancels it.
18386        *
18387        * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
18388        * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
18389        *
18390        * @param {string} name Event name to emit.
18391        * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
18392        * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
18393        */
18394       $emit: function(name, args) {
18395         var empty = [],
18396             namedListeners,
18397             scope = this,
18398             stopPropagation = false,
18399             event = {
18400               name: name,
18401               targetScope: scope,
18402               stopPropagation: function() {stopPropagation = true;},
18403               preventDefault: function() {
18404                 event.defaultPrevented = true;
18405               },
18406               defaultPrevented: false
18407             },
18408             listenerArgs = concat([event], arguments, 1),
18409             i, length;
18410
18411         do {
18412           namedListeners = scope.$$listeners[name] || empty;
18413           event.currentScope = scope;
18414           for (i = 0, length = namedListeners.length; i < length; i++) {
18415
18416             // if listeners were deregistered, defragment the array
18417             if (!namedListeners[i]) {
18418               namedListeners.splice(i, 1);
18419               i--;
18420               length--;
18421               continue;
18422             }
18423             try {
18424               //allow all listeners attached to the current scope to run
18425               namedListeners[i].apply(null, listenerArgs);
18426             } catch (e) {
18427               $exceptionHandler(e);
18428             }
18429           }
18430           //if any listener on the current scope stops propagation, prevent bubbling
18431           if (stopPropagation) {
18432             event.currentScope = null;
18433             return event;
18434           }
18435           //traverse upwards
18436           scope = scope.$parent;
18437         } while (scope);
18438
18439         event.currentScope = null;
18440
18441         return event;
18442       },
18443
18444
18445       /**
18446        * @ngdoc method
18447        * @name $rootScope.Scope#$broadcast
18448        * @kind function
18449        *
18450        * @description
18451        * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
18452        * registered {@link ng.$rootScope.Scope#$on} listeners.
18453        *
18454        * The event life cycle starts at the scope on which `$broadcast` was called. All
18455        * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
18456        * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
18457        * scope and calls all registered listeners along the way. The event cannot be canceled.
18458        *
18459        * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
18460        * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
18461        *
18462        * @param {string} name Event name to broadcast.
18463        * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
18464        * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
18465        */
18466       $broadcast: function(name, args) {
18467         var target = this,
18468             current = target,
18469             next = target,
18470             event = {
18471               name: name,
18472               targetScope: target,
18473               preventDefault: function() {
18474                 event.defaultPrevented = true;
18475               },
18476               defaultPrevented: false
18477             };
18478
18479         if (!target.$$listenerCount[name]) return event;
18480
18481         var listenerArgs = concat([event], arguments, 1),
18482             listeners, i, length;
18483
18484         //down while you can, then up and next sibling or up and next sibling until back at root
18485         while ((current = next)) {
18486           event.currentScope = current;
18487           listeners = current.$$listeners[name] || [];
18488           for (i = 0, length = listeners.length; i < length; i++) {
18489             // if listeners were deregistered, defragment the array
18490             if (!listeners[i]) {
18491               listeners.splice(i, 1);
18492               i--;
18493               length--;
18494               continue;
18495             }
18496
18497             try {
18498               listeners[i].apply(null, listenerArgs);
18499             } catch (e) {
18500               $exceptionHandler(e);
18501             }
18502           }
18503
18504           // Insanity Warning: scope depth-first traversal
18505           // yes, this code is a bit crazy, but it works and we have tests to prove it!
18506           // this piece should be kept in sync with the traversal in $digest
18507           // (though it differs due to having the extra check for $$listenerCount)
18508           if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
18509               (current !== target && current.$$nextSibling)))) {
18510             while (current !== target && !(next = current.$$nextSibling)) {
18511               current = current.$parent;
18512             }
18513           }
18514         }
18515
18516         event.currentScope = null;
18517         return event;
18518       }
18519     };
18520
18521     var $rootScope = new Scope();
18522
18523     //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
18524     var asyncQueue = $rootScope.$$asyncQueue = [];
18525     var postDigestQueue = $rootScope.$$postDigestQueue = [];
18526     var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
18527
18528     var postDigestQueuePosition = 0;
18529
18530     return $rootScope;
18531
18532
18533     function beginPhase(phase) {
18534       if ($rootScope.$$phase) {
18535         throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
18536       }
18537
18538       $rootScope.$$phase = phase;
18539     }
18540
18541     function clearPhase() {
18542       $rootScope.$$phase = null;
18543     }
18544
18545     function incrementWatchersCount(current, count) {
18546       do {
18547         current.$$watchersCount += count;
18548       } while ((current = current.$parent));
18549     }
18550
18551     function decrementListenerCount(current, count, name) {
18552       do {
18553         current.$$listenerCount[name] -= count;
18554
18555         if (current.$$listenerCount[name] === 0) {
18556           delete current.$$listenerCount[name];
18557         }
18558       } while ((current = current.$parent));
18559     }
18560
18561     /**
18562      * function used as an initial value for watchers.
18563      * because it's unique we can easily tell it apart from other values
18564      */
18565     function initWatchVal() {}
18566
18567     function flushApplyAsync() {
18568       while (applyAsyncQueue.length) {
18569         try {
18570           applyAsyncQueue.shift()();
18571         } catch (e) {
18572           $exceptionHandler(e);
18573         }
18574       }
18575       applyAsyncId = null;
18576     }
18577
18578     function scheduleApplyAsync() {
18579       if (applyAsyncId === null) {
18580         applyAsyncId = $browser.defer(function() {
18581           $rootScope.$apply(flushApplyAsync);
18582         });
18583       }
18584     }
18585   }];
18586 }
18587
18588 /**
18589  * @ngdoc service
18590  * @name $rootElement
18591  *
18592  * @description
18593  * The root element of Angular application. This is either the element where {@link
18594  * ng.directive:ngApp ngApp} was declared or the element passed into
18595  * {@link angular.bootstrap}. The element represents the root element of application. It is also the
18596  * location where the application's {@link auto.$injector $injector} service gets
18597  * published, and can be retrieved using `$rootElement.injector()`.
18598  */
18599
18600
18601 // the implementation is in angular.bootstrap
18602
18603 /**
18604  * @this
18605  * @description
18606  * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
18607  */
18608 function $$SanitizeUriProvider() {
18609   var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
18610     imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
18611
18612   /**
18613    * @description
18614    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
18615    * urls during a[href] sanitization.
18616    *
18617    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
18618    *
18619    * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
18620    * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
18621    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
18622    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
18623    *
18624    * @param {RegExp=} regexp New regexp to whitelist urls with.
18625    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
18626    *    chaining otherwise.
18627    */
18628   this.aHrefSanitizationWhitelist = function(regexp) {
18629     if (isDefined(regexp)) {
18630       aHrefSanitizationWhitelist = regexp;
18631       return this;
18632     }
18633     return aHrefSanitizationWhitelist;
18634   };
18635
18636
18637   /**
18638    * @description
18639    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
18640    * urls during img[src] sanitization.
18641    *
18642    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
18643    *
18644    * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
18645    * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
18646    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
18647    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
18648    *
18649    * @param {RegExp=} regexp New regexp to whitelist urls with.
18650    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
18651    *    chaining otherwise.
18652    */
18653   this.imgSrcSanitizationWhitelist = function(regexp) {
18654     if (isDefined(regexp)) {
18655       imgSrcSanitizationWhitelist = regexp;
18656       return this;
18657     }
18658     return imgSrcSanitizationWhitelist;
18659   };
18660
18661   this.$get = function() {
18662     return function sanitizeUri(uri, isImage) {
18663       var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
18664       var normalizedVal;
18665       normalizedVal = urlResolve(uri).href;
18666       if (normalizedVal !== '' && !normalizedVal.match(regex)) {
18667         return 'unsafe:' + normalizedVal;
18668       }
18669       return uri;
18670     };
18671   };
18672 }
18673
18674 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18675  *     Any commits to this file should be reviewed with security in mind.  *
18676  *   Changes to this file can potentially create security vulnerabilities. *
18677  *          An approval from 2 Core members with history of modifying      *
18678  *                         this file is required.                          *
18679  *                                                                         *
18680  *  Does the change somehow allow for arbitrary javascript to be executed? *
18681  *    Or allows for someone to change the prototype of built-in objects?   *
18682  *     Or gives undesired access to variables likes document or window?    *
18683  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
18684
18685 /* exported $SceProvider, $SceDelegateProvider */
18686
18687 var $sceMinErr = minErr('$sce');
18688
18689 var SCE_CONTEXTS = {
18690   HTML: 'html',
18691   CSS: 'css',
18692   URL: 'url',
18693   // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
18694   // url.  (e.g. ng-include, script src, templateUrl)
18695   RESOURCE_URL: 'resourceUrl',
18696   JS: 'js'
18697 };
18698
18699 // Helper functions follow.
18700
18701 var UNDERSCORE_LOWERCASE_REGEXP = /_([a-z])/g;
18702
18703 function snakeToCamel(name) {
18704   return name
18705     .replace(UNDERSCORE_LOWERCASE_REGEXP, fnCamelCaseReplace);
18706 }
18707
18708 function adjustMatcher(matcher) {
18709   if (matcher === 'self') {
18710     return matcher;
18711   } else if (isString(matcher)) {
18712     // Strings match exactly except for 2 wildcards - '*' and '**'.
18713     // '*' matches any character except those from the set ':/.?&'.
18714     // '**' matches any character (like .* in a RegExp).
18715     // More than 2 *'s raises an error as it's ill defined.
18716     if (matcher.indexOf('***') > -1) {
18717       throw $sceMinErr('iwcard',
18718           'Illegal sequence *** in string matcher.  String: {0}', matcher);
18719     }
18720     matcher = escapeForRegexp(matcher).
18721                   replace(/\\\*\\\*/g, '.*').
18722                   replace(/\\\*/g, '[^:/.?&;]*');
18723     return new RegExp('^' + matcher + '$');
18724   } else if (isRegExp(matcher)) {
18725     // The only other type of matcher allowed is a Regexp.
18726     // Match entire URL / disallow partial matches.
18727     // Flags are reset (i.e. no global, ignoreCase or multiline)
18728     return new RegExp('^' + matcher.source + '$');
18729   } else {
18730     throw $sceMinErr('imatcher',
18731         'Matchers may only be "self", string patterns or RegExp objects');
18732   }
18733 }
18734
18735
18736 function adjustMatchers(matchers) {
18737   var adjustedMatchers = [];
18738   if (isDefined(matchers)) {
18739     forEach(matchers, function(matcher) {
18740       adjustedMatchers.push(adjustMatcher(matcher));
18741     });
18742   }
18743   return adjustedMatchers;
18744 }
18745
18746
18747 /**
18748  * @ngdoc service
18749  * @name $sceDelegate
18750  * @kind function
18751  *
18752  * @description
18753  *
18754  * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
18755  * Contextual Escaping (SCE)} services to AngularJS.
18756  *
18757  * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
18758  * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
18759  * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
18760  * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
18761  * work because `$sce` delegates to `$sceDelegate` for these operations.
18762  *
18763  * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
18764  *
18765  * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
18766  * can override it completely to change the behavior of `$sce`, the common case would
18767  * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
18768  * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
18769  * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
18770  * $sceDelegateProvider.resourceUrlWhitelist} and {@link
18771  * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
18772  */
18773
18774 /**
18775  * @ngdoc provider
18776  * @name $sceDelegateProvider
18777  * @this
18778  *
18779  * @description
18780  *
18781  * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
18782  * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
18783  * that the URLs used for sourcing Angular templates are safe.  Refer {@link
18784  * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
18785  * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
18786  *
18787  * For the general details about this service in Angular, read the main page for {@link ng.$sce
18788  * Strict Contextual Escaping (SCE)}.
18789  *
18790  * **Example**:  Consider the following case. <a name="example"></a>
18791  *
18792  * - your app is hosted at url `http://myapp.example.com/`
18793  * - but some of your templates are hosted on other domains you control such as
18794  *   `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
18795  * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
18796  *
18797  * Here is what a secure configuration for this scenario might look like:
18798  *
18799  * ```
18800  *  angular.module('myApp', []).config(function($sceDelegateProvider) {
18801  *    $sceDelegateProvider.resourceUrlWhitelist([
18802  *      // Allow same origin resource loads.
18803  *      'self',
18804  *      // Allow loading from our assets domain.  Notice the difference between * and **.
18805  *      'http://srv*.assets.example.com/**'
18806  *    ]);
18807  *
18808  *    // The blacklist overrides the whitelist so the open redirect here is blocked.
18809  *    $sceDelegateProvider.resourceUrlBlacklist([
18810  *      'http://myapp.example.com/clickThru**'
18811  *    ]);
18812  *  });
18813  * ```
18814  */
18815
18816 function $SceDelegateProvider() {
18817   this.SCE_CONTEXTS = SCE_CONTEXTS;
18818
18819   // Resource URLs can also be trusted by policy.
18820   var resourceUrlWhitelist = ['self'],
18821       resourceUrlBlacklist = [];
18822
18823   /**
18824    * @ngdoc method
18825    * @name $sceDelegateProvider#resourceUrlWhitelist
18826    * @kind function
18827    *
18828    * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
18829    *    provided.  This must be an array or null.  A snapshot of this array is used so further
18830    *    changes to the array are ignored.
18831    *
18832    *    Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
18833    *    allowed in this array.
18834    *
18835    *    <div class="alert alert-warning">
18836    *    **Note:** an empty whitelist array will block all URLs!
18837    *    </div>
18838    *
18839    * @return {Array} the currently set whitelist array.
18840    *
18841    * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
18842    * same origin resource requests.
18843    *
18844    * @description
18845    * Sets/Gets the whitelist of trusted resource URLs.
18846    */
18847   this.resourceUrlWhitelist = function(value) {
18848     if (arguments.length) {
18849       resourceUrlWhitelist = adjustMatchers(value);
18850     }
18851     return resourceUrlWhitelist;
18852   };
18853
18854   /**
18855    * @ngdoc method
18856    * @name $sceDelegateProvider#resourceUrlBlacklist
18857    * @kind function
18858    *
18859    * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
18860    *    provided.  This must be an array or null.  A snapshot of this array is used so further
18861    *    changes to the array are ignored.
18862    *
18863    *    Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
18864    *    allowed in this array.
18865    *
18866    *    The typical usage for the blacklist is to **block
18867    *    [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
18868    *    these would otherwise be trusted but actually return content from the redirected domain.
18869    *
18870    *    Finally, **the blacklist overrides the whitelist** and has the final say.
18871    *
18872    * @return {Array} the currently set blacklist array.
18873    *
18874    * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
18875    * is no blacklist.)
18876    *
18877    * @description
18878    * Sets/Gets the blacklist of trusted resource URLs.
18879    */
18880
18881   this.resourceUrlBlacklist = function(value) {
18882     if (arguments.length) {
18883       resourceUrlBlacklist = adjustMatchers(value);
18884     }
18885     return resourceUrlBlacklist;
18886   };
18887
18888   this.$get = ['$injector', function($injector) {
18889
18890     var htmlSanitizer = function htmlSanitizer(html) {
18891       throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
18892     };
18893
18894     if ($injector.has('$sanitize')) {
18895       htmlSanitizer = $injector.get('$sanitize');
18896     }
18897
18898
18899     function matchUrl(matcher, parsedUrl) {
18900       if (matcher === 'self') {
18901         return urlIsSameOrigin(parsedUrl);
18902       } else {
18903         // definitely a regex.  See adjustMatchers()
18904         return !!matcher.exec(parsedUrl.href);
18905       }
18906     }
18907
18908     function isResourceUrlAllowedByPolicy(url) {
18909       var parsedUrl = urlResolve(url.toString());
18910       var i, n, allowed = false;
18911       // Ensure that at least one item from the whitelist allows this url.
18912       for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
18913         if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
18914           allowed = true;
18915           break;
18916         }
18917       }
18918       if (allowed) {
18919         // Ensure that no item from the blacklist blocked this url.
18920         for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
18921           if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
18922             allowed = false;
18923             break;
18924           }
18925         }
18926       }
18927       return allowed;
18928     }
18929
18930     function generateHolderType(Base) {
18931       var holderType = function TrustedValueHolderType(trustedValue) {
18932         this.$$unwrapTrustedValue = function() {
18933           return trustedValue;
18934         };
18935       };
18936       if (Base) {
18937         holderType.prototype = new Base();
18938       }
18939       holderType.prototype.valueOf = function sceValueOf() {
18940         return this.$$unwrapTrustedValue();
18941       };
18942       holderType.prototype.toString = function sceToString() {
18943         return this.$$unwrapTrustedValue().toString();
18944       };
18945       return holderType;
18946     }
18947
18948     var trustedValueHolderBase = generateHolderType(),
18949         byType = {};
18950
18951     byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
18952     byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
18953     byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
18954     byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
18955     byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
18956
18957     /**
18958      * @ngdoc method
18959      * @name $sceDelegate#trustAs
18960      *
18961      * @description
18962      * Returns an object that is trusted by angular for use in specified strict
18963      * contextual escaping contexts (such as ng-bind-html, ng-include, any src
18964      * attribute interpolation, any dom event binding attribute interpolation
18965      * such as for onclick,  etc.) that uses the provided value.
18966      * See {@link ng.$sce $sce} for enabling strict contextual escaping.
18967      *
18968      * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
18969      *   resourceUrl, html, js and css.
18970      * @param {*} value The value that that should be considered trusted/safe.
18971      * @returns {*} A value that can be used to stand in for the provided `value` in places
18972      * where Angular expects a $sce.trustAs() return value.
18973      */
18974     function trustAs(type, trustedValue) {
18975       var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
18976       if (!Constructor) {
18977         throw $sceMinErr('icontext',
18978             'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
18979             type, trustedValue);
18980       }
18981       if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
18982         return trustedValue;
18983       }
18984       // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
18985       // mutable objects, we ensure here that the value passed in is actually a string.
18986       if (typeof trustedValue !== 'string') {
18987         throw $sceMinErr('itype',
18988             'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
18989             type);
18990       }
18991       return new Constructor(trustedValue);
18992     }
18993
18994     /**
18995      * @ngdoc method
18996      * @name $sceDelegate#valueOf
18997      *
18998      * @description
18999      * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
19000      * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
19001      * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
19002      *
19003      * If the passed parameter is not a value that had been returned by {@link
19004      * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
19005      *
19006      * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
19007      *      call or anything else.
19008      * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
19009      *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
19010      *     `value` unchanged.
19011      */
19012     function valueOf(maybeTrusted) {
19013       if (maybeTrusted instanceof trustedValueHolderBase) {
19014         return maybeTrusted.$$unwrapTrustedValue();
19015       } else {
19016         return maybeTrusted;
19017       }
19018     }
19019
19020     /**
19021      * @ngdoc method
19022      * @name $sceDelegate#getTrusted
19023      *
19024      * @description
19025      * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
19026      * returns the originally supplied value if the queried context type is a supertype of the
19027      * created type.  If this condition isn't satisfied, throws an exception.
19028      *
19029      * <div class="alert alert-danger">
19030      * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
19031      * (XSS) vulnerability in your application.
19032      * </div>
19033      *
19034      * @param {string} type The kind of context in which this value is to be used.
19035      * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
19036      *     `$sceDelegate.trustAs`} call.
19037      * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
19038      *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
19039      */
19040     function getTrusted(type, maybeTrusted) {
19041       if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
19042         return maybeTrusted;
19043       }
19044       var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
19045       if (constructor && maybeTrusted instanceof constructor) {
19046         return maybeTrusted.$$unwrapTrustedValue();
19047       }
19048       // If we get here, then we may only take one of two actions.
19049       // 1. sanitize the value for the requested type, or
19050       // 2. throw an exception.
19051       if (type === SCE_CONTEXTS.RESOURCE_URL) {
19052         if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
19053           return maybeTrusted;
19054         } else {
19055           throw $sceMinErr('insecurl',
19056               'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
19057               maybeTrusted.toString());
19058         }
19059       } else if (type === SCE_CONTEXTS.HTML) {
19060         return htmlSanitizer(maybeTrusted);
19061       }
19062       throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
19063     }
19064
19065     return { trustAs: trustAs,
19066              getTrusted: getTrusted,
19067              valueOf: valueOf };
19068   }];
19069 }
19070
19071
19072 /**
19073  * @ngdoc provider
19074  * @name $sceProvider
19075  * @this
19076  *
19077  * @description
19078  *
19079  * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
19080  * -   enable/disable Strict Contextual Escaping (SCE) in a module
19081  * -   override the default implementation with a custom delegate
19082  *
19083  * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
19084  */
19085
19086 /**
19087  * @ngdoc service
19088  * @name $sce
19089  * @kind function
19090  *
19091  * @description
19092  *
19093  * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
19094  *
19095  * # Strict Contextual Escaping
19096  *
19097  * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
19098  * contexts to result in a value that is marked as safe to use for that context.  One example of
19099  * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
19100  * to these contexts as privileged or SCE contexts.
19101  *
19102  * As of version 1.2, Angular ships with SCE enabled by default.
19103  *
19104  * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
19105  * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
19106  * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
19107  * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
19108  * to the top of your HTML document.
19109  *
19110  * SCE assists in writing code in a way that (a) is secure by default and (b) makes auditing for
19111  * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
19112  *
19113  * Here's an example of a binding in a privileged context:
19114  *
19115  * ```
19116  * <input ng-model="userHtml" aria-label="User input">
19117  * <div ng-bind-html="userHtml"></div>
19118  * ```
19119  *
19120  * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
19121  * disabled, this application allows the user to render arbitrary HTML into the DIV.
19122  * In a more realistic example, one may be rendering user comments, blog articles, etc. via
19123  * bindings.  (HTML is just one example of a context where rendering user controlled input creates
19124  * security vulnerabilities.)
19125  *
19126  * For the case of HTML, you might use a library, either on the client side, or on the server side,
19127  * to sanitize unsafe HTML before binding to the value and rendering it in the document.
19128  *
19129  * How would you ensure that every place that used these types of bindings was bound to a value that
19130  * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
19131  * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
19132  * properties/fields and forgot to update the binding to the sanitized value?
19133  *
19134  * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
19135  * determine that something explicitly says it's safe to use a value for binding in that
19136  * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
19137  * for those values that you can easily tell are safe - because they were received from your server,
19138  * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
19139  * allowing only the files in a specific directory to do this.  Ensuring that the internal API
19140  * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
19141  *
19142  * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
19143  * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
19144  * obtain values that will be accepted by SCE / privileged contexts.
19145  *
19146  *
19147  * ## How does it work?
19148  *
19149  * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
19150  * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
19151  * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
19152  * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
19153  *
19154  * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
19155  * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
19156  * simplified):
19157  *
19158  * ```
19159  * var ngBindHtmlDirective = ['$sce', function($sce) {
19160  *   return function(scope, element, attr) {
19161  *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
19162  *       element.html(value || '');
19163  *     });
19164  *   };
19165  * }];
19166  * ```
19167  *
19168  * ## Impact on loading templates
19169  *
19170  * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
19171  * `templateUrl`'s specified by {@link guide/directive directives}.
19172  *
19173  * By default, Angular only loads templates from the same domain and protocol as the application
19174  * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
19175  * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
19176  * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
19177  * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
19178  *
19179  * *Please note*:
19180  * The browser's
19181  * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
19182  * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
19183  * policy apply in addition to this and may further restrict whether the template is successfully
19184  * loaded.  This means that without the right CORS policy, loading templates from a different domain
19185  * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
19186  * browsers.
19187  *
19188  * ## This feels like too much overhead
19189  *
19190  * It's important to remember that SCE only applies to interpolation expressions.
19191  *
19192  * If your expressions are constant literals, they're automatically trusted and you don't need to
19193  * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
19194  * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
19195  *
19196  * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
19197  * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
19198  *
19199  * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
19200  * templates in `ng-include` from your application's domain without having to even know about SCE.
19201  * It blocks loading templates from other domains or loading templates over http from an https
19202  * served document.  You can change these by setting your own custom {@link
19203  * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
19204  * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
19205  *
19206  * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
19207  * application that's secure and can be audited to verify that with much more ease than bolting
19208  * security onto an application later.
19209  *
19210  * <a name="contexts"></a>
19211  * ## What trusted context types are supported?
19212  *
19213  * | Context             | Notes          |
19214  * |---------------------|----------------|
19215  * | `$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. |
19216  * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
19217  * | `$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. |
19218  * | `$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`, `VIDEO`, `AUDIO`, `SOURCE`, and `TRACK` (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. |
19219  * | `$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. |
19220  *
19221  * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
19222  *
19223  *  Each element in these arrays must be one of the following:
19224  *
19225  *  - **'self'**
19226  *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
19227  *      domain** as the application document using the **same protocol**.
19228  *  - **String** (except the special value `'self'`)
19229  *    - The string is matched against the full *normalized / absolute URL* of the resource
19230  *      being tested (substring matches are not good enough.)
19231  *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
19232  *      match themselves.
19233  *    - `*`: matches zero or more occurrences of any character other than one of the following 6
19234  *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
19235  *      in a whitelist.
19236  *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
19237  *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
19238  *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
19239  *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
19240  *      http://foo.example.com/templates/**).
19241  *  - **RegExp** (*see caveat below*)
19242  *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
19243  *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
19244  *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
19245  *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
19246  *      small number of cases.  A `.` character in the regex used when matching the scheme or a
19247  *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
19248  *      is highly recommended to use the string patterns and only fall back to regular expressions
19249  *      as a last resort.
19250  *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
19251  *      matched against the **entire** *normalized / absolute URL* of the resource being tested
19252  *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
19253  *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
19254  *    - If you are generating your JavaScript from some other templating engine (not
19255  *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
19256  *      remember to escape your regular expression (and be aware that you might need more than
19257  *      one level of escaping depending on your templating engine and the way you interpolated
19258  *      the value.)  Do make use of your platform's escaping mechanism as it might be good
19259  *      enough before coding your own.  E.g. Ruby has
19260  *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
19261  *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
19262  *      Javascript lacks a similar built in function for escaping.  Take a look at Google
19263  *      Closure library's [goog.string.regExpEscape(s)](
19264  *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
19265  *
19266  * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
19267  *
19268  * ## Show me an example using SCE.
19269  *
19270  * <example module="mySceApp" deps="angular-sanitize.js" name="sce-service">
19271  * <file name="index.html">
19272  *   <div ng-controller="AppController as myCtrl">
19273  *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
19274  *     <b>User comments</b><br>
19275  *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
19276  *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
19277  *     exploit.
19278  *     <div class="well">
19279  *       <div ng-repeat="userComment in myCtrl.userComments">
19280  *         <b>{{userComment.name}}</b>:
19281  *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
19282  *         <br>
19283  *       </div>
19284  *     </div>
19285  *   </div>
19286  * </file>
19287  *
19288  * <file name="script.js">
19289  *   angular.module('mySceApp', ['ngSanitize'])
19290  *     .controller('AppController', ['$http', '$templateCache', '$sce',
19291  *       function AppController($http, $templateCache, $sce) {
19292  *         var self = this;
19293  *         $http.get('test_data.json', {cache: $templateCache}).then(function(response) {
19294  *           self.userComments = response.data;
19295  *         });
19296  *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
19297  *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
19298  *             'sanitization.&quot;">Hover over this text.</span>');
19299  *       }]);
19300  * </file>
19301  *
19302  * <file name="test_data.json">
19303  * [
19304  *   { "name": "Alice",
19305  *     "htmlComment":
19306  *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
19307  *   },
19308  *   { "name": "Bob",
19309  *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
19310  *   }
19311  * ]
19312  * </file>
19313  *
19314  * <file name="protractor.js" type="protractor">
19315  *   describe('SCE doc demo', function() {
19316  *     it('should sanitize untrusted values', function() {
19317  *       expect(element.all(by.css('.htmlComment')).first().getAttribute('innerHTML'))
19318  *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
19319  *     });
19320  *
19321  *     it('should NOT sanitize explicitly trusted values', function() {
19322  *       expect(element(by.id('explicitlyTrustedHtml')).getAttribute('innerHTML')).toBe(
19323  *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
19324  *           'sanitization.&quot;">Hover over this text.</span>');
19325  *     });
19326  *   });
19327  * </file>
19328  * </example>
19329  *
19330  *
19331  *
19332  * ## Can I disable SCE completely?
19333  *
19334  * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
19335  * for little coding overhead.  It will be much harder to take an SCE disabled application and
19336  * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
19337  * for cases where you have a lot of existing code that was written before SCE was introduced and
19338  * you're migrating them a module at a time.
19339  *
19340  * That said, here's how you can completely disable SCE:
19341  *
19342  * ```
19343  * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
19344  *   // Completely disable SCE.  For demonstration purposes only!
19345  *   // Do not use in new projects.
19346  *   $sceProvider.enabled(false);
19347  * });
19348  * ```
19349  *
19350  */
19351
19352 function $SceProvider() {
19353   var enabled = true;
19354
19355   /**
19356    * @ngdoc method
19357    * @name $sceProvider#enabled
19358    * @kind function
19359    *
19360    * @param {boolean=} value If provided, then enables/disables SCE.
19361    * @return {boolean} true if SCE is enabled, false otherwise.
19362    *
19363    * @description
19364    * Enables/disables SCE and returns the current value.
19365    */
19366   this.enabled = function(value) {
19367     if (arguments.length) {
19368       enabled = !!value;
19369     }
19370     return enabled;
19371   };
19372
19373
19374   /* Design notes on the default implementation for SCE.
19375    *
19376    * The API contract for the SCE delegate
19377    * -------------------------------------
19378    * The SCE delegate object must provide the following 3 methods:
19379    *
19380    * - trustAs(contextEnum, value)
19381    *     This method is used to tell the SCE service that the provided value is OK to use in the
19382    *     contexts specified by contextEnum.  It must return an object that will be accepted by
19383    *     getTrusted() for a compatible contextEnum and return this value.
19384    *
19385    * - valueOf(value)
19386    *     For values that were not produced by trustAs(), return them as is.  For values that were
19387    *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
19388    *     trustAs is wrapping the given values into some type, this operation unwraps it when given
19389    *     such a value.
19390    *
19391    * - getTrusted(contextEnum, value)
19392    *     This function should return the a value that is safe to use in the context specified by
19393    *     contextEnum or throw and exception otherwise.
19394    *
19395    * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
19396    * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
19397    * instance, an implementation could maintain a registry of all trusted objects by context.  In
19398    * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
19399    * return the same object passed in if it was found in the registry under a compatible context or
19400    * throw an exception otherwise.  An implementation might only wrap values some of the time based
19401    * on some criteria.  getTrusted() might return a value and not throw an exception for special
19402    * constants or objects even if not wrapped.  All such implementations fulfill this contract.
19403    *
19404    *
19405    * A note on the inheritance model for SCE contexts
19406    * ------------------------------------------------
19407    * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
19408    * is purely an implementation details.
19409    *
19410    * The contract is simply this:
19411    *
19412    *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
19413    *     will also succeed.
19414    *
19415    * Inheritance happens to capture this in a natural way.  In some future, we
19416    * may not use inheritance anymore.  That is OK because no code outside of
19417    * sce.js and sceSpecs.js would need to be aware of this detail.
19418    */
19419
19420   this.$get = ['$parse', '$sceDelegate', function(
19421                 $parse,   $sceDelegate) {
19422     // Support: IE 9-11 only
19423     // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
19424     // the "expression(javascript expression)" syntax which is insecure.
19425     if (enabled && msie < 8) {
19426       throw $sceMinErr('iequirks',
19427         'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
19428         'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
19429         'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
19430     }
19431
19432     var sce = shallowCopy(SCE_CONTEXTS);
19433
19434     /**
19435      * @ngdoc method
19436      * @name $sce#isEnabled
19437      * @kind function
19438      *
19439      * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
19440      * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
19441      *
19442      * @description
19443      * Returns a boolean indicating if SCE is enabled.
19444      */
19445     sce.isEnabled = function() {
19446       return enabled;
19447     };
19448     sce.trustAs = $sceDelegate.trustAs;
19449     sce.getTrusted = $sceDelegate.getTrusted;
19450     sce.valueOf = $sceDelegate.valueOf;
19451
19452     if (!enabled) {
19453       sce.trustAs = sce.getTrusted = function(type, value) { return value; };
19454       sce.valueOf = identity;
19455     }
19456
19457     /**
19458      * @ngdoc method
19459      * @name $sce#parseAs
19460      *
19461      * @description
19462      * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
19463      * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
19464      * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
19465      * *result*)}
19466      *
19467      * @param {string} type The kind of SCE context in which this result will be used.
19468      * @param {string} expression String expression to compile.
19469      * @returns {function(context, locals)} a function which represents the compiled expression:
19470      *
19471      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
19472      *      are evaluated against (typically a scope object).
19473      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
19474      *      `context`.
19475      */
19476     sce.parseAs = function sceParseAs(type, expr) {
19477       var parsed = $parse(expr);
19478       if (parsed.literal && parsed.constant) {
19479         return parsed;
19480       } else {
19481         return $parse(expr, function(value) {
19482           return sce.getTrusted(type, value);
19483         });
19484       }
19485     };
19486
19487     /**
19488      * @ngdoc method
19489      * @name $sce#trustAs
19490      *
19491      * @description
19492      * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
19493      * returns an object that is trusted by angular for use in specified strict contextual
19494      * escaping contexts (such as ng-bind-html, ng-include, any src attribute
19495      * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
19496      * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
19497      * escaping.
19498      *
19499      * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
19500      *   resourceUrl, html, js and css.
19501      * @param {*} value The value that that should be considered trusted/safe.
19502      * @returns {*} A value that can be used to stand in for the provided `value` in places
19503      * where Angular expects a $sce.trustAs() return value.
19504      */
19505
19506     /**
19507      * @ngdoc method
19508      * @name $sce#trustAsHtml
19509      *
19510      * @description
19511      * Shorthand method.  `$sce.trustAsHtml(value)` â†’
19512      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
19513      *
19514      * @param {*} value The value to trustAs.
19515      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
19516      *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
19517      *     only accept expressions that are either literal constants or are the
19518      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
19519      */
19520
19521     /**
19522      * @ngdoc method
19523      * @name $sce#trustAsUrl
19524      *
19525      * @description
19526      * Shorthand method.  `$sce.trustAsUrl(value)` â†’
19527      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
19528      *
19529      * @param {*} value The value to trustAs.
19530      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
19531      *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
19532      *     only accept expressions that are either literal constants or are the
19533      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
19534      */
19535
19536     /**
19537      * @ngdoc method
19538      * @name $sce#trustAsResourceUrl
19539      *
19540      * @description
19541      * Shorthand method.  `$sce.trustAsResourceUrl(value)` â†’
19542      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
19543      *
19544      * @param {*} value The value to trustAs.
19545      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
19546      *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
19547      *     only accept expressions that are either literal constants or are the return
19548      *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
19549      */
19550
19551     /**
19552      * @ngdoc method
19553      * @name $sce#trustAsJs
19554      *
19555      * @description
19556      * Shorthand method.  `$sce.trustAsJs(value)` â†’
19557      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
19558      *
19559      * @param {*} value The value to trustAs.
19560      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
19561      *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
19562      *     only accept expressions that are either literal constants or are the
19563      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
19564      */
19565
19566     /**
19567      * @ngdoc method
19568      * @name $sce#getTrusted
19569      *
19570      * @description
19571      * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
19572      * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
19573      * originally supplied value if the queried context type is a supertype of the created type.
19574      * If this condition isn't satisfied, throws an exception.
19575      *
19576      * @param {string} type The kind of context in which this value is to be used.
19577      * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
19578      *                         call.
19579      * @returns {*} The value the was originally provided to
19580      *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
19581      *              Otherwise, throws an exception.
19582      */
19583
19584     /**
19585      * @ngdoc method
19586      * @name $sce#getTrustedHtml
19587      *
19588      * @description
19589      * Shorthand method.  `$sce.getTrustedHtml(value)` â†’
19590      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
19591      *
19592      * @param {*} value The value to pass to `$sce.getTrusted`.
19593      * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
19594      */
19595
19596     /**
19597      * @ngdoc method
19598      * @name $sce#getTrustedCss
19599      *
19600      * @description
19601      * Shorthand method.  `$sce.getTrustedCss(value)` â†’
19602      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
19603      *
19604      * @param {*} value The value to pass to `$sce.getTrusted`.
19605      * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
19606      */
19607
19608     /**
19609      * @ngdoc method
19610      * @name $sce#getTrustedUrl
19611      *
19612      * @description
19613      * Shorthand method.  `$sce.getTrustedUrl(value)` â†’
19614      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
19615      *
19616      * @param {*} value The value to pass to `$sce.getTrusted`.
19617      * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
19618      */
19619
19620     /**
19621      * @ngdoc method
19622      * @name $sce#getTrustedResourceUrl
19623      *
19624      * @description
19625      * Shorthand method.  `$sce.getTrustedResourceUrl(value)` â†’
19626      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
19627      *
19628      * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
19629      * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
19630      */
19631
19632     /**
19633      * @ngdoc method
19634      * @name $sce#getTrustedJs
19635      *
19636      * @description
19637      * Shorthand method.  `$sce.getTrustedJs(value)` â†’
19638      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
19639      *
19640      * @param {*} value The value to pass to `$sce.getTrusted`.
19641      * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
19642      */
19643
19644     /**
19645      * @ngdoc method
19646      * @name $sce#parseAsHtml
19647      *
19648      * @description
19649      * Shorthand method.  `$sce.parseAsHtml(expression string)` â†’
19650      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
19651      *
19652      * @param {string} expression String expression to compile.
19653      * @returns {function(context, locals)} a function which represents the compiled expression:
19654      *
19655      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
19656      *      are evaluated against (typically a scope object).
19657      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
19658      *      `context`.
19659      */
19660
19661     /**
19662      * @ngdoc method
19663      * @name $sce#parseAsCss
19664      *
19665      * @description
19666      * Shorthand method.  `$sce.parseAsCss(value)` â†’
19667      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
19668      *
19669      * @param {string} expression String expression to compile.
19670      * @returns {function(context, locals)} a function which represents the compiled expression:
19671      *
19672      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
19673      *      are evaluated against (typically a scope object).
19674      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
19675      *      `context`.
19676      */
19677
19678     /**
19679      * @ngdoc method
19680      * @name $sce#parseAsUrl
19681      *
19682      * @description
19683      * Shorthand method.  `$sce.parseAsUrl(value)` â†’
19684      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
19685      *
19686      * @param {string} expression String expression to compile.
19687      * @returns {function(context, locals)} a function which represents the compiled expression:
19688      *
19689      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
19690      *      are evaluated against (typically a scope object).
19691      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
19692      *      `context`.
19693      */
19694
19695     /**
19696      * @ngdoc method
19697      * @name $sce#parseAsResourceUrl
19698      *
19699      * @description
19700      * Shorthand method.  `$sce.parseAsResourceUrl(value)` â†’
19701      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
19702      *
19703      * @param {string} expression String expression to compile.
19704      * @returns {function(context, locals)} a function which represents the compiled expression:
19705      *
19706      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
19707      *      are evaluated against (typically a scope object).
19708      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
19709      *      `context`.
19710      */
19711
19712     /**
19713      * @ngdoc method
19714      * @name $sce#parseAsJs
19715      *
19716      * @description
19717      * Shorthand method.  `$sce.parseAsJs(value)` â†’
19718      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
19719      *
19720      * @param {string} expression String expression to compile.
19721      * @returns {function(context, locals)} a function which represents the compiled expression:
19722      *
19723      *    * `context` â€“ `{object}` â€“ an object against which any expressions embedded in the strings
19724      *      are evaluated against (typically a scope object).
19725      *    * `locals` â€“ `{object=}` â€“ local variables context object, useful for overriding values in
19726      *      `context`.
19727      */
19728
19729     // Shorthand delegations.
19730     var parse = sce.parseAs,
19731         getTrusted = sce.getTrusted,
19732         trustAs = sce.trustAs;
19733
19734     forEach(SCE_CONTEXTS, function(enumValue, name) {
19735       var lName = lowercase(name);
19736       sce[snakeToCamel('parse_as_' + lName)] = function(expr) {
19737         return parse(enumValue, expr);
19738       };
19739       sce[snakeToCamel('get_trusted_' + lName)] = function(value) {
19740         return getTrusted(enumValue, value);
19741       };
19742       sce[snakeToCamel('trust_as_' + lName)] = function(value) {
19743         return trustAs(enumValue, value);
19744       };
19745     });
19746
19747     return sce;
19748   }];
19749 }
19750
19751 /* exported $SnifferProvider */
19752
19753 /**
19754  * !!! This is an undocumented "private" service !!!
19755  *
19756  * @name $sniffer
19757  * @requires $window
19758  * @requires $document
19759  * @this
19760  *
19761  * @property {boolean} history Does the browser support html5 history api ?
19762  * @property {boolean} transitions Does the browser support CSS transition events ?
19763  * @property {boolean} animations Does the browser support CSS animation events ?
19764  *
19765  * @description
19766  * This is very simple implementation of testing browser's features.
19767  */
19768 function $SnifferProvider() {
19769   this.$get = ['$window', '$document', function($window, $document) {
19770     var eventSupport = {},
19771         // Chrome Packaged Apps are not allowed to access `history.pushState`.
19772         // If not sandboxed, they can be detected by the presence of `chrome.app.runtime`
19773         // (see https://developer.chrome.com/apps/api_index). If sandboxed, they can be detected by
19774         // the presence of an extension runtime ID and the absence of other Chrome runtime APIs
19775         // (see https://developer.chrome.com/apps/manifest/sandbox).
19776         // (NW.js apps have access to Chrome APIs, but do support `history`.)
19777         isNw = $window.nw && $window.nw.process,
19778         isChromePackagedApp =
19779             !isNw &&
19780             $window.chrome &&
19781             ($window.chrome.app && $window.chrome.app.runtime ||
19782                 !$window.chrome.app && $window.chrome.runtime && $window.chrome.runtime.id),
19783         hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
19784         android =
19785           toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
19786         boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
19787         document = $document[0] || {},
19788         bodyStyle = document.body && document.body.style,
19789         transitions = false,
19790         animations = false;
19791
19792     if (bodyStyle) {
19793       // Support: Android <5, Blackberry Browser 10, default Chrome in Android 4.4.x
19794       // Mentioned browsers need a -webkit- prefix for transitions & animations.
19795       transitions = !!('transition' in bodyStyle || 'webkitTransition' in bodyStyle);
19796       animations = !!('animation' in bodyStyle || 'webkitAnimation' in bodyStyle);
19797     }
19798
19799
19800     return {
19801       // Android has history.pushState, but it does not update location correctly
19802       // so let's not use the history API at all.
19803       // http://code.google.com/p/android/issues/detail?id=17471
19804       // https://github.com/angular/angular.js/issues/904
19805
19806       // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
19807       // so let's not use the history API also
19808       // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
19809       history: !!(hasHistoryPushState && !(android < 4) && !boxee),
19810       hasEvent: function(event) {
19811         // Support: IE 9-11 only
19812         // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
19813         // it. In particular the event is not fired when backspace or delete key are pressed or
19814         // when cut operation is performed.
19815         // IE10+ implements 'input' event but it erroneously fires under various situations,
19816         // e.g. when placeholder changes, or a form is focused.
19817         if (event === 'input' && msie) return false;
19818
19819         if (isUndefined(eventSupport[event])) {
19820           var divElm = document.createElement('div');
19821           eventSupport[event] = 'on' + event in divElm;
19822         }
19823
19824         return eventSupport[event];
19825       },
19826       csp: csp(),
19827       transitions: transitions,
19828       animations: animations,
19829       android: android
19830     };
19831   }];
19832 }
19833
19834 var $templateRequestMinErr = minErr('$compile');
19835
19836 /**
19837  * @ngdoc provider
19838  * @name $templateRequestProvider
19839  * @this
19840  *
19841  * @description
19842  * Used to configure the options passed to the {@link $http} service when making a template request.
19843  *
19844  * For example, it can be used for specifying the "Accept" header that is sent to the server, when
19845  * requesting a template.
19846  */
19847 function $TemplateRequestProvider() {
19848
19849   var httpOptions;
19850
19851   /**
19852    * @ngdoc method
19853    * @name $templateRequestProvider#httpOptions
19854    * @description
19855    * The options to be passed to the {@link $http} service when making the request.
19856    * You can use this to override options such as the "Accept" header for template requests.
19857    *
19858    * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
19859    * options if not overridden here.
19860    *
19861    * @param {string=} value new value for the {@link $http} options.
19862    * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
19863    */
19864   this.httpOptions = function(val) {
19865     if (val) {
19866       httpOptions = val;
19867       return this;
19868     }
19869     return httpOptions;
19870   };
19871
19872   /**
19873    * @ngdoc service
19874    * @name $templateRequest
19875    *
19876    * @description
19877    * The `$templateRequest` service runs security checks then downloads the provided template using
19878    * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
19879    * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
19880    * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
19881    * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
19882    * when `tpl` is of type string and `$templateCache` has the matching entry.
19883    *
19884    * If you want to pass custom options to the `$http` service, such as setting the Accept header you
19885    * can configure this via {@link $templateRequestProvider#httpOptions}.
19886    *
19887    * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
19888    * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
19889    *
19890    * @return {Promise} a promise for the HTTP response data of the given URL.
19891    *
19892    * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
19893    */
19894   this.$get = ['$exceptionHandler', '$templateCache', '$http', '$q', '$sce',
19895     function($exceptionHandler, $templateCache, $http, $q, $sce) {
19896
19897       function handleRequestFn(tpl, ignoreRequestError) {
19898         handleRequestFn.totalPendingRequests++;
19899
19900         // We consider the template cache holds only trusted templates, so
19901         // there's no need to go through whitelisting again for keys that already
19902         // are included in there. This also makes Angular accept any script
19903         // directive, no matter its name. However, we still need to unwrap trusted
19904         // types.
19905         if (!isString(tpl) || isUndefined($templateCache.get(tpl))) {
19906           tpl = $sce.getTrustedResourceUrl(tpl);
19907         }
19908
19909         var transformResponse = $http.defaults && $http.defaults.transformResponse;
19910
19911         if (isArray(transformResponse)) {
19912           transformResponse = transformResponse.filter(function(transformer) {
19913             return transformer !== defaultHttpResponseTransform;
19914           });
19915         } else if (transformResponse === defaultHttpResponseTransform) {
19916           transformResponse = null;
19917         }
19918
19919         return $http.get(tpl, extend({
19920             cache: $templateCache,
19921             transformResponse: transformResponse
19922           }, httpOptions))
19923           .finally(function() {
19924             handleRequestFn.totalPendingRequests--;
19925           })
19926           .then(function(response) {
19927             $templateCache.put(tpl, response.data);
19928             return response.data;
19929           }, handleError);
19930
19931         function handleError(resp) {
19932           if (!ignoreRequestError) {
19933             resp = $templateRequestMinErr('tpload',
19934                 'Failed to load template: {0} (HTTP status: {1} {2})',
19935                 tpl, resp.status, resp.statusText);
19936
19937             $exceptionHandler(resp);
19938           }
19939
19940           return $q.reject(resp);
19941         }
19942       }
19943
19944       handleRequestFn.totalPendingRequests = 0;
19945
19946       return handleRequestFn;
19947     }
19948   ];
19949 }
19950
19951 /** @this */
19952 function $$TestabilityProvider() {
19953   this.$get = ['$rootScope', '$browser', '$location',
19954        function($rootScope,   $browser,   $location) {
19955
19956     /**
19957      * @name $testability
19958      *
19959      * @description
19960      * The private $$testability service provides a collection of methods for use when debugging
19961      * or by automated test and debugging tools.
19962      */
19963     var testability = {};
19964
19965     /**
19966      * @name $$testability#findBindings
19967      *
19968      * @description
19969      * Returns an array of elements that are bound (via ng-bind or {{}})
19970      * to expressions matching the input.
19971      *
19972      * @param {Element} element The element root to search from.
19973      * @param {string} expression The binding expression to match.
19974      * @param {boolean} opt_exactMatch If true, only returns exact matches
19975      *     for the expression. Filters and whitespace are ignored.
19976      */
19977     testability.findBindings = function(element, expression, opt_exactMatch) {
19978       var bindings = element.getElementsByClassName('ng-binding');
19979       var matches = [];
19980       forEach(bindings, function(binding) {
19981         var dataBinding = angular.element(binding).data('$binding');
19982         if (dataBinding) {
19983           forEach(dataBinding, function(bindingName) {
19984             if (opt_exactMatch) {
19985               var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
19986               if (matcher.test(bindingName)) {
19987                 matches.push(binding);
19988               }
19989             } else {
19990               if (bindingName.indexOf(expression) !== -1) {
19991                 matches.push(binding);
19992               }
19993             }
19994           });
19995         }
19996       });
19997       return matches;
19998     };
19999
20000     /**
20001      * @name $$testability#findModels
20002      *
20003      * @description
20004      * Returns an array of elements that are two-way found via ng-model to
20005      * expressions matching the input.
20006      *
20007      * @param {Element} element The element root to search from.
20008      * @param {string} expression The model expression to match.
20009      * @param {boolean} opt_exactMatch If true, only returns exact matches
20010      *     for the expression.
20011      */
20012     testability.findModels = function(element, expression, opt_exactMatch) {
20013       var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
20014       for (var p = 0; p < prefixes.length; ++p) {
20015         var attributeEquals = opt_exactMatch ? '=' : '*=';
20016         var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
20017         var elements = element.querySelectorAll(selector);
20018         if (elements.length) {
20019           return elements;
20020         }
20021       }
20022     };
20023
20024     /**
20025      * @name $$testability#getLocation
20026      *
20027      * @description
20028      * Shortcut for getting the location in a browser agnostic way. Returns
20029      *     the path, search, and hash. (e.g. /path?a=b#hash)
20030      */
20031     testability.getLocation = function() {
20032       return $location.url();
20033     };
20034
20035     /**
20036      * @name $$testability#setLocation
20037      *
20038      * @description
20039      * Shortcut for navigating to a location without doing a full page reload.
20040      *
20041      * @param {string} url The location url (path, search and hash,
20042      *     e.g. /path?a=b#hash) to go to.
20043      */
20044     testability.setLocation = function(url) {
20045       if (url !== $location.url()) {
20046         $location.url(url);
20047         $rootScope.$digest();
20048       }
20049     };
20050
20051     /**
20052      * @name $$testability#whenStable
20053      *
20054      * @description
20055      * Calls the callback when $timeout and $http requests are completed.
20056      *
20057      * @param {function} callback
20058      */
20059     testability.whenStable = function(callback) {
20060       $browser.notifyWhenNoOutstandingRequests(callback);
20061     };
20062
20063     return testability;
20064   }];
20065 }
20066
20067 /** @this */
20068 function $TimeoutProvider() {
20069   this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
20070        function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
20071
20072     var deferreds = {};
20073
20074
20075      /**
20076       * @ngdoc service
20077       * @name $timeout
20078       *
20079       * @description
20080       * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
20081       * block and delegates any exceptions to
20082       * {@link ng.$exceptionHandler $exceptionHandler} service.
20083       *
20084       * The return value of calling `$timeout` is a promise, which will be resolved when
20085       * the delay has passed and the timeout function, if provided, is executed.
20086       *
20087       * To cancel a timeout request, call `$timeout.cancel(promise)`.
20088       *
20089       * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
20090       * synchronously flush the queue of deferred functions.
20091       *
20092       * If you only want a promise that will be resolved after some specified delay
20093       * then you can call `$timeout` without the `fn` function.
20094       *
20095       * @param {function()=} fn A function, whose execution should be delayed.
20096       * @param {number=} [delay=0] Delay in milliseconds.
20097       * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
20098       *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
20099       * @param {...*=} Pass additional parameters to the executed function.
20100       * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
20101       *   will be resolved with the return value of the `fn` function.
20102       *
20103       */
20104     function timeout(fn, delay, invokeApply) {
20105       if (!isFunction(fn)) {
20106         invokeApply = delay;
20107         delay = fn;
20108         fn = noop;
20109       }
20110
20111       var args = sliceArgs(arguments, 3),
20112           skipApply = (isDefined(invokeApply) && !invokeApply),
20113           deferred = (skipApply ? $$q : $q).defer(),
20114           promise = deferred.promise,
20115           timeoutId;
20116
20117       timeoutId = $browser.defer(function() {
20118         try {
20119           deferred.resolve(fn.apply(null, args));
20120         } catch (e) {
20121           deferred.reject(e);
20122           $exceptionHandler(e);
20123         } finally {
20124           delete deferreds[promise.$$timeoutId];
20125         }
20126
20127         if (!skipApply) $rootScope.$apply();
20128       }, delay);
20129
20130       promise.$$timeoutId = timeoutId;
20131       deferreds[timeoutId] = deferred;
20132
20133       return promise;
20134     }
20135
20136
20137      /**
20138       * @ngdoc method
20139       * @name $timeout#cancel
20140       *
20141       * @description
20142       * Cancels a task associated with the `promise`. As a result of this, the promise will be
20143       * resolved with a rejection.
20144       *
20145       * @param {Promise=} promise Promise returned by the `$timeout` function.
20146       * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
20147       *   canceled.
20148       */
20149     timeout.cancel = function(promise) {
20150       if (promise && promise.$$timeoutId in deferreds) {
20151         // Timeout cancels should not report an unhandled promise.
20152         deferreds[promise.$$timeoutId].promise.catch(noop);
20153         deferreds[promise.$$timeoutId].reject('canceled');
20154         delete deferreds[promise.$$timeoutId];
20155         return $browser.defer.cancel(promise.$$timeoutId);
20156       }
20157       return false;
20158     };
20159
20160     return timeout;
20161   }];
20162 }
20163
20164 // NOTE:  The usage of window and document instead of $window and $document here is
20165 // deliberate.  This service depends on the specific behavior of anchor nodes created by the
20166 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
20167 // cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
20168 // doesn't know about mocked locations and resolves URLs to the real document - which is
20169 // exactly the behavior needed here.  There is little value is mocking these out for this
20170 // service.
20171 var urlParsingNode = window.document.createElement('a');
20172 var originUrl = urlResolve(window.location.href);
20173
20174
20175 /**
20176  *
20177  * Implementation Notes for non-IE browsers
20178  * ----------------------------------------
20179  * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
20180  * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
20181  * URL will be resolved into an absolute URL in the context of the application document.
20182  * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
20183  * properties are all populated to reflect the normalized URL.  This approach has wide
20184  * compatibility - Safari 1+, Mozilla 1+ etc.  See
20185  * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
20186  *
20187  * Implementation Notes for IE
20188  * ---------------------------
20189  * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
20190  * browsers.  However, the parsed components will not be set if the URL assigned did not specify
20191  * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
20192  * work around that by performing the parsing in a 2nd step by taking a previously normalized
20193  * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
20194  * properties such as protocol, hostname, port, etc.
20195  *
20196  * References:
20197  *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
20198  *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
20199  *   http://url.spec.whatwg.org/#urlutils
20200  *   https://github.com/angular/angular.js/pull/2902
20201  *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
20202  *
20203  * @kind function
20204  * @param {string} url The URL to be parsed.
20205  * @description Normalizes and parses a URL.
20206  * @returns {object} Returns the normalized URL as a dictionary.
20207  *
20208  *   | member name   | Description    |
20209  *   |---------------|----------------|
20210  *   | href          | A normalized version of the provided URL if it was not an absolute URL |
20211  *   | protocol      | The protocol including the trailing colon                              |
20212  *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
20213  *   | search        | The search params, minus the question mark                             |
20214  *   | hash          | The hash string, minus the hash symbol
20215  *   | hostname      | The hostname
20216  *   | port          | The port, without ":"
20217  *   | pathname      | The pathname, beginning with "/"
20218  *
20219  */
20220 function urlResolve(url) {
20221   var href = url;
20222
20223   // Support: IE 9-11 only
20224   if (msie) {
20225     // Normalize before parse.  Refer Implementation Notes on why this is
20226     // done in two steps on IE.
20227     urlParsingNode.setAttribute('href', href);
20228     href = urlParsingNode.href;
20229   }
20230
20231   urlParsingNode.setAttribute('href', href);
20232
20233   // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
20234   return {
20235     href: urlParsingNode.href,
20236     protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
20237     host: urlParsingNode.host,
20238     search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
20239     hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
20240     hostname: urlParsingNode.hostname,
20241     port: urlParsingNode.port,
20242     pathname: (urlParsingNode.pathname.charAt(0) === '/')
20243       ? urlParsingNode.pathname
20244       : '/' + urlParsingNode.pathname
20245   };
20246 }
20247
20248 /**
20249  * Parse a request URL and determine whether this is a same-origin request as the application document.
20250  *
20251  * @param {string|object} requestUrl The url of the request as a string that will be resolved
20252  * or a parsed URL object.
20253  * @returns {boolean} Whether the request is for the same origin as the application document.
20254  */
20255 function urlIsSameOrigin(requestUrl) {
20256   var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
20257   return (parsed.protocol === originUrl.protocol &&
20258           parsed.host === originUrl.host);
20259 }
20260
20261 /**
20262  * @ngdoc service
20263  * @name $window
20264  * @this
20265  *
20266  * @description
20267  * A reference to the browser's `window` object. While `window`
20268  * is globally available in JavaScript, it causes testability problems, because
20269  * it is a global variable. In angular we always refer to it through the
20270  * `$window` service, so it may be overridden, removed or mocked for testing.
20271  *
20272  * Expressions, like the one defined for the `ngClick` directive in the example
20273  * below, are evaluated with respect to the current scope.  Therefore, there is
20274  * no risk of inadvertently coding in a dependency on a global value in such an
20275  * expression.
20276  *
20277  * @example
20278    <example module="windowExample" name="window-service">
20279      <file name="index.html">
20280        <script>
20281          angular.module('windowExample', [])
20282            .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
20283              $scope.greeting = 'Hello, World!';
20284              $scope.doGreeting = function(greeting) {
20285                $window.alert(greeting);
20286              };
20287            }]);
20288        </script>
20289        <div ng-controller="ExampleController">
20290          <input type="text" ng-model="greeting" aria-label="greeting" />
20291          <button ng-click="doGreeting(greeting)">ALERT</button>
20292        </div>
20293      </file>
20294      <file name="protractor.js" type="protractor">
20295       it('should display the greeting in the input box', function() {
20296        element(by.model('greeting')).sendKeys('Hello, E2E Tests');
20297        // If we click the button it will block the test runner
20298        // element(':button').click();
20299       });
20300      </file>
20301    </example>
20302  */
20303 function $WindowProvider() {
20304   this.$get = valueFn(window);
20305 }
20306
20307 /**
20308  * @name $$cookieReader
20309  * @requires $document
20310  *
20311  * @description
20312  * This is a private service for reading cookies used by $http and ngCookies
20313  *
20314  * @return {Object} a key/value map of the current cookies
20315  */
20316 function $$CookieReader($document) {
20317   var rawDocument = $document[0] || {};
20318   var lastCookies = {};
20319   var lastCookieString = '';
20320
20321   function safeGetCookie(rawDocument) {
20322     try {
20323       return rawDocument.cookie || '';
20324     } catch (e) {
20325       return '';
20326     }
20327   }
20328
20329   function safeDecodeURIComponent(str) {
20330     try {
20331       return decodeURIComponent(str);
20332     } catch (e) {
20333       return str;
20334     }
20335   }
20336
20337   return function() {
20338     var cookieArray, cookie, i, index, name;
20339     var currentCookieString = safeGetCookie(rawDocument);
20340
20341     if (currentCookieString !== lastCookieString) {
20342       lastCookieString = currentCookieString;
20343       cookieArray = lastCookieString.split('; ');
20344       lastCookies = {};
20345
20346       for (i = 0; i < cookieArray.length; i++) {
20347         cookie = cookieArray[i];
20348         index = cookie.indexOf('=');
20349         if (index > 0) { //ignore nameless cookies
20350           name = safeDecodeURIComponent(cookie.substring(0, index));
20351           // the first value that is seen for a cookie is the most
20352           // specific one.  values for the same cookie name that
20353           // follow are for less specific paths.
20354           if (isUndefined(lastCookies[name])) {
20355             lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
20356           }
20357         }
20358       }
20359     }
20360     return lastCookies;
20361   };
20362 }
20363
20364 $$CookieReader.$inject = ['$document'];
20365
20366 /** @this */
20367 function $$CookieReaderProvider() {
20368   this.$get = $$CookieReader;
20369 }
20370
20371 /* global currencyFilter: true,
20372  dateFilter: true,
20373  filterFilter: true,
20374  jsonFilter: true,
20375  limitToFilter: true,
20376  lowercaseFilter: true,
20377  numberFilter: true,
20378  orderByFilter: true,
20379  uppercaseFilter: true,
20380  */
20381
20382 /**
20383  * @ngdoc provider
20384  * @name $filterProvider
20385  * @description
20386  *
20387  * Filters are just functions which transform input to an output. However filters need to be
20388  * Dependency Injected. To achieve this a filter definition consists of a factory function which is
20389  * annotated with dependencies and is responsible for creating a filter function.
20390  *
20391  * <div class="alert alert-warning">
20392  * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
20393  * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
20394  * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
20395  * (`myapp_subsection_filterx`).
20396  * </div>
20397  *
20398  * ```js
20399  *   // Filter registration
20400  *   function MyModule($provide, $filterProvider) {
20401  *     // create a service to demonstrate injection (not always needed)
20402  *     $provide.value('greet', function(name){
20403  *       return 'Hello ' + name + '!';
20404  *     });
20405  *
20406  *     // register a filter factory which uses the
20407  *     // greet service to demonstrate DI.
20408  *     $filterProvider.register('greet', function(greet){
20409  *       // return the filter function which uses the greet service
20410  *       // to generate salutation
20411  *       return function(text) {
20412  *         // filters need to be forgiving so check input validity
20413  *         return text && greet(text) || text;
20414  *       };
20415  *     });
20416  *   }
20417  * ```
20418  *
20419  * The filter function is registered with the `$injector` under the filter name suffix with
20420  * `Filter`.
20421  *
20422  * ```js
20423  *   it('should be the same instance', inject(
20424  *     function($filterProvider) {
20425  *       $filterProvider.register('reverse', function(){
20426  *         return ...;
20427  *       });
20428  *     },
20429  *     function($filter, reverseFilter) {
20430  *       expect($filter('reverse')).toBe(reverseFilter);
20431  *     });
20432  * ```
20433  *
20434  *
20435  * For more information about how angular filters work, and how to create your own filters, see
20436  * {@link guide/filter Filters} in the Angular Developer Guide.
20437  */
20438
20439 /**
20440  * @ngdoc service
20441  * @name $filter
20442  * @kind function
20443  * @description
20444  * Filters are used for formatting data displayed to the user.
20445  *
20446  * They can be used in view templates, controllers or services.Angular comes
20447  * with a collection of [built-in filters](api/ng/filter), but it is easy to
20448  * define your own as well.
20449  *
20450  * The general syntax in templates is as follows:
20451  *
20452  * ```html
20453  * {{ expression [| filter_name[:parameter_value] ... ] }}
20454  * ```
20455  *
20456  * @param {String} name Name of the filter function to retrieve
20457  * @return {Function} the filter function
20458  * @example
20459    <example name="$filter" module="filterExample">
20460      <file name="index.html">
20461        <div ng-controller="MainCtrl">
20462         <h3>{{ originalText }}</h3>
20463         <h3>{{ filteredText }}</h3>
20464        </div>
20465      </file>
20466
20467      <file name="script.js">
20468       angular.module('filterExample', [])
20469       .controller('MainCtrl', function($scope, $filter) {
20470         $scope.originalText = 'hello';
20471         $scope.filteredText = $filter('uppercase')($scope.originalText);
20472       });
20473      </file>
20474    </example>
20475   */
20476 $FilterProvider.$inject = ['$provide'];
20477 /** @this */
20478 function $FilterProvider($provide) {
20479   var suffix = 'Filter';
20480
20481   /**
20482    * @ngdoc method
20483    * @name $filterProvider#register
20484    * @param {string|Object} name Name of the filter function, or an object map of filters where
20485    *    the keys are the filter names and the values are the filter factories.
20486    *
20487    *    <div class="alert alert-warning">
20488    *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
20489    *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
20490    *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
20491    *    (`myapp_subsection_filterx`).
20492    *    </div>
20493     * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
20494    * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
20495    *    of the registered filter instances.
20496    */
20497   function register(name, factory) {
20498     if (isObject(name)) {
20499       var filters = {};
20500       forEach(name, function(filter, key) {
20501         filters[key] = register(key, filter);
20502       });
20503       return filters;
20504     } else {
20505       return $provide.factory(name + suffix, factory);
20506     }
20507   }
20508   this.register = register;
20509
20510   this.$get = ['$injector', function($injector) {
20511     return function(name) {
20512       return $injector.get(name + suffix);
20513     };
20514   }];
20515
20516   ////////////////////////////////////////
20517
20518   /* global
20519     currencyFilter: false,
20520     dateFilter: false,
20521     filterFilter: false,
20522     jsonFilter: false,
20523     limitToFilter: false,
20524     lowercaseFilter: false,
20525     numberFilter: false,
20526     orderByFilter: false,
20527     uppercaseFilter: false
20528   */
20529
20530   register('currency', currencyFilter);
20531   register('date', dateFilter);
20532   register('filter', filterFilter);
20533   register('json', jsonFilter);
20534   register('limitTo', limitToFilter);
20535   register('lowercase', lowercaseFilter);
20536   register('number', numberFilter);
20537   register('orderBy', orderByFilter);
20538   register('uppercase', uppercaseFilter);
20539 }
20540
20541 /**
20542  * @ngdoc filter
20543  * @name filter
20544  * @kind function
20545  *
20546  * @description
20547  * Selects a subset of items from `array` and returns it as a new array.
20548  *
20549  * @param {Array} array The source array.
20550  * <div class="alert alert-info">
20551  *   **Note**: If the array contains objects that reference themselves, filtering is not possible.
20552  * </div>
20553  * @param {string|Object|function()} expression The predicate to be used for selecting items from
20554  *   `array`.
20555  *
20556  *   Can be one of:
20557  *
20558  *   - `string`: The string is used for matching against the contents of the `array`. All strings or
20559  *     objects with string properties in `array` that match this string will be returned. This also
20560  *     applies to nested object properties.
20561  *     The predicate can be negated by prefixing the string with `!`.
20562  *
20563  *   - `Object`: A pattern object can be used to filter specific properties on objects contained
20564  *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
20565  *     which have property `name` containing "M" and property `phone` containing "1". A special
20566  *     property name (`$` by default) can be used (e.g. as in `{$: "text"}`) to accept a match
20567  *     against any property of the object or its nested object properties. That's equivalent to the
20568  *     simple substring match with a `string` as described above. The special property name can be
20569  *     overwritten, using the `anyPropertyKey` parameter.
20570  *     The predicate can be negated by prefixing the string with `!`.
20571  *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
20572  *     not containing "M".
20573  *
20574  *     Note that a named property will match properties on the same level only, while the special
20575  *     `$` property will match properties on the same level or deeper. E.g. an array item like
20576  *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
20577  *     **will** be matched by `{$: 'John'}`.
20578  *
20579  *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
20580  *     The function is called for each element of the array, with the element, its index, and
20581  *     the entire array itself as arguments.
20582  *
20583  *     The final result is an array of those elements that the predicate returned true for.
20584  *
20585  * @param {function(actual, expected)|true|false} [comparator] Comparator which is used in
20586  *     determining if the expected value (from the filter expression) and actual value (from
20587  *     the object in the array) should be considered a match.
20588  *
20589  *   Can be one of:
20590  *
20591  *   - `function(actual, expected)`:
20592  *     The function will be given the object value and the predicate value to compare and
20593  *     should return true if both values should be considered equal.
20594  *
20595  *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
20596  *     This is essentially strict comparison of expected and actual.
20597  *
20598  *   - `false`: A short hand for a function which will look for a substring match in a case
20599  *     insensitive way. Primitive values are converted to strings. Objects are not compared against
20600  *     primitives, unless they have a custom `toString` method (e.g. `Date` objects).
20601  *
20602  *
20603  *   Defaults to `false`.
20604  *
20605  * @param {string} [anyPropertyKey] The special property name that matches against any property.
20606  *     By default `$`.
20607  *
20608  * @example
20609    <example name="filter-filter">
20610      <file name="index.html">
20611        <div ng-init="friends = [{name:'John', phone:'555-1276'},
20612                                 {name:'Mary', phone:'800-BIG-MARY'},
20613                                 {name:'Mike', phone:'555-4321'},
20614                                 {name:'Adam', phone:'555-5678'},
20615                                 {name:'Julie', phone:'555-8765'},
20616                                 {name:'Juliette', phone:'555-5678'}]"></div>
20617
20618        <label>Search: <input ng-model="searchText"></label>
20619        <table id="searchTextResults">
20620          <tr><th>Name</th><th>Phone</th></tr>
20621          <tr ng-repeat="friend in friends | filter:searchText">
20622            <td>{{friend.name}}</td>
20623            <td>{{friend.phone}}</td>
20624          </tr>
20625        </table>
20626        <hr>
20627        <label>Any: <input ng-model="search.$"></label> <br>
20628        <label>Name only <input ng-model="search.name"></label><br>
20629        <label>Phone only <input ng-model="search.phone"></label><br>
20630        <label>Equality <input type="checkbox" ng-model="strict"></label><br>
20631        <table id="searchObjResults">
20632          <tr><th>Name</th><th>Phone</th></tr>
20633          <tr ng-repeat="friendObj in friends | filter:search:strict">
20634            <td>{{friendObj.name}}</td>
20635            <td>{{friendObj.phone}}</td>
20636          </tr>
20637        </table>
20638      </file>
20639      <file name="protractor.js" type="protractor">
20640        var expectFriendNames = function(expectedNames, key) {
20641          element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
20642            arr.forEach(function(wd, i) {
20643              expect(wd.getText()).toMatch(expectedNames[i]);
20644            });
20645          });
20646        };
20647
20648        it('should search across all fields when filtering with a string', function() {
20649          var searchText = element(by.model('searchText'));
20650          searchText.clear();
20651          searchText.sendKeys('m');
20652          expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
20653
20654          searchText.clear();
20655          searchText.sendKeys('76');
20656          expectFriendNames(['John', 'Julie'], 'friend');
20657        });
20658
20659        it('should search in specific fields when filtering with a predicate object', function() {
20660          var searchAny = element(by.model('search.$'));
20661          searchAny.clear();
20662          searchAny.sendKeys('i');
20663          expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
20664        });
20665        it('should use a equal comparison when comparator is true', function() {
20666          var searchName = element(by.model('search.name'));
20667          var strict = element(by.model('strict'));
20668          searchName.clear();
20669          searchName.sendKeys('Julie');
20670          strict.click();
20671          expectFriendNames(['Julie'], 'friendObj');
20672        });
20673      </file>
20674    </example>
20675  */
20676
20677 function filterFilter() {
20678   return function(array, expression, comparator, anyPropertyKey) {
20679     if (!isArrayLike(array)) {
20680       if (array == null) {
20681         return array;
20682       } else {
20683         throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
20684       }
20685     }
20686
20687     anyPropertyKey = anyPropertyKey || '$';
20688     var expressionType = getTypeForFilter(expression);
20689     var predicateFn;
20690     var matchAgainstAnyProp;
20691
20692     switch (expressionType) {
20693       case 'function':
20694         predicateFn = expression;
20695         break;
20696       case 'boolean':
20697       case 'null':
20698       case 'number':
20699       case 'string':
20700         matchAgainstAnyProp = true;
20701         // falls through
20702       case 'object':
20703         predicateFn = createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp);
20704         break;
20705       default:
20706         return array;
20707     }
20708
20709     return Array.prototype.filter.call(array, predicateFn);
20710   };
20711 }
20712
20713 // Helper functions for `filterFilter`
20714 function createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp) {
20715   var shouldMatchPrimitives = isObject(expression) && (anyPropertyKey in expression);
20716   var predicateFn;
20717
20718   if (comparator === true) {
20719     comparator = equals;
20720   } else if (!isFunction(comparator)) {
20721     comparator = function(actual, expected) {
20722       if (isUndefined(actual)) {
20723         // No substring matching against `undefined`
20724         return false;
20725       }
20726       if ((actual === null) || (expected === null)) {
20727         // No substring matching against `null`; only match against `null`
20728         return actual === expected;
20729       }
20730       if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
20731         // Should not compare primitives against objects, unless they have custom `toString` method
20732         return false;
20733       }
20734
20735       actual = lowercase('' + actual);
20736       expected = lowercase('' + expected);
20737       return actual.indexOf(expected) !== -1;
20738     };
20739   }
20740
20741   predicateFn = function(item) {
20742     if (shouldMatchPrimitives && !isObject(item)) {
20743       return deepCompare(item, expression[anyPropertyKey], comparator, anyPropertyKey, false);
20744     }
20745     return deepCompare(item, expression, comparator, anyPropertyKey, matchAgainstAnyProp);
20746   };
20747
20748   return predicateFn;
20749 }
20750
20751 function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstAnyProp, dontMatchWholeObject) {
20752   var actualType = getTypeForFilter(actual);
20753   var expectedType = getTypeForFilter(expected);
20754
20755   if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
20756     return !deepCompare(actual, expected.substring(1), comparator, anyPropertyKey, matchAgainstAnyProp);
20757   } else if (isArray(actual)) {
20758     // In case `actual` is an array, consider it a match
20759     // if ANY of it's items matches `expected`
20760     return actual.some(function(item) {
20761       return deepCompare(item, expected, comparator, anyPropertyKey, matchAgainstAnyProp);
20762     });
20763   }
20764
20765   switch (actualType) {
20766     case 'object':
20767       var key;
20768       if (matchAgainstAnyProp) {
20769         for (key in actual) {
20770           // Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined
20771           // See: https://github.com/angular/angular.js/issues/15644
20772           if (key.charAt && (key.charAt(0) !== '$') &&
20773               deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
20774             return true;
20775           }
20776         }
20777         return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, anyPropertyKey, false);
20778       } else if (expectedType === 'object') {
20779         for (key in expected) {
20780           var expectedVal = expected[key];
20781           if (isFunction(expectedVal) || isUndefined(expectedVal)) {
20782             continue;
20783           }
20784
20785           var matchAnyProperty = key === anyPropertyKey;
20786           var actualVal = matchAnyProperty ? actual : actual[key];
20787           if (!deepCompare(actualVal, expectedVal, comparator, anyPropertyKey, matchAnyProperty, matchAnyProperty)) {
20788             return false;
20789           }
20790         }
20791         return true;
20792       } else {
20793         return comparator(actual, expected);
20794       }
20795     case 'function':
20796       return false;
20797     default:
20798       return comparator(actual, expected);
20799   }
20800 }
20801
20802 // Used for easily differentiating between `null` and actual `object`
20803 function getTypeForFilter(val) {
20804   return (val === null) ? 'null' : typeof val;
20805 }
20806
20807 var MAX_DIGITS = 22;
20808 var DECIMAL_SEP = '.';
20809 var ZERO_CHAR = '0';
20810
20811 /**
20812  * @ngdoc filter
20813  * @name currency
20814  * @kind function
20815  *
20816  * @description
20817  * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
20818  * symbol for current locale is used.
20819  *
20820  * @param {number} amount Input to filter.
20821  * @param {string=} symbol Currency symbol or identifier to be displayed.
20822  * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
20823  * @returns {string} Formatted number.
20824  *
20825  *
20826  * @example
20827    <example module="currencyExample" name="currency-filter">
20828      <file name="index.html">
20829        <script>
20830          angular.module('currencyExample', [])
20831            .controller('ExampleController', ['$scope', function($scope) {
20832              $scope.amount = 1234.56;
20833            }]);
20834        </script>
20835        <div ng-controller="ExampleController">
20836          <input type="number" ng-model="amount" aria-label="amount"> <br>
20837          default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
20838          custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span><br>
20839          no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
20840        </div>
20841      </file>
20842      <file name="protractor.js" type="protractor">
20843        it('should init with 1234.56', function() {
20844          expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
20845          expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
20846          expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
20847        });
20848        it('should update', function() {
20849          if (browser.params.browser === 'safari') {
20850            // Safari does not understand the minus key. See
20851            // https://github.com/angular/protractor/issues/481
20852            return;
20853          }
20854          element(by.model('amount')).clear();
20855          element(by.model('amount')).sendKeys('-1234');
20856          expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
20857          expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
20858          expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
20859        });
20860      </file>
20861    </example>
20862  */
20863 currencyFilter.$inject = ['$locale'];
20864 function currencyFilter($locale) {
20865   var formats = $locale.NUMBER_FORMATS;
20866   return function(amount, currencySymbol, fractionSize) {
20867     if (isUndefined(currencySymbol)) {
20868       currencySymbol = formats.CURRENCY_SYM;
20869     }
20870
20871     if (isUndefined(fractionSize)) {
20872       fractionSize = formats.PATTERNS[1].maxFrac;
20873     }
20874
20875     // if null or undefined pass it through
20876     return (amount == null)
20877         ? amount
20878         : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
20879             replace(/\u00A4/g, currencySymbol);
20880   };
20881 }
20882
20883 /**
20884  * @ngdoc filter
20885  * @name number
20886  * @kind function
20887  *
20888  * @description
20889  * Formats a number as text.
20890  *
20891  * If the input is null or undefined, it will just be returned.
20892  * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
20893  * If the input is not a number an empty string is returned.
20894  *
20895  *
20896  * @param {number|string} number Number to format.
20897  * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
20898  * If this is not provided then the fraction size is computed from the current locale's number
20899  * formatting pattern. In the case of the default locale, it will be 3.
20900  * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
20901  *                   locale (e.g., in the en_US locale it will have "." as the decimal separator and
20902  *                   include "," group separators after each third digit).
20903  *
20904  * @example
20905    <example module="numberFilterExample" name="number-filter">
20906      <file name="index.html">
20907        <script>
20908          angular.module('numberFilterExample', [])
20909            .controller('ExampleController', ['$scope', function($scope) {
20910              $scope.val = 1234.56789;
20911            }]);
20912        </script>
20913        <div ng-controller="ExampleController">
20914          <label>Enter number: <input ng-model='val'></label><br>
20915          Default formatting: <span id='number-default'>{{val | number}}</span><br>
20916          No fractions: <span>{{val | number:0}}</span><br>
20917          Negative number: <span>{{-val | number:4}}</span>
20918        </div>
20919      </file>
20920      <file name="protractor.js" type="protractor">
20921        it('should format numbers', function() {
20922          expect(element(by.id('number-default')).getText()).toBe('1,234.568');
20923          expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
20924          expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
20925        });
20926
20927        it('should update', function() {
20928          element(by.model('val')).clear();
20929          element(by.model('val')).sendKeys('3374.333');
20930          expect(element(by.id('number-default')).getText()).toBe('3,374.333');
20931          expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
20932          expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
20933       });
20934      </file>
20935    </example>
20936  */
20937 numberFilter.$inject = ['$locale'];
20938 function numberFilter($locale) {
20939   var formats = $locale.NUMBER_FORMATS;
20940   return function(number, fractionSize) {
20941
20942     // if null or undefined pass it through
20943     return (number == null)
20944         ? number
20945         : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
20946                        fractionSize);
20947   };
20948 }
20949
20950 /**
20951  * Parse a number (as a string) into three components that can be used
20952  * for formatting the number.
20953  *
20954  * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
20955  *
20956  * @param  {string} numStr The number to parse
20957  * @return {object} An object describing this number, containing the following keys:
20958  *  - d : an array of digits containing leading zeros as necessary
20959  *  - i : the number of the digits in `d` that are to the left of the decimal point
20960  *  - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
20961  *
20962  */
20963 function parse(numStr) {
20964   var exponent = 0, digits, numberOfIntegerDigits;
20965   var i, j, zeros;
20966
20967   // Decimal point?
20968   if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
20969     numStr = numStr.replace(DECIMAL_SEP, '');
20970   }
20971
20972   // Exponential form?
20973   if ((i = numStr.search(/e/i)) > 0) {
20974     // Work out the exponent.
20975     if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
20976     numberOfIntegerDigits += +numStr.slice(i + 1);
20977     numStr = numStr.substring(0, i);
20978   } else if (numberOfIntegerDigits < 0) {
20979     // There was no decimal point or exponent so it is an integer.
20980     numberOfIntegerDigits = numStr.length;
20981   }
20982
20983   // Count the number of leading zeros.
20984   for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) { /* empty */ }
20985
20986   if (i === (zeros = numStr.length)) {
20987     // The digits are all zero.
20988     digits = [0];
20989     numberOfIntegerDigits = 1;
20990   } else {
20991     // Count the number of trailing zeros
20992     zeros--;
20993     while (numStr.charAt(zeros) === ZERO_CHAR) zeros--;
20994
20995     // Trailing zeros are insignificant so ignore them
20996     numberOfIntegerDigits -= i;
20997     digits = [];
20998     // Convert string to array of digits without leading/trailing zeros.
20999     for (j = 0; i <= zeros; i++, j++) {
21000       digits[j] = +numStr.charAt(i);
21001     }
21002   }
21003
21004   // If the number overflows the maximum allowed digits then use an exponent.
21005   if (numberOfIntegerDigits > MAX_DIGITS) {
21006     digits = digits.splice(0, MAX_DIGITS - 1);
21007     exponent = numberOfIntegerDigits - 1;
21008     numberOfIntegerDigits = 1;
21009   }
21010
21011   return { d: digits, e: exponent, i: numberOfIntegerDigits };
21012 }
21013
21014 /**
21015  * Round the parsed number to the specified number of decimal places
21016  * This function changed the parsedNumber in-place
21017  */
21018 function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
21019     var digits = parsedNumber.d;
21020     var fractionLen = digits.length - parsedNumber.i;
21021
21022     // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
21023     fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
21024
21025     // The index of the digit to where rounding is to occur
21026     var roundAt = fractionSize + parsedNumber.i;
21027     var digit = digits[roundAt];
21028
21029     if (roundAt > 0) {
21030       // Drop fractional digits beyond `roundAt`
21031       digits.splice(Math.max(parsedNumber.i, roundAt));
21032
21033       // Set non-fractional digits beyond `roundAt` to 0
21034       for (var j = roundAt; j < digits.length; j++) {
21035         digits[j] = 0;
21036       }
21037     } else {
21038       // We rounded to zero so reset the parsedNumber
21039       fractionLen = Math.max(0, fractionLen);
21040       parsedNumber.i = 1;
21041       digits.length = Math.max(1, roundAt = fractionSize + 1);
21042       digits[0] = 0;
21043       for (var i = 1; i < roundAt; i++) digits[i] = 0;
21044     }
21045
21046     if (digit >= 5) {
21047       if (roundAt - 1 < 0) {
21048         for (var k = 0; k > roundAt; k--) {
21049           digits.unshift(0);
21050           parsedNumber.i++;
21051         }
21052         digits.unshift(1);
21053         parsedNumber.i++;
21054       } else {
21055         digits[roundAt - 1]++;
21056       }
21057     }
21058
21059     // Pad out with zeros to get the required fraction length
21060     for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
21061
21062
21063     // Do any carrying, e.g. a digit was rounded up to 10
21064     var carry = digits.reduceRight(function(carry, d, i, digits) {
21065       d = d + carry;
21066       digits[i] = d % 10;
21067       return Math.floor(d / 10);
21068     }, 0);
21069     if (carry) {
21070       digits.unshift(carry);
21071       parsedNumber.i++;
21072     }
21073 }
21074
21075 /**
21076  * Format a number into a string
21077  * @param  {number} number       The number to format
21078  * @param  {{
21079  *           minFrac, // the minimum number of digits required in the fraction part of the number
21080  *           maxFrac, // the maximum number of digits required in the fraction part of the number
21081  *           gSize,   // number of digits in each group of separated digits
21082  *           lgSize,  // number of digits in the last group of digits before the decimal separator
21083  *           negPre,  // the string to go in front of a negative number (e.g. `-` or `(`))
21084  *           posPre,  // the string to go in front of a positive number
21085  *           negSuf,  // the string to go after a negative number (e.g. `)`)
21086  *           posSuf   // the string to go after a positive number
21087  *         }} pattern
21088  * @param  {string} groupSep     The string to separate groups of number (e.g. `,`)
21089  * @param  {string} decimalSep   The string to act as the decimal separator (e.g. `.`)
21090  * @param  {[type]} fractionSize The size of the fractional part of the number
21091  * @return {string}              The number formatted as a string
21092  */
21093 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
21094
21095   if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
21096
21097   var isInfinity = !isFinite(number);
21098   var isZero = false;
21099   var numStr = Math.abs(number) + '',
21100       formattedText = '',
21101       parsedNumber;
21102
21103   if (isInfinity) {
21104     formattedText = '\u221e';
21105   } else {
21106     parsedNumber = parse(numStr);
21107
21108     roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
21109
21110     var digits = parsedNumber.d;
21111     var integerLen = parsedNumber.i;
21112     var exponent = parsedNumber.e;
21113     var decimals = [];
21114     isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
21115
21116     // pad zeros for small numbers
21117     while (integerLen < 0) {
21118       digits.unshift(0);
21119       integerLen++;
21120     }
21121
21122     // extract decimals digits
21123     if (integerLen > 0) {
21124       decimals = digits.splice(integerLen, digits.length);
21125     } else {
21126       decimals = digits;
21127       digits = [0];
21128     }
21129
21130     // format the integer digits with grouping separators
21131     var groups = [];
21132     if (digits.length >= pattern.lgSize) {
21133       groups.unshift(digits.splice(-pattern.lgSize, digits.length).join(''));
21134     }
21135     while (digits.length > pattern.gSize) {
21136       groups.unshift(digits.splice(-pattern.gSize, digits.length).join(''));
21137     }
21138     if (digits.length) {
21139       groups.unshift(digits.join(''));
21140     }
21141     formattedText = groups.join(groupSep);
21142
21143     // append the decimal digits
21144     if (decimals.length) {
21145       formattedText += decimalSep + decimals.join('');
21146     }
21147
21148     if (exponent) {
21149       formattedText += 'e+' + exponent;
21150     }
21151   }
21152   if (number < 0 && !isZero) {
21153     return pattern.negPre + formattedText + pattern.negSuf;
21154   } else {
21155     return pattern.posPre + formattedText + pattern.posSuf;
21156   }
21157 }
21158
21159 function padNumber(num, digits, trim, negWrap) {
21160   var neg = '';
21161   if (num < 0 || (negWrap && num <= 0)) {
21162     if (negWrap) {
21163       num = -num + 1;
21164     } else {
21165       num = -num;
21166       neg = '-';
21167     }
21168   }
21169   num = '' + num;
21170   while (num.length < digits) num = ZERO_CHAR + num;
21171   if (trim) {
21172     num = num.substr(num.length - digits);
21173   }
21174   return neg + num;
21175 }
21176
21177
21178 function dateGetter(name, size, offset, trim, negWrap) {
21179   offset = offset || 0;
21180   return function(date) {
21181     var value = date['get' + name]();
21182     if (offset > 0 || value > -offset) {
21183       value += offset;
21184     }
21185     if (value === 0 && offset === -12) value = 12;
21186     return padNumber(value, size, trim, negWrap);
21187   };
21188 }
21189
21190 function dateStrGetter(name, shortForm, standAlone) {
21191   return function(date, formats) {
21192     var value = date['get' + name]();
21193     var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
21194     var get = uppercase(propPrefix + name);
21195
21196     return formats[get][value];
21197   };
21198 }
21199
21200 function timeZoneGetter(date, formats, offset) {
21201   var zone = -1 * offset;
21202   var paddedZone = (zone >= 0) ? '+' : '';
21203
21204   paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
21205                 padNumber(Math.abs(zone % 60), 2);
21206
21207   return paddedZone;
21208 }
21209
21210 function getFirstThursdayOfYear(year) {
21211     // 0 = index of January
21212     var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
21213     // 4 = index of Thursday (+1 to account for 1st = 5)
21214     // 11 = index of *next* Thursday (+1 account for 1st = 12)
21215     return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
21216 }
21217
21218 function getThursdayThisWeek(datetime) {
21219     return new Date(datetime.getFullYear(), datetime.getMonth(),
21220       // 4 = index of Thursday
21221       datetime.getDate() + (4 - datetime.getDay()));
21222 }
21223
21224 function weekGetter(size) {
21225    return function(date) {
21226       var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
21227          thisThurs = getThursdayThisWeek(date);
21228
21229       var diff = +thisThurs - +firstThurs,
21230          result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
21231
21232       return padNumber(result, size);
21233    };
21234 }
21235
21236 function ampmGetter(date, formats) {
21237   return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
21238 }
21239
21240 function eraGetter(date, formats) {
21241   return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
21242 }
21243
21244 function longEraGetter(date, formats) {
21245   return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
21246 }
21247
21248 var DATE_FORMATS = {
21249   yyyy: dateGetter('FullYear', 4, 0, false, true),
21250     yy: dateGetter('FullYear', 2, 0, true, true),
21251      y: dateGetter('FullYear', 1, 0, false, true),
21252   MMMM: dateStrGetter('Month'),
21253    MMM: dateStrGetter('Month', true),
21254     MM: dateGetter('Month', 2, 1),
21255      M: dateGetter('Month', 1, 1),
21256   LLLL: dateStrGetter('Month', false, true),
21257     dd: dateGetter('Date', 2),
21258      d: dateGetter('Date', 1),
21259     HH: dateGetter('Hours', 2),
21260      H: dateGetter('Hours', 1),
21261     hh: dateGetter('Hours', 2, -12),
21262      h: dateGetter('Hours', 1, -12),
21263     mm: dateGetter('Minutes', 2),
21264      m: dateGetter('Minutes', 1),
21265     ss: dateGetter('Seconds', 2),
21266      s: dateGetter('Seconds', 1),
21267      // while ISO 8601 requires fractions to be prefixed with `.` or `,`
21268      // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
21269    sss: dateGetter('Milliseconds', 3),
21270   EEEE: dateStrGetter('Day'),
21271    EEE: dateStrGetter('Day', true),
21272      a: ampmGetter,
21273      Z: timeZoneGetter,
21274     ww: weekGetter(2),
21275      w: weekGetter(1),
21276      G: eraGetter,
21277      GG: eraGetter,
21278      GGG: eraGetter,
21279      GGGG: longEraGetter
21280 };
21281
21282 var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
21283     NUMBER_STRING = /^-?\d+$/;
21284
21285 /**
21286  * @ngdoc filter
21287  * @name date
21288  * @kind function
21289  *
21290  * @description
21291  *   Formats `date` to a string based on the requested `format`.
21292  *
21293  *   `format` string can be composed of the following elements:
21294  *
21295  *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
21296  *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
21297  *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
21298  *   * `'MMMM'`: Month in year (January-December)
21299  *   * `'MMM'`: Month in year (Jan-Dec)
21300  *   * `'MM'`: Month in year, padded (01-12)
21301  *   * `'M'`: Month in year (1-12)
21302  *   * `'LLLL'`: Stand-alone month in year (January-December)
21303  *   * `'dd'`: Day in month, padded (01-31)
21304  *   * `'d'`: Day in month (1-31)
21305  *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
21306  *   * `'EEE'`: Day in Week, (Sun-Sat)
21307  *   * `'HH'`: Hour in day, padded (00-23)
21308  *   * `'H'`: Hour in day (0-23)
21309  *   * `'hh'`: Hour in AM/PM, padded (01-12)
21310  *   * `'h'`: Hour in AM/PM, (1-12)
21311  *   * `'mm'`: Minute in hour, padded (00-59)
21312  *   * `'m'`: Minute in hour (0-59)
21313  *   * `'ss'`: Second in minute, padded (00-59)
21314  *   * `'s'`: Second in minute (0-59)
21315  *   * `'sss'`: Millisecond in second, padded (000-999)
21316  *   * `'a'`: AM/PM marker
21317  *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
21318  *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
21319  *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
21320  *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
21321  *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
21322  *
21323  *   `format` string can also be one of the following predefined
21324  *   {@link guide/i18n localizable formats}:
21325  *
21326  *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
21327  *     (e.g. Sep 3, 2010 12:05:08 PM)
21328  *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
21329  *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
21330  *     (e.g. Friday, September 3, 2010)
21331  *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
21332  *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
21333  *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
21334  *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
21335  *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
21336  *
21337  *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
21338  *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
21339  *   (e.g. `"h 'o''clock'"`).
21340  *
21341  * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
21342  *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
21343  *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
21344  *    specified in the string input, the time is considered to be in the local timezone.
21345  * @param {string=} format Formatting rules (see Description). If not specified,
21346  *    `mediumDate` is used.
21347  * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
21348  *    continental US time zone abbreviations, but for general use, use a time zone offset, for
21349  *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
21350  *    If not specified, the timezone of the browser will be used.
21351  * @returns {string} Formatted string or the input if input is not recognized as date/millis.
21352  *
21353  * @example
21354    <example name="filter-date">
21355      <file name="index.html">
21356        <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
21357            <span>{{1288323623006 | date:'medium'}}</span><br>
21358        <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
21359           <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
21360        <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
21361           <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
21362        <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
21363           <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
21364      </file>
21365      <file name="protractor.js" type="protractor">
21366        it('should format date', function() {
21367          expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
21368             toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
21369          expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
21370             toMatch(/2010-10-2\d \d{2}:\d{2}:\d{2} (-|\+)?\d{4}/);
21371          expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
21372             toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
21373          expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
21374             toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
21375        });
21376      </file>
21377    </example>
21378  */
21379 dateFilter.$inject = ['$locale'];
21380 function dateFilter($locale) {
21381
21382
21383   var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
21384                      // 1        2       3         4          5          6          7          8  9     10      11
21385   function jsonStringToDate(string) {
21386     var match;
21387     if ((match = string.match(R_ISO8601_STR))) {
21388       var date = new Date(0),
21389           tzHour = 0,
21390           tzMin  = 0,
21391           dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
21392           timeSetter = match[8] ? date.setUTCHours : date.setHours;
21393
21394       if (match[9]) {
21395         tzHour = toInt(match[9] + match[10]);
21396         tzMin = toInt(match[9] + match[11]);
21397       }
21398       dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
21399       var h = toInt(match[4] || 0) - tzHour;
21400       var m = toInt(match[5] || 0) - tzMin;
21401       var s = toInt(match[6] || 0);
21402       var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
21403       timeSetter.call(date, h, m, s, ms);
21404       return date;
21405     }
21406     return string;
21407   }
21408
21409
21410   return function(date, format, timezone) {
21411     var text = '',
21412         parts = [],
21413         fn, match;
21414
21415     format = format || 'mediumDate';
21416     format = $locale.DATETIME_FORMATS[format] || format;
21417     if (isString(date)) {
21418       date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
21419     }
21420
21421     if (isNumber(date)) {
21422       date = new Date(date);
21423     }
21424
21425     if (!isDate(date) || !isFinite(date.getTime())) {
21426       return date;
21427     }
21428
21429     while (format) {
21430       match = DATE_FORMATS_SPLIT.exec(format);
21431       if (match) {
21432         parts = concat(parts, match, 1);
21433         format = parts.pop();
21434       } else {
21435         parts.push(format);
21436         format = null;
21437       }
21438     }
21439
21440     var dateTimezoneOffset = date.getTimezoneOffset();
21441     if (timezone) {
21442       dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
21443       date = convertTimezoneToLocal(date, timezone, true);
21444     }
21445     forEach(parts, function(value) {
21446       fn = DATE_FORMATS[value];
21447       text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
21448                  : value === '\'\'' ? '\'' : value.replace(/(^'|'$)/g, '').replace(/''/g, '\'');
21449     });
21450
21451     return text;
21452   };
21453 }
21454
21455
21456 /**
21457  * @ngdoc filter
21458  * @name json
21459  * @kind function
21460  *
21461  * @description
21462  *   Allows you to convert a JavaScript object into JSON string.
21463  *
21464  *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
21465  *   the binding is automatically converted to JSON.
21466  *
21467  * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
21468  * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
21469  * @returns {string} JSON string.
21470  *
21471  *
21472  * @example
21473    <example name="filter-json">
21474      <file name="index.html">
21475        <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
21476        <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
21477      </file>
21478      <file name="protractor.js" type="protractor">
21479        it('should jsonify filtered objects', function() {
21480          expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n {2}"name": ?"value"\n}/);
21481          expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n {4}"name": ?"value"\n}/);
21482        });
21483      </file>
21484    </example>
21485  *
21486  */
21487 function jsonFilter() {
21488   return function(object, spacing) {
21489     if (isUndefined(spacing)) {
21490         spacing = 2;
21491     }
21492     return toJson(object, spacing);
21493   };
21494 }
21495
21496
21497 /**
21498  * @ngdoc filter
21499  * @name lowercase
21500  * @kind function
21501  * @description
21502  * Converts string to lowercase.
21503  * @see angular.lowercase
21504  */
21505 var lowercaseFilter = valueFn(lowercase);
21506
21507
21508 /**
21509  * @ngdoc filter
21510  * @name uppercase
21511  * @kind function
21512  * @description
21513  * Converts string to uppercase.
21514  * @see angular.uppercase
21515  */
21516 var uppercaseFilter = valueFn(uppercase);
21517
21518 /**
21519  * @ngdoc filter
21520  * @name limitTo
21521  * @kind function
21522  *
21523  * @description
21524  * Creates a new array or string containing only a specified number of elements. The elements are
21525  * taken from either the beginning or the end of the source array, string or number, as specified by
21526  * the value and sign (positive or negative) of `limit`. Other array-like objects are also supported
21527  * (e.g. array subclasses, NodeLists, jqLite/jQuery collections etc). If a number is used as input,
21528  * it is converted to a string.
21529  *
21530  * @param {Array|ArrayLike|string|number} input - Array/array-like, string or number to be limited.
21531  * @param {string|number} limit - The length of the returned array or string. If the `limit` number
21532  *     is positive, `limit` number of items from the beginning of the source array/string are copied.
21533  *     If the number is negative, `limit` number  of items from the end of the source array/string
21534  *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
21535  *     the input will be returned unchanged.
21536  * @param {(string|number)=} begin - Index at which to begin limitation. As a negative index,
21537  *     `begin` indicates an offset from the end of `input`. Defaults to `0`.
21538  * @returns {Array|string} A new sub-array or substring of length `limit` or less if the input had
21539  *     less than `limit` elements.
21540  *
21541  * @example
21542    <example module="limitToExample" name="limit-to-filter">
21543      <file name="index.html">
21544        <script>
21545          angular.module('limitToExample', [])
21546            .controller('ExampleController', ['$scope', function($scope) {
21547              $scope.numbers = [1,2,3,4,5,6,7,8,9];
21548              $scope.letters = "abcdefghi";
21549              $scope.longNumber = 2345432342;
21550              $scope.numLimit = 3;
21551              $scope.letterLimit = 3;
21552              $scope.longNumberLimit = 3;
21553            }]);
21554        </script>
21555        <div ng-controller="ExampleController">
21556          <label>
21557             Limit {{numbers}} to:
21558             <input type="number" step="1" ng-model="numLimit">
21559          </label>
21560          <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
21561          <label>
21562             Limit {{letters}} to:
21563             <input type="number" step="1" ng-model="letterLimit">
21564          </label>
21565          <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
21566          <label>
21567             Limit {{longNumber}} to:
21568             <input type="number" step="1" ng-model="longNumberLimit">
21569          </label>
21570          <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
21571        </div>
21572      </file>
21573      <file name="protractor.js" type="protractor">
21574        var numLimitInput = element(by.model('numLimit'));
21575        var letterLimitInput = element(by.model('letterLimit'));
21576        var longNumberLimitInput = element(by.model('longNumberLimit'));
21577        var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
21578        var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
21579        var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
21580
21581        it('should limit the number array to first three items', function() {
21582          expect(numLimitInput.getAttribute('value')).toBe('3');
21583          expect(letterLimitInput.getAttribute('value')).toBe('3');
21584          expect(longNumberLimitInput.getAttribute('value')).toBe('3');
21585          expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
21586          expect(limitedLetters.getText()).toEqual('Output letters: abc');
21587          expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
21588        });
21589
21590        // There is a bug in safari and protractor that doesn't like the minus key
21591        // it('should update the output when -3 is entered', function() {
21592        //   numLimitInput.clear();
21593        //   numLimitInput.sendKeys('-3');
21594        //   letterLimitInput.clear();
21595        //   letterLimitInput.sendKeys('-3');
21596        //   longNumberLimitInput.clear();
21597        //   longNumberLimitInput.sendKeys('-3');
21598        //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
21599        //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
21600        //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
21601        // });
21602
21603        it('should not exceed the maximum size of input array', function() {
21604          numLimitInput.clear();
21605          numLimitInput.sendKeys('100');
21606          letterLimitInput.clear();
21607          letterLimitInput.sendKeys('100');
21608          longNumberLimitInput.clear();
21609          longNumberLimitInput.sendKeys('100');
21610          expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
21611          expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
21612          expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
21613        });
21614      </file>
21615    </example>
21616 */
21617 function limitToFilter() {
21618   return function(input, limit, begin) {
21619     if (Math.abs(Number(limit)) === Infinity) {
21620       limit = Number(limit);
21621     } else {
21622       limit = toInt(limit);
21623     }
21624     if (isNumberNaN(limit)) return input;
21625
21626     if (isNumber(input)) input = input.toString();
21627     if (!isArrayLike(input)) return input;
21628
21629     begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
21630     begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
21631
21632     if (limit >= 0) {
21633       return sliceFn(input, begin, begin + limit);
21634     } else {
21635       if (begin === 0) {
21636         return sliceFn(input, limit, input.length);
21637       } else {
21638         return sliceFn(input, Math.max(0, begin + limit), begin);
21639       }
21640     }
21641   };
21642 }
21643
21644 function sliceFn(input, begin, end) {
21645   if (isString(input)) return input.slice(begin, end);
21646
21647   return slice.call(input, begin, end);
21648 }
21649
21650 /**
21651  * @ngdoc filter
21652  * @name orderBy
21653  * @kind function
21654  *
21655  * @description
21656  * Returns an array containing the items from the specified `collection`, ordered by a `comparator`
21657  * function based on the values computed using the `expression` predicate.
21658  *
21659  * For example, `[{id: 'foo'}, {id: 'bar'}] | orderBy:'id'` would result in
21660  * `[{id: 'bar'}, {id: 'foo'}]`.
21661  *
21662  * The `collection` can be an Array or array-like object (e.g. NodeList, jQuery object, TypedArray,
21663  * String, etc).
21664  *
21665  * The `expression` can be a single predicate, or a list of predicates each serving as a tie-breaker
21666  * for the preceding one. The `expression` is evaluated against each item and the output is used
21667  * for comparing with other items.
21668  *
21669  * You can change the sorting order by setting `reverse` to `true`. By default, items are sorted in
21670  * ascending order.
21671  *
21672  * The comparison is done using the `comparator` function. If none is specified, a default, built-in
21673  * comparator is used (see below for details - in a nutshell, it compares numbers numerically and
21674  * strings alphabetically).
21675  *
21676  * ### Under the hood
21677  *
21678  * Ordering the specified `collection` happens in two phases:
21679  *
21680  * 1. All items are passed through the predicate (or predicates), and the returned values are saved
21681  *    along with their type (`string`, `number` etc). For example, an item `{label: 'foo'}`, passed
21682  *    through a predicate that extracts the value of the `label` property, would be transformed to:
21683  *    ```
21684  *    {
21685  *      value: 'foo',
21686  *      type: 'string',
21687  *      index: ...
21688  *    }
21689  *    ```
21690  * 2. The comparator function is used to sort the items, based on the derived values, types and
21691  *    indices.
21692  *
21693  * If you use a custom comparator, it will be called with pairs of objects of the form
21694  * `{value: ..., type: '...', index: ...}` and is expected to return `0` if the objects are equal
21695  * (as far as the comparator is concerned), `-1` if the 1st one should be ranked higher than the
21696  * second, or `1` otherwise.
21697  *
21698  * In order to ensure that the sorting will be deterministic across platforms, if none of the
21699  * specified predicates can distinguish between two items, `orderBy` will automatically introduce a
21700  * dummy predicate that returns the item's index as `value`.
21701  * (If you are using a custom comparator, make sure it can handle this predicate as well.)
21702  *
21703  * Finally, in an attempt to simplify things, if a predicate returns an object as the extracted
21704  * value for an item, `orderBy` will try to convert that object to a primitive value, before passing
21705  * it to the comparator. The following rules govern the conversion:
21706  *
21707  * 1. If the object has a `valueOf()` method that returns a primitive, its return value will be
21708  *    used instead.<br />
21709  *    (If the object has a `valueOf()` method that returns another object, then the returned object
21710  *    will be used in subsequent steps.)
21711  * 2. If the object has a custom `toString()` method (i.e. not the one inherited from `Object`) that
21712  *    returns a primitive, its return value will be used instead.<br />
21713  *    (If the object has a `toString()` method that returns another object, then the returned object
21714  *    will be used in subsequent steps.)
21715  * 3. No conversion; the object itself is used.
21716  *
21717  * ### The default comparator
21718  *
21719  * The default, built-in comparator should be sufficient for most usecases. In short, it compares
21720  * numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to
21721  * using their index in the original collection, and sorts values of different types by type.
21722  *
21723  * More specifically, it follows these steps to determine the relative order of items:
21724  *
21725  * 1. If the compared values are of different types, compare the types themselves alphabetically.
21726  * 2. If both values are of type `string`, compare them alphabetically in a case- and
21727  *    locale-insensitive way.
21728  * 3. If both values are objects, compare their indices instead.
21729  * 4. Otherwise, return:
21730  *    -  `0`, if the values are equal (by strict equality comparison, i.e. using `===`).
21731  *    - `-1`, if the 1st value is "less than" the 2nd value (compared using the `<` operator).
21732  *    -  `1`, otherwise.
21733  *
21734  * **Note:** If you notice numbers not being sorted as expected, make sure they are actually being
21735  *           saved as numbers and not strings.
21736  * **Note:** For the purpose of sorting, `null` values are treated as the string `'null'` (i.e.
21737  *           `type: 'string'`, `value: 'null'`). This may cause unexpected sort order relative to
21738  *           other values.
21739  *
21740  * @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort.
21741  * @param {(Function|string|Array.<Function|string>)=} expression - A predicate (or list of
21742  *    predicates) to be used by the comparator to determine the order of elements.
21743  *
21744  *    Can be one of:
21745  *
21746  *    - `Function`: A getter function. This function will be called with each item as argument and
21747  *      the return value will be used for sorting.
21748  *    - `string`: An Angular expression. This expression will be evaluated against each item and the
21749  *      result will be used for sorting. For example, use `'label'` to sort by a property called
21750  *      `label` or `'label.substring(0, 3)'` to sort by the first 3 characters of the `label`
21751  *      property.<br />
21752  *      (The result of a constant expression is interpreted as a property name to be used for
21753  *      comparison. For example, use `'"special name"'` (note the extra pair of quotes) to sort by a
21754  *      property called `special name`.)<br />
21755  *      An expression can be optionally prefixed with `+` or `-` to control the sorting direction,
21756  *      ascending or descending. For example, `'+label'` or `'-label'`. If no property is provided,
21757  *      (e.g. `'+'` or `'-'`), the collection element itself is used in comparisons.
21758  *    - `Array`: An array of function and/or string predicates. If a predicate cannot determine the
21759  *      relative order of two items, the next predicate is used as a tie-breaker.
21760  *
21761  * **Note:** If the predicate is missing or empty then it defaults to `'+'`.
21762  *
21763  * @param {boolean=} reverse - If `true`, reverse the sorting order.
21764  * @param {(Function)=} comparator - The comparator function used to determine the relative order of
21765  *    value pairs. If omitted, the built-in comparator will be used.
21766  *
21767  * @returns {Array} - The sorted array.
21768  *
21769  *
21770  * @example
21771  * ### Ordering a table with `ngRepeat`
21772  *
21773  * The example below demonstrates a simple {@link ngRepeat ngRepeat}, where the data is sorted by
21774  * age in descending order (expression is set to `'-age'`). The `comparator` is not set, which means
21775  * it defaults to the built-in comparator.
21776  *
21777    <example name="orderBy-static" module="orderByExample1">
21778      <file name="index.html">
21779        <div ng-controller="ExampleController">
21780          <table class="friends">
21781            <tr>
21782              <th>Name</th>
21783              <th>Phone Number</th>
21784              <th>Age</th>
21785            </tr>
21786            <tr ng-repeat="friend in friends | orderBy:'-age'">
21787              <td>{{friend.name}}</td>
21788              <td>{{friend.phone}}</td>
21789              <td>{{friend.age}}</td>
21790            </tr>
21791          </table>
21792        </div>
21793      </file>
21794      <file name="script.js">
21795        angular.module('orderByExample1', [])
21796          .controller('ExampleController', ['$scope', function($scope) {
21797            $scope.friends = [
21798              {name: 'John',   phone: '555-1212',  age: 10},
21799              {name: 'Mary',   phone: '555-9876',  age: 19},
21800              {name: 'Mike',   phone: '555-4321',  age: 21},
21801              {name: 'Adam',   phone: '555-5678',  age: 35},
21802              {name: 'Julie',  phone: '555-8765',  age: 29}
21803            ];
21804          }]);
21805      </file>
21806      <file name="style.css">
21807        .friends {
21808          border-collapse: collapse;
21809        }
21810
21811        .friends th {
21812          border-bottom: 1px solid;
21813        }
21814        .friends td, .friends th {
21815          border-left: 1px solid;
21816          padding: 5px 10px;
21817        }
21818        .friends td:first-child, .friends th:first-child {
21819          border-left: none;
21820        }
21821      </file>
21822      <file name="protractor.js" type="protractor">
21823        // Element locators
21824        var names = element.all(by.repeater('friends').column('friend.name'));
21825
21826        it('should sort friends by age in reverse order', function() {
21827          expect(names.get(0).getText()).toBe('Adam');
21828          expect(names.get(1).getText()).toBe('Julie');
21829          expect(names.get(2).getText()).toBe('Mike');
21830          expect(names.get(3).getText()).toBe('Mary');
21831          expect(names.get(4).getText()).toBe('John');
21832        });
21833      </file>
21834    </example>
21835  * <hr />
21836  *
21837  * @example
21838  * ### Changing parameters dynamically
21839  *
21840  * All parameters can be changed dynamically. The next example shows how you can make the columns of
21841  * a table sortable, by binding the `expression` and `reverse` parameters to scope properties.
21842  *
21843    <example name="orderBy-dynamic" module="orderByExample2">
21844      <file name="index.html">
21845        <div ng-controller="ExampleController">
21846          <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
21847          <hr/>
21848          <button ng-click="propertyName = null; reverse = false">Set to unsorted</button>
21849          <hr/>
21850          <table class="friends">
21851            <tr>
21852              <th>
21853                <button ng-click="sortBy('name')">Name</button>
21854                <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
21855              </th>
21856              <th>
21857                <button ng-click="sortBy('phone')">Phone Number</button>
21858                <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
21859              </th>
21860              <th>
21861                <button ng-click="sortBy('age')">Age</button>
21862                <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
21863              </th>
21864            </tr>
21865            <tr ng-repeat="friend in friends | orderBy:propertyName:reverse">
21866              <td>{{friend.name}}</td>
21867              <td>{{friend.phone}}</td>
21868              <td>{{friend.age}}</td>
21869            </tr>
21870          </table>
21871        </div>
21872      </file>
21873      <file name="script.js">
21874        angular.module('orderByExample2', [])
21875          .controller('ExampleController', ['$scope', function($scope) {
21876            var friends = [
21877              {name: 'John',   phone: '555-1212',  age: 10},
21878              {name: 'Mary',   phone: '555-9876',  age: 19},
21879              {name: 'Mike',   phone: '555-4321',  age: 21},
21880              {name: 'Adam',   phone: '555-5678',  age: 35},
21881              {name: 'Julie',  phone: '555-8765',  age: 29}
21882            ];
21883
21884            $scope.propertyName = 'age';
21885            $scope.reverse = true;
21886            $scope.friends = friends;
21887
21888            $scope.sortBy = function(propertyName) {
21889              $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;
21890              $scope.propertyName = propertyName;
21891            };
21892          }]);
21893      </file>
21894      <file name="style.css">
21895        .friends {
21896          border-collapse: collapse;
21897        }
21898
21899        .friends th {
21900          border-bottom: 1px solid;
21901        }
21902        .friends td, .friends th {
21903          border-left: 1px solid;
21904          padding: 5px 10px;
21905        }
21906        .friends td:first-child, .friends th:first-child {
21907          border-left: none;
21908        }
21909
21910        .sortorder:after {
21911          content: '\25b2';   // BLACK UP-POINTING TRIANGLE
21912        }
21913        .sortorder.reverse:after {
21914          content: '\25bc';   // BLACK DOWN-POINTING TRIANGLE
21915        }
21916      </file>
21917      <file name="protractor.js" type="protractor">
21918        // Element locators
21919        var unsortButton = element(by.partialButtonText('unsorted'));
21920        var nameHeader = element(by.partialButtonText('Name'));
21921        var phoneHeader = element(by.partialButtonText('Phone'));
21922        var ageHeader = element(by.partialButtonText('Age'));
21923        var firstName = element(by.repeater('friends').column('friend.name').row(0));
21924        var lastName = element(by.repeater('friends').column('friend.name').row(4));
21925
21926        it('should sort friends by some property, when clicking on the column header', function() {
21927          expect(firstName.getText()).toBe('Adam');
21928          expect(lastName.getText()).toBe('John');
21929
21930          phoneHeader.click();
21931          expect(firstName.getText()).toBe('John');
21932          expect(lastName.getText()).toBe('Mary');
21933
21934          nameHeader.click();
21935          expect(firstName.getText()).toBe('Adam');
21936          expect(lastName.getText()).toBe('Mike');
21937
21938          ageHeader.click();
21939          expect(firstName.getText()).toBe('John');
21940          expect(lastName.getText()).toBe('Adam');
21941        });
21942
21943        it('should sort friends in reverse order, when clicking on the same column', function() {
21944          expect(firstName.getText()).toBe('Adam');
21945          expect(lastName.getText()).toBe('John');
21946
21947          ageHeader.click();
21948          expect(firstName.getText()).toBe('John');
21949          expect(lastName.getText()).toBe('Adam');
21950
21951          ageHeader.click();
21952          expect(firstName.getText()).toBe('Adam');
21953          expect(lastName.getText()).toBe('John');
21954        });
21955
21956        it('should restore the original order, when clicking "Set to unsorted"', function() {
21957          expect(firstName.getText()).toBe('Adam');
21958          expect(lastName.getText()).toBe('John');
21959
21960          unsortButton.click();
21961          expect(firstName.getText()).toBe('John');
21962          expect(lastName.getText()).toBe('Julie');
21963        });
21964      </file>
21965    </example>
21966  * <hr />
21967  *
21968  * @example
21969  * ### Using `orderBy` inside a controller
21970  *
21971  * It is also possible to call the `orderBy` filter manually, by injecting `orderByFilter`, and
21972  * calling it with the desired parameters. (Alternatively, you could inject the `$filter` factory
21973  * and retrieve the `orderBy` filter with `$filter('orderBy')`.)
21974  *
21975    <example name="orderBy-call-manually" module="orderByExample3">
21976      <file name="index.html">
21977        <div ng-controller="ExampleController">
21978          <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
21979          <hr/>
21980          <button ng-click="sortBy(null)">Set to unsorted</button>
21981          <hr/>
21982          <table class="friends">
21983            <tr>
21984              <th>
21985                <button ng-click="sortBy('name')">Name</button>
21986                <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
21987              </th>
21988              <th>
21989                <button ng-click="sortBy('phone')">Phone Number</button>
21990                <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
21991              </th>
21992              <th>
21993                <button ng-click="sortBy('age')">Age</button>
21994                <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
21995              </th>
21996            </tr>
21997            <tr ng-repeat="friend in friends">
21998              <td>{{friend.name}}</td>
21999              <td>{{friend.phone}}</td>
22000              <td>{{friend.age}}</td>
22001            </tr>
22002          </table>
22003        </div>
22004      </file>
22005      <file name="script.js">
22006        angular.module('orderByExample3', [])
22007          .controller('ExampleController', ['$scope', 'orderByFilter', function($scope, orderBy) {
22008            var friends = [
22009              {name: 'John',   phone: '555-1212',  age: 10},
22010              {name: 'Mary',   phone: '555-9876',  age: 19},
22011              {name: 'Mike',   phone: '555-4321',  age: 21},
22012              {name: 'Adam',   phone: '555-5678',  age: 35},
22013              {name: 'Julie',  phone: '555-8765',  age: 29}
22014            ];
22015
22016            $scope.propertyName = 'age';
22017            $scope.reverse = true;
22018            $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
22019
22020            $scope.sortBy = function(propertyName) {
22021              $scope.reverse = (propertyName !== null && $scope.propertyName === propertyName)
22022                  ? !$scope.reverse : false;
22023              $scope.propertyName = propertyName;
22024              $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
22025            };
22026          }]);
22027      </file>
22028      <file name="style.css">
22029        .friends {
22030          border-collapse: collapse;
22031        }
22032
22033        .friends th {
22034          border-bottom: 1px solid;
22035        }
22036        .friends td, .friends th {
22037          border-left: 1px solid;
22038          padding: 5px 10px;
22039        }
22040        .friends td:first-child, .friends th:first-child {
22041          border-left: none;
22042        }
22043
22044        .sortorder:after {
22045          content: '\25b2';   // BLACK UP-POINTING TRIANGLE
22046        }
22047        .sortorder.reverse:after {
22048          content: '\25bc';   // BLACK DOWN-POINTING TRIANGLE
22049        }
22050      </file>
22051      <file name="protractor.js" type="protractor">
22052        // Element locators
22053        var unsortButton = element(by.partialButtonText('unsorted'));
22054        var nameHeader = element(by.partialButtonText('Name'));
22055        var phoneHeader = element(by.partialButtonText('Phone'));
22056        var ageHeader = element(by.partialButtonText('Age'));
22057        var firstName = element(by.repeater('friends').column('friend.name').row(0));
22058        var lastName = element(by.repeater('friends').column('friend.name').row(4));
22059
22060        it('should sort friends by some property, when clicking on the column header', function() {
22061          expect(firstName.getText()).toBe('Adam');
22062          expect(lastName.getText()).toBe('John');
22063
22064          phoneHeader.click();
22065          expect(firstName.getText()).toBe('John');
22066          expect(lastName.getText()).toBe('Mary');
22067
22068          nameHeader.click();
22069          expect(firstName.getText()).toBe('Adam');
22070          expect(lastName.getText()).toBe('Mike');
22071
22072          ageHeader.click();
22073          expect(firstName.getText()).toBe('John');
22074          expect(lastName.getText()).toBe('Adam');
22075        });
22076
22077        it('should sort friends in reverse order, when clicking on the same column', function() {
22078          expect(firstName.getText()).toBe('Adam');
22079          expect(lastName.getText()).toBe('John');
22080
22081          ageHeader.click();
22082          expect(firstName.getText()).toBe('John');
22083          expect(lastName.getText()).toBe('Adam');
22084
22085          ageHeader.click();
22086          expect(firstName.getText()).toBe('Adam');
22087          expect(lastName.getText()).toBe('John');
22088        });
22089
22090        it('should restore the original order, when clicking "Set to unsorted"', function() {
22091          expect(firstName.getText()).toBe('Adam');
22092          expect(lastName.getText()).toBe('John');
22093
22094          unsortButton.click();
22095          expect(firstName.getText()).toBe('John');
22096          expect(lastName.getText()).toBe('Julie');
22097        });
22098      </file>
22099    </example>
22100  * <hr />
22101  *
22102  * @example
22103  * ### Using a custom comparator
22104  *
22105  * If you have very specific requirements about the way items are sorted, you can pass your own
22106  * comparator function. For example, you might need to compare some strings in a locale-sensitive
22107  * way. (When specifying a custom comparator, you also need to pass a value for the `reverse`
22108  * argument - passing `false` retains the default sorting order, i.e. ascending.)
22109  *
22110    <example name="orderBy-custom-comparator" module="orderByExample4">
22111      <file name="index.html">
22112        <div ng-controller="ExampleController">
22113          <div class="friends-container custom-comparator">
22114            <h3>Locale-sensitive Comparator</h3>
22115            <table class="friends">
22116              <tr>
22117                <th>Name</th>
22118                <th>Favorite Letter</th>
22119              </tr>
22120              <tr ng-repeat="friend in friends | orderBy:'favoriteLetter':false:localeSensitiveComparator">
22121                <td>{{friend.name}}</td>
22122                <td>{{friend.favoriteLetter}}</td>
22123              </tr>
22124            </table>
22125          </div>
22126          <div class="friends-container default-comparator">
22127            <h3>Default Comparator</h3>
22128            <table class="friends">
22129              <tr>
22130                <th>Name</th>
22131                <th>Favorite Letter</th>
22132              </tr>
22133              <tr ng-repeat="friend in friends | orderBy:'favoriteLetter'">
22134                <td>{{friend.name}}</td>
22135                <td>{{friend.favoriteLetter}}</td>
22136              </tr>
22137            </table>
22138          </div>
22139        </div>
22140      </file>
22141      <file name="script.js">
22142        angular.module('orderByExample4', [])
22143          .controller('ExampleController', ['$scope', function($scope) {
22144            $scope.friends = [
22145              {name: 'John',   favoriteLetter: 'Ä'},
22146              {name: 'Mary',   favoriteLetter: 'Ü'},
22147              {name: 'Mike',   favoriteLetter: 'Ö'},
22148              {name: 'Adam',   favoriteLetter: 'H'},
22149              {name: 'Julie',  favoriteLetter: 'Z'}
22150            ];
22151
22152            $scope.localeSensitiveComparator = function(v1, v2) {
22153              // If we don't get strings, just compare by index
22154              if (v1.type !== 'string' || v2.type !== 'string') {
22155                return (v1.index < v2.index) ? -1 : 1;
22156              }
22157
22158              // Compare strings alphabetically, taking locale into account
22159              return v1.value.localeCompare(v2.value);
22160            };
22161          }]);
22162      </file>
22163      <file name="style.css">
22164        .friends-container {
22165          display: inline-block;
22166          margin: 0 30px;
22167        }
22168
22169        .friends {
22170          border-collapse: collapse;
22171        }
22172
22173        .friends th {
22174          border-bottom: 1px solid;
22175        }
22176        .friends td, .friends th {
22177          border-left: 1px solid;
22178          padding: 5px 10px;
22179        }
22180        .friends td:first-child, .friends th:first-child {
22181          border-left: none;
22182        }
22183      </file>
22184      <file name="protractor.js" type="protractor">
22185        // Element locators
22186        var container = element(by.css('.custom-comparator'));
22187        var names = container.all(by.repeater('friends').column('friend.name'));
22188
22189        it('should sort friends by favorite letter (in correct alphabetical order)', function() {
22190          expect(names.get(0).getText()).toBe('John');
22191          expect(names.get(1).getText()).toBe('Adam');
22192          expect(names.get(2).getText()).toBe('Mike');
22193          expect(names.get(3).getText()).toBe('Mary');
22194          expect(names.get(4).getText()).toBe('Julie');
22195        });
22196      </file>
22197    </example>
22198  *
22199  */
22200 orderByFilter.$inject = ['$parse'];
22201 function orderByFilter($parse) {
22202   return function(array, sortPredicate, reverseOrder, compareFn) {
22203
22204     if (array == null) return array;
22205     if (!isArrayLike(array)) {
22206       throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
22207     }
22208
22209     if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
22210     if (sortPredicate.length === 0) { sortPredicate = ['+']; }
22211
22212     var predicates = processPredicates(sortPredicate);
22213
22214     var descending = reverseOrder ? -1 : 1;
22215
22216     // Define the `compare()` function. Use a default comparator if none is specified.
22217     var compare = isFunction(compareFn) ? compareFn : defaultCompare;
22218
22219     // The next three lines are a version of a Swartzian Transform idiom from Perl
22220     // (sometimes called the Decorate-Sort-Undecorate idiom)
22221     // See https://en.wikipedia.org/wiki/Schwartzian_transform
22222     var compareValues = Array.prototype.map.call(array, getComparisonObject);
22223     compareValues.sort(doComparison);
22224     array = compareValues.map(function(item) { return item.value; });
22225
22226     return array;
22227
22228     function getComparisonObject(value, index) {
22229       // NOTE: We are adding an extra `tieBreaker` value based on the element's index.
22230       // This will be used to keep the sort stable when none of the input predicates can
22231       // distinguish between two elements.
22232       return {
22233         value: value,
22234         tieBreaker: {value: index, type: 'number', index: index},
22235         predicateValues: predicates.map(function(predicate) {
22236           return getPredicateValue(predicate.get(value), index);
22237         })
22238       };
22239     }
22240
22241     function doComparison(v1, v2) {
22242       for (var i = 0, ii = predicates.length; i < ii; i++) {
22243         var result = compare(v1.predicateValues[i], v2.predicateValues[i]);
22244         if (result) {
22245           return result * predicates[i].descending * descending;
22246         }
22247       }
22248
22249       return compare(v1.tieBreaker, v2.tieBreaker) * descending;
22250     }
22251   };
22252
22253   function processPredicates(sortPredicates) {
22254     return sortPredicates.map(function(predicate) {
22255       var descending = 1, get = identity;
22256
22257       if (isFunction(predicate)) {
22258         get = predicate;
22259       } else if (isString(predicate)) {
22260         if ((predicate.charAt(0) === '+' || predicate.charAt(0) === '-')) {
22261           descending = predicate.charAt(0) === '-' ? -1 : 1;
22262           predicate = predicate.substring(1);
22263         }
22264         if (predicate !== '') {
22265           get = $parse(predicate);
22266           if (get.constant) {
22267             var key = get();
22268             get = function(value) { return value[key]; };
22269           }
22270         }
22271       }
22272       return {get: get, descending: descending};
22273     });
22274   }
22275
22276   function isPrimitive(value) {
22277     switch (typeof value) {
22278       case 'number': /* falls through */
22279       case 'boolean': /* falls through */
22280       case 'string':
22281         return true;
22282       default:
22283         return false;
22284     }
22285   }
22286
22287   function objectValue(value) {
22288     // If `valueOf` is a valid function use that
22289     if (isFunction(value.valueOf)) {
22290       value = value.valueOf();
22291       if (isPrimitive(value)) return value;
22292     }
22293     // If `toString` is a valid function and not the one from `Object.prototype` use that
22294     if (hasCustomToString(value)) {
22295       value = value.toString();
22296       if (isPrimitive(value)) return value;
22297     }
22298
22299     return value;
22300   }
22301
22302   function getPredicateValue(value, index) {
22303     var type = typeof value;
22304     if (value === null) {
22305       type = 'string';
22306       value = 'null';
22307     } else if (type === 'object') {
22308       value = objectValue(value);
22309     }
22310     return {value: value, type: type, index: index};
22311   }
22312
22313   function defaultCompare(v1, v2) {
22314     var result = 0;
22315     var type1 = v1.type;
22316     var type2 = v2.type;
22317
22318     if (type1 === type2) {
22319       var value1 = v1.value;
22320       var value2 = v2.value;
22321
22322       if (type1 === 'string') {
22323         // Compare strings case-insensitively
22324         value1 = value1.toLowerCase();
22325         value2 = value2.toLowerCase();
22326       } else if (type1 === 'object') {
22327         // For basic objects, use the position of the object
22328         // in the collection instead of the value
22329         if (isObject(value1)) value1 = v1.index;
22330         if (isObject(value2)) value2 = v2.index;
22331       }
22332
22333       if (value1 !== value2) {
22334         result = value1 < value2 ? -1 : 1;
22335       }
22336     } else {
22337       result = type1 < type2 ? -1 : 1;
22338     }
22339
22340     return result;
22341   }
22342 }
22343
22344 function ngDirective(directive) {
22345   if (isFunction(directive)) {
22346     directive = {
22347       link: directive
22348     };
22349   }
22350   directive.restrict = directive.restrict || 'AC';
22351   return valueFn(directive);
22352 }
22353
22354 /**
22355  * @ngdoc directive
22356  * @name a
22357  * @restrict E
22358  *
22359  * @description
22360  * Modifies the default behavior of the html a tag so that the default action is prevented when
22361  * the href attribute is empty.
22362  *
22363  * For dynamically creating `href` attributes for a tags, see the {@link ng.ngHref `ngHref`} directive.
22364  */
22365 var htmlAnchorDirective = valueFn({
22366   restrict: 'E',
22367   compile: function(element, attr) {
22368     if (!attr.href && !attr.xlinkHref) {
22369       return function(scope, element) {
22370         // If the linked element is not an anchor tag anymore, do nothing
22371         if (element[0].nodeName.toLowerCase() !== 'a') return;
22372
22373         // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
22374         var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
22375                    'xlink:href' : 'href';
22376         element.on('click', function(event) {
22377           // if we have no href url, then don't navigate anywhere.
22378           if (!element.attr(href)) {
22379             event.preventDefault();
22380           }
22381         });
22382       };
22383     }
22384   }
22385 });
22386
22387 /**
22388  * @ngdoc directive
22389  * @name ngHref
22390  * @restrict A
22391  * @priority 99
22392  *
22393  * @description
22394  * Using Angular markup like `{{hash}}` in an href attribute will
22395  * make the link go to the wrong URL if the user clicks it before
22396  * Angular has a chance to replace the `{{hash}}` markup with its
22397  * value. Until Angular replaces the markup the link will be broken
22398  * and will most likely return a 404 error. The `ngHref` directive
22399  * solves this problem.
22400  *
22401  * The wrong way to write it:
22402  * ```html
22403  * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
22404  * ```
22405  *
22406  * The correct way to write it:
22407  * ```html
22408  * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
22409  * ```
22410  *
22411  * @element A
22412  * @param {template} ngHref any string which can contain `{{}}` markup.
22413  *
22414  * @example
22415  * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
22416  * in links and their different behaviors:
22417     <example name="ng-href">
22418       <file name="index.html">
22419         <input ng-model="value" /><br />
22420         <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
22421         <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
22422         <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
22423         <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
22424         <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
22425         <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
22426       </file>
22427       <file name="protractor.js" type="protractor">
22428         it('should execute ng-click but not reload when href without value', function() {
22429           element(by.id('link-1')).click();
22430           expect(element(by.model('value')).getAttribute('value')).toEqual('1');
22431           expect(element(by.id('link-1')).getAttribute('href')).toBe('');
22432         });
22433
22434         it('should execute ng-click but not reload when href empty string', function() {
22435           element(by.id('link-2')).click();
22436           expect(element(by.model('value')).getAttribute('value')).toEqual('2');
22437           expect(element(by.id('link-2')).getAttribute('href')).toBe('');
22438         });
22439
22440         it('should execute ng-click and change url when ng-href specified', function() {
22441           expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
22442
22443           element(by.id('link-3')).click();
22444
22445           // At this point, we navigate away from an Angular page, so we need
22446           // to use browser.driver to get the base webdriver.
22447
22448           browser.wait(function() {
22449             return browser.driver.getCurrentUrl().then(function(url) {
22450               return url.match(/\/123$/);
22451             });
22452           }, 5000, 'page should navigate to /123');
22453         });
22454
22455         it('should execute ng-click but not reload when href empty string and name specified', function() {
22456           element(by.id('link-4')).click();
22457           expect(element(by.model('value')).getAttribute('value')).toEqual('4');
22458           expect(element(by.id('link-4')).getAttribute('href')).toBe('');
22459         });
22460
22461         it('should execute ng-click but not reload when no href but name specified', function() {
22462           element(by.id('link-5')).click();
22463           expect(element(by.model('value')).getAttribute('value')).toEqual('5');
22464           expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
22465         });
22466
22467         it('should only change url when only ng-href', function() {
22468           element(by.model('value')).clear();
22469           element(by.model('value')).sendKeys('6');
22470           expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
22471
22472           element(by.id('link-6')).click();
22473
22474           // At this point, we navigate away from an Angular page, so we need
22475           // to use browser.driver to get the base webdriver.
22476           browser.wait(function() {
22477             return browser.driver.getCurrentUrl().then(function(url) {
22478               return url.match(/\/6$/);
22479             });
22480           }, 5000, 'page should navigate to /6');
22481         });
22482       </file>
22483     </example>
22484  */
22485
22486 /**
22487  * @ngdoc directive
22488  * @name ngSrc
22489  * @restrict A
22490  * @priority 99
22491  *
22492  * @description
22493  * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
22494  * work right: The browser will fetch from the URL with the literal
22495  * text `{{hash}}` until Angular replaces the expression inside
22496  * `{{hash}}`. The `ngSrc` directive solves this problem.
22497  *
22498  * The buggy way to write it:
22499  * ```html
22500  * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
22501  * ```
22502  *
22503  * The correct way to write it:
22504  * ```html
22505  * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
22506  * ```
22507  *
22508  * @element IMG
22509  * @param {template} ngSrc any string which can contain `{{}}` markup.
22510  */
22511
22512 /**
22513  * @ngdoc directive
22514  * @name ngSrcset
22515  * @restrict A
22516  * @priority 99
22517  *
22518  * @description
22519  * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
22520  * work right: The browser will fetch from the URL with the literal
22521  * text `{{hash}}` until Angular replaces the expression inside
22522  * `{{hash}}`. The `ngSrcset` directive solves this problem.
22523  *
22524  * The buggy way to write it:
22525  * ```html
22526  * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
22527  * ```
22528  *
22529  * The correct way to write it:
22530  * ```html
22531  * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
22532  * ```
22533  *
22534  * @element IMG
22535  * @param {template} ngSrcset any string which can contain `{{}}` markup.
22536  */
22537
22538 /**
22539  * @ngdoc directive
22540  * @name ngDisabled
22541  * @restrict A
22542  * @priority 100
22543  *
22544  * @description
22545  *
22546  * This directive sets the `disabled` attribute on the element (typically a form control,
22547  * e.g. `input`, `button`, `select` etc.) if the
22548  * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
22549  *
22550  * A special directive is necessary because we cannot use interpolation inside the `disabled`
22551  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22552  *
22553  * @example
22554     <example name="ng-disabled">
22555       <file name="index.html">
22556         <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
22557         <button ng-model="button" ng-disabled="checked">Button</button>
22558       </file>
22559       <file name="protractor.js" type="protractor">
22560         it('should toggle button', function() {
22561           expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
22562           element(by.model('checked')).click();
22563           expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
22564         });
22565       </file>
22566     </example>
22567  *
22568  * @element INPUT
22569  * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
22570  *     then the `disabled` attribute will be set on the element
22571  */
22572
22573
22574 /**
22575  * @ngdoc directive
22576  * @name ngChecked
22577  * @restrict A
22578  * @priority 100
22579  *
22580  * @description
22581  * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
22582  *
22583  * Note that this directive should not be used together with {@link ngModel `ngModel`},
22584  * as this can lead to unexpected behavior.
22585  *
22586  * A special directive is necessary because we cannot use interpolation inside the `checked`
22587  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22588  *
22589  * @example
22590     <example name="ng-checked">
22591       <file name="index.html">
22592         <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
22593         <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
22594       </file>
22595       <file name="protractor.js" type="protractor">
22596         it('should check both checkBoxes', function() {
22597           expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
22598           element(by.model('master')).click();
22599           expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
22600         });
22601       </file>
22602     </example>
22603  *
22604  * @element INPUT
22605  * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
22606  *     then the `checked` attribute will be set on the element
22607  */
22608
22609
22610 /**
22611  * @ngdoc directive
22612  * @name ngReadonly
22613  * @restrict A
22614  * @priority 100
22615  *
22616  * @description
22617  *
22618  * Sets the `readonly` attribute on the element, if the expression inside `ngReadonly` is truthy.
22619  * Note that `readonly` applies only to `input` elements with specific types. [See the input docs on
22620  * MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) for more information.
22621  *
22622  * A special directive is necessary because we cannot use interpolation inside the `readonly`
22623  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22624  *
22625  * @example
22626     <example name="ng-readonly">
22627       <file name="index.html">
22628         <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
22629         <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
22630       </file>
22631       <file name="protractor.js" type="protractor">
22632         it('should toggle readonly attr', function() {
22633           expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
22634           element(by.model('checked')).click();
22635           expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
22636         });
22637       </file>
22638     </example>
22639  *
22640  * @element INPUT
22641  * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
22642  *     then special attribute "readonly" will be set on the element
22643  */
22644
22645
22646 /**
22647  * @ngdoc directive
22648  * @name ngSelected
22649  * @restrict A
22650  * @priority 100
22651  *
22652  * @description
22653  *
22654  * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
22655  *
22656  * A special directive is necessary because we cannot use interpolation inside the `selected`
22657  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22658  *
22659  * <div class="alert alert-warning">
22660  *   **Note:** `ngSelected` does not interact with the `select` and `ngModel` directives, it only
22661  *   sets the `selected` attribute on the element. If you are using `ngModel` on the select, you
22662  *   should not use `ngSelected` on the options, as `ngModel` will set the select value and
22663  *   selected options.
22664  * </div>
22665  *
22666  * @example
22667     <example name="ng-selected">
22668       <file name="index.html">
22669         <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
22670         <select aria-label="ngSelected demo">
22671           <option>Hello!</option>
22672           <option id="greet" ng-selected="selected">Greetings!</option>
22673         </select>
22674       </file>
22675       <file name="protractor.js" type="protractor">
22676         it('should select Greetings!', function() {
22677           expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
22678           element(by.model('selected')).click();
22679           expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
22680         });
22681       </file>
22682     </example>
22683  *
22684  * @element OPTION
22685  * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
22686  *     then special attribute "selected" will be set on the element
22687  */
22688
22689 /**
22690  * @ngdoc directive
22691  * @name ngOpen
22692  * @restrict A
22693  * @priority 100
22694  *
22695  * @description
22696  *
22697  * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
22698  *
22699  * A special directive is necessary because we cannot use interpolation inside the `open`
22700  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22701  *
22702  * ## A note about browser compatibility
22703  *
22704  * Edge, Firefox, and Internet Explorer do not support the `details` element, it is
22705  * recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead.
22706  *
22707  * @example
22708      <example name="ng-open">
22709        <file name="index.html">
22710          <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
22711          <details id="details" ng-open="open">
22712             <summary>Show/Hide me</summary>
22713          </details>
22714        </file>
22715        <file name="protractor.js" type="protractor">
22716          it('should toggle open', function() {
22717            expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
22718            element(by.model('open')).click();
22719            expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
22720          });
22721        </file>
22722      </example>
22723  *
22724  * @element DETAILS
22725  * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
22726  *     then special attribute "open" will be set on the element
22727  */
22728
22729 var ngAttributeAliasDirectives = {};
22730
22731 // boolean attrs are evaluated
22732 forEach(BOOLEAN_ATTR, function(propName, attrName) {
22733   // binding to multiple is not supported
22734   if (propName === 'multiple') return;
22735
22736   function defaultLinkFn(scope, element, attr) {
22737     scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
22738       attr.$set(attrName, !!value);
22739     });
22740   }
22741
22742   var normalized = directiveNormalize('ng-' + attrName);
22743   var linkFn = defaultLinkFn;
22744
22745   if (propName === 'checked') {
22746     linkFn = function(scope, element, attr) {
22747       // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
22748       if (attr.ngModel !== attr[normalized]) {
22749         defaultLinkFn(scope, element, attr);
22750       }
22751     };
22752   }
22753
22754   ngAttributeAliasDirectives[normalized] = function() {
22755     return {
22756       restrict: 'A',
22757       priority: 100,
22758       link: linkFn
22759     };
22760   };
22761 });
22762
22763 // aliased input attrs are evaluated
22764 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
22765   ngAttributeAliasDirectives[ngAttr] = function() {
22766     return {
22767       priority: 100,
22768       link: function(scope, element, attr) {
22769         //special case ngPattern when a literal regular expression value
22770         //is used as the expression (this way we don't have to watch anything).
22771         if (ngAttr === 'ngPattern' && attr.ngPattern.charAt(0) === '/') {
22772           var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
22773           if (match) {
22774             attr.$set('ngPattern', new RegExp(match[1], match[2]));
22775             return;
22776           }
22777         }
22778
22779         scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
22780           attr.$set(ngAttr, value);
22781         });
22782       }
22783     };
22784   };
22785 });
22786
22787 // ng-src, ng-srcset, ng-href are interpolated
22788 forEach(['src', 'srcset', 'href'], function(attrName) {
22789   var normalized = directiveNormalize('ng-' + attrName);
22790   ngAttributeAliasDirectives[normalized] = function() {
22791     return {
22792       priority: 99, // it needs to run after the attributes are interpolated
22793       link: function(scope, element, attr) {
22794         var propName = attrName,
22795             name = attrName;
22796
22797         if (attrName === 'href' &&
22798             toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
22799           name = 'xlinkHref';
22800           attr.$attr[name] = 'xlink:href';
22801           propName = null;
22802         }
22803
22804         attr.$observe(normalized, function(value) {
22805           if (!value) {
22806             if (attrName === 'href') {
22807               attr.$set(name, null);
22808             }
22809             return;
22810           }
22811
22812           attr.$set(name, value);
22813
22814           // Support: IE 9-11 only
22815           // On IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
22816           // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
22817           // to set the property as well to achieve the desired effect.
22818           // We use attr[attrName] value since $set can sanitize the url.
22819           if (msie && propName) element.prop(propName, attr[name]);
22820         });
22821       }
22822     };
22823   };
22824 });
22825
22826 /* global -nullFormCtrl, -PENDING_CLASS, -SUBMITTED_CLASS
22827  */
22828 var nullFormCtrl = {
22829   $addControl: noop,
22830   $$renameControl: nullFormRenameControl,
22831   $removeControl: noop,
22832   $setValidity: noop,
22833   $setDirty: noop,
22834   $setPristine: noop,
22835   $setSubmitted: noop
22836 },
22837 PENDING_CLASS = 'ng-pending',
22838 SUBMITTED_CLASS = 'ng-submitted';
22839
22840 function nullFormRenameControl(control, name) {
22841   control.$name = name;
22842 }
22843
22844 /**
22845  * @ngdoc type
22846  * @name form.FormController
22847  *
22848  * @property {boolean} $pristine True if user has not interacted with the form yet.
22849  * @property {boolean} $dirty True if user has already interacted with the form.
22850  * @property {boolean} $valid True if all of the containing forms and controls are valid.
22851  * @property {boolean} $invalid True if at least one containing control or form is invalid.
22852  * @property {boolean} $pending True if at least one containing control or form is pending.
22853  * @property {boolean} $submitted True if user has submitted the form even if its invalid.
22854  *
22855  * @property {Object} $error Is an object hash, containing references to controls or
22856  *  forms with failing validators, where:
22857  *
22858  *  - keys are validation tokens (error names),
22859  *  - values are arrays of controls or forms that have a failing validator for given error name.
22860  *
22861  *  Built-in validation tokens:
22862  *
22863  *  - `email`
22864  *  - `max`
22865  *  - `maxlength`
22866  *  - `min`
22867  *  - `minlength`
22868  *  - `number`
22869  *  - `pattern`
22870  *  - `required`
22871  *  - `url`
22872  *  - `date`
22873  *  - `datetimelocal`
22874  *  - `time`
22875  *  - `week`
22876  *  - `month`
22877  *
22878  * @description
22879  * `FormController` keeps track of all its controls and nested forms as well as the state of them,
22880  * such as being valid/invalid or dirty/pristine.
22881  *
22882  * Each {@link ng.directive:form form} directive creates an instance
22883  * of `FormController`.
22884  *
22885  */
22886 //asks for $scope to fool the BC controller module
22887 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
22888 function FormController($element, $attrs, $scope, $animate, $interpolate) {
22889   this.$$controls = [];
22890
22891   // init state
22892   this.$error = {};
22893   this.$$success = {};
22894   this.$pending = undefined;
22895   this.$name = $interpolate($attrs.name || $attrs.ngForm || '')($scope);
22896   this.$dirty = false;
22897   this.$pristine = true;
22898   this.$valid = true;
22899   this.$invalid = false;
22900   this.$submitted = false;
22901   this.$$parentForm = nullFormCtrl;
22902
22903   this.$$element = $element;
22904   this.$$animate = $animate;
22905
22906   setupValidity(this);
22907 }
22908
22909 FormController.prototype = {
22910   /**
22911    * @ngdoc method
22912    * @name form.FormController#$rollbackViewValue
22913    *
22914    * @description
22915    * Rollback all form controls pending updates to the `$modelValue`.
22916    *
22917    * Updates may be pending by a debounced event or because the input is waiting for a some future
22918    * event defined in `ng-model-options`. This method is typically needed by the reset button of
22919    * a form that uses `ng-model-options` to pend updates.
22920    */
22921   $rollbackViewValue: function() {
22922     forEach(this.$$controls, function(control) {
22923       control.$rollbackViewValue();
22924     });
22925   },
22926
22927   /**
22928    * @ngdoc method
22929    * @name form.FormController#$commitViewValue
22930    *
22931    * @description
22932    * Commit all form controls pending updates to the `$modelValue`.
22933    *
22934    * Updates may be pending by a debounced event or because the input is waiting for a some future
22935    * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
22936    * usually handles calling this in response to input events.
22937    */
22938   $commitViewValue: function() {
22939     forEach(this.$$controls, function(control) {
22940       control.$commitViewValue();
22941     });
22942   },
22943
22944   /**
22945    * @ngdoc method
22946    * @name form.FormController#$addControl
22947    * @param {object} control control object, either a {@link form.FormController} or an
22948    * {@link ngModel.NgModelController}
22949    *
22950    * @description
22951    * Register a control with the form. Input elements using ngModelController do this automatically
22952    * when they are linked.
22953    *
22954    * Note that the current state of the control will not be reflected on the new parent form. This
22955    * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
22956    * state.
22957    *
22958    * However, if the method is used programmatically, for example by adding dynamically created controls,
22959    * or controls that have been previously removed without destroying their corresponding DOM element,
22960    * it's the developers responsibility to make sure the current state propagates to the parent form.
22961    *
22962    * For example, if an input control is added that is already `$dirty` and has `$error` properties,
22963    * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
22964    */
22965   $addControl: function(control) {
22966     // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
22967     // and not added to the scope.  Now we throw an error.
22968     assertNotHasOwnProperty(control.$name, 'input');
22969     this.$$controls.push(control);
22970
22971     if (control.$name) {
22972       this[control.$name] = control;
22973     }
22974
22975     control.$$parentForm = this;
22976   },
22977
22978   // Private API: rename a form control
22979   $$renameControl: function(control, newName) {
22980     var oldName = control.$name;
22981
22982     if (this[oldName] === control) {
22983       delete this[oldName];
22984     }
22985     this[newName] = control;
22986     control.$name = newName;
22987   },
22988
22989   /**
22990    * @ngdoc method
22991    * @name form.FormController#$removeControl
22992    * @param {object} control control object, either a {@link form.FormController} or an
22993    * {@link ngModel.NgModelController}
22994    *
22995    * @description
22996    * Deregister a control from the form.
22997    *
22998    * Input elements using ngModelController do this automatically when they are destroyed.
22999    *
23000    * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
23001    * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
23002    * different from case to case. For example, removing the only `$dirty` control from a form may or
23003    * may not mean that the form is still `$dirty`.
23004    */
23005   $removeControl: function(control) {
23006     if (control.$name && this[control.$name] === control) {
23007       delete this[control.$name];
23008     }
23009     forEach(this.$pending, function(value, name) {
23010       // eslint-disable-next-line no-invalid-this
23011       this.$setValidity(name, null, control);
23012     }, this);
23013     forEach(this.$error, function(value, name) {
23014       // eslint-disable-next-line no-invalid-this
23015       this.$setValidity(name, null, control);
23016     }, this);
23017     forEach(this.$$success, function(value, name) {
23018       // eslint-disable-next-line no-invalid-this
23019       this.$setValidity(name, null, control);
23020     }, this);
23021
23022     arrayRemove(this.$$controls, control);
23023     control.$$parentForm = nullFormCtrl;
23024   },
23025
23026   /**
23027    * @ngdoc method
23028    * @name form.FormController#$setDirty
23029    *
23030    * @description
23031    * Sets the form to a dirty state.
23032    *
23033    * This method can be called to add the 'ng-dirty' class and set the form to a dirty
23034    * state (ng-dirty class). This method will also propagate to parent forms.
23035    */
23036   $setDirty: function() {
23037     this.$$animate.removeClass(this.$$element, PRISTINE_CLASS);
23038     this.$$animate.addClass(this.$$element, DIRTY_CLASS);
23039     this.$dirty = true;
23040     this.$pristine = false;
23041     this.$$parentForm.$setDirty();
23042   },
23043
23044   /**
23045    * @ngdoc method
23046    * @name form.FormController#$setPristine
23047    *
23048    * @description
23049    * Sets the form to its pristine state.
23050    *
23051    * This method sets the form's `$pristine` state to true, the `$dirty` state to false, removes
23052    * the `ng-dirty` class and adds the `ng-pristine` class. Additionally, it sets the `$submitted`
23053    * state to false.
23054    *
23055    * This method will also propagate to all the controls contained in this form.
23056    *
23057    * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
23058    * saving or resetting it.
23059    */
23060   $setPristine: function() {
23061     this.$$animate.setClass(this.$$element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
23062     this.$dirty = false;
23063     this.$pristine = true;
23064     this.$submitted = false;
23065     forEach(this.$$controls, function(control) {
23066       control.$setPristine();
23067     });
23068   },
23069
23070   /**
23071    * @ngdoc method
23072    * @name form.FormController#$setUntouched
23073    *
23074    * @description
23075    * Sets the form to its untouched state.
23076    *
23077    * This method can be called to remove the 'ng-touched' class and set the form controls to their
23078    * untouched state (ng-untouched class).
23079    *
23080    * Setting a form controls back to their untouched state is often useful when setting the form
23081    * back to its pristine state.
23082    */
23083   $setUntouched: function() {
23084     forEach(this.$$controls, function(control) {
23085       control.$setUntouched();
23086     });
23087   },
23088
23089   /**
23090    * @ngdoc method
23091    * @name form.FormController#$setSubmitted
23092    *
23093    * @description
23094    * Sets the form to its submitted state.
23095    */
23096   $setSubmitted: function() {
23097     this.$$animate.addClass(this.$$element, SUBMITTED_CLASS);
23098     this.$submitted = true;
23099     this.$$parentForm.$setSubmitted();
23100   }
23101 };
23102
23103 /**
23104  * @ngdoc method
23105  * @name form.FormController#$setValidity
23106  *
23107  * @description
23108  * Sets the validity of a form control.
23109  *
23110  * This method will also propagate to parent forms.
23111  */
23112 addSetValidityMethod({
23113   clazz: FormController,
23114   set: function(object, property, controller) {
23115     var list = object[property];
23116     if (!list) {
23117       object[property] = [controller];
23118     } else {
23119       var index = list.indexOf(controller);
23120       if (index === -1) {
23121         list.push(controller);
23122       }
23123     }
23124   },
23125   unset: function(object, property, controller) {
23126     var list = object[property];
23127     if (!list) {
23128       return;
23129     }
23130     arrayRemove(list, controller);
23131     if (list.length === 0) {
23132       delete object[property];
23133     }
23134   }
23135 });
23136
23137 /**
23138  * @ngdoc directive
23139  * @name ngForm
23140  * @restrict EAC
23141  *
23142  * @description
23143  * Nestable alias of {@link ng.directive:form `form`} directive. HTML
23144  * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
23145  * sub-group of controls needs to be determined.
23146  *
23147  * Note: the purpose of `ngForm` is to group controls,
23148  * but not to be a replacement for the `<form>` tag with all of its capabilities
23149  * (e.g. posting to the server, ...).
23150  *
23151  * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
23152  *                       related scope, under this name.
23153  *
23154  */
23155
23156  /**
23157  * @ngdoc directive
23158  * @name form
23159  * @restrict E
23160  *
23161  * @description
23162  * Directive that instantiates
23163  * {@link form.FormController FormController}.
23164  *
23165  * If the `name` attribute is specified, the form controller is published onto the current scope under
23166  * this name.
23167  *
23168  * # Alias: {@link ng.directive:ngForm `ngForm`}
23169  *
23170  * In Angular, forms can be nested. This means that the outer form is valid when all of the child
23171  * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
23172  * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
23173  * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
23174  * of controls needs to be determined.
23175  *
23176  * # CSS classes
23177  *  - `ng-valid` is set if the form is valid.
23178  *  - `ng-invalid` is set if the form is invalid.
23179  *  - `ng-pending` is set if the form is pending.
23180  *  - `ng-pristine` is set if the form is pristine.
23181  *  - `ng-dirty` is set if the form is dirty.
23182  *  - `ng-submitted` is set if the form was submitted.
23183  *
23184  * Keep in mind that ngAnimate can detect each of these classes when added and removed.
23185  *
23186  *
23187  * # Submitting a form and preventing the default action
23188  *
23189  * Since the role of forms in client-side Angular applications is different than in classical
23190  * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
23191  * page reload that sends the data to the server. Instead some javascript logic should be triggered
23192  * to handle the form submission in an application-specific way.
23193  *
23194  * For this reason, Angular prevents the default action (form submission to the server) unless the
23195  * `<form>` element has an `action` attribute specified.
23196  *
23197  * You can use one of the following two ways to specify what javascript method should be called when
23198  * a form is submitted:
23199  *
23200  * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
23201  * - {@link ng.directive:ngClick ngClick} directive on the first
23202   *  button or input field of type submit (input[type=submit])
23203  *
23204  * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
23205  * or {@link ng.directive:ngClick ngClick} directives.
23206  * This is because of the following form submission rules in the HTML specification:
23207  *
23208  * - If a form has only one input field then hitting enter in this field triggers form submit
23209  * (`ngSubmit`)
23210  * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
23211  * doesn't trigger submit
23212  * - if a form has one or more input fields and one or more buttons or input[type=submit] then
23213  * hitting enter in any of the input fields will trigger the click handler on the *first* button or
23214  * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
23215  *
23216  * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
23217  * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
23218  * to have access to the updated model.
23219  *
23220  * ## Animation Hooks
23221  *
23222  * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
23223  * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
23224  * other validations that are performed within the form. Animations in ngForm are similar to how
23225  * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
23226  * as JS animations.
23227  *
23228  * The following example shows a simple way to utilize CSS transitions to style a form element
23229  * that has been rendered as invalid after it has been validated:
23230  *
23231  * <pre>
23232  * //be sure to include ngAnimate as a module to hook into more
23233  * //advanced animations
23234  * .my-form {
23235  *   transition:0.5s linear all;
23236  *   background: white;
23237  * }
23238  * .my-form.ng-invalid {
23239  *   background: red;
23240  *   color:white;
23241  * }
23242  * </pre>
23243  *
23244  * @example
23245     <example name="ng-form" deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
23246       <file name="index.html">
23247        <script>
23248          angular.module('formExample', [])
23249            .controller('FormController', ['$scope', function($scope) {
23250              $scope.userType = 'guest';
23251            }]);
23252        </script>
23253        <style>
23254         .my-form {
23255           transition:all linear 0.5s;
23256           background: transparent;
23257         }
23258         .my-form.ng-invalid {
23259           background: red;
23260         }
23261        </style>
23262        <form name="myForm" ng-controller="FormController" class="my-form">
23263          userType: <input name="input" ng-model="userType" required>
23264          <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
23265          <code>userType = {{userType}}</code><br>
23266          <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
23267          <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
23268          <code>myForm.$valid = {{myForm.$valid}}</code><br>
23269          <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
23270         </form>
23271       </file>
23272       <file name="protractor.js" type="protractor">
23273         it('should initialize to model', function() {
23274           var userType = element(by.binding('userType'));
23275           var valid = element(by.binding('myForm.input.$valid'));
23276
23277           expect(userType.getText()).toContain('guest');
23278           expect(valid.getText()).toContain('true');
23279         });
23280
23281         it('should be invalid if empty', function() {
23282           var userType = element(by.binding('userType'));
23283           var valid = element(by.binding('myForm.input.$valid'));
23284           var userInput = element(by.model('userType'));
23285
23286           userInput.clear();
23287           userInput.sendKeys('');
23288
23289           expect(userType.getText()).toEqual('userType =');
23290           expect(valid.getText()).toContain('false');
23291         });
23292       </file>
23293     </example>
23294  *
23295  * @param {string=} name Name of the form. If specified, the form controller will be published into
23296  *                       related scope, under this name.
23297  */
23298 var formDirectiveFactory = function(isNgForm) {
23299   return ['$timeout', '$parse', function($timeout, $parse) {
23300     var formDirective = {
23301       name: 'form',
23302       restrict: isNgForm ? 'EAC' : 'E',
23303       require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
23304       controller: FormController,
23305       compile: function ngFormCompile(formElement, attr) {
23306         // Setup initial state of the control
23307         formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
23308
23309         var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
23310
23311         return {
23312           pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
23313             var controller = ctrls[0];
23314
23315             // if `action` attr is not present on the form, prevent the default action (submission)
23316             if (!('action' in attr)) {
23317               // we can't use jq events because if a form is destroyed during submission the default
23318               // action is not prevented. see #1238
23319               //
23320               // IE 9 is not affected because it doesn't fire a submit event and try to do a full
23321               // page reload if the form was destroyed by submission of the form via a click handler
23322               // on a button in the form. Looks like an IE9 specific bug.
23323               var handleFormSubmission = function(event) {
23324                 scope.$apply(function() {
23325                   controller.$commitViewValue();
23326                   controller.$setSubmitted();
23327                 });
23328
23329                 event.preventDefault();
23330               };
23331
23332               formElement[0].addEventListener('submit', handleFormSubmission);
23333
23334               // unregister the preventDefault listener so that we don't not leak memory but in a
23335               // way that will achieve the prevention of the default action.
23336               formElement.on('$destroy', function() {
23337                 $timeout(function() {
23338                   formElement[0].removeEventListener('submit', handleFormSubmission);
23339                 }, 0, false);
23340               });
23341             }
23342
23343             var parentFormCtrl = ctrls[1] || controller.$$parentForm;
23344             parentFormCtrl.$addControl(controller);
23345
23346             var setter = nameAttr ? getSetter(controller.$name) : noop;
23347
23348             if (nameAttr) {
23349               setter(scope, controller);
23350               attr.$observe(nameAttr, function(newValue) {
23351                 if (controller.$name === newValue) return;
23352                 setter(scope, undefined);
23353                 controller.$$parentForm.$$renameControl(controller, newValue);
23354                 setter = getSetter(controller.$name);
23355                 setter(scope, controller);
23356               });
23357             }
23358             formElement.on('$destroy', function() {
23359               controller.$$parentForm.$removeControl(controller);
23360               setter(scope, undefined);
23361               extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
23362             });
23363           }
23364         };
23365       }
23366     };
23367
23368     return formDirective;
23369
23370     function getSetter(expression) {
23371       if (expression === '') {
23372         //create an assignable expression, so forms with an empty name can be renamed later
23373         return $parse('this[""]').assign;
23374       }
23375       return $parse(expression).assign || noop;
23376     }
23377   }];
23378 };
23379
23380 var formDirective = formDirectiveFactory();
23381 var ngFormDirective = formDirectiveFactory(true);
23382
23383
23384
23385 // helper methods
23386 function setupValidity(instance) {
23387   instance.$$classCache = {};
23388   instance.$$classCache[INVALID_CLASS] = !(instance.$$classCache[VALID_CLASS] = instance.$$element.hasClass(VALID_CLASS));
23389 }
23390 function addSetValidityMethod(context) {
23391   var clazz = context.clazz,
23392       set = context.set,
23393       unset = context.unset;
23394
23395   clazz.prototype.$setValidity = function(validationErrorKey, state, controller) {
23396     if (isUndefined(state)) {
23397       createAndSet(this, '$pending', validationErrorKey, controller);
23398     } else {
23399       unsetAndCleanup(this, '$pending', validationErrorKey, controller);
23400     }
23401     if (!isBoolean(state)) {
23402       unset(this.$error, validationErrorKey, controller);
23403       unset(this.$$success, validationErrorKey, controller);
23404     } else {
23405       if (state) {
23406         unset(this.$error, validationErrorKey, controller);
23407         set(this.$$success, validationErrorKey, controller);
23408       } else {
23409         set(this.$error, validationErrorKey, controller);
23410         unset(this.$$success, validationErrorKey, controller);
23411       }
23412     }
23413     if (this.$pending) {
23414       cachedToggleClass(this, PENDING_CLASS, true);
23415       this.$valid = this.$invalid = undefined;
23416       toggleValidationCss(this, '', null);
23417     } else {
23418       cachedToggleClass(this, PENDING_CLASS, false);
23419       this.$valid = isObjectEmpty(this.$error);
23420       this.$invalid = !this.$valid;
23421       toggleValidationCss(this, '', this.$valid);
23422     }
23423
23424     // re-read the state as the set/unset methods could have
23425     // combined state in this.$error[validationError] (used for forms),
23426     // where setting/unsetting only increments/decrements the value,
23427     // and does not replace it.
23428     var combinedState;
23429     if (this.$pending && this.$pending[validationErrorKey]) {
23430       combinedState = undefined;
23431     } else if (this.$error[validationErrorKey]) {
23432       combinedState = false;
23433     } else if (this.$$success[validationErrorKey]) {
23434       combinedState = true;
23435     } else {
23436       combinedState = null;
23437     }
23438
23439     toggleValidationCss(this, validationErrorKey, combinedState);
23440     this.$$parentForm.$setValidity(validationErrorKey, combinedState, this);
23441   };
23442
23443   function createAndSet(ctrl, name, value, controller) {
23444     if (!ctrl[name]) {
23445       ctrl[name] = {};
23446     }
23447     set(ctrl[name], value, controller);
23448   }
23449
23450   function unsetAndCleanup(ctrl, name, value, controller) {
23451     if (ctrl[name]) {
23452       unset(ctrl[name], value, controller);
23453     }
23454     if (isObjectEmpty(ctrl[name])) {
23455       ctrl[name] = undefined;
23456     }
23457   }
23458
23459   function cachedToggleClass(ctrl, className, switchValue) {
23460     if (switchValue && !ctrl.$$classCache[className]) {
23461       ctrl.$$animate.addClass(ctrl.$$element, className);
23462       ctrl.$$classCache[className] = true;
23463     } else if (!switchValue && ctrl.$$classCache[className]) {
23464       ctrl.$$animate.removeClass(ctrl.$$element, className);
23465       ctrl.$$classCache[className] = false;
23466     }
23467   }
23468
23469   function toggleValidationCss(ctrl, validationErrorKey, isValid) {
23470     validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
23471
23472     cachedToggleClass(ctrl, VALID_CLASS + validationErrorKey, isValid === true);
23473     cachedToggleClass(ctrl, INVALID_CLASS + validationErrorKey, isValid === false);
23474   }
23475 }
23476
23477 function isObjectEmpty(obj) {
23478   if (obj) {
23479     for (var prop in obj) {
23480       if (obj.hasOwnProperty(prop)) {
23481         return false;
23482       }
23483     }
23484   }
23485   return true;
23486 }
23487
23488 /* global
23489   VALID_CLASS: false,
23490   INVALID_CLASS: false,
23491   PRISTINE_CLASS: false,
23492   DIRTY_CLASS: false,
23493   ngModelMinErr: false
23494 */
23495
23496 // Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
23497 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)$/;
23498 // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
23499 // Note: We are being more lenient, because browsers are too.
23500 //   1. Scheme
23501 //   2. Slashes
23502 //   3. Username
23503 //   4. Password
23504 //   5. Hostname
23505 //   6. Port
23506 //   7. Path
23507 //   8. Query
23508 //   9. Fragment
23509 //                 1111111111111111 222   333333    44444        55555555555555555555555     666     77777777     8888888     999
23510 var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
23511 // eslint-disable-next-line max-len
23512 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])?)*$/;
23513 var NUMBER_REGEXP = /^\s*(-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
23514 var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
23515 var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
23516 var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
23517 var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
23518 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
23519
23520 var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
23521 var PARTIAL_VALIDATION_TYPES = createMap();
23522 forEach('date,datetime-local,month,time,week'.split(','), function(type) {
23523   PARTIAL_VALIDATION_TYPES[type] = true;
23524 });
23525
23526 var inputType = {
23527
23528   /**
23529    * @ngdoc input
23530    * @name input[text]
23531    *
23532    * @description
23533    * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
23534    *
23535    *
23536    * @param {string} ngModel Assignable angular expression to data-bind to.
23537    * @param {string=} name Property name of the form under which the control is published.
23538    * @param {string=} required Adds `required` validation error key if the value is not entered.
23539    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23540    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23541    *    `required` when you want to data-bind to the `required` attribute.
23542    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
23543    *    minlength.
23544    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
23545    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
23546    *    any length.
23547    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
23548    *    that contains the regular expression body that will be converted to a regular expression
23549    *    as in the ngPattern directive.
23550    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
23551    *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
23552    *    If the expression evaluates to a RegExp object, then this is used directly.
23553    *    If the expression evaluates to a string, then it will be converted to a RegExp
23554    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
23555    *    `new RegExp('^abc$')`.<br />
23556    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
23557    *    start at the index of the last search's match, thus not taking the whole input value into
23558    *    account.
23559    * @param {string=} ngChange Angular expression to be executed when input changes due to user
23560    *    interaction with the input element.
23561    * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
23562    *    This parameter is ignored for input[type=password] controls, which will never trim the
23563    *    input.
23564    *
23565    * @example
23566       <example name="text-input-directive" module="textInputExample">
23567         <file name="index.html">
23568          <script>
23569            angular.module('textInputExample', [])
23570              .controller('ExampleController', ['$scope', function($scope) {
23571                $scope.example = {
23572                  text: 'guest',
23573                  word: /^\s*\w*\s*$/
23574                };
23575              }]);
23576          </script>
23577          <form name="myForm" ng-controller="ExampleController">
23578            <label>Single word:
23579              <input type="text" name="input" ng-model="example.text"
23580                     ng-pattern="example.word" required ng-trim="false">
23581            </label>
23582            <div role="alert">
23583              <span class="error" ng-show="myForm.input.$error.required">
23584                Required!</span>
23585              <span class="error" ng-show="myForm.input.$error.pattern">
23586                Single word only!</span>
23587            </div>
23588            <code>text = {{example.text}}</code><br/>
23589            <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br/>
23590            <code>myForm.input.$error = {{myForm.input.$error}}</code><br/>
23591            <code>myForm.$valid = {{myForm.$valid}}</code><br/>
23592            <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br/>
23593           </form>
23594         </file>
23595         <file name="protractor.js" type="protractor">
23596           var text = element(by.binding('example.text'));
23597           var valid = element(by.binding('myForm.input.$valid'));
23598           var input = element(by.model('example.text'));
23599
23600           it('should initialize to model', function() {
23601             expect(text.getText()).toContain('guest');
23602             expect(valid.getText()).toContain('true');
23603           });
23604
23605           it('should be invalid if empty', function() {
23606             input.clear();
23607             input.sendKeys('');
23608
23609             expect(text.getText()).toEqual('text =');
23610             expect(valid.getText()).toContain('false');
23611           });
23612
23613           it('should be invalid if multi word', function() {
23614             input.clear();
23615             input.sendKeys('hello world');
23616
23617             expect(valid.getText()).toContain('false');
23618           });
23619         </file>
23620       </example>
23621    */
23622   'text': textInputType,
23623
23624     /**
23625      * @ngdoc input
23626      * @name input[date]
23627      *
23628      * @description
23629      * Input with date validation and transformation. In browsers that do not yet support
23630      * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
23631      * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
23632      * modern browsers do not yet support this input type, it is important to provide cues to users on the
23633      * expected input format via a placeholder or label.
23634      *
23635      * The model must always be a Date object, otherwise Angular will throw an error.
23636      * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23637      *
23638      * The timezone to be used to read/write the `Date` instance in the model can be defined using
23639      * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23640      *
23641      * @param {string} ngModel Assignable angular expression to data-bind to.
23642      * @param {string=} name Property name of the form under which the control is published.
23643      * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
23644      *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
23645      *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
23646      *   constraint validation.
23647      * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
23648      *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
23649      *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
23650      *   constraint validation.
23651      * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
23652      *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23653      * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
23654      *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23655      * @param {string=} required Sets `required` validation error key if the value is not entered.
23656      * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23657      *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23658      *    `required` when you want to data-bind to the `required` attribute.
23659      * @param {string=} ngChange Angular expression to be executed when input changes due to user
23660      *    interaction with the input element.
23661      *
23662      * @example
23663      <example name="date-input-directive" module="dateInputExample">
23664      <file name="index.html">
23665        <script>
23666           angular.module('dateInputExample', [])
23667             .controller('DateController', ['$scope', function($scope) {
23668               $scope.example = {
23669                 value: new Date(2013, 9, 22)
23670               };
23671             }]);
23672        </script>
23673        <form name="myForm" ng-controller="DateController as dateCtrl">
23674           <label for="exampleInput">Pick a date in 2013:</label>
23675           <input type="date" id="exampleInput" name="input" ng-model="example.value"
23676               placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
23677           <div role="alert">
23678             <span class="error" ng-show="myForm.input.$error.required">
23679                 Required!</span>
23680             <span class="error" ng-show="myForm.input.$error.date">
23681                 Not a valid date!</span>
23682            </div>
23683            <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
23684            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23685            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23686            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23687            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23688        </form>
23689      </file>
23690      <file name="protractor.js" type="protractor">
23691         var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
23692         var valid = element(by.binding('myForm.input.$valid'));
23693
23694         // currently protractor/webdriver does not support
23695         // sending keys to all known HTML5 input controls
23696         // for various browsers (see https://github.com/angular/protractor/issues/562).
23697         function setInput(val) {
23698           // set the value of the element and force validation.
23699           var scr = "var ipt = document.getElementById('exampleInput'); " +
23700           "ipt.value = '" + val + "';" +
23701           "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
23702           browser.executeScript(scr);
23703         }
23704
23705         it('should initialize to model', function() {
23706           expect(value.getText()).toContain('2013-10-22');
23707           expect(valid.getText()).toContain('myForm.input.$valid = true');
23708         });
23709
23710         it('should be invalid if empty', function() {
23711           setInput('');
23712           expect(value.getText()).toEqual('value =');
23713           expect(valid.getText()).toContain('myForm.input.$valid = false');
23714         });
23715
23716         it('should be invalid if over max', function() {
23717           setInput('2015-01-01');
23718           expect(value.getText()).toContain('');
23719           expect(valid.getText()).toContain('myForm.input.$valid = false');
23720         });
23721      </file>
23722      </example>
23723      */
23724   'date': createDateInputType('date', DATE_REGEXP,
23725          createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
23726          'yyyy-MM-dd'),
23727
23728    /**
23729     * @ngdoc input
23730     * @name input[datetime-local]
23731     *
23732     * @description
23733     * Input with datetime validation and transformation. In browsers that do not yet support
23734     * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
23735     * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
23736     *
23737     * The model must always be a Date object, otherwise Angular will throw an error.
23738     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23739     *
23740     * The timezone to be used to read/write the `Date` instance in the model can be defined using
23741     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23742     *
23743     * @param {string} ngModel Assignable angular expression to data-bind to.
23744     * @param {string=} name Property name of the form under which the control is published.
23745     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
23746     *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
23747     *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
23748     *   Note that `min` will also add native HTML5 constraint validation.
23749     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
23750     *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
23751     *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
23752     *   Note that `max` will also add native HTML5 constraint validation.
23753     * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
23754     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23755     * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
23756     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23757     * @param {string=} required Sets `required` validation error key if the value is not entered.
23758     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23759     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23760     *    `required` when you want to data-bind to the `required` attribute.
23761     * @param {string=} ngChange Angular expression to be executed when input changes due to user
23762     *    interaction with the input element.
23763     *
23764     * @example
23765     <example name="datetimelocal-input-directive" module="dateExample">
23766     <file name="index.html">
23767       <script>
23768         angular.module('dateExample', [])
23769           .controller('DateController', ['$scope', function($scope) {
23770             $scope.example = {
23771               value: new Date(2010, 11, 28, 14, 57)
23772             };
23773           }]);
23774       </script>
23775       <form name="myForm" ng-controller="DateController as dateCtrl">
23776         <label for="exampleInput">Pick a date between in 2013:</label>
23777         <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
23778             placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
23779         <div role="alert">
23780           <span class="error" ng-show="myForm.input.$error.required">
23781               Required!</span>
23782           <span class="error" ng-show="myForm.input.$error.datetimelocal">
23783               Not a valid date!</span>
23784         </div>
23785         <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
23786         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23787         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23788         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23789         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23790       </form>
23791     </file>
23792     <file name="protractor.js" type="protractor">
23793       var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
23794       var valid = element(by.binding('myForm.input.$valid'));
23795
23796       // currently protractor/webdriver does not support
23797       // sending keys to all known HTML5 input controls
23798       // for various browsers (https://github.com/angular/protractor/issues/562).
23799       function setInput(val) {
23800         // set the value of the element and force validation.
23801         var scr = "var ipt = document.getElementById('exampleInput'); " +
23802         "ipt.value = '" + val + "';" +
23803         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
23804         browser.executeScript(scr);
23805       }
23806
23807       it('should initialize to model', function() {
23808         expect(value.getText()).toContain('2010-12-28T14:57:00');
23809         expect(valid.getText()).toContain('myForm.input.$valid = true');
23810       });
23811
23812       it('should be invalid if empty', function() {
23813         setInput('');
23814         expect(value.getText()).toEqual('value =');
23815         expect(valid.getText()).toContain('myForm.input.$valid = false');
23816       });
23817
23818       it('should be invalid if over max', function() {
23819         setInput('2015-01-01T23:59:00');
23820         expect(value.getText()).toContain('');
23821         expect(valid.getText()).toContain('myForm.input.$valid = false');
23822       });
23823     </file>
23824     </example>
23825     */
23826   'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
23827       createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
23828       'yyyy-MM-ddTHH:mm:ss.sss'),
23829
23830   /**
23831    * @ngdoc input
23832    * @name input[time]
23833    *
23834    * @description
23835    * Input with time validation and transformation. In browsers that do not yet support
23836    * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
23837    * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
23838    * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
23839    *
23840    * The model must always be a Date object, otherwise Angular will throw an error.
23841    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23842    *
23843    * The timezone to be used to read/write the `Date` instance in the model can be defined using
23844    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23845    *
23846    * @param {string} ngModel Assignable angular expression to data-bind to.
23847    * @param {string=} name Property name of the form under which the control is published.
23848    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
23849    *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
23850    *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
23851    *   native HTML5 constraint validation.
23852    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
23853    *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
23854    *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
23855    *   native HTML5 constraint validation.
23856    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
23857    *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23858    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
23859    *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23860    * @param {string=} required Sets `required` validation error key if the value is not entered.
23861    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23862    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23863    *    `required` when you want to data-bind to the `required` attribute.
23864    * @param {string=} ngChange Angular expression to be executed when input changes due to user
23865    *    interaction with the input element.
23866    *
23867    * @example
23868    <example name="time-input-directive" module="timeExample">
23869    <file name="index.html">
23870      <script>
23871       angular.module('timeExample', [])
23872         .controller('DateController', ['$scope', function($scope) {
23873           $scope.example = {
23874             value: new Date(1970, 0, 1, 14, 57, 0)
23875           };
23876         }]);
23877      </script>
23878      <form name="myForm" ng-controller="DateController as dateCtrl">
23879         <label for="exampleInput">Pick a time between 8am and 5pm:</label>
23880         <input type="time" id="exampleInput" name="input" ng-model="example.value"
23881             placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
23882         <div role="alert">
23883           <span class="error" ng-show="myForm.input.$error.required">
23884               Required!</span>
23885           <span class="error" ng-show="myForm.input.$error.time">
23886               Not a valid date!</span>
23887         </div>
23888         <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
23889         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23890         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23891         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23892         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23893      </form>
23894    </file>
23895    <file name="protractor.js" type="protractor">
23896       var value = element(by.binding('example.value | date: "HH:mm:ss"'));
23897       var valid = element(by.binding('myForm.input.$valid'));
23898
23899       // currently protractor/webdriver does not support
23900       // sending keys to all known HTML5 input controls
23901       // for various browsers (https://github.com/angular/protractor/issues/562).
23902       function setInput(val) {
23903         // set the value of the element and force validation.
23904         var scr = "var ipt = document.getElementById('exampleInput'); " +
23905         "ipt.value = '" + val + "';" +
23906         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
23907         browser.executeScript(scr);
23908       }
23909
23910       it('should initialize to model', function() {
23911         expect(value.getText()).toContain('14:57:00');
23912         expect(valid.getText()).toContain('myForm.input.$valid = true');
23913       });
23914
23915       it('should be invalid if empty', function() {
23916         setInput('');
23917         expect(value.getText()).toEqual('value =');
23918         expect(valid.getText()).toContain('myForm.input.$valid = false');
23919       });
23920
23921       it('should be invalid if over max', function() {
23922         setInput('23:59:00');
23923         expect(value.getText()).toContain('');
23924         expect(valid.getText()).toContain('myForm.input.$valid = false');
23925       });
23926    </file>
23927    </example>
23928    */
23929   'time': createDateInputType('time', TIME_REGEXP,
23930       createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
23931      'HH:mm:ss.sss'),
23932
23933    /**
23934     * @ngdoc input
23935     * @name input[week]
23936     *
23937     * @description
23938     * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
23939     * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
23940     * week format (yyyy-W##), for example: `2013-W02`.
23941     *
23942     * The model must always be a Date object, otherwise Angular will throw an error.
23943     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23944     *
23945     * The timezone to be used to read/write the `Date` instance in the model can be defined using
23946     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23947     *
23948     * @param {string} ngModel Assignable angular expression to data-bind to.
23949     * @param {string=} name Property name of the form under which the control is published.
23950     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
23951     *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
23952     *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
23953     *   native HTML5 constraint validation.
23954     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
23955     *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
23956     *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
23957     *   native HTML5 constraint validation.
23958     * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
23959     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23960     * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
23961     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23962     * @param {string=} required Sets `required` validation error key if the value is not entered.
23963     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23964     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23965     *    `required` when you want to data-bind to the `required` attribute.
23966     * @param {string=} ngChange Angular expression to be executed when input changes due to user
23967     *    interaction with the input element.
23968     *
23969     * @example
23970     <example name="week-input-directive" module="weekExample">
23971     <file name="index.html">
23972       <script>
23973       angular.module('weekExample', [])
23974         .controller('DateController', ['$scope', function($scope) {
23975           $scope.example = {
23976             value: new Date(2013, 0, 3)
23977           };
23978         }]);
23979       </script>
23980       <form name="myForm" ng-controller="DateController as dateCtrl">
23981         <label>Pick a date between in 2013:
23982           <input id="exampleInput" type="week" name="input" ng-model="example.value"
23983                  placeholder="YYYY-W##" min="2012-W32"
23984                  max="2013-W52" required />
23985         </label>
23986         <div role="alert">
23987           <span class="error" ng-show="myForm.input.$error.required">
23988               Required!</span>
23989           <span class="error" ng-show="myForm.input.$error.week">
23990               Not a valid date!</span>
23991         </div>
23992         <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
23993         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23994         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23995         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23996         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23997       </form>
23998     </file>
23999     <file name="protractor.js" type="protractor">
24000       var value = element(by.binding('example.value | date: "yyyy-Www"'));
24001       var valid = element(by.binding('myForm.input.$valid'));
24002
24003       // currently protractor/webdriver does not support
24004       // sending keys to all known HTML5 input controls
24005       // for various browsers (https://github.com/angular/protractor/issues/562).
24006       function setInput(val) {
24007         // set the value of the element and force validation.
24008         var scr = "var ipt = document.getElementById('exampleInput'); " +
24009         "ipt.value = '" + val + "';" +
24010         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
24011         browser.executeScript(scr);
24012       }
24013
24014       it('should initialize to model', function() {
24015         expect(value.getText()).toContain('2013-W01');
24016         expect(valid.getText()).toContain('myForm.input.$valid = true');
24017       });
24018
24019       it('should be invalid if empty', function() {
24020         setInput('');
24021         expect(value.getText()).toEqual('value =');
24022         expect(valid.getText()).toContain('myForm.input.$valid = false');
24023       });
24024
24025       it('should be invalid if over max', function() {
24026         setInput('2015-W01');
24027         expect(value.getText()).toContain('');
24028         expect(valid.getText()).toContain('myForm.input.$valid = false');
24029       });
24030     </file>
24031     </example>
24032     */
24033   'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
24034
24035   /**
24036    * @ngdoc input
24037    * @name input[month]
24038    *
24039    * @description
24040    * Input with month validation and transformation. In browsers that do not yet support
24041    * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
24042    * month format (yyyy-MM), for example: `2009-01`.
24043    *
24044    * The model must always be a Date object, otherwise Angular will throw an error.
24045    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
24046    * If the model is not set to the first of the month, the next view to model update will set it
24047    * to the first of the month.
24048    *
24049    * The timezone to be used to read/write the `Date` instance in the model can be defined using
24050    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
24051    *
24052    * @param {string} ngModel Assignable angular expression to data-bind to.
24053    * @param {string=} name Property name of the form under which the control is published.
24054    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
24055    *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
24056    *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
24057    *   native HTML5 constraint validation.
24058    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
24059    *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
24060    *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
24061    *   native HTML5 constraint validation.
24062    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
24063    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
24064    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
24065    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
24066
24067    * @param {string=} required Sets `required` validation error key if the value is not entered.
24068    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24069    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24070    *    `required` when you want to data-bind to the `required` attribute.
24071    * @param {string=} ngChange Angular expression to be executed when input changes due to user
24072    *    interaction with the input element.
24073    *
24074    * @example
24075    <example name="month-input-directive" module="monthExample">
24076    <file name="index.html">
24077      <script>
24078       angular.module('monthExample', [])
24079         .controller('DateController', ['$scope', function($scope) {
24080           $scope.example = {
24081             value: new Date(2013, 9, 1)
24082           };
24083         }]);
24084      </script>
24085      <form name="myForm" ng-controller="DateController as dateCtrl">
24086        <label for="exampleInput">Pick a month in 2013:</label>
24087        <input id="exampleInput" type="month" name="input" ng-model="example.value"
24088           placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
24089        <div role="alert">
24090          <span class="error" ng-show="myForm.input.$error.required">
24091             Required!</span>
24092          <span class="error" ng-show="myForm.input.$error.month">
24093             Not a valid month!</span>
24094        </div>
24095        <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
24096        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24097        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24098        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24099        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24100      </form>
24101    </file>
24102    <file name="protractor.js" type="protractor">
24103       var value = element(by.binding('example.value | date: "yyyy-MM"'));
24104       var valid = element(by.binding('myForm.input.$valid'));
24105
24106       // currently protractor/webdriver does not support
24107       // sending keys to all known HTML5 input controls
24108       // for various browsers (https://github.com/angular/protractor/issues/562).
24109       function setInput(val) {
24110         // set the value of the element and force validation.
24111         var scr = "var ipt = document.getElementById('exampleInput'); " +
24112         "ipt.value = '" + val + "';" +
24113         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
24114         browser.executeScript(scr);
24115       }
24116
24117       it('should initialize to model', function() {
24118         expect(value.getText()).toContain('2013-10');
24119         expect(valid.getText()).toContain('myForm.input.$valid = true');
24120       });
24121
24122       it('should be invalid if empty', function() {
24123         setInput('');
24124         expect(value.getText()).toEqual('value =');
24125         expect(valid.getText()).toContain('myForm.input.$valid = false');
24126       });
24127
24128       it('should be invalid if over max', function() {
24129         setInput('2015-01');
24130         expect(value.getText()).toContain('');
24131         expect(valid.getText()).toContain('myForm.input.$valid = false');
24132       });
24133    </file>
24134    </example>
24135    */
24136   'month': createDateInputType('month', MONTH_REGEXP,
24137      createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
24138      'yyyy-MM'),
24139
24140   /**
24141    * @ngdoc input
24142    * @name input[number]
24143    *
24144    * @description
24145    * Text input with number validation and transformation. Sets the `number` validation
24146    * error if not a valid number.
24147    *
24148    * <div class="alert alert-warning">
24149    * The model must always be of type `number` otherwise Angular will throw an error.
24150    * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
24151    * error docs for more information and an example of how to convert your model if necessary.
24152    * </div>
24153    *
24154    * ## Issues with HTML5 constraint validation
24155    *
24156    * In browsers that follow the
24157    * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
24158    * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
24159    * If a non-number is entered in the input, the browser will report the value as an empty string,
24160    * which means the view / model values in `ngModel` and subsequently the scope value
24161    * will also be an empty string.
24162    *
24163    *
24164    * @param {string} ngModel Assignable angular expression to data-bind to.
24165    * @param {string=} name Property name of the form under which the control is published.
24166    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
24167    *    Can be interpolated.
24168    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
24169    *    Can be interpolated.
24170    * @param {string=} ngMin Like `min`, sets the `min` validation error key if the value entered is less than `ngMin`,
24171    *    but does not trigger HTML5 native validation. Takes an expression.
24172    * @param {string=} ngMax Like `max`, sets the `max` validation error key if the value entered is greater than `ngMax`,
24173    *    but does not trigger HTML5 native validation. Takes an expression.
24174    * @param {string=} step Sets the `step` validation error key if the value entered does not fit the `step` constraint.
24175    *    Can be interpolated.
24176    * @param {string=} ngStep Like `step`, sets the `step` validation error key if the value entered does not fit the `ngStep` constraint,
24177    *    but does not trigger HTML5 native validation. Takes an expression.
24178    * @param {string=} required Sets `required` validation error key if the value is not entered.
24179    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24180    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24181    *    `required` when you want to data-bind to the `required` attribute.
24182    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
24183    *    minlength.
24184    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
24185    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
24186    *    any length.
24187    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
24188    *    that contains the regular expression body that will be converted to a regular expression
24189    *    as in the ngPattern directive.
24190    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
24191    *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
24192    *    If the expression evaluates to a RegExp object, then this is used directly.
24193    *    If the expression evaluates to a string, then it will be converted to a RegExp
24194    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
24195    *    `new RegExp('^abc$')`.<br />
24196    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
24197    *    start at the index of the last search's match, thus not taking the whole input value into
24198    *    account.
24199    * @param {string=} ngChange Angular expression to be executed when input changes due to user
24200    *    interaction with the input element.
24201    *
24202    * @example
24203       <example name="number-input-directive" module="numberExample">
24204         <file name="index.html">
24205          <script>
24206            angular.module('numberExample', [])
24207              .controller('ExampleController', ['$scope', function($scope) {
24208                $scope.example = {
24209                  value: 12
24210                };
24211              }]);
24212          </script>
24213          <form name="myForm" ng-controller="ExampleController">
24214            <label>Number:
24215              <input type="number" name="input" ng-model="example.value"
24216                     min="0" max="99" required>
24217           </label>
24218            <div role="alert">
24219              <span class="error" ng-show="myForm.input.$error.required">
24220                Required!</span>
24221              <span class="error" ng-show="myForm.input.$error.number">
24222                Not valid number!</span>
24223            </div>
24224            <tt>value = {{example.value}}</tt><br/>
24225            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24226            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24227            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24228            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24229           </form>
24230         </file>
24231         <file name="protractor.js" type="protractor">
24232           var value = element(by.binding('example.value'));
24233           var valid = element(by.binding('myForm.input.$valid'));
24234           var input = element(by.model('example.value'));
24235
24236           it('should initialize to model', function() {
24237             expect(value.getText()).toContain('12');
24238             expect(valid.getText()).toContain('true');
24239           });
24240
24241           it('should be invalid if empty', function() {
24242             input.clear();
24243             input.sendKeys('');
24244             expect(value.getText()).toEqual('value =');
24245             expect(valid.getText()).toContain('false');
24246           });
24247
24248           it('should be invalid if over max', function() {
24249             input.clear();
24250             input.sendKeys('123');
24251             expect(value.getText()).toEqual('value =');
24252             expect(valid.getText()).toContain('false');
24253           });
24254         </file>
24255       </example>
24256    */
24257   'number': numberInputType,
24258
24259
24260   /**
24261    * @ngdoc input
24262    * @name input[url]
24263    *
24264    * @description
24265    * Text input with URL validation. Sets the `url` validation error key if the content is not a
24266    * valid URL.
24267    *
24268    * <div class="alert alert-warning">
24269    * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
24270    * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
24271    * the built-in validators (see the {@link guide/forms Forms guide})
24272    * </div>
24273    *
24274    * @param {string} ngModel Assignable angular expression to data-bind to.
24275    * @param {string=} name Property name of the form under which the control is published.
24276    * @param {string=} required Sets `required` validation error key if the value is not entered.
24277    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24278    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24279    *    `required` when you want to data-bind to the `required` attribute.
24280    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
24281    *    minlength.
24282    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
24283    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
24284    *    any length.
24285    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
24286    *    that contains the regular expression body that will be converted to a regular expression
24287    *    as in the ngPattern directive.
24288    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
24289    *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
24290    *    If the expression evaluates to a RegExp object, then this is used directly.
24291    *    If the expression evaluates to a string, then it will be converted to a RegExp
24292    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
24293    *    `new RegExp('^abc$')`.<br />
24294    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
24295    *    start at the index of the last search's match, thus not taking the whole input value into
24296    *    account.
24297    * @param {string=} ngChange Angular expression to be executed when input changes due to user
24298    *    interaction with the input element.
24299    *
24300    * @example
24301       <example name="url-input-directive" module="urlExample">
24302         <file name="index.html">
24303          <script>
24304            angular.module('urlExample', [])
24305              .controller('ExampleController', ['$scope', function($scope) {
24306                $scope.url = {
24307                  text: 'http://google.com'
24308                };
24309              }]);
24310          </script>
24311          <form name="myForm" ng-controller="ExampleController">
24312            <label>URL:
24313              <input type="url" name="input" ng-model="url.text" required>
24314            <label>
24315            <div role="alert">
24316              <span class="error" ng-show="myForm.input.$error.required">
24317                Required!</span>
24318              <span class="error" ng-show="myForm.input.$error.url">
24319                Not valid url!</span>
24320            </div>
24321            <tt>text = {{url.text}}</tt><br/>
24322            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24323            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24324            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24325            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24326            <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
24327           </form>
24328         </file>
24329         <file name="protractor.js" type="protractor">
24330           var text = element(by.binding('url.text'));
24331           var valid = element(by.binding('myForm.input.$valid'));
24332           var input = element(by.model('url.text'));
24333
24334           it('should initialize to model', function() {
24335             expect(text.getText()).toContain('http://google.com');
24336             expect(valid.getText()).toContain('true');
24337           });
24338
24339           it('should be invalid if empty', function() {
24340             input.clear();
24341             input.sendKeys('');
24342
24343             expect(text.getText()).toEqual('text =');
24344             expect(valid.getText()).toContain('false');
24345           });
24346
24347           it('should be invalid if not url', function() {
24348             input.clear();
24349             input.sendKeys('box');
24350
24351             expect(valid.getText()).toContain('false');
24352           });
24353         </file>
24354       </example>
24355    */
24356   'url': urlInputType,
24357
24358
24359   /**
24360    * @ngdoc input
24361    * @name input[email]
24362    *
24363    * @description
24364    * Text input with email validation. Sets the `email` validation error key if not a valid email
24365    * address.
24366    *
24367    * <div class="alert alert-warning">
24368    * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
24369    * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
24370    * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
24371    * </div>
24372    *
24373    * @param {string} ngModel Assignable angular expression to data-bind to.
24374    * @param {string=} name Property name of the form under which the control is published.
24375    * @param {string=} required Sets `required` validation error key if the value is not entered.
24376    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24377    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24378    *    `required` when you want to data-bind to the `required` attribute.
24379    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
24380    *    minlength.
24381    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
24382    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
24383    *    any length.
24384    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
24385    *    that contains the regular expression body that will be converted to a regular expression
24386    *    as in the ngPattern directive.
24387    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
24388    *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
24389    *    If the expression evaluates to a RegExp object, then this is used directly.
24390    *    If the expression evaluates to a string, then it will be converted to a RegExp
24391    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
24392    *    `new RegExp('^abc$')`.<br />
24393    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
24394    *    start at the index of the last search's match, thus not taking the whole input value into
24395    *    account.
24396    * @param {string=} ngChange Angular expression to be executed when input changes due to user
24397    *    interaction with the input element.
24398    *
24399    * @example
24400       <example name="email-input-directive" module="emailExample">
24401         <file name="index.html">
24402          <script>
24403            angular.module('emailExample', [])
24404              .controller('ExampleController', ['$scope', function($scope) {
24405                $scope.email = {
24406                  text: 'me@example.com'
24407                };
24408              }]);
24409          </script>
24410            <form name="myForm" ng-controller="ExampleController">
24411              <label>Email:
24412                <input type="email" name="input" ng-model="email.text" required>
24413              </label>
24414              <div role="alert">
24415                <span class="error" ng-show="myForm.input.$error.required">
24416                  Required!</span>
24417                <span class="error" ng-show="myForm.input.$error.email">
24418                  Not valid email!</span>
24419              </div>
24420              <tt>text = {{email.text}}</tt><br/>
24421              <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24422              <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24423              <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24424              <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24425              <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
24426            </form>
24427          </file>
24428         <file name="protractor.js" type="protractor">
24429           var text = element(by.binding('email.text'));
24430           var valid = element(by.binding('myForm.input.$valid'));
24431           var input = element(by.model('email.text'));
24432
24433           it('should initialize to model', function() {
24434             expect(text.getText()).toContain('me@example.com');
24435             expect(valid.getText()).toContain('true');
24436           });
24437
24438           it('should be invalid if empty', function() {
24439             input.clear();
24440             input.sendKeys('');
24441             expect(text.getText()).toEqual('text =');
24442             expect(valid.getText()).toContain('false');
24443           });
24444
24445           it('should be invalid if not email', function() {
24446             input.clear();
24447             input.sendKeys('xxx');
24448
24449             expect(valid.getText()).toContain('false');
24450           });
24451         </file>
24452       </example>
24453    */
24454   'email': emailInputType,
24455
24456
24457   /**
24458    * @ngdoc input
24459    * @name input[radio]
24460    *
24461    * @description
24462    * HTML radio button.
24463    *
24464    * @param {string} ngModel Assignable angular expression to data-bind to.
24465    * @param {string} value The value to which the `ngModel` expression should be set when selected.
24466    *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
24467    *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
24468    * @param {string=} name Property name of the form under which the control is published.
24469    * @param {string=} ngChange Angular expression to be executed when input changes due to user
24470    *    interaction with the input element.
24471    * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
24472    *    is selected. Should be used instead of the `value` attribute if you need
24473    *    a non-string `ngModel` (`boolean`, `array`, ...).
24474    *
24475    * @example
24476       <example name="radio-input-directive" module="radioExample">
24477         <file name="index.html">
24478          <script>
24479            angular.module('radioExample', [])
24480              .controller('ExampleController', ['$scope', function($scope) {
24481                $scope.color = {
24482                  name: 'blue'
24483                };
24484                $scope.specialValue = {
24485                  "id": "12345",
24486                  "value": "green"
24487                };
24488              }]);
24489          </script>
24490          <form name="myForm" ng-controller="ExampleController">
24491            <label>
24492              <input type="radio" ng-model="color.name" value="red">
24493              Red
24494            </label><br/>
24495            <label>
24496              <input type="radio" ng-model="color.name" ng-value="specialValue">
24497              Green
24498            </label><br/>
24499            <label>
24500              <input type="radio" ng-model="color.name" value="blue">
24501              Blue
24502            </label><br/>
24503            <tt>color = {{color.name | json}}</tt><br/>
24504           </form>
24505           Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
24506         </file>
24507         <file name="protractor.js" type="protractor">
24508           it('should change state', function() {
24509             var inputs = element.all(by.model('color.name'));
24510             var color = element(by.binding('color.name'));
24511
24512             expect(color.getText()).toContain('blue');
24513
24514             inputs.get(0).click();
24515             expect(color.getText()).toContain('red');
24516
24517             inputs.get(1).click();
24518             expect(color.getText()).toContain('green');
24519           });
24520         </file>
24521       </example>
24522    */
24523   'radio': radioInputType,
24524
24525   /**
24526    * @ngdoc input
24527    * @name input[range]
24528    *
24529    * @description
24530    * Native range input with validation and transformation.
24531    *
24532    * The model for the range input must always be a `Number`.
24533    *
24534    * IE9 and other browsers that do not support the `range` type fall back
24535    * to a text input without any default values for `min`, `max` and `step`. Model binding,
24536    * validation and number parsing are nevertheless supported.
24537    *
24538    * Browsers that support range (latest Chrome, Safari, Firefox, Edge) treat `input[range]`
24539    * in a way that never allows the input to hold an invalid value. That means:
24540    * - any non-numerical value is set to `(max + min) / 2`.
24541    * - any numerical value that is less than the current min val, or greater than the current max val
24542    * is set to the min / max val respectively.
24543    * - additionally, the current `step` is respected, so the nearest value that satisfies a step
24544    * is used.
24545    *
24546    * See the [HTML Spec on input[type=range]](https://www.w3.org/TR/html5/forms.html#range-state-(type=range))
24547    * for more info.
24548    *
24549    * This has the following consequences for Angular:
24550    *
24551    * Since the element value should always reflect the current model value, a range input
24552    * will set the bound ngModel expression to the value that the browser has set for the
24553    * input element. For example, in the following input `<input type="range" ng-model="model.value">`,
24554    * if the application sets `model.value = null`, the browser will set the input to `'50'`.
24555    * Angular will then set the model to `50`, to prevent input and model value being out of sync.
24556    *
24557    * That means the model for range will immediately be set to `50` after `ngModel` has been
24558    * initialized. It also means a range input can never have the required error.
24559    *
24560    * This does not only affect changes to the model value, but also to the values of the `min`,
24561    * `max`, and `step` attributes. When these change in a way that will cause the browser to modify
24562    * the input value, Angular will also update the model value.
24563    *
24564    * Automatic value adjustment also means that a range input element can never have the `required`,
24565    * `min`, or `max` errors.
24566    *
24567    * However, `step` is currently only fully implemented by Firefox. Other browsers have problems
24568    * when the step value changes dynamically - they do not adjust the element value correctly, but
24569    * instead may set the `stepMismatch` error. If that's the case, the Angular will set the `step`
24570    * error on the input, and set the model to `undefined`.
24571    *
24572    * Note that `input[range]` is not compatible with`ngMax`, `ngMin`, and `ngStep`, because they do
24573    * not set the `min` and `max` attributes, which means that the browser won't automatically adjust
24574    * the input value based on their values, and will always assume min = 0, max = 100, and step = 1.
24575    *
24576    * @param {string}  ngModel Assignable angular expression to data-bind to.
24577    * @param {string=} name Property name of the form under which the control is published.
24578    * @param {string=} min Sets the `min` validation to ensure that the value entered is greater
24579    *                  than `min`. Can be interpolated.
24580    * @param {string=} max Sets the `max` validation to ensure that the value entered is less than `max`.
24581    *                  Can be interpolated.
24582    * @param {string=} step Sets the `step` validation to ensure that the value entered matches the `step`
24583    *                  Can be interpolated.
24584    * @param {string=} ngChange Angular expression to be executed when the ngModel value changes due
24585    *                  to user interaction with the input element.
24586    * @param {expression=} ngChecked If the expression is truthy, then the `checked` attribute will be set on the
24587    *                      element. **Note** : `ngChecked` should not be used alongside `ngModel`.
24588    *                      Checkout {@link ng.directive:ngChecked ngChecked} for usage.
24589    *
24590    * @example
24591       <example name="range-input-directive" module="rangeExample">
24592         <file name="index.html">
24593           <script>
24594             angular.module('rangeExample', [])
24595               .controller('ExampleController', ['$scope', function($scope) {
24596                 $scope.value = 75;
24597                 $scope.min = 10;
24598                 $scope.max = 90;
24599               }]);
24600           </script>
24601           <form name="myForm" ng-controller="ExampleController">
24602
24603             Model as range: <input type="range" name="range" ng-model="value" min="{{min}}"  max="{{max}}">
24604             <hr>
24605             Model as number: <input type="number" ng-model="value"><br>
24606             Min: <input type="number" ng-model="min"><br>
24607             Max: <input type="number" ng-model="max"><br>
24608             value = <code>{{value}}</code><br/>
24609             myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/>
24610             myForm.range.$error = <code>{{myForm.range.$error}}</code>
24611           </form>
24612         </file>
24613       </example>
24614
24615    * ## Range Input with ngMin & ngMax attributes
24616
24617    * @example
24618       <example name="range-input-directive-ng" module="rangeExample">
24619         <file name="index.html">
24620           <script>
24621             angular.module('rangeExample', [])
24622               .controller('ExampleController', ['$scope', function($scope) {
24623                 $scope.value = 75;
24624                 $scope.min = 10;
24625                 $scope.max = 90;
24626               }]);
24627           </script>
24628           <form name="myForm" ng-controller="ExampleController">
24629             Model as range: <input type="range" name="range" ng-model="value" ng-min="min" ng-max="max">
24630             <hr>
24631             Model as number: <input type="number" ng-model="value"><br>
24632             Min: <input type="number" ng-model="min"><br>
24633             Max: <input type="number" ng-model="max"><br>
24634             value = <code>{{value}}</code><br/>
24635             myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/>
24636             myForm.range.$error = <code>{{myForm.range.$error}}</code>
24637           </form>
24638         </file>
24639       </example>
24640
24641    */
24642   'range': rangeInputType,
24643
24644   /**
24645    * @ngdoc input
24646    * @name input[checkbox]
24647    *
24648    * @description
24649    * HTML checkbox.
24650    *
24651    * @param {string} ngModel Assignable angular expression to data-bind to.
24652    * @param {string=} name Property name of the form under which the control is published.
24653    * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
24654    * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
24655    * @param {string=} ngChange Angular expression to be executed when input changes due to user
24656    *    interaction with the input element.
24657    *
24658    * @example
24659       <example name="checkbox-input-directive" module="checkboxExample">
24660         <file name="index.html">
24661          <script>
24662            angular.module('checkboxExample', [])
24663              .controller('ExampleController', ['$scope', function($scope) {
24664                $scope.checkboxModel = {
24665                 value1 : true,
24666                 value2 : 'YES'
24667               };
24668              }]);
24669          </script>
24670          <form name="myForm" ng-controller="ExampleController">
24671            <label>Value1:
24672              <input type="checkbox" ng-model="checkboxModel.value1">
24673            </label><br/>
24674            <label>Value2:
24675              <input type="checkbox" ng-model="checkboxModel.value2"
24676                     ng-true-value="'YES'" ng-false-value="'NO'">
24677             </label><br/>
24678            <tt>value1 = {{checkboxModel.value1}}</tt><br/>
24679            <tt>value2 = {{checkboxModel.value2}}</tt><br/>
24680           </form>
24681         </file>
24682         <file name="protractor.js" type="protractor">
24683           it('should change state', function() {
24684             var value1 = element(by.binding('checkboxModel.value1'));
24685             var value2 = element(by.binding('checkboxModel.value2'));
24686
24687             expect(value1.getText()).toContain('true');
24688             expect(value2.getText()).toContain('YES');
24689
24690             element(by.model('checkboxModel.value1')).click();
24691             element(by.model('checkboxModel.value2')).click();
24692
24693             expect(value1.getText()).toContain('false');
24694             expect(value2.getText()).toContain('NO');
24695           });
24696         </file>
24697       </example>
24698    */
24699   'checkbox': checkboxInputType,
24700
24701   'hidden': noop,
24702   'button': noop,
24703   'submit': noop,
24704   'reset': noop,
24705   'file': noop
24706 };
24707
24708 function stringBasedInputType(ctrl) {
24709   ctrl.$formatters.push(function(value) {
24710     return ctrl.$isEmpty(value) ? value : value.toString();
24711   });
24712 }
24713
24714 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
24715   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
24716   stringBasedInputType(ctrl);
24717 }
24718
24719 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
24720   var type = lowercase(element[0].type);
24721
24722   // In composition mode, users are still inputting intermediate text buffer,
24723   // hold the listener until composition is done.
24724   // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
24725   if (!$sniffer.android) {
24726     var composing = false;
24727
24728     element.on('compositionstart', function() {
24729       composing = true;
24730     });
24731
24732     element.on('compositionend', function() {
24733       composing = false;
24734       listener();
24735     });
24736   }
24737
24738   var timeout;
24739
24740   var listener = function(ev) {
24741     if (timeout) {
24742       $browser.defer.cancel(timeout);
24743       timeout = null;
24744     }
24745     if (composing) return;
24746     var value = element.val(),
24747         event = ev && ev.type;
24748
24749     // By default we will trim the value
24750     // If the attribute ng-trim exists we will avoid trimming
24751     // If input type is 'password', the value is never trimmed
24752     if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
24753       value = trim(value);
24754     }
24755
24756     // If a control is suffering from bad input (due to native validators), browsers discard its
24757     // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
24758     // control's value is the same empty value twice in a row.
24759     if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
24760       ctrl.$setViewValue(value, event);
24761     }
24762   };
24763
24764   // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
24765   // input event on backspace, delete or cut
24766   if ($sniffer.hasEvent('input')) {
24767     element.on('input', listener);
24768   } else {
24769     var deferListener = function(ev, input, origValue) {
24770       if (!timeout) {
24771         timeout = $browser.defer(function() {
24772           timeout = null;
24773           if (!input || input.value !== origValue) {
24774             listener(ev);
24775           }
24776         });
24777       }
24778     };
24779
24780     element.on('keydown', /** @this */ function(event) {
24781       var key = event.keyCode;
24782
24783       // ignore
24784       //    command            modifiers                   arrows
24785       if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
24786
24787       deferListener(event, this, this.value);
24788     });
24789
24790     // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
24791     if ($sniffer.hasEvent('paste')) {
24792       element.on('paste cut', deferListener);
24793     }
24794   }
24795
24796   // if user paste into input using mouse on older browser
24797   // or form autocomplete on newer browser, we need "change" event to catch it
24798   element.on('change', listener);
24799
24800   // Some native input types (date-family) have the ability to change validity without
24801   // firing any input/change events.
24802   // For these event types, when native validators are present and the browser supports the type,
24803   // check for validity changes on various DOM events.
24804   if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
24805     element.on(PARTIAL_VALIDATION_EVENTS, /** @this */ function(ev) {
24806       if (!timeout) {
24807         var validity = this[VALIDITY_STATE_PROPERTY];
24808         var origBadInput = validity.badInput;
24809         var origTypeMismatch = validity.typeMismatch;
24810         timeout = $browser.defer(function() {
24811           timeout = null;
24812           if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
24813             listener(ev);
24814           }
24815         });
24816       }
24817     });
24818   }
24819
24820   ctrl.$render = function() {
24821     // Workaround for Firefox validation #12102.
24822     var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
24823     if (element.val() !== value) {
24824       element.val(value);
24825     }
24826   };
24827 }
24828
24829 function weekParser(isoWeek, existingDate) {
24830   if (isDate(isoWeek)) {
24831     return isoWeek;
24832   }
24833
24834   if (isString(isoWeek)) {
24835     WEEK_REGEXP.lastIndex = 0;
24836     var parts = WEEK_REGEXP.exec(isoWeek);
24837     if (parts) {
24838       var year = +parts[1],
24839           week = +parts[2],
24840           hours = 0,
24841           minutes = 0,
24842           seconds = 0,
24843           milliseconds = 0,
24844           firstThurs = getFirstThursdayOfYear(year),
24845           addDays = (week - 1) * 7;
24846
24847       if (existingDate) {
24848         hours = existingDate.getHours();
24849         minutes = existingDate.getMinutes();
24850         seconds = existingDate.getSeconds();
24851         milliseconds = existingDate.getMilliseconds();
24852       }
24853
24854       return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
24855     }
24856   }
24857
24858   return NaN;
24859 }
24860
24861 function createDateParser(regexp, mapping) {
24862   return function(iso, date) {
24863     var parts, map;
24864
24865     if (isDate(iso)) {
24866       return iso;
24867     }
24868
24869     if (isString(iso)) {
24870       // When a date is JSON'ified to wraps itself inside of an extra
24871       // set of double quotes. This makes the date parsing code unable
24872       // to match the date string and parse it as a date.
24873       if (iso.charAt(0) === '"' && iso.charAt(iso.length - 1) === '"') {
24874         iso = iso.substring(1, iso.length - 1);
24875       }
24876       if (ISO_DATE_REGEXP.test(iso)) {
24877         return new Date(iso);
24878       }
24879       regexp.lastIndex = 0;
24880       parts = regexp.exec(iso);
24881
24882       if (parts) {
24883         parts.shift();
24884         if (date) {
24885           map = {
24886             yyyy: date.getFullYear(),
24887             MM: date.getMonth() + 1,
24888             dd: date.getDate(),
24889             HH: date.getHours(),
24890             mm: date.getMinutes(),
24891             ss: date.getSeconds(),
24892             sss: date.getMilliseconds() / 1000
24893           };
24894         } else {
24895           map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
24896         }
24897
24898         forEach(parts, function(part, index) {
24899           if (index < mapping.length) {
24900             map[mapping[index]] = +part;
24901           }
24902         });
24903         return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
24904       }
24905     }
24906
24907     return NaN;
24908   };
24909 }
24910
24911 function createDateInputType(type, regexp, parseDate, format) {
24912   return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
24913     badInputChecker(scope, element, attr, ctrl);
24914     baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
24915     var timezone = ctrl && ctrl.$options.getOption('timezone');
24916     var previousDate;
24917
24918     ctrl.$$parserName = type;
24919     ctrl.$parsers.push(function(value) {
24920       if (ctrl.$isEmpty(value)) return null;
24921       if (regexp.test(value)) {
24922         // Note: We cannot read ctrl.$modelValue, as there might be a different
24923         // parser/formatter in the processing chain so that the model
24924         // contains some different data format!
24925         var parsedDate = parseDate(value, previousDate);
24926         if (timezone) {
24927           parsedDate = convertTimezoneToLocal(parsedDate, timezone);
24928         }
24929         return parsedDate;
24930       }
24931       return undefined;
24932     });
24933
24934     ctrl.$formatters.push(function(value) {
24935       if (value && !isDate(value)) {
24936         throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
24937       }
24938       if (isValidDate(value)) {
24939         previousDate = value;
24940         if (previousDate && timezone) {
24941           previousDate = convertTimezoneToLocal(previousDate, timezone, true);
24942         }
24943         return $filter('date')(value, format, timezone);
24944       } else {
24945         previousDate = null;
24946         return '';
24947       }
24948     });
24949
24950     if (isDefined(attr.min) || attr.ngMin) {
24951       var minVal;
24952       ctrl.$validators.min = function(value) {
24953         return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
24954       };
24955       attr.$observe('min', function(val) {
24956         minVal = parseObservedDateValue(val);
24957         ctrl.$validate();
24958       });
24959     }
24960
24961     if (isDefined(attr.max) || attr.ngMax) {
24962       var maxVal;
24963       ctrl.$validators.max = function(value) {
24964         return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
24965       };
24966       attr.$observe('max', function(val) {
24967         maxVal = parseObservedDateValue(val);
24968         ctrl.$validate();
24969       });
24970     }
24971
24972     function isValidDate(value) {
24973       // Invalid Date: getTime() returns NaN
24974       return value && !(value.getTime && value.getTime() !== value.getTime());
24975     }
24976
24977     function parseObservedDateValue(val) {
24978       return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
24979     }
24980   };
24981 }
24982
24983 function badInputChecker(scope, element, attr, ctrl) {
24984   var node = element[0];
24985   var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
24986   if (nativeValidation) {
24987     ctrl.$parsers.push(function(value) {
24988       var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
24989       return validity.badInput || validity.typeMismatch ? undefined : value;
24990     });
24991   }
24992 }
24993
24994 function numberFormatterParser(ctrl) {
24995   ctrl.$$parserName = 'number';
24996   ctrl.$parsers.push(function(value) {
24997     if (ctrl.$isEmpty(value))      return null;
24998     if (NUMBER_REGEXP.test(value)) return parseFloat(value);
24999     return undefined;
25000   });
25001
25002   ctrl.$formatters.push(function(value) {
25003     if (!ctrl.$isEmpty(value)) {
25004       if (!isNumber(value)) {
25005         throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
25006       }
25007       value = value.toString();
25008     }
25009     return value;
25010   });
25011 }
25012
25013 function parseNumberAttrVal(val) {
25014   if (isDefined(val) && !isNumber(val)) {
25015     val = parseFloat(val);
25016   }
25017   return !isNumberNaN(val) ? val : undefined;
25018 }
25019
25020 function isNumberInteger(num) {
25021   // See http://stackoverflow.com/questions/14636536/how-to-check-if-a-variable-is-an-integer-in-javascript#14794066
25022   // (minus the assumption that `num` is a number)
25023
25024   // eslint-disable-next-line no-bitwise
25025   return (num | 0) === num;
25026 }
25027
25028 function countDecimals(num) {
25029   var numString = num.toString();
25030   var decimalSymbolIndex = numString.indexOf('.');
25031
25032   if (decimalSymbolIndex === -1) {
25033     if (-1 < num && num < 1) {
25034       // It may be in the exponential notation format (`1e-X`)
25035       var match = /e-(\d+)$/.exec(numString);
25036
25037       if (match) {
25038         return Number(match[1]);
25039       }
25040     }
25041
25042     return 0;
25043   }
25044
25045   return numString.length - decimalSymbolIndex - 1;
25046 }
25047
25048 function isValidForStep(viewValue, stepBase, step) {
25049   // At this point `stepBase` and `step` are expected to be non-NaN values
25050   // and `viewValue` is expected to be a valid stringified number.
25051   var value = Number(viewValue);
25052
25053   var isNonIntegerValue = !isNumberInteger(value);
25054   var isNonIntegerStepBase = !isNumberInteger(stepBase);
25055   var isNonIntegerStep = !isNumberInteger(step);
25056
25057   // Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or
25058   // `0.5 % 0.1 !== 0`), we need to convert all numbers to integers.
25059   if (isNonIntegerValue || isNonIntegerStepBase || isNonIntegerStep) {
25060     var valueDecimals = isNonIntegerValue ? countDecimals(value) : 0;
25061     var stepBaseDecimals = isNonIntegerStepBase ? countDecimals(stepBase) : 0;
25062     var stepDecimals = isNonIntegerStep ? countDecimals(step) : 0;
25063
25064     var decimalCount = Math.max(valueDecimals, stepBaseDecimals, stepDecimals);
25065     var multiplier = Math.pow(10, decimalCount);
25066
25067     value = value * multiplier;
25068     stepBase = stepBase * multiplier;
25069     step = step * multiplier;
25070
25071     if (isNonIntegerValue) value = Math.round(value);
25072     if (isNonIntegerStepBase) stepBase = Math.round(stepBase);
25073     if (isNonIntegerStep) step = Math.round(step);
25074   }
25075
25076   return (value - stepBase) % step === 0;
25077 }
25078
25079 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25080   badInputChecker(scope, element, attr, ctrl);
25081   numberFormatterParser(ctrl);
25082   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25083
25084   var minVal;
25085   var maxVal;
25086
25087   if (isDefined(attr.min) || attr.ngMin) {
25088     ctrl.$validators.min = function(value) {
25089       return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
25090     };
25091
25092     attr.$observe('min', function(val) {
25093       minVal = parseNumberAttrVal(val);
25094       // TODO(matsko): implement validateLater to reduce number of validations
25095       ctrl.$validate();
25096     });
25097   }
25098
25099   if (isDefined(attr.max) || attr.ngMax) {
25100     ctrl.$validators.max = function(value) {
25101       return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
25102     };
25103
25104     attr.$observe('max', function(val) {
25105       maxVal = parseNumberAttrVal(val);
25106       // TODO(matsko): implement validateLater to reduce number of validations
25107       ctrl.$validate();
25108     });
25109   }
25110
25111   if (isDefined(attr.step) || attr.ngStep) {
25112     var stepVal;
25113     ctrl.$validators.step = function(modelValue, viewValue) {
25114       return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) ||
25115              isValidForStep(viewValue, minVal || 0, stepVal);
25116     };
25117
25118     attr.$observe('step', function(val) {
25119       stepVal = parseNumberAttrVal(val);
25120       // TODO(matsko): implement validateLater to reduce number of validations
25121       ctrl.$validate();
25122     });
25123   }
25124 }
25125
25126 function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25127   badInputChecker(scope, element, attr, ctrl);
25128   numberFormatterParser(ctrl);
25129   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25130
25131   var supportsRange = ctrl.$$hasNativeValidators && element[0].type === 'range',
25132       minVal = supportsRange ? 0 : undefined,
25133       maxVal = supportsRange ? 100 : undefined,
25134       stepVal = supportsRange ? 1 : undefined,
25135       validity = element[0].validity,
25136       hasMinAttr = isDefined(attr.min),
25137       hasMaxAttr = isDefined(attr.max),
25138       hasStepAttr = isDefined(attr.step);
25139
25140   var originalRender = ctrl.$render;
25141
25142   ctrl.$render = supportsRange && isDefined(validity.rangeUnderflow) && isDefined(validity.rangeOverflow) ?
25143     //Browsers that implement range will set these values automatically, but reading the adjusted values after
25144     //$render would cause the min / max validators to be applied with the wrong value
25145     function rangeRender() {
25146       originalRender();
25147       ctrl.$setViewValue(element.val());
25148     } :
25149     originalRender;
25150
25151   if (hasMinAttr) {
25152     ctrl.$validators.min = supportsRange ?
25153       // Since all browsers set the input to a valid value, we don't need to check validity
25154       function noopMinValidator() { return true; } :
25155       // non-support browsers validate the min val
25156       function minValidator(modelValue, viewValue) {
25157         return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal;
25158       };
25159
25160     setInitialValueAndObserver('min', minChange);
25161   }
25162
25163   if (hasMaxAttr) {
25164     ctrl.$validators.max = supportsRange ?
25165       // Since all browsers set the input to a valid value, we don't need to check validity
25166       function noopMaxValidator() { return true; } :
25167       // non-support browsers validate the max val
25168       function maxValidator(modelValue, viewValue) {
25169         return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal;
25170       };
25171
25172     setInitialValueAndObserver('max', maxChange);
25173   }
25174
25175   if (hasStepAttr) {
25176     ctrl.$validators.step = supportsRange ?
25177       function nativeStepValidator() {
25178         // Currently, only FF implements the spec on step change correctly (i.e. adjusting the
25179         // input element value to a valid value). It's possible that other browsers set the stepMismatch
25180         // validity error instead, so we can at least report an error in that case.
25181         return !validity.stepMismatch;
25182       } :
25183       // ngStep doesn't set the setp attr, so the browser doesn't adjust the input value as setting step would
25184       function stepValidator(modelValue, viewValue) {
25185         return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) ||
25186                isValidForStep(viewValue, minVal || 0, stepVal);
25187       };
25188
25189     setInitialValueAndObserver('step', stepChange);
25190   }
25191
25192   function setInitialValueAndObserver(htmlAttrName, changeFn) {
25193     // interpolated attributes set the attribute value only after a digest, but we need the
25194     // attribute value when the input is first rendered, so that the browser can adjust the
25195     // input value based on the min/max value
25196     element.attr(htmlAttrName, attr[htmlAttrName]);
25197     attr.$observe(htmlAttrName, changeFn);
25198   }
25199
25200   function minChange(val) {
25201     minVal = parseNumberAttrVal(val);
25202     // ignore changes before model is initialized
25203     if (isNumberNaN(ctrl.$modelValue)) {
25204       return;
25205     }
25206
25207     if (supportsRange) {
25208       var elVal = element.val();
25209       // IE11 doesn't set the el val correctly if the minVal is greater than the element value
25210       if (minVal > elVal) {
25211         elVal = minVal;
25212         element.val(elVal);
25213       }
25214       ctrl.$setViewValue(elVal);
25215     } else {
25216       // TODO(matsko): implement validateLater to reduce number of validations
25217       ctrl.$validate();
25218     }
25219   }
25220
25221   function maxChange(val) {
25222     maxVal = parseNumberAttrVal(val);
25223     // ignore changes before model is initialized
25224     if (isNumberNaN(ctrl.$modelValue)) {
25225       return;
25226     }
25227
25228     if (supportsRange) {
25229       var elVal = element.val();
25230       // IE11 doesn't set the el val correctly if the maxVal is less than the element value
25231       if (maxVal < elVal) {
25232         element.val(maxVal);
25233         // IE11 and Chrome don't set the value to the minVal when max < min
25234         elVal = maxVal < minVal ? minVal : maxVal;
25235       }
25236       ctrl.$setViewValue(elVal);
25237     } else {
25238       // TODO(matsko): implement validateLater to reduce number of validations
25239       ctrl.$validate();
25240     }
25241   }
25242
25243   function stepChange(val) {
25244     stepVal = parseNumberAttrVal(val);
25245     // ignore changes before model is initialized
25246     if (isNumberNaN(ctrl.$modelValue)) {
25247       return;
25248     }
25249
25250     // Some browsers don't adjust the input value correctly, but set the stepMismatch error
25251     if (supportsRange && ctrl.$viewValue !== element.val()) {
25252       ctrl.$setViewValue(element.val());
25253     } else {
25254       // TODO(matsko): implement validateLater to reduce number of validations
25255       ctrl.$validate();
25256     }
25257   }
25258 }
25259
25260 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25261   // Note: no badInputChecker here by purpose as `url` is only a validation
25262   // in browsers, i.e. we can always read out input.value even if it is not valid!
25263   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25264   stringBasedInputType(ctrl);
25265
25266   ctrl.$$parserName = 'url';
25267   ctrl.$validators.url = function(modelValue, viewValue) {
25268     var value = modelValue || viewValue;
25269     return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
25270   };
25271 }
25272
25273 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25274   // Note: no badInputChecker here by purpose as `url` is only a validation
25275   // in browsers, i.e. we can always read out input.value even if it is not valid!
25276   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25277   stringBasedInputType(ctrl);
25278
25279   ctrl.$$parserName = 'email';
25280   ctrl.$validators.email = function(modelValue, viewValue) {
25281     var value = modelValue || viewValue;
25282     return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
25283   };
25284 }
25285
25286 function radioInputType(scope, element, attr, ctrl) {
25287   var doTrim = !attr.ngTrim || trim(attr.ngTrim) !== 'false';
25288   // make the name unique, if not defined
25289   if (isUndefined(attr.name)) {
25290     element.attr('name', nextUid());
25291   }
25292
25293   var listener = function(ev) {
25294     var value;
25295     if (element[0].checked) {
25296       value = attr.value;
25297       if (doTrim) {
25298         value = trim(value);
25299       }
25300       ctrl.$setViewValue(value, ev && ev.type);
25301     }
25302   };
25303
25304   element.on('click', listener);
25305
25306   ctrl.$render = function() {
25307     var value = attr.value;
25308     if (doTrim) {
25309       value = trim(value);
25310     }
25311     element[0].checked = (value === ctrl.$viewValue);
25312   };
25313
25314   attr.$observe('value', ctrl.$render);
25315 }
25316
25317 function parseConstantExpr($parse, context, name, expression, fallback) {
25318   var parseFn;
25319   if (isDefined(expression)) {
25320     parseFn = $parse(expression);
25321     if (!parseFn.constant) {
25322       throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
25323                                    '`{1}`.', name, expression);
25324     }
25325     return parseFn(context);
25326   }
25327   return fallback;
25328 }
25329
25330 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
25331   var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
25332   var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
25333
25334   var listener = function(ev) {
25335     ctrl.$setViewValue(element[0].checked, ev && ev.type);
25336   };
25337
25338   element.on('click', listener);
25339
25340   ctrl.$render = function() {
25341     element[0].checked = ctrl.$viewValue;
25342   };
25343
25344   // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
25345   // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
25346   // it to a boolean.
25347   ctrl.$isEmpty = function(value) {
25348     return value === false;
25349   };
25350
25351   ctrl.$formatters.push(function(value) {
25352     return equals(value, trueValue);
25353   });
25354
25355   ctrl.$parsers.push(function(value) {
25356     return value ? trueValue : falseValue;
25357   });
25358 }
25359
25360
25361 /**
25362  * @ngdoc directive
25363  * @name textarea
25364  * @restrict E
25365  *
25366  * @description
25367  * HTML textarea element control with angular data-binding. The data-binding and validation
25368  * properties of this element are exactly the same as those of the
25369  * {@link ng.directive:input input element}.
25370  *
25371  * @param {string} ngModel Assignable angular expression to data-bind to.
25372  * @param {string=} name Property name of the form under which the control is published.
25373  * @param {string=} required Sets `required` validation error key if the value is not entered.
25374  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25375  *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25376  *    `required` when you want to data-bind to the `required` attribute.
25377  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
25378  *    minlength.
25379  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
25380  *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
25381  *    length.
25382  * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
25383  *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
25384  *    If the expression evaluates to a RegExp object, then this is used directly.
25385  *    If the expression evaluates to a string, then it will be converted to a RegExp
25386  *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
25387  *    `new RegExp('^abc$')`.<br />
25388  *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
25389  *    start at the index of the last search's match, thus not taking the whole input value into
25390  *    account.
25391  * @param {string=} ngChange Angular expression to be executed when input changes due to user
25392  *    interaction with the input element.
25393  * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
25394  *
25395  * @knownIssue
25396  *
25397  * When specifying the `placeholder` attribute of `<textarea>`, Internet Explorer will temporarily
25398  * insert the placeholder value as the textarea's content. If the placeholder value contains
25399  * interpolation (`{{ ... }}`), an error will be logged in the console when Angular tries to update
25400  * the value of the by-then-removed text node. This doesn't affect the functionality of the
25401  * textarea, but can be undesirable.
25402  *
25403  * You can work around this Internet Explorer issue by using `ng-attr-placeholder` instead of
25404  * `placeholder` on textareas, whenever you need interpolation in the placeholder value. You can
25405  * find more details on `ngAttr` in the
25406  * [Interpolation](guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes) section of the
25407  * Developer Guide.
25408  */
25409
25410
25411 /**
25412  * @ngdoc directive
25413  * @name input
25414  * @restrict E
25415  *
25416  * @description
25417  * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
25418  * input state control, and validation.
25419  * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
25420  *
25421  * <div class="alert alert-warning">
25422  * **Note:** Not every feature offered is available for all input types.
25423  * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
25424  * </div>
25425  *
25426  * @param {string} ngModel Assignable angular expression to data-bind to.
25427  * @param {string=} name Property name of the form under which the control is published.
25428  * @param {string=} required Sets `required` validation error key if the value is not entered.
25429  * @param {boolean=} ngRequired Sets `required` attribute if set to true
25430  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
25431  *    minlength.
25432  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
25433  *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
25434  *    length.
25435  * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
25436  *    value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
25437  *    If the expression evaluates to a RegExp object, then this is used directly.
25438  *    If the expression evaluates to a string, then it will be converted to a RegExp
25439  *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
25440  *    `new RegExp('^abc$')`.<br />
25441  *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
25442  *    start at the index of the last search's match, thus not taking the whole input value into
25443  *    account.
25444  * @param {string=} ngChange Angular expression to be executed when input changes due to user
25445  *    interaction with the input element.
25446  * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
25447  *    This parameter is ignored for input[type=password] controls, which will never trim the
25448  *    input.
25449  *
25450  * @example
25451     <example name="input-directive" module="inputExample">
25452       <file name="index.html">
25453        <script>
25454           angular.module('inputExample', [])
25455             .controller('ExampleController', ['$scope', function($scope) {
25456               $scope.user = {name: 'guest', last: 'visitor'};
25457             }]);
25458        </script>
25459        <div ng-controller="ExampleController">
25460          <form name="myForm">
25461            <label>
25462               User name:
25463               <input type="text" name="userName" ng-model="user.name" required>
25464            </label>
25465            <div role="alert">
25466              <span class="error" ng-show="myForm.userName.$error.required">
25467               Required!</span>
25468            </div>
25469            <label>
25470               Last name:
25471               <input type="text" name="lastName" ng-model="user.last"
25472               ng-minlength="3" ng-maxlength="10">
25473            </label>
25474            <div role="alert">
25475              <span class="error" ng-show="myForm.lastName.$error.minlength">
25476                Too short!</span>
25477              <span class="error" ng-show="myForm.lastName.$error.maxlength">
25478                Too long!</span>
25479            </div>
25480          </form>
25481          <hr>
25482          <tt>user = {{user}}</tt><br/>
25483          <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
25484          <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
25485          <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
25486          <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
25487          <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25488          <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25489          <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
25490          <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
25491        </div>
25492       </file>
25493       <file name="protractor.js" type="protractor">
25494         var user = element(by.exactBinding('user'));
25495         var userNameValid = element(by.binding('myForm.userName.$valid'));
25496         var lastNameValid = element(by.binding('myForm.lastName.$valid'));
25497         var lastNameError = element(by.binding('myForm.lastName.$error'));
25498         var formValid = element(by.binding('myForm.$valid'));
25499         var userNameInput = element(by.model('user.name'));
25500         var userLastInput = element(by.model('user.last'));
25501
25502         it('should initialize to model', function() {
25503           expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
25504           expect(userNameValid.getText()).toContain('true');
25505           expect(formValid.getText()).toContain('true');
25506         });
25507
25508         it('should be invalid if empty when required', function() {
25509           userNameInput.clear();
25510           userNameInput.sendKeys('');
25511
25512           expect(user.getText()).toContain('{"last":"visitor"}');
25513           expect(userNameValid.getText()).toContain('false');
25514           expect(formValid.getText()).toContain('false');
25515         });
25516
25517         it('should be valid if empty when min length is set', function() {
25518           userLastInput.clear();
25519           userLastInput.sendKeys('');
25520
25521           expect(user.getText()).toContain('{"name":"guest","last":""}');
25522           expect(lastNameValid.getText()).toContain('true');
25523           expect(formValid.getText()).toContain('true');
25524         });
25525
25526         it('should be invalid if less than required min length', function() {
25527           userLastInput.clear();
25528           userLastInput.sendKeys('xx');
25529
25530           expect(user.getText()).toContain('{"name":"guest"}');
25531           expect(lastNameValid.getText()).toContain('false');
25532           expect(lastNameError.getText()).toContain('minlength');
25533           expect(formValid.getText()).toContain('false');
25534         });
25535
25536         it('should be invalid if longer than max length', function() {
25537           userLastInput.clear();
25538           userLastInput.sendKeys('some ridiculously long name');
25539
25540           expect(user.getText()).toContain('{"name":"guest"}');
25541           expect(lastNameValid.getText()).toContain('false');
25542           expect(lastNameError.getText()).toContain('maxlength');
25543           expect(formValid.getText()).toContain('false');
25544         });
25545       </file>
25546     </example>
25547  */
25548 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
25549     function($browser, $sniffer, $filter, $parse) {
25550   return {
25551     restrict: 'E',
25552     require: ['?ngModel'],
25553     link: {
25554       pre: function(scope, element, attr, ctrls) {
25555         if (ctrls[0]) {
25556           (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
25557                                                               $browser, $filter, $parse);
25558         }
25559       }
25560     }
25561   };
25562 }];
25563
25564
25565
25566 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
25567 /**
25568  * @ngdoc directive
25569  * @name ngValue
25570  *
25571  * @description
25572  * Binds the given expression to the value of the element.
25573  *
25574  * It is mainly used on {@link input[radio] `input[radio]`} and option elements,
25575  * so that when the element is selected, the {@link ngModel `ngModel`} of that element (or its
25576  * {@link select `select`} parent element) is set to the bound value. It is especially useful
25577  * for dynamically generated lists using {@link ngRepeat `ngRepeat`}, as shown below.
25578  *
25579  * It can also be used to achieve one-way binding of a given expression to an input element
25580  * such as an `input[text]` or a `textarea`, when that element does not use ngModel.
25581  *
25582  * @element input
25583  * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
25584  * and `value` property of the element.
25585  *
25586  * @example
25587     <example name="ngValue-directive" module="valueExample">
25588       <file name="index.html">
25589        <script>
25590           angular.module('valueExample', [])
25591             .controller('ExampleController', ['$scope', function($scope) {
25592               $scope.names = ['pizza', 'unicorns', 'robots'];
25593               $scope.my = { favorite: 'unicorns' };
25594             }]);
25595        </script>
25596         <form ng-controller="ExampleController">
25597           <h2>Which is your favorite?</h2>
25598             <label ng-repeat="name in names" for="{{name}}">
25599               {{name}}
25600               <input type="radio"
25601                      ng-model="my.favorite"
25602                      ng-value="name"
25603                      id="{{name}}"
25604                      name="favorite">
25605             </label>
25606           <div>You chose {{my.favorite}}</div>
25607         </form>
25608       </file>
25609       <file name="protractor.js" type="protractor">
25610         var favorite = element(by.binding('my.favorite'));
25611
25612         it('should initialize to model', function() {
25613           expect(favorite.getText()).toContain('unicorns');
25614         });
25615         it('should bind the values to the inputs', function() {
25616           element.all(by.model('my.favorite')).get(0).click();
25617           expect(favorite.getText()).toContain('pizza');
25618         });
25619       </file>
25620     </example>
25621  */
25622 var ngValueDirective = function() {
25623   /**
25624    *  inputs use the value attribute as their default value if the value property is not set.
25625    *  Once the value property has been set (by adding input), it will not react to changes to
25626    *  the value attribute anymore. Setting both attribute and property fixes this behavior, and
25627    *  makes it possible to use ngValue as a sort of one-way bind.
25628    */
25629   function updateElementValue(element, attr, value) {
25630     // Support: IE9 only
25631     // In IE9 values are converted to string (e.g. `input.value = null` results in `input.value === 'null'`).
25632     var propValue = isDefined(value) ? value : (msie === 9) ? '' : null;
25633     element.prop('value', propValue);
25634     attr.$set('value', value);
25635   }
25636
25637   return {
25638     restrict: 'A',
25639     priority: 100,
25640     compile: function(tpl, tplAttr) {
25641       if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
25642         return function ngValueConstantLink(scope, elm, attr) {
25643           var value = scope.$eval(attr.ngValue);
25644           updateElementValue(elm, attr, value);
25645         };
25646       } else {
25647         return function ngValueLink(scope, elm, attr) {
25648           scope.$watch(attr.ngValue, function valueWatchAction(value) {
25649             updateElementValue(elm, attr, value);
25650           });
25651         };
25652       }
25653     }
25654   };
25655 };
25656
25657 /**
25658  * @ngdoc directive
25659  * @name ngBind
25660  * @restrict AC
25661  *
25662  * @description
25663  * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
25664  * with the value of a given expression, and to update the text content when the value of that
25665  * expression changes.
25666  *
25667  * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
25668  * `{{ expression }}` which is similar but less verbose.
25669  *
25670  * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
25671  * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
25672  * element attribute, it makes the bindings invisible to the user while the page is loading.
25673  *
25674  * An alternative solution to this problem would be using the
25675  * {@link ng.directive:ngCloak ngCloak} directive.
25676  *
25677  *
25678  * @element ANY
25679  * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
25680  *
25681  * @example
25682  * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
25683    <example module="bindExample" name="ng-bind">
25684      <file name="index.html">
25685        <script>
25686          angular.module('bindExample', [])
25687            .controller('ExampleController', ['$scope', function($scope) {
25688              $scope.name = 'Whirled';
25689            }]);
25690        </script>
25691        <div ng-controller="ExampleController">
25692          <label>Enter name: <input type="text" ng-model="name"></label><br>
25693          Hello <span ng-bind="name"></span>!
25694        </div>
25695      </file>
25696      <file name="protractor.js" type="protractor">
25697        it('should check ng-bind', function() {
25698          var nameInput = element(by.model('name'));
25699
25700          expect(element(by.binding('name')).getText()).toBe('Whirled');
25701          nameInput.clear();
25702          nameInput.sendKeys('world');
25703          expect(element(by.binding('name')).getText()).toBe('world');
25704        });
25705      </file>
25706    </example>
25707  */
25708 var ngBindDirective = ['$compile', function($compile) {
25709   return {
25710     restrict: 'AC',
25711     compile: function ngBindCompile(templateElement) {
25712       $compile.$$addBindingClass(templateElement);
25713       return function ngBindLink(scope, element, attr) {
25714         $compile.$$addBindingInfo(element, attr.ngBind);
25715         element = element[0];
25716         scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
25717           element.textContent = stringify(value);
25718         });
25719       };
25720     }
25721   };
25722 }];
25723
25724
25725 /**
25726  * @ngdoc directive
25727  * @name ngBindTemplate
25728  *
25729  * @description
25730  * The `ngBindTemplate` directive specifies that the element
25731  * text content should be replaced with the interpolation of the template
25732  * in the `ngBindTemplate` attribute.
25733  * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
25734  * expressions. This directive is needed since some HTML elements
25735  * (such as TITLE and OPTION) cannot contain SPAN elements.
25736  *
25737  * @element ANY
25738  * @param {string} ngBindTemplate template of form
25739  *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
25740  *
25741  * @example
25742  * Try it here: enter text in text box and watch the greeting change.
25743    <example module="bindExample" name="ng-bind-template">
25744      <file name="index.html">
25745        <script>
25746          angular.module('bindExample', [])
25747            .controller('ExampleController', ['$scope', function($scope) {
25748              $scope.salutation = 'Hello';
25749              $scope.name = 'World';
25750            }]);
25751        </script>
25752        <div ng-controller="ExampleController">
25753         <label>Salutation: <input type="text" ng-model="salutation"></label><br>
25754         <label>Name: <input type="text" ng-model="name"></label><br>
25755         <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
25756        </div>
25757      </file>
25758      <file name="protractor.js" type="protractor">
25759        it('should check ng-bind', function() {
25760          var salutationElem = element(by.binding('salutation'));
25761          var salutationInput = element(by.model('salutation'));
25762          var nameInput = element(by.model('name'));
25763
25764          expect(salutationElem.getText()).toBe('Hello World!');
25765
25766          salutationInput.clear();
25767          salutationInput.sendKeys('Greetings');
25768          nameInput.clear();
25769          nameInput.sendKeys('user');
25770
25771          expect(salutationElem.getText()).toBe('Greetings user!');
25772        });
25773      </file>
25774    </example>
25775  */
25776 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
25777   return {
25778     compile: function ngBindTemplateCompile(templateElement) {
25779       $compile.$$addBindingClass(templateElement);
25780       return function ngBindTemplateLink(scope, element, attr) {
25781         var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
25782         $compile.$$addBindingInfo(element, interpolateFn.expressions);
25783         element = element[0];
25784         attr.$observe('ngBindTemplate', function(value) {
25785           element.textContent = isUndefined(value) ? '' : value;
25786         });
25787       };
25788     }
25789   };
25790 }];
25791
25792
25793 /**
25794  * @ngdoc directive
25795  * @name ngBindHtml
25796  *
25797  * @description
25798  * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
25799  * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
25800  * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
25801  * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
25802  * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
25803  *
25804  * You may also bypass sanitization for values you know are safe. To do so, bind to
25805  * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
25806  * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
25807  *
25808  * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
25809  * will have an exception (instead of an exploit.)
25810  *
25811  * @element ANY
25812  * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
25813  *
25814  * @example
25815
25816    <example module="bindHtmlExample" deps="angular-sanitize.js" name="ng-bind-html">
25817      <file name="index.html">
25818        <div ng-controller="ExampleController">
25819         <p ng-bind-html="myHTML"></p>
25820        </div>
25821      </file>
25822
25823      <file name="script.js">
25824        angular.module('bindHtmlExample', ['ngSanitize'])
25825          .controller('ExampleController', ['$scope', function($scope) {
25826            $scope.myHTML =
25827               'I am an <code>HTML</code>string with ' +
25828               '<a href="#">links!</a> and other <em>stuff</em>';
25829          }]);
25830      </file>
25831
25832      <file name="protractor.js" type="protractor">
25833        it('should check ng-bind-html', function() {
25834          expect(element(by.binding('myHTML')).getText()).toBe(
25835              'I am an HTMLstring with links! and other stuff');
25836        });
25837      </file>
25838    </example>
25839  */
25840 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
25841   return {
25842     restrict: 'A',
25843     compile: function ngBindHtmlCompile(tElement, tAttrs) {
25844       var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
25845       var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function sceValueOf(val) {
25846         // Unwrap the value to compare the actual inner safe value, not the wrapper object.
25847         return $sce.valueOf(val);
25848       });
25849       $compile.$$addBindingClass(tElement);
25850
25851       return function ngBindHtmlLink(scope, element, attr) {
25852         $compile.$$addBindingInfo(element, attr.ngBindHtml);
25853
25854         scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
25855           // The watched value is the unwrapped value. To avoid re-escaping, use the direct getter.
25856           var value = ngBindHtmlGetter(scope);
25857           element.html($sce.getTrustedHtml(value) || '');
25858         });
25859       };
25860     }
25861   };
25862 }];
25863
25864 /**
25865  * @ngdoc directive
25866  * @name ngChange
25867  *
25868  * @description
25869  * Evaluate the given expression when the user changes the input.
25870  * The expression is evaluated immediately, unlike the JavaScript onchange event
25871  * which only triggers at the end of a change (usually, when the user leaves the
25872  * form element or presses the return key).
25873  *
25874  * The `ngChange` expression is only evaluated when a change in the input value causes
25875  * a new value to be committed to the model.
25876  *
25877  * It will not be evaluated:
25878  * * if the value returned from the `$parsers` transformation pipeline has not changed
25879  * * if the input has continued to be invalid since the model will stay `null`
25880  * * if the model is changed programmatically and not by a change to the input value
25881  *
25882  *
25883  * Note, this directive requires `ngModel` to be present.
25884  *
25885  * @element input
25886  * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
25887  * in input value.
25888  *
25889  * @example
25890  * <example name="ngChange-directive" module="changeExample">
25891  *   <file name="index.html">
25892  *     <script>
25893  *       angular.module('changeExample', [])
25894  *         .controller('ExampleController', ['$scope', function($scope) {
25895  *           $scope.counter = 0;
25896  *           $scope.change = function() {
25897  *             $scope.counter++;
25898  *           };
25899  *         }]);
25900  *     </script>
25901  *     <div ng-controller="ExampleController">
25902  *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
25903  *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
25904  *       <label for="ng-change-example2">Confirmed</label><br />
25905  *       <tt>debug = {{confirmed}}</tt><br/>
25906  *       <tt>counter = {{counter}}</tt><br/>
25907  *     </div>
25908  *   </file>
25909  *   <file name="protractor.js" type="protractor">
25910  *     var counter = element(by.binding('counter'));
25911  *     var debug = element(by.binding('confirmed'));
25912  *
25913  *     it('should evaluate the expression if changing from view', function() {
25914  *       expect(counter.getText()).toContain('0');
25915  *
25916  *       element(by.id('ng-change-example1')).click();
25917  *
25918  *       expect(counter.getText()).toContain('1');
25919  *       expect(debug.getText()).toContain('true');
25920  *     });
25921  *
25922  *     it('should not evaluate the expression if changing from model', function() {
25923  *       element(by.id('ng-change-example2')).click();
25924
25925  *       expect(counter.getText()).toContain('0');
25926  *       expect(debug.getText()).toContain('true');
25927  *     });
25928  *   </file>
25929  * </example>
25930  */
25931 var ngChangeDirective = valueFn({
25932   restrict: 'A',
25933   require: 'ngModel',
25934   link: function(scope, element, attr, ctrl) {
25935     ctrl.$viewChangeListeners.push(function() {
25936       scope.$eval(attr.ngChange);
25937     });
25938   }
25939 });
25940
25941 /* exported
25942   ngClassDirective,
25943   ngClassEvenDirective,
25944   ngClassOddDirective
25945 */
25946
25947 function classDirective(name, selector) {
25948   name = 'ngClass' + name;
25949   var indexWatchExpression;
25950
25951   return ['$parse', function($parse) {
25952     return {
25953       restrict: 'AC',
25954       link: function(scope, element, attr) {
25955         var expression = attr[name].trim();
25956         var isOneTime = (expression.charAt(0) === ':') && (expression.charAt(1) === ':');
25957
25958         var watchInterceptor = isOneTime ? toFlatValue : toClassString;
25959         var watchExpression = $parse(expression, watchInterceptor);
25960         var watchAction = isOneTime ? ngClassOneTimeWatchAction : ngClassWatchAction;
25961
25962         var classCounts = element.data('$classCounts');
25963         var oldModulo = true;
25964         var oldClassString;
25965
25966         if (!classCounts) {
25967           // Use createMap() to prevent class assumptions involving property
25968           // names in Object.prototype
25969           classCounts = createMap();
25970           element.data('$classCounts', classCounts);
25971         }
25972
25973         if (name !== 'ngClass') {
25974           if (!indexWatchExpression) {
25975             indexWatchExpression = $parse('$index', function moduloTwo($index) {
25976               // eslint-disable-next-line no-bitwise
25977               return $index & 1;
25978             });
25979           }
25980
25981           scope.$watch(indexWatchExpression, ngClassIndexWatchAction);
25982         }
25983
25984         scope.$watch(watchExpression, watchAction, isOneTime);
25985
25986         function addClasses(classString) {
25987           classString = digestClassCounts(split(classString), 1);
25988           attr.$addClass(classString);
25989         }
25990
25991         function removeClasses(classString) {
25992           classString = digestClassCounts(split(classString), -1);
25993           attr.$removeClass(classString);
25994         }
25995
25996         function updateClasses(oldClassString, newClassString) {
25997           var oldClassArray = split(oldClassString);
25998           var newClassArray = split(newClassString);
25999
26000           var toRemoveArray = arrayDifference(oldClassArray, newClassArray);
26001           var toAddArray = arrayDifference(newClassArray, oldClassArray);
26002
26003           var toRemoveString = digestClassCounts(toRemoveArray, -1);
26004           var toAddString = digestClassCounts(toAddArray, 1);
26005
26006           attr.$addClass(toAddString);
26007           attr.$removeClass(toRemoveString);
26008         }
26009
26010         function digestClassCounts(classArray, count) {
26011           var classesToUpdate = [];
26012
26013           forEach(classArray, function(className) {
26014             if (count > 0 || classCounts[className]) {
26015               classCounts[className] = (classCounts[className] || 0) + count;
26016               if (classCounts[className] === +(count > 0)) {
26017                 classesToUpdate.push(className);
26018               }
26019             }
26020           });
26021
26022           return classesToUpdate.join(' ');
26023         }
26024
26025         function ngClassIndexWatchAction(newModulo) {
26026           // This watch-action should run before the `ngClass[OneTime]WatchAction()`, thus it
26027           // adds/removes `oldClassString`. If the `ngClass` expression has changed as well, the
26028           // `ngClass[OneTime]WatchAction()` will update the classes.
26029           if (newModulo === selector) {
26030             addClasses(oldClassString);
26031           } else {
26032             removeClasses(oldClassString);
26033           }
26034
26035           oldModulo = newModulo;
26036         }
26037
26038         function ngClassOneTimeWatchAction(newClassValue) {
26039           var newClassString = toClassString(newClassValue);
26040
26041           if (newClassString !== oldClassString) {
26042             ngClassWatchAction(newClassString);
26043           }
26044         }
26045
26046         function ngClassWatchAction(newClassString) {
26047           if (oldModulo === selector) {
26048             updateClasses(oldClassString, newClassString);
26049           }
26050
26051           oldClassString = newClassString;
26052         }
26053       }
26054     };
26055   }];
26056
26057   // Helpers
26058   function arrayDifference(tokens1, tokens2) {
26059     if (!tokens1 || !tokens1.length) return [];
26060     if (!tokens2 || !tokens2.length) return tokens1;
26061
26062     var values = [];
26063
26064     outer:
26065     for (var i = 0; i < tokens1.length; i++) {
26066       var token = tokens1[i];
26067       for (var j = 0; j < tokens2.length; j++) {
26068         if (token === tokens2[j]) continue outer;
26069       }
26070       values.push(token);
26071     }
26072
26073     return values;
26074   }
26075
26076   function split(classString) {
26077     return classString && classString.split(' ');
26078   }
26079
26080   function toClassString(classValue) {
26081     var classString = classValue;
26082
26083     if (isArray(classValue)) {
26084       classString = classValue.map(toClassString).join(' ');
26085     } else if (isObject(classValue)) {
26086       classString = Object.keys(classValue).
26087         filter(function(key) { return classValue[key]; }).
26088         join(' ');
26089     }
26090
26091     return classString;
26092   }
26093
26094   function toFlatValue(classValue) {
26095     var flatValue = classValue;
26096
26097     if (isArray(classValue)) {
26098       flatValue = classValue.map(toFlatValue);
26099     } else if (isObject(classValue)) {
26100       var hasUndefined = false;
26101
26102       flatValue = Object.keys(classValue).filter(function(key) {
26103         var value = classValue[key];
26104
26105         if (!hasUndefined && isUndefined(value)) {
26106           hasUndefined = true;
26107         }
26108
26109         return value;
26110       });
26111
26112       if (hasUndefined) {
26113         // Prevent the `oneTimeLiteralWatchInterceptor` from unregistering
26114         // the watcher, by including at least one `undefined` value.
26115         flatValue.push(undefined);
26116       }
26117     }
26118
26119     return flatValue;
26120   }
26121 }
26122
26123 /**
26124  * @ngdoc directive
26125  * @name ngClass
26126  * @restrict AC
26127  *
26128  * @description
26129  * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
26130  * an expression that represents all classes to be added.
26131  *
26132  * The directive operates in three different ways, depending on which of three types the expression
26133  * evaluates to:
26134  *
26135  * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
26136  * names.
26137  *
26138  * 2. If the expression evaluates to an object, then for each key-value pair of the
26139  * object with a truthy value the corresponding key is used as a class name.
26140  *
26141  * 3. If the expression evaluates to an array, each element of the array should either be a string as in
26142  * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
26143  * to give you more control over what CSS classes appear. See the code below for an example of this.
26144  *
26145  *
26146  * The directive won't add duplicate classes if a particular class was already set.
26147  *
26148  * When the expression changes, the previously added classes are removed and only then are the
26149  * new classes added.
26150  *
26151  * @knownIssue
26152  * You should not use {@link guide/interpolation interpolation} in the value of the `class`
26153  * attribute, when using the `ngClass` directive on the same element.
26154  * See {@link guide/interpolation#known-issues here} for more info.
26155  *
26156  * @animations
26157  * | Animation                        | Occurs                              |
26158  * |----------------------------------|-------------------------------------|
26159  * | {@link ng.$animate#addClass addClass}       | just before the class is applied to the element   |
26160  * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
26161  *
26162  * @element ANY
26163  * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
26164  *   of the evaluation can be a string representing space delimited class
26165  *   names, an array, or a map of class names to boolean values. In the case of a map, the
26166  *   names of the properties whose values are truthy will be added as css classes to the
26167  *   element.
26168  *
26169  * @example Example that demonstrates basic bindings via ngClass directive.
26170    <example name="ng-class">
26171      <file name="index.html">
26172        <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
26173        <label>
26174           <input type="checkbox" ng-model="deleted">
26175           deleted (apply "strike" class)
26176        </label><br>
26177        <label>
26178           <input type="checkbox" ng-model="important">
26179           important (apply "bold" class)
26180        </label><br>
26181        <label>
26182           <input type="checkbox" ng-model="error">
26183           error (apply "has-error" class)
26184        </label>
26185        <hr>
26186        <p ng-class="style">Using String Syntax</p>
26187        <input type="text" ng-model="style"
26188               placeholder="Type: bold strike red" aria-label="Type: bold strike red">
26189        <hr>
26190        <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
26191        <input ng-model="style1"
26192               placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
26193        <input ng-model="style2"
26194               placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
26195        <input ng-model="style3"
26196               placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
26197        <hr>
26198        <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
26199        <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
26200        <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
26201      </file>
26202      <file name="style.css">
26203        .strike {
26204            text-decoration: line-through;
26205        }
26206        .bold {
26207            font-weight: bold;
26208        }
26209        .red {
26210            color: red;
26211        }
26212        .has-error {
26213            color: red;
26214            background-color: yellow;
26215        }
26216        .orange {
26217            color: orange;
26218        }
26219      </file>
26220      <file name="protractor.js" type="protractor">
26221        var ps = element.all(by.css('p'));
26222
26223        it('should let you toggle the class', function() {
26224
26225          expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
26226          expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
26227
26228          element(by.model('important')).click();
26229          expect(ps.first().getAttribute('class')).toMatch(/bold/);
26230
26231          element(by.model('error')).click();
26232          expect(ps.first().getAttribute('class')).toMatch(/has-error/);
26233        });
26234
26235        it('should let you toggle string example', function() {
26236          expect(ps.get(1).getAttribute('class')).toBe('');
26237          element(by.model('style')).clear();
26238          element(by.model('style')).sendKeys('red');
26239          expect(ps.get(1).getAttribute('class')).toBe('red');
26240        });
26241
26242        it('array example should have 3 classes', function() {
26243          expect(ps.get(2).getAttribute('class')).toBe('');
26244          element(by.model('style1')).sendKeys('bold');
26245          element(by.model('style2')).sendKeys('strike');
26246          element(by.model('style3')).sendKeys('red');
26247          expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
26248        });
26249
26250        it('array with map example should have 2 classes', function() {
26251          expect(ps.last().getAttribute('class')).toBe('');
26252          element(by.model('style4')).sendKeys('bold');
26253          element(by.model('warning')).click();
26254          expect(ps.last().getAttribute('class')).toBe('bold orange');
26255        });
26256      </file>
26257    </example>
26258
26259    ## Animations
26260
26261    The example below demonstrates how to perform animations using ngClass.
26262
26263    <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-class">
26264      <file name="index.html">
26265       <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
26266       <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
26267       <br>
26268       <span class="base-class" ng-class="myVar">Sample Text</span>
26269      </file>
26270      <file name="style.css">
26271        .base-class {
26272          transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
26273        }
26274
26275        .base-class.my-class {
26276          color: red;
26277          font-size:3em;
26278        }
26279      </file>
26280      <file name="protractor.js" type="protractor">
26281        it('should check ng-class', function() {
26282          expect(element(by.css('.base-class')).getAttribute('class')).not.
26283            toMatch(/my-class/);
26284
26285          element(by.id('setbtn')).click();
26286
26287          expect(element(by.css('.base-class')).getAttribute('class')).
26288            toMatch(/my-class/);
26289
26290          element(by.id('clearbtn')).click();
26291
26292          expect(element(by.css('.base-class')).getAttribute('class')).not.
26293            toMatch(/my-class/);
26294        });
26295      </file>
26296    </example>
26297
26298
26299    ## ngClass and pre-existing CSS3 Transitions/Animations
26300    The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
26301    Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
26302    any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
26303    to view the step by step details of {@link $animate#addClass $animate.addClass} and
26304    {@link $animate#removeClass $animate.removeClass}.
26305  */
26306 var ngClassDirective = classDirective('', true);
26307
26308 /**
26309  * @ngdoc directive
26310  * @name ngClassOdd
26311  * @restrict AC
26312  *
26313  * @description
26314  * The `ngClassOdd` and `ngClassEven` directives work exactly as
26315  * {@link ng.directive:ngClass ngClass}, except they work in
26316  * conjunction with `ngRepeat` and take effect only on odd (even) rows.
26317  *
26318  * This directive can be applied only within the scope of an
26319  * {@link ng.directive:ngRepeat ngRepeat}.
26320  *
26321  * @element ANY
26322  * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
26323  *   of the evaluation can be a string representing space delimited class names or an array.
26324  *
26325  * @example
26326    <example name="ng-class-odd">
26327      <file name="index.html">
26328         <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
26329           <li ng-repeat="name in names">
26330            <span ng-class-odd="'odd'" ng-class-even="'even'">
26331              {{name}}
26332            </span>
26333           </li>
26334         </ol>
26335      </file>
26336      <file name="style.css">
26337        .odd {
26338          color: red;
26339        }
26340        .even {
26341          color: blue;
26342        }
26343      </file>
26344      <file name="protractor.js" type="protractor">
26345        it('should check ng-class-odd and ng-class-even', function() {
26346          expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
26347            toMatch(/odd/);
26348          expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
26349            toMatch(/even/);
26350        });
26351      </file>
26352    </example>
26353  */
26354 var ngClassOddDirective = classDirective('Odd', 0);
26355
26356 /**
26357  * @ngdoc directive
26358  * @name ngClassEven
26359  * @restrict AC
26360  *
26361  * @description
26362  * The `ngClassOdd` and `ngClassEven` directives work exactly as
26363  * {@link ng.directive:ngClass ngClass}, except they work in
26364  * conjunction with `ngRepeat` and take effect only on odd (even) rows.
26365  *
26366  * This directive can be applied only within the scope of an
26367  * {@link ng.directive:ngRepeat ngRepeat}.
26368  *
26369  * @element ANY
26370  * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
26371  *   result of the evaluation can be a string representing space delimited class names or an array.
26372  *
26373  * @example
26374    <example name="ng-class-even">
26375      <file name="index.html">
26376         <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
26377           <li ng-repeat="name in names">
26378            <span ng-class-odd="'odd'" ng-class-even="'even'">
26379              {{name}} &nbsp; &nbsp; &nbsp;
26380            </span>
26381           </li>
26382         </ol>
26383      </file>
26384      <file name="style.css">
26385        .odd {
26386          color: red;
26387        }
26388        .even {
26389          color: blue;
26390        }
26391      </file>
26392      <file name="protractor.js" type="protractor">
26393        it('should check ng-class-odd and ng-class-even', function() {
26394          expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
26395            toMatch(/odd/);
26396          expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
26397            toMatch(/even/);
26398        });
26399      </file>
26400    </example>
26401  */
26402 var ngClassEvenDirective = classDirective('Even', 1);
26403
26404 /**
26405  * @ngdoc directive
26406  * @name ngCloak
26407  * @restrict AC
26408  *
26409  * @description
26410  * The `ngCloak` directive is used to prevent the Angular html template from being briefly
26411  * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
26412  * directive to avoid the undesirable flicker effect caused by the html template display.
26413  *
26414  * The directive can be applied to the `<body>` element, but the preferred usage is to apply
26415  * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
26416  * of the browser view.
26417  *
26418  * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
26419  * `angular.min.js`.
26420  * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
26421  *
26422  * ```css
26423  * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
26424  *   display: none !important;
26425  * }
26426  * ```
26427  *
26428  * When this css rule is loaded by the browser, all html elements (including their children) that
26429  * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
26430  * during the compilation of the template it deletes the `ngCloak` element attribute, making
26431  * the compiled element visible.
26432  *
26433  * For the best result, the `angular.js` script must be loaded in the head section of the html
26434  * document; alternatively, the css rule above must be included in the external stylesheet of the
26435  * application.
26436  *
26437  * @element ANY
26438  *
26439  * @example
26440    <example name="ng-cloak">
26441      <file name="index.html">
26442         <div id="template1" ng-cloak>{{ 'hello' }}</div>
26443         <div id="template2" class="ng-cloak">{{ 'world' }}</div>
26444      </file>
26445      <file name="protractor.js" type="protractor">
26446        it('should remove the template directive and css class', function() {
26447          expect($('#template1').getAttribute('ng-cloak')).
26448            toBeNull();
26449          expect($('#template2').getAttribute('ng-cloak')).
26450            toBeNull();
26451        });
26452      </file>
26453    </example>
26454  *
26455  */
26456 var ngCloakDirective = ngDirective({
26457   compile: function(element, attr) {
26458     attr.$set('ngCloak', undefined);
26459     element.removeClass('ng-cloak');
26460   }
26461 });
26462
26463 /**
26464  * @ngdoc directive
26465  * @name ngController
26466  *
26467  * @description
26468  * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
26469  * supports the principles behind the Model-View-Controller design pattern.
26470  *
26471  * MVC components in angular:
26472  *
26473  * * Model â€” Models are the properties of a scope; scopes are attached to the DOM where scope properties
26474  *   are accessed through bindings.
26475  * * View â€” The template (HTML with data bindings) that is rendered into the View.
26476  * * Controller â€” The `ngController` directive specifies a Controller class; the class contains business
26477  *   logic behind the application to decorate the scope with functions and values
26478  *
26479  * Note that you can also attach controllers to the DOM by declaring it in a route definition
26480  * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
26481  * again using `ng-controller` in the template itself.  This will cause the controller to be attached
26482  * and executed twice.
26483  *
26484  * @element ANY
26485  * @scope
26486  * @priority 500
26487  * @param {expression} ngController Name of a constructor function registered with the current
26488  * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
26489  * that on the current scope evaluates to a constructor function.
26490  *
26491  * The controller instance can be published into a scope property by specifying
26492  * `ng-controller="as propertyName"`.
26493  *
26494  * If the current `$controllerProvider` is configured to use globals (via
26495  * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
26496  * also be the name of a globally accessible constructor function (deprecated, not recommended).
26497  *
26498  * @example
26499  * Here is a simple form for editing user contact information. Adding, removing, clearing, and
26500  * greeting are methods declared on the controller (see source tab). These methods can
26501  * easily be called from the angular markup. Any changes to the data are automatically reflected
26502  * in the View without the need for a manual update.
26503  *
26504  * Two different declaration styles are included below:
26505  *
26506  * * one binds methods and properties directly onto the controller using `this`:
26507  * `ng-controller="SettingsController1 as settings"`
26508  * * one injects `$scope` into the controller:
26509  * `ng-controller="SettingsController2"`
26510  *
26511  * The second option is more common in the Angular community, and is generally used in boilerplates
26512  * and in this guide. However, there are advantages to binding properties directly to the controller
26513  * and avoiding scope.
26514  *
26515  * * Using `controller as` makes it obvious which controller you are accessing in the template when
26516  * multiple controllers apply to an element.
26517  * * If you are writing your controllers as classes you have easier access to the properties and
26518  * methods, which will appear on the scope, from inside the controller code.
26519  * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
26520  * inheritance masking primitives.
26521  *
26522  * This example demonstrates the `controller as` syntax.
26523  *
26524  * <example name="ngControllerAs" module="controllerAsExample">
26525  *   <file name="index.html">
26526  *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
26527  *      <label>Name: <input type="text" ng-model="settings.name"/></label>
26528  *      <button ng-click="settings.greet()">greet</button><br/>
26529  *      Contact:
26530  *      <ul>
26531  *        <li ng-repeat="contact in settings.contacts">
26532  *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
26533  *             <option>phone</option>
26534  *             <option>email</option>
26535  *          </select>
26536  *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
26537  *          <button ng-click="settings.clearContact(contact)">clear</button>
26538  *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
26539  *        </li>
26540  *        <li><button ng-click="settings.addContact()">add</button></li>
26541  *     </ul>
26542  *    </div>
26543  *   </file>
26544  *   <file name="app.js">
26545  *    angular.module('controllerAsExample', [])
26546  *      .controller('SettingsController1', SettingsController1);
26547  *
26548  *    function SettingsController1() {
26549  *      this.name = 'John Smith';
26550  *      this.contacts = [
26551  *        {type: 'phone', value: '408 555 1212'},
26552  *        {type: 'email', value: 'john.smith@example.org'}
26553  *      ];
26554  *    }
26555  *
26556  *    SettingsController1.prototype.greet = function() {
26557  *      alert(this.name);
26558  *    };
26559  *
26560  *    SettingsController1.prototype.addContact = function() {
26561  *      this.contacts.push({type: 'email', value: 'yourname@example.org'});
26562  *    };
26563  *
26564  *    SettingsController1.prototype.removeContact = function(contactToRemove) {
26565  *     var index = this.contacts.indexOf(contactToRemove);
26566  *      this.contacts.splice(index, 1);
26567  *    };
26568  *
26569  *    SettingsController1.prototype.clearContact = function(contact) {
26570  *      contact.type = 'phone';
26571  *      contact.value = '';
26572  *    };
26573  *   </file>
26574  *   <file name="protractor.js" type="protractor">
26575  *     it('should check controller as', function() {
26576  *       var container = element(by.id('ctrl-as-exmpl'));
26577  *         expect(container.element(by.model('settings.name'))
26578  *           .getAttribute('value')).toBe('John Smith');
26579  *
26580  *       var firstRepeat =
26581  *           container.element(by.repeater('contact in settings.contacts').row(0));
26582  *       var secondRepeat =
26583  *           container.element(by.repeater('contact in settings.contacts').row(1));
26584  *
26585  *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
26586  *           .toBe('408 555 1212');
26587  *
26588  *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
26589  *           .toBe('john.smith@example.org');
26590  *
26591  *       firstRepeat.element(by.buttonText('clear')).click();
26592  *
26593  *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
26594  *           .toBe('');
26595  *
26596  *       container.element(by.buttonText('add')).click();
26597  *
26598  *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
26599  *           .element(by.model('contact.value'))
26600  *           .getAttribute('value'))
26601  *           .toBe('yourname@example.org');
26602  *     });
26603  *   </file>
26604  * </example>
26605  *
26606  * This example demonstrates the "attach to `$scope`" style of controller.
26607  *
26608  * <example name="ngController" module="controllerExample">
26609  *  <file name="index.html">
26610  *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
26611  *     <label>Name: <input type="text" ng-model="name"/></label>
26612  *     <button ng-click="greet()">greet</button><br/>
26613  *     Contact:
26614  *     <ul>
26615  *       <li ng-repeat="contact in contacts">
26616  *         <select ng-model="contact.type" id="select_{{$index}}">
26617  *            <option>phone</option>
26618  *            <option>email</option>
26619  *         </select>
26620  *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
26621  *         <button ng-click="clearContact(contact)">clear</button>
26622  *         <button ng-click="removeContact(contact)">X</button>
26623  *       </li>
26624  *       <li>[ <button ng-click="addContact()">add</button> ]</li>
26625  *    </ul>
26626  *   </div>
26627  *  </file>
26628  *  <file name="app.js">
26629  *   angular.module('controllerExample', [])
26630  *     .controller('SettingsController2', ['$scope', SettingsController2]);
26631  *
26632  *   function SettingsController2($scope) {
26633  *     $scope.name = 'John Smith';
26634  *     $scope.contacts = [
26635  *       {type:'phone', value:'408 555 1212'},
26636  *       {type:'email', value:'john.smith@example.org'}
26637  *     ];
26638  *
26639  *     $scope.greet = function() {
26640  *       alert($scope.name);
26641  *     };
26642  *
26643  *     $scope.addContact = function() {
26644  *       $scope.contacts.push({type:'email', value:'yourname@example.org'});
26645  *     };
26646  *
26647  *     $scope.removeContact = function(contactToRemove) {
26648  *       var index = $scope.contacts.indexOf(contactToRemove);
26649  *       $scope.contacts.splice(index, 1);
26650  *     };
26651  *
26652  *     $scope.clearContact = function(contact) {
26653  *       contact.type = 'phone';
26654  *       contact.value = '';
26655  *     };
26656  *   }
26657  *  </file>
26658  *  <file name="protractor.js" type="protractor">
26659  *    it('should check controller', function() {
26660  *      var container = element(by.id('ctrl-exmpl'));
26661  *
26662  *      expect(container.element(by.model('name'))
26663  *          .getAttribute('value')).toBe('John Smith');
26664  *
26665  *      var firstRepeat =
26666  *          container.element(by.repeater('contact in contacts').row(0));
26667  *      var secondRepeat =
26668  *          container.element(by.repeater('contact in contacts').row(1));
26669  *
26670  *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
26671  *          .toBe('408 555 1212');
26672  *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
26673  *          .toBe('john.smith@example.org');
26674  *
26675  *      firstRepeat.element(by.buttonText('clear')).click();
26676  *
26677  *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
26678  *          .toBe('');
26679  *
26680  *      container.element(by.buttonText('add')).click();
26681  *
26682  *      expect(container.element(by.repeater('contact in contacts').row(2))
26683  *          .element(by.model('contact.value'))
26684  *          .getAttribute('value'))
26685  *          .toBe('yourname@example.org');
26686  *    });
26687  *  </file>
26688  *</example>
26689
26690  */
26691 var ngControllerDirective = [function() {
26692   return {
26693     restrict: 'A',
26694     scope: true,
26695     controller: '@',
26696     priority: 500
26697   };
26698 }];
26699
26700 /**
26701  * @ngdoc directive
26702  * @name ngCsp
26703  *
26704  * @restrict A
26705  * @element ANY
26706  * @description
26707  *
26708  * Angular has some features that can conflict with certain restrictions that are applied when using
26709  * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
26710  *
26711  * If you intend to implement CSP with these rules then you must tell Angular not to use these
26712  * features.
26713  *
26714  * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
26715  *
26716  *
26717  * The following default rules in CSP affect Angular:
26718  *
26719  * * The use of `eval()`, `Function(string)` and similar functions to dynamically create and execute
26720  * code from strings is forbidden. Angular makes use of this in the {@link $parse} service to
26721  * provide a 30% increase in the speed of evaluating Angular expressions. (This CSP rule can be
26722  * disabled with the CSP keyword `unsafe-eval`, but it is generally not recommended as it would
26723  * weaken the protections offered by CSP.)
26724  *
26725  * * The use of inline resources, such as inline `<script>` and `<style>` elements, are forbidden.
26726  * This prevents apps from injecting custom styles directly into the document. Angular makes use of
26727  * this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}). To make these
26728  * directives work when a CSP rule is blocking inline styles, you must link to the `angular-csp.css`
26729  * in your HTML manually. (This CSP rule can be disabled with the CSP keyword `unsafe-inline`, but
26730  * it is generally not recommended as it would weaken the protections offered by CSP.)
26731  *
26732  * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking dynamic code
26733  * creation from strings (e.g., `unsafe-eval` not specified in CSP header) and automatically
26734  * deactivates this feature in the {@link $parse} service. This autodetection, however, triggers a
26735  * CSP error to be logged in the console:
26736  *
26737  * ```
26738  * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
26739  * script in the following Content Security Policy directive: "default-src 'self'". Note that
26740  * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
26741  * ```
26742  *
26743  * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
26744  * directive on an element of the HTML document that appears before the `<script>` tag that loads
26745  * the `angular.js` file.
26746  *
26747  * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
26748  *
26749  * You can specify which of the CSP related Angular features should be deactivated by providing
26750  * a value for the `ng-csp` attribute. The options are as follows:
26751  *
26752  * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
26753  *
26754  * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
26755  *
26756  * You can use these values in the following combinations:
26757  *
26758  *
26759  * * No declaration means that Angular will assume that you can do inline styles, but it will do
26760  * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous
26761  * versions of Angular.
26762  *
26763  * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
26764  * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous
26765  * versions of Angular.
26766  *
26767  * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can
26768  * inject inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
26769  *
26770  * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
26771  * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
26772  *
26773  * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
26774  * styles nor use eval, which is the same as an empty: ng-csp.
26775  * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
26776  *
26777  * @example
26778  * This example shows how to apply the `ngCsp` directive to the `html` tag.
26779    ```html
26780      <!doctype html>
26781      <html ng-app ng-csp>
26782      ...
26783      ...
26784      </html>
26785    ```
26786   * @example
26787       <!-- Note: the `.csp` suffix in the example name triggers CSP mode in our http server! -->
26788       <example name="example.csp" module="cspExample" ng-csp="true">
26789         <file name="index.html">
26790           <div ng-controller="MainController as ctrl">
26791             <div>
26792               <button ng-click="ctrl.inc()" id="inc">Increment</button>
26793               <span id="counter">
26794                 {{ctrl.counter}}
26795               </span>
26796             </div>
26797
26798             <div>
26799               <button ng-click="ctrl.evil()" id="evil">Evil</button>
26800               <span id="evilError">
26801                 {{ctrl.evilError}}
26802               </span>
26803             </div>
26804           </div>
26805         </file>
26806         <file name="script.js">
26807            angular.module('cspExample', [])
26808              .controller('MainController', function MainController() {
26809                 this.counter = 0;
26810                 this.inc = function() {
26811                   this.counter++;
26812                 };
26813                 this.evil = function() {
26814                   try {
26815                     eval('1+2'); // eslint-disable-line no-eval
26816                   } catch (e) {
26817                     this.evilError = e.message;
26818                   }
26819                 };
26820               });
26821         </file>
26822         <file name="protractor.js" type="protractor">
26823           var util, webdriver;
26824
26825           var incBtn = element(by.id('inc'));
26826           var counter = element(by.id('counter'));
26827           var evilBtn = element(by.id('evil'));
26828           var evilError = element(by.id('evilError'));
26829
26830           function getAndClearSevereErrors() {
26831             return browser.manage().logs().get('browser').then(function(browserLog) {
26832               return browserLog.filter(function(logEntry) {
26833                 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
26834               });
26835             });
26836           }
26837
26838           function clearErrors() {
26839             getAndClearSevereErrors();
26840           }
26841
26842           function expectNoErrors() {
26843             getAndClearSevereErrors().then(function(filteredLog) {
26844               expect(filteredLog.length).toEqual(0);
26845               if (filteredLog.length) {
26846                 console.log('browser console errors: ' + util.inspect(filteredLog));
26847               }
26848             });
26849           }
26850
26851           function expectError(regex) {
26852             getAndClearSevereErrors().then(function(filteredLog) {
26853               var found = false;
26854               filteredLog.forEach(function(log) {
26855                 if (log.message.match(regex)) {
26856                   found = true;
26857                 }
26858               });
26859               if (!found) {
26860                 throw new Error('expected an error that matches ' + regex);
26861               }
26862             });
26863           }
26864
26865           beforeEach(function() {
26866             util = require('util');
26867             webdriver = require('selenium-webdriver');
26868           });
26869
26870           // For now, we only test on Chrome,
26871           // as Safari does not load the page with Protractor's injected scripts,
26872           // and Firefox webdriver always disables content security policy (#6358)
26873           if (browser.params.browser !== 'chrome') {
26874             return;
26875           }
26876
26877           it('should not report errors when the page is loaded', function() {
26878             // clear errors so we are not dependent on previous tests
26879             clearErrors();
26880             // Need to reload the page as the page is already loaded when
26881             // we come here
26882             browser.driver.getCurrentUrl().then(function(url) {
26883               browser.get(url);
26884             });
26885             expectNoErrors();
26886           });
26887
26888           it('should evaluate expressions', function() {
26889             expect(counter.getText()).toEqual('0');
26890             incBtn.click();
26891             expect(counter.getText()).toEqual('1');
26892             expectNoErrors();
26893           });
26894
26895           it('should throw and report an error when using "eval"', function() {
26896             evilBtn.click();
26897             expect(evilError.getText()).toMatch(/Content Security Policy/);
26898             expectError(/Content Security Policy/);
26899           });
26900         </file>
26901       </example>
26902   */
26903
26904 // `ngCsp` is not implemented as a proper directive any more, because we need it be processed while
26905 // we bootstrap the app (before `$parse` is instantiated). For this reason, we just have the `csp()`
26906 // fn that looks for the `ng-csp` attribute anywhere in the current doc.
26907
26908 /**
26909  * @ngdoc directive
26910  * @name ngClick
26911  *
26912  * @description
26913  * The ngClick directive allows you to specify custom behavior when
26914  * an element is clicked.
26915  *
26916  * @element ANY
26917  * @priority 0
26918  * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
26919  * click. ({@link guide/expression#-event- Event object is available as `$event`})
26920  *
26921  * @example
26922    <example name="ng-click">
26923      <file name="index.html">
26924       <button ng-click="count = count + 1" ng-init="count=0">
26925         Increment
26926       </button>
26927       <span>
26928         count: {{count}}
26929       </span>
26930      </file>
26931      <file name="protractor.js" type="protractor">
26932        it('should check ng-click', function() {
26933          expect(element(by.binding('count')).getText()).toMatch('0');
26934          element(by.css('button')).click();
26935          expect(element(by.binding('count')).getText()).toMatch('1');
26936        });
26937      </file>
26938    </example>
26939  */
26940 /*
26941  * A collection of directives that allows creation of custom event handlers that are defined as
26942  * angular expressions and are compiled and executed within the current scope.
26943  */
26944 var ngEventDirectives = {};
26945
26946 // For events that might fire synchronously during DOM manipulation
26947 // we need to execute their event handlers asynchronously using $evalAsync,
26948 // so that they are not executed in an inconsistent state.
26949 var forceAsyncEvents = {
26950   'blur': true,
26951   'focus': true
26952 };
26953 forEach(
26954   'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
26955   function(eventName) {
26956     var directiveName = directiveNormalize('ng-' + eventName);
26957     ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
26958       return {
26959         restrict: 'A',
26960         compile: function($element, attr) {
26961           // NOTE:
26962           // We expose the powerful `$event` object on the scope that provides access to the Window,
26963           // etc. This is OK, because expressions are not sandboxed any more (and the expression
26964           // sandbox was never meant to be a security feature anyway).
26965           var fn = $parse(attr[directiveName]);
26966           return function ngEventHandler(scope, element) {
26967             element.on(eventName, function(event) {
26968               var callback = function() {
26969                 fn(scope, {$event: event});
26970               };
26971               if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
26972                 scope.$evalAsync(callback);
26973               } else {
26974                 scope.$apply(callback);
26975               }
26976             });
26977           };
26978         }
26979       };
26980     }];
26981   }
26982 );
26983
26984 /**
26985  * @ngdoc directive
26986  * @name ngDblclick
26987  *
26988  * @description
26989  * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
26990  *
26991  * @element ANY
26992  * @priority 0
26993  * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
26994  * a dblclick. (The Event object is available as `$event`)
26995  *
26996  * @example
26997    <example name="ng-dblclick">
26998      <file name="index.html">
26999       <button ng-dblclick="count = count + 1" ng-init="count=0">
27000         Increment (on double click)
27001       </button>
27002       count: {{count}}
27003      </file>
27004    </example>
27005  */
27006
27007
27008 /**
27009  * @ngdoc directive
27010  * @name ngMousedown
27011  *
27012  * @description
27013  * The ngMousedown directive allows you to specify custom behavior on mousedown event.
27014  *
27015  * @element ANY
27016  * @priority 0
27017  * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
27018  * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
27019  *
27020  * @example
27021    <example name="ng-mousedown">
27022      <file name="index.html">
27023       <button ng-mousedown="count = count + 1" ng-init="count=0">
27024         Increment (on mouse down)
27025       </button>
27026       count: {{count}}
27027      </file>
27028    </example>
27029  */
27030
27031
27032 /**
27033  * @ngdoc directive
27034  * @name ngMouseup
27035  *
27036  * @description
27037  * Specify custom behavior on mouseup event.
27038  *
27039  * @element ANY
27040  * @priority 0
27041  * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
27042  * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
27043  *
27044  * @example
27045    <example name="ng-mouseup">
27046      <file name="index.html">
27047       <button ng-mouseup="count = count + 1" ng-init="count=0">
27048         Increment (on mouse up)
27049       </button>
27050       count: {{count}}
27051      </file>
27052    </example>
27053  */
27054
27055 /**
27056  * @ngdoc directive
27057  * @name ngMouseover
27058  *
27059  * @description
27060  * Specify custom behavior on mouseover event.
27061  *
27062  * @element ANY
27063  * @priority 0
27064  * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
27065  * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
27066  *
27067  * @example
27068    <example name="ng-mouseover">
27069      <file name="index.html">
27070       <button ng-mouseover="count = count + 1" ng-init="count=0">
27071         Increment (when mouse is over)
27072       </button>
27073       count: {{count}}
27074      </file>
27075    </example>
27076  */
27077
27078
27079 /**
27080  * @ngdoc directive
27081  * @name ngMouseenter
27082  *
27083  * @description
27084  * Specify custom behavior on mouseenter event.
27085  *
27086  * @element ANY
27087  * @priority 0
27088  * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
27089  * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
27090  *
27091  * @example
27092    <example name="ng-mouseenter">
27093      <file name="index.html">
27094       <button ng-mouseenter="count = count + 1" ng-init="count=0">
27095         Increment (when mouse enters)
27096       </button>
27097       count: {{count}}
27098      </file>
27099    </example>
27100  */
27101
27102
27103 /**
27104  * @ngdoc directive
27105  * @name ngMouseleave
27106  *
27107  * @description
27108  * Specify custom behavior on mouseleave event.
27109  *
27110  * @element ANY
27111  * @priority 0
27112  * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
27113  * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
27114  *
27115  * @example
27116    <example name="ng-mouseleave">
27117      <file name="index.html">
27118       <button ng-mouseleave="count = count + 1" ng-init="count=0">
27119         Increment (when mouse leaves)
27120       </button>
27121       count: {{count}}
27122      </file>
27123    </example>
27124  */
27125
27126
27127 /**
27128  * @ngdoc directive
27129  * @name ngMousemove
27130  *
27131  * @description
27132  * Specify custom behavior on mousemove event.
27133  *
27134  * @element ANY
27135  * @priority 0
27136  * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
27137  * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
27138  *
27139  * @example
27140    <example name="ng-mousemove">
27141      <file name="index.html">
27142       <button ng-mousemove="count = count + 1" ng-init="count=0">
27143         Increment (when mouse moves)
27144       </button>
27145       count: {{count}}
27146      </file>
27147    </example>
27148  */
27149
27150
27151 /**
27152  * @ngdoc directive
27153  * @name ngKeydown
27154  *
27155  * @description
27156  * Specify custom behavior on keydown event.
27157  *
27158  * @element ANY
27159  * @priority 0
27160  * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
27161  * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
27162  *
27163  * @example
27164    <example name="ng-keydown">
27165      <file name="index.html">
27166       <input ng-keydown="count = count + 1" ng-init="count=0">
27167       key down count: {{count}}
27168      </file>
27169    </example>
27170  */
27171
27172
27173 /**
27174  * @ngdoc directive
27175  * @name ngKeyup
27176  *
27177  * @description
27178  * Specify custom behavior on keyup event.
27179  *
27180  * @element ANY
27181  * @priority 0
27182  * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
27183  * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
27184  *
27185  * @example
27186    <example name="ng-keyup">
27187      <file name="index.html">
27188        <p>Typing in the input box below updates the key count</p>
27189        <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
27190
27191        <p>Typing in the input box below updates the keycode</p>
27192        <input ng-keyup="event=$event">
27193        <p>event keyCode: {{ event.keyCode }}</p>
27194        <p>event altKey: {{ event.altKey }}</p>
27195      </file>
27196    </example>
27197  */
27198
27199
27200 /**
27201  * @ngdoc directive
27202  * @name ngKeypress
27203  *
27204  * @description
27205  * Specify custom behavior on keypress event.
27206  *
27207  * @element ANY
27208  * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
27209  * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
27210  * and can be interrogated for keyCode, altKey, etc.)
27211  *
27212  * @example
27213    <example name="ng-keypress">
27214      <file name="index.html">
27215       <input ng-keypress="count = count + 1" ng-init="count=0">
27216       key press count: {{count}}
27217      </file>
27218    </example>
27219  */
27220
27221
27222 /**
27223  * @ngdoc directive
27224  * @name ngSubmit
27225  *
27226  * @description
27227  * Enables binding angular expressions to onsubmit events.
27228  *
27229  * Additionally it prevents the default action (which for form means sending the request to the
27230  * server and reloading the current page), but only if the form does not contain `action`,
27231  * `data-action`, or `x-action` attributes.
27232  *
27233  * <div class="alert alert-warning">
27234  * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
27235  * `ngSubmit` handlers together. See the
27236  * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
27237  * for a detailed discussion of when `ngSubmit` may be triggered.
27238  * </div>
27239  *
27240  * @element form
27241  * @priority 0
27242  * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
27243  * ({@link guide/expression#-event- Event object is available as `$event`})
27244  *
27245  * @example
27246    <example module="submitExample" name="ng-submit">
27247      <file name="index.html">
27248       <script>
27249         angular.module('submitExample', [])
27250           .controller('ExampleController', ['$scope', function($scope) {
27251             $scope.list = [];
27252             $scope.text = 'hello';
27253             $scope.submit = function() {
27254               if ($scope.text) {
27255                 $scope.list.push(this.text);
27256                 $scope.text = '';
27257               }
27258             };
27259           }]);
27260       </script>
27261       <form ng-submit="submit()" ng-controller="ExampleController">
27262         Enter text and hit enter:
27263         <input type="text" ng-model="text" name="text" />
27264         <input type="submit" id="submit" value="Submit" />
27265         <pre>list={{list}}</pre>
27266       </form>
27267      </file>
27268      <file name="protractor.js" type="protractor">
27269        it('should check ng-submit', function() {
27270          expect(element(by.binding('list')).getText()).toBe('list=[]');
27271          element(by.css('#submit')).click();
27272          expect(element(by.binding('list')).getText()).toContain('hello');
27273          expect(element(by.model('text')).getAttribute('value')).toBe('');
27274        });
27275        it('should ignore empty strings', function() {
27276          expect(element(by.binding('list')).getText()).toBe('list=[]');
27277          element(by.css('#submit')).click();
27278          element(by.css('#submit')).click();
27279          expect(element(by.binding('list')).getText()).toContain('hello');
27280         });
27281      </file>
27282    </example>
27283  */
27284
27285 /**
27286  * @ngdoc directive
27287  * @name ngFocus
27288  *
27289  * @description
27290  * Specify custom behavior on focus event.
27291  *
27292  * Note: As the `focus` event is executed synchronously when calling `input.focus()`
27293  * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
27294  * during an `$apply` to ensure a consistent state.
27295  *
27296  * @element window, input, select, textarea, a
27297  * @priority 0
27298  * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
27299  * focus. ({@link guide/expression#-event- Event object is available as `$event`})
27300  *
27301  * @example
27302  * See {@link ng.directive:ngClick ngClick}
27303  */
27304
27305 /**
27306  * @ngdoc directive
27307  * @name ngBlur
27308  *
27309  * @description
27310  * Specify custom behavior on blur event.
27311  *
27312  * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
27313  * an element has lost focus.
27314  *
27315  * Note: As the `blur` event is executed synchronously also during DOM manipulations
27316  * (e.g. removing a focussed input),
27317  * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
27318  * during an `$apply` to ensure a consistent state.
27319  *
27320  * @element window, input, select, textarea, a
27321  * @priority 0
27322  * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
27323  * blur. ({@link guide/expression#-event- Event object is available as `$event`})
27324  *
27325  * @example
27326  * See {@link ng.directive:ngClick ngClick}
27327  */
27328
27329 /**
27330  * @ngdoc directive
27331  * @name ngCopy
27332  *
27333  * @description
27334  * Specify custom behavior on copy event.
27335  *
27336  * @element window, input, select, textarea, a
27337  * @priority 0
27338  * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
27339  * copy. ({@link guide/expression#-event- Event object is available as `$event`})
27340  *
27341  * @example
27342    <example name="ng-copy">
27343      <file name="index.html">
27344       <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
27345       copied: {{copied}}
27346      </file>
27347    </example>
27348  */
27349
27350 /**
27351  * @ngdoc directive
27352  * @name ngCut
27353  *
27354  * @description
27355  * Specify custom behavior on cut event.
27356  *
27357  * @element window, input, select, textarea, a
27358  * @priority 0
27359  * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
27360  * cut. ({@link guide/expression#-event- Event object is available as `$event`})
27361  *
27362  * @example
27363    <example name="ng-cut">
27364      <file name="index.html">
27365       <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
27366       cut: {{cut}}
27367      </file>
27368    </example>
27369  */
27370
27371 /**
27372  * @ngdoc directive
27373  * @name ngPaste
27374  *
27375  * @description
27376  * Specify custom behavior on paste event.
27377  *
27378  * @element window, input, select, textarea, a
27379  * @priority 0
27380  * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
27381  * paste. ({@link guide/expression#-event- Event object is available as `$event`})
27382  *
27383  * @example
27384    <example name="ng-paste">
27385      <file name="index.html">
27386       <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
27387       pasted: {{paste}}
27388      </file>
27389    </example>
27390  */
27391
27392 /**
27393  * @ngdoc directive
27394  * @name ngIf
27395  * @restrict A
27396  * @multiElement
27397  *
27398  * @description
27399  * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
27400  * {expression}. If the expression assigned to `ngIf` evaluates to a false
27401  * value then the element is removed from the DOM, otherwise a clone of the
27402  * element is reinserted into the DOM.
27403  *
27404  * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
27405  * element in the DOM rather than changing its visibility via the `display` css property.  A common
27406  * case when this difference is significant is when using css selectors that rely on an element's
27407  * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
27408  *
27409  * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
27410  * is created when the element is restored.  The scope created within `ngIf` inherits from
27411  * its parent scope using
27412  * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
27413  * An important implication of this is if `ngModel` is used within `ngIf` to bind to
27414  * a javascript primitive defined in the parent scope. In this case any modifications made to the
27415  * variable within the child scope will override (hide) the value in the parent scope.
27416  *
27417  * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
27418  * is if an element's class attribute is directly modified after it's compiled, using something like
27419  * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
27420  * the added class will be lost because the original compiled state is used to regenerate the element.
27421  *
27422  * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
27423  * and `leave` effects.
27424  *
27425  * @animations
27426  * | Animation                        | Occurs                               |
27427  * |----------------------------------|-------------------------------------|
27428  * | {@link ng.$animate#enter enter}  | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
27429  * | {@link ng.$animate#leave leave}  | just before the `ngIf` contents are removed from the DOM |
27430  *
27431  * @element ANY
27432  * @scope
27433  * @priority 600
27434  * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
27435  *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
27436  *     element is added to the DOM tree.
27437  *
27438  * @example
27439   <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-if">
27440     <file name="index.html">
27441       <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
27442       Show when checked:
27443       <span ng-if="checked" class="animate-if">
27444         This is removed when the checkbox is unchecked.
27445       </span>
27446     </file>
27447     <file name="animations.css">
27448       .animate-if {
27449         background:white;
27450         border:1px solid black;
27451         padding:10px;
27452       }
27453
27454       .animate-if.ng-enter, .animate-if.ng-leave {
27455         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
27456       }
27457
27458       .animate-if.ng-enter,
27459       .animate-if.ng-leave.ng-leave-active {
27460         opacity:0;
27461       }
27462
27463       .animate-if.ng-leave,
27464       .animate-if.ng-enter.ng-enter-active {
27465         opacity:1;
27466       }
27467     </file>
27468   </example>
27469  */
27470 var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
27471   return {
27472     multiElement: true,
27473     transclude: 'element',
27474     priority: 600,
27475     terminal: true,
27476     restrict: 'A',
27477     $$tlb: true,
27478     link: function($scope, $element, $attr, ctrl, $transclude) {
27479         var block, childScope, previousElements;
27480         $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
27481
27482           if (value) {
27483             if (!childScope) {
27484               $transclude(function(clone, newScope) {
27485                 childScope = newScope;
27486                 clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
27487                 // Note: We only need the first/last node of the cloned nodes.
27488                 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27489                 // by a directive with templateUrl when its template arrives.
27490                 block = {
27491                   clone: clone
27492                 };
27493                 $animate.enter(clone, $element.parent(), $element);
27494               });
27495             }
27496           } else {
27497             if (previousElements) {
27498               previousElements.remove();
27499               previousElements = null;
27500             }
27501             if (childScope) {
27502               childScope.$destroy();
27503               childScope = null;
27504             }
27505             if (block) {
27506               previousElements = getBlockNodes(block.clone);
27507               $animate.leave(previousElements).done(function(response) {
27508                 if (response !== false) previousElements = null;
27509               });
27510               block = null;
27511             }
27512           }
27513         });
27514     }
27515   };
27516 }];
27517
27518 /**
27519  * @ngdoc directive
27520  * @name ngInclude
27521  * @restrict ECA
27522  *
27523  * @description
27524  * Fetches, compiles and includes an external HTML fragment.
27525  *
27526  * By default, the template URL is restricted to the same domain and protocol as the
27527  * application document. This is done by calling {@link $sce#getTrustedResourceUrl
27528  * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
27529  * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
27530  * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
27531  * ng.$sce Strict Contextual Escaping}.
27532  *
27533  * In addition, the browser's
27534  * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
27535  * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
27536  * policy may further restrict whether the template is successfully loaded.
27537  * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
27538  * access on some browsers.
27539  *
27540  * @animations
27541  * | Animation                        | Occurs                              |
27542  * |----------------------------------|-------------------------------------|
27543  * | {@link ng.$animate#enter enter}  | when the expression changes, on the new include |
27544  * | {@link ng.$animate#leave leave}  | when the expression changes, on the old include |
27545  *
27546  * The enter and leave animation occur concurrently.
27547  *
27548  * @scope
27549  * @priority 400
27550  *
27551  * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
27552  *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
27553  * @param {string=} onload Expression to evaluate when a new partial is loaded.
27554  *                  <div class="alert alert-warning">
27555  *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
27556  *                  a function with the name on the window element, which will usually throw a
27557  *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
27558  *                  different form that {@link guide/directive#normalization matches} `onload`.
27559  *                  </div>
27560    *
27561  * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
27562  *                  $anchorScroll} to scroll the viewport after the content is loaded.
27563  *
27564  *                  - If the attribute is not set, disable scrolling.
27565  *                  - If the attribute is set without value, enable scrolling.
27566  *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
27567  *
27568  * @example
27569   <example module="includeExample" deps="angular-animate.js" animations="true" name="ng-include">
27570     <file name="index.html">
27571      <div ng-controller="ExampleController">
27572        <select ng-model="template" ng-options="t.name for t in templates">
27573         <option value="">(blank)</option>
27574        </select>
27575        url of the template: <code>{{template.url}}</code>
27576        <hr/>
27577        <div class="slide-animate-container">
27578          <div class="slide-animate" ng-include="template.url"></div>
27579        </div>
27580      </div>
27581     </file>
27582     <file name="script.js">
27583       angular.module('includeExample', ['ngAnimate'])
27584         .controller('ExampleController', ['$scope', function($scope) {
27585           $scope.templates =
27586             [{ name: 'template1.html', url: 'template1.html'},
27587              { name: 'template2.html', url: 'template2.html'}];
27588           $scope.template = $scope.templates[0];
27589         }]);
27590      </file>
27591     <file name="template1.html">
27592       Content of template1.html
27593     </file>
27594     <file name="template2.html">
27595       Content of template2.html
27596     </file>
27597     <file name="animations.css">
27598       .slide-animate-container {
27599         position:relative;
27600         background:white;
27601         border:1px solid black;
27602         height:40px;
27603         overflow:hidden;
27604       }
27605
27606       .slide-animate {
27607         padding:10px;
27608       }
27609
27610       .slide-animate.ng-enter, .slide-animate.ng-leave {
27611         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
27612
27613         position:absolute;
27614         top:0;
27615         left:0;
27616         right:0;
27617         bottom:0;
27618         display:block;
27619         padding:10px;
27620       }
27621
27622       .slide-animate.ng-enter {
27623         top:-50px;
27624       }
27625       .slide-animate.ng-enter.ng-enter-active {
27626         top:0;
27627       }
27628
27629       .slide-animate.ng-leave {
27630         top:0;
27631       }
27632       .slide-animate.ng-leave.ng-leave-active {
27633         top:50px;
27634       }
27635     </file>
27636     <file name="protractor.js" type="protractor">
27637       var templateSelect = element(by.model('template'));
27638       var includeElem = element(by.css('[ng-include]'));
27639
27640       it('should load template1.html', function() {
27641         expect(includeElem.getText()).toMatch(/Content of template1.html/);
27642       });
27643
27644       it('should load template2.html', function() {
27645         if (browser.params.browser === 'firefox') {
27646           // Firefox can't handle using selects
27647           // See https://github.com/angular/protractor/issues/480
27648           return;
27649         }
27650         templateSelect.click();
27651         templateSelect.all(by.css('option')).get(2).click();
27652         expect(includeElem.getText()).toMatch(/Content of template2.html/);
27653       });
27654
27655       it('should change to blank', function() {
27656         if (browser.params.browser === 'firefox') {
27657           // Firefox can't handle using selects
27658           return;
27659         }
27660         templateSelect.click();
27661         templateSelect.all(by.css('option')).get(0).click();
27662         expect(includeElem.isPresent()).toBe(false);
27663       });
27664     </file>
27665   </example>
27666  */
27667
27668
27669 /**
27670  * @ngdoc event
27671  * @name ngInclude#$includeContentRequested
27672  * @eventType emit on the scope ngInclude was declared in
27673  * @description
27674  * Emitted every time the ngInclude content is requested.
27675  *
27676  * @param {Object} angularEvent Synthetic event object.
27677  * @param {String} src URL of content to load.
27678  */
27679
27680
27681 /**
27682  * @ngdoc event
27683  * @name ngInclude#$includeContentLoaded
27684  * @eventType emit on the current ngInclude scope
27685  * @description
27686  * Emitted every time the ngInclude content is reloaded.
27687  *
27688  * @param {Object} angularEvent Synthetic event object.
27689  * @param {String} src URL of content to load.
27690  */
27691
27692
27693 /**
27694  * @ngdoc event
27695  * @name ngInclude#$includeContentError
27696  * @eventType emit on the scope ngInclude was declared in
27697  * @description
27698  * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
27699  *
27700  * @param {Object} angularEvent Synthetic event object.
27701  * @param {String} src URL of content to load.
27702  */
27703 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
27704                   function($templateRequest,   $anchorScroll,   $animate) {
27705   return {
27706     restrict: 'ECA',
27707     priority: 400,
27708     terminal: true,
27709     transclude: 'element',
27710     controller: angular.noop,
27711     compile: function(element, attr) {
27712       var srcExp = attr.ngInclude || attr.src,
27713           onloadExp = attr.onload || '',
27714           autoScrollExp = attr.autoscroll;
27715
27716       return function(scope, $element, $attr, ctrl, $transclude) {
27717         var changeCounter = 0,
27718             currentScope,
27719             previousElement,
27720             currentElement;
27721
27722         var cleanupLastIncludeContent = function() {
27723           if (previousElement) {
27724             previousElement.remove();
27725             previousElement = null;
27726           }
27727           if (currentScope) {
27728             currentScope.$destroy();
27729             currentScope = null;
27730           }
27731           if (currentElement) {
27732             $animate.leave(currentElement).done(function(response) {
27733               if (response !== false) previousElement = null;
27734             });
27735             previousElement = currentElement;
27736             currentElement = null;
27737           }
27738         };
27739
27740         scope.$watch(srcExp, function ngIncludeWatchAction(src) {
27741           var afterAnimation = function(response) {
27742             if (response !== false && isDefined(autoScrollExp) &&
27743               (!autoScrollExp || scope.$eval(autoScrollExp))) {
27744                 $anchorScroll();
27745             }
27746           };
27747           var thisChangeId = ++changeCounter;
27748
27749           if (src) {
27750             //set the 2nd param to true to ignore the template request error so that the inner
27751             //contents and scope can be cleaned up.
27752             $templateRequest(src, true).then(function(response) {
27753               if (scope.$$destroyed) return;
27754
27755               if (thisChangeId !== changeCounter) return;
27756               var newScope = scope.$new();
27757               ctrl.template = response;
27758
27759               // Note: This will also link all children of ng-include that were contained in the original
27760               // html. If that content contains controllers, ... they could pollute/change the scope.
27761               // However, using ng-include on an element with additional content does not make sense...
27762               // Note: We can't remove them in the cloneAttchFn of $transclude as that
27763               // function is called before linking the content, which would apply child
27764               // directives to non existing elements.
27765               var clone = $transclude(newScope, function(clone) {
27766                 cleanupLastIncludeContent();
27767                 $animate.enter(clone, null, $element).done(afterAnimation);
27768               });
27769
27770               currentScope = newScope;
27771               currentElement = clone;
27772
27773               currentScope.$emit('$includeContentLoaded', src);
27774               scope.$eval(onloadExp);
27775             }, function() {
27776               if (scope.$$destroyed) return;
27777
27778               if (thisChangeId === changeCounter) {
27779                 cleanupLastIncludeContent();
27780                 scope.$emit('$includeContentError', src);
27781               }
27782             });
27783             scope.$emit('$includeContentRequested', src);
27784           } else {
27785             cleanupLastIncludeContent();
27786             ctrl.template = null;
27787           }
27788         });
27789       };
27790     }
27791   };
27792 }];
27793
27794 // This directive is called during the $transclude call of the first `ngInclude` directive.
27795 // It will replace and compile the content of the element with the loaded template.
27796 // We need this directive so that the element content is already filled when
27797 // the link function of another directive on the same element as ngInclude
27798 // is called.
27799 var ngIncludeFillContentDirective = ['$compile',
27800   function($compile) {
27801     return {
27802       restrict: 'ECA',
27803       priority: -400,
27804       require: 'ngInclude',
27805       link: function(scope, $element, $attr, ctrl) {
27806         if (toString.call($element[0]).match(/SVG/)) {
27807           // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
27808           // support innerHTML, so detect this here and try to generate the contents
27809           // specially.
27810           $element.empty();
27811           $compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
27812               function namespaceAdaptedClone(clone) {
27813             $element.append(clone);
27814           }, {futureParentElement: $element});
27815           return;
27816         }
27817
27818         $element.html(ctrl.template);
27819         $compile($element.contents())(scope);
27820       }
27821     };
27822   }];
27823
27824 /**
27825  * @ngdoc directive
27826  * @name ngInit
27827  * @restrict AC
27828  *
27829  * @description
27830  * The `ngInit` directive allows you to evaluate an expression in the
27831  * current scope.
27832  *
27833  * <div class="alert alert-danger">
27834  * This directive can be abused to add unnecessary amounts of logic into your templates.
27835  * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
27836  * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
27837  * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
27838  * rather than `ngInit` to initialize values on a scope.
27839  * </div>
27840  *
27841  * <div class="alert alert-warning">
27842  * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
27843  * sure you have parentheses to ensure correct operator precedence:
27844  * <pre class="prettyprint">
27845  * `<div ng-init="test1 = ($index | toString)"></div>`
27846  * </pre>
27847  * </div>
27848  *
27849  * @priority 450
27850  *
27851  * @element ANY
27852  * @param {expression} ngInit {@link guide/expression Expression} to eval.
27853  *
27854  * @example
27855    <example module="initExample" name="ng-init">
27856      <file name="index.html">
27857    <script>
27858      angular.module('initExample', [])
27859        .controller('ExampleController', ['$scope', function($scope) {
27860          $scope.list = [['a', 'b'], ['c', 'd']];
27861        }]);
27862    </script>
27863    <div ng-controller="ExampleController">
27864      <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
27865        <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
27866           <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
27867        </div>
27868      </div>
27869    </div>
27870      </file>
27871      <file name="protractor.js" type="protractor">
27872        it('should alias index positions', function() {
27873          var elements = element.all(by.css('.example-init'));
27874          expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
27875          expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
27876          expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
27877          expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
27878        });
27879      </file>
27880    </example>
27881  */
27882 var ngInitDirective = ngDirective({
27883   priority: 450,
27884   compile: function() {
27885     return {
27886       pre: function(scope, element, attrs) {
27887         scope.$eval(attrs.ngInit);
27888       }
27889     };
27890   }
27891 });
27892
27893 /**
27894  * @ngdoc directive
27895  * @name ngList
27896  *
27897  * @description
27898  * Text input that converts between a delimited string and an array of strings. The default
27899  * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
27900  * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
27901  *
27902  * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
27903  * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
27904  *   list item is respected. This implies that the user of the directive is responsible for
27905  *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
27906  *   tab or newline character.
27907  * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
27908  *   when joining the list items back together) and whitespace around each list item is stripped
27909  *   before it is added to the model.
27910  *
27911  * ### Example with Validation
27912  *
27913  * <example name="ngList-directive" module="listExample">
27914  *   <file name="app.js">
27915  *      angular.module('listExample', [])
27916  *        .controller('ExampleController', ['$scope', function($scope) {
27917  *          $scope.names = ['morpheus', 'neo', 'trinity'];
27918  *        }]);
27919  *   </file>
27920  *   <file name="index.html">
27921  *    <form name="myForm" ng-controller="ExampleController">
27922  *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
27923  *      <span role="alert">
27924  *        <span class="error" ng-show="myForm.namesInput.$error.required">
27925  *        Required!</span>
27926  *      </span>
27927  *      <br>
27928  *      <tt>names = {{names}}</tt><br/>
27929  *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
27930  *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
27931  *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
27932  *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
27933  *     </form>
27934  *   </file>
27935  *   <file name="protractor.js" type="protractor">
27936  *     var listInput = element(by.model('names'));
27937  *     var names = element(by.exactBinding('names'));
27938  *     var valid = element(by.binding('myForm.namesInput.$valid'));
27939  *     var error = element(by.css('span.error'));
27940  *
27941  *     it('should initialize to model', function() {
27942  *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
27943  *       expect(valid.getText()).toContain('true');
27944  *       expect(error.getCssValue('display')).toBe('none');
27945  *     });
27946  *
27947  *     it('should be invalid if empty', function() {
27948  *       listInput.clear();
27949  *       listInput.sendKeys('');
27950  *
27951  *       expect(names.getText()).toContain('');
27952  *       expect(valid.getText()).toContain('false');
27953  *       expect(error.getCssValue('display')).not.toBe('none');
27954  *     });
27955  *   </file>
27956  * </example>
27957  *
27958  * ### Example - splitting on newline
27959  * <example name="ngList-directive-newlines">
27960  *   <file name="index.html">
27961  *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
27962  *    <pre>{{ list | json }}</pre>
27963  *   </file>
27964  *   <file name="protractor.js" type="protractor">
27965  *     it("should split the text by newlines", function() {
27966  *       var listInput = element(by.model('list'));
27967  *       var output = element(by.binding('list | json'));
27968  *       listInput.sendKeys('abc\ndef\nghi');
27969  *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
27970  *     });
27971  *   </file>
27972  * </example>
27973  *
27974  * @element input
27975  * @param {string=} ngList optional delimiter that should be used to split the value.
27976  */
27977 var ngListDirective = function() {
27978   return {
27979     restrict: 'A',
27980     priority: 100,
27981     require: 'ngModel',
27982     link: function(scope, element, attr, ctrl) {
27983       var ngList = attr.ngList || ', ';
27984       var trimValues = attr.ngTrim !== 'false';
27985       var separator = trimValues ? trim(ngList) : ngList;
27986
27987       var parse = function(viewValue) {
27988         // If the viewValue is invalid (say required but empty) it will be `undefined`
27989         if (isUndefined(viewValue)) return;
27990
27991         var list = [];
27992
27993         if (viewValue) {
27994           forEach(viewValue.split(separator), function(value) {
27995             if (value) list.push(trimValues ? trim(value) : value);
27996           });
27997         }
27998
27999         return list;
28000       };
28001
28002       ctrl.$parsers.push(parse);
28003       ctrl.$formatters.push(function(value) {
28004         if (isArray(value)) {
28005           return value.join(ngList);
28006         }
28007
28008         return undefined;
28009       });
28010
28011       // Override the standard $isEmpty because an empty array means the input is empty.
28012       ctrl.$isEmpty = function(value) {
28013         return !value || !value.length;
28014       };
28015     }
28016   };
28017 };
28018
28019 /* global VALID_CLASS: true,
28020   INVALID_CLASS: true,
28021   PRISTINE_CLASS: true,
28022   DIRTY_CLASS: true,
28023   UNTOUCHED_CLASS: true,
28024   TOUCHED_CLASS: true,
28025   PENDING_CLASS: true,
28026   addSetValidityMethod: true,
28027   setupValidity: true,
28028   defaultModelOptions: false
28029 */
28030
28031
28032 var VALID_CLASS = 'ng-valid',
28033     INVALID_CLASS = 'ng-invalid',
28034     PRISTINE_CLASS = 'ng-pristine',
28035     DIRTY_CLASS = 'ng-dirty',
28036     UNTOUCHED_CLASS = 'ng-untouched',
28037     TOUCHED_CLASS = 'ng-touched',
28038     EMPTY_CLASS = 'ng-empty',
28039     NOT_EMPTY_CLASS = 'ng-not-empty';
28040
28041 var ngModelMinErr = minErr('ngModel');
28042
28043 /**
28044  * @ngdoc type
28045  * @name ngModel.NgModelController
28046  *
28047  * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
28048  * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
28049  * is set.
28050  *
28051  * @property {*} $modelValue The value in the model that the control is bound to.
28052  *
28053  * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
28054  *  the control updates the ngModelController with a new {@link ngModel.NgModelController#$viewValue
28055     `$viewValue`} from the DOM, usually via user input.
28056     See {@link ngModel.NgModelController#$setViewValue `$setViewValue()`} for a detailed lifecycle explanation.
28057     Note that the `$parsers` are not called when the bound ngModel expression changes programmatically.
28058
28059   The functions are called in array order, each passing
28060     its return value through to the next. The last return value is forwarded to the
28061     {@link ngModel.NgModelController#$validators `$validators`} collection.
28062
28063   Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
28064     `$viewValue`}.
28065
28066   Returning `undefined` from a parser means a parse error occurred. In that case,
28067     no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
28068     will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
28069     is set to `true`. The parse error is stored in `ngModel.$error.parse`.
28070
28071   This simple example shows a parser that would convert text input value to lowercase:
28072  * ```js
28073  * function parse(value) {
28074  *   if (value) {
28075  *     return value.toLowerCase();
28076  *   }
28077  * }
28078  * ngModelController.$parsers.push(parse);
28079  * ```
28080
28081  *
28082  * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
28083     the bound ngModel expression changes programmatically. The `$formatters` are not called when the
28084     value of the control is changed by user interaction.
28085
28086   Formatters are used to format / convert the {@link ngModel.NgModelController#$modelValue
28087     `$modelValue`} for display in the control.
28088
28089   The functions are called in reverse array order, each passing the value through to the
28090     next. The last return value is used as the actual DOM value.
28091
28092   This simple example shows a formatter that would convert the model value to uppercase:
28093
28094  * ```js
28095  * function format(value) {
28096  *   if (value) {
28097  *     return value.toUpperCase();
28098  *   }
28099  * }
28100  * ngModel.$formatters.push(format);
28101  * ```
28102  *
28103  * @property {Object.<string, function>} $validators A collection of validators that are applied
28104  *      whenever the model value changes. The key value within the object refers to the name of the
28105  *      validator while the function refers to the validation operation. The validation operation is
28106  *      provided with the model value as an argument and must return a true or false value depending
28107  *      on the response of that validation.
28108  *
28109  * ```js
28110  * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
28111  *   var value = modelValue || viewValue;
28112  *   return /[0-9]+/.test(value) &&
28113  *          /[a-z]+/.test(value) &&
28114  *          /[A-Z]+/.test(value) &&
28115  *          /\W+/.test(value);
28116  * };
28117  * ```
28118  *
28119  * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
28120  *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
28121  *      is expected to return a promise when it is run during the model validation process. Once the promise
28122  *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
28123  *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
28124  *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
28125  *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
28126  *      will only run once all synchronous validators have passed.
28127  *
28128  * Please note that if $http is used then it is important that the server returns a success HTTP response code
28129  * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
28130  *
28131  * ```js
28132  * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
28133  *   var value = modelValue || viewValue;
28134  *
28135  *   // Lookup user by username
28136  *   return $http.get('/api/users/' + value).
28137  *      then(function resolved() {
28138  *        //username exists, this means validation fails
28139  *        return $q.reject('exists');
28140  *      }, function rejected() {
28141  *        //username does not exist, therefore this validation passes
28142  *        return true;
28143  *      });
28144  * };
28145  * ```
28146  *
28147  * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
28148  *     view value has changed. It is called with no arguments, and its return value is ignored.
28149  *     This can be used in place of additional $watches against the model value.
28150  *
28151  * @property {Object} $error An object hash with all failing validator ids as keys.
28152  * @property {Object} $pending An object hash with all pending validator ids as keys.
28153  *
28154  * @property {boolean} $untouched True if control has not lost focus yet.
28155  * @property {boolean} $touched True if control has lost focus.
28156  * @property {boolean} $pristine True if user has not interacted with the control yet.
28157  * @property {boolean} $dirty True if user has already interacted with the control.
28158  * @property {boolean} $valid True if there is no error.
28159  * @property {boolean} $invalid True if at least one error on the control.
28160  * @property {string} $name The name attribute of the control.
28161  *
28162  * @description
28163  *
28164  * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
28165  * The controller contains services for data-binding, validation, CSS updates, and value formatting
28166  * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
28167  * listening to DOM events.
28168  * Such DOM related logic should be provided by other directives which make use of
28169  * `NgModelController` for data-binding to control elements.
28170  * Angular provides this DOM logic for most {@link input `input`} elements.
28171  * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
28172  * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
28173  *
28174  * @example
28175  * ### Custom Control Example
28176  * This example shows how to use `NgModelController` with a custom control to achieve
28177  * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
28178  * collaborate together to achieve the desired result.
28179  *
28180  * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
28181  * contents be edited in place by the user.
28182  *
28183  * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
28184  * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
28185  * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
28186  * that content using the `$sce` service.
28187  *
28188  * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
28189     <file name="style.css">
28190       [contenteditable] {
28191         border: 1px solid black;
28192         background-color: white;
28193         min-height: 20px;
28194       }
28195
28196       .ng-invalid {
28197         border: 1px solid red;
28198       }
28199
28200     </file>
28201     <file name="script.js">
28202       angular.module('customControl', ['ngSanitize']).
28203         directive('contenteditable', ['$sce', function($sce) {
28204           return {
28205             restrict: 'A', // only activate on element attribute
28206             require: '?ngModel', // get a hold of NgModelController
28207             link: function(scope, element, attrs, ngModel) {
28208               if (!ngModel) return; // do nothing if no ng-model
28209
28210               // Specify how UI should be updated
28211               ngModel.$render = function() {
28212                 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
28213               };
28214
28215               // Listen for change events to enable binding
28216               element.on('blur keyup change', function() {
28217                 scope.$evalAsync(read);
28218               });
28219               read(); // initialize
28220
28221               // Write data to the model
28222               function read() {
28223                 var html = element.html();
28224                 // When we clear the content editable the browser leaves a <br> behind
28225                 // If strip-br attribute is provided then we strip this out
28226                 if (attrs.stripBr && html === '<br>') {
28227                   html = '';
28228                 }
28229                 ngModel.$setViewValue(html);
28230               }
28231             }
28232           };
28233         }]);
28234     </file>
28235     <file name="index.html">
28236       <form name="myForm">
28237        <div contenteditable
28238             name="myWidget" ng-model="userContent"
28239             strip-br="true"
28240             required>Change me!</div>
28241         <span ng-show="myForm.myWidget.$error.required">Required!</span>
28242        <hr>
28243        <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
28244       </form>
28245     </file>
28246     <file name="protractor.js" type="protractor">
28247     it('should data-bind and become invalid', function() {
28248       if (browser.params.browser === 'safari' || browser.params.browser === 'firefox') {
28249         // SafariDriver can't handle contenteditable
28250         // and Firefox driver can't clear contenteditables very well
28251         return;
28252       }
28253       var contentEditable = element(by.css('[contenteditable]'));
28254       var content = 'Change me!';
28255
28256       expect(contentEditable.getText()).toEqual(content);
28257
28258       contentEditable.clear();
28259       contentEditable.sendKeys(protractor.Key.BACK_SPACE);
28260       expect(contentEditable.getText()).toEqual('');
28261       expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
28262     });
28263     </file>
28264  * </example>
28265  *
28266  *
28267  */
28268 NgModelController.$inject = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$q', '$interpolate'];
28269 function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $q, $interpolate) {
28270   this.$viewValue = Number.NaN;
28271   this.$modelValue = Number.NaN;
28272   this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
28273   this.$validators = {};
28274   this.$asyncValidators = {};
28275   this.$parsers = [];
28276   this.$formatters = [];
28277   this.$viewChangeListeners = [];
28278   this.$untouched = true;
28279   this.$touched = false;
28280   this.$pristine = true;
28281   this.$dirty = false;
28282   this.$valid = true;
28283   this.$invalid = false;
28284   this.$error = {}; // keep invalid keys here
28285   this.$$success = {}; // keep valid keys here
28286   this.$pending = undefined; // keep pending keys here
28287   this.$name = $interpolate($attr.name || '', false)($scope);
28288   this.$$parentForm = nullFormCtrl;
28289   this.$options = defaultModelOptions;
28290
28291   this.$$parsedNgModel = $parse($attr.ngModel);
28292   this.$$parsedNgModelAssign = this.$$parsedNgModel.assign;
28293   this.$$ngModelGet = this.$$parsedNgModel;
28294   this.$$ngModelSet = this.$$parsedNgModelAssign;
28295   this.$$pendingDebounce = null;
28296   this.$$parserValid = undefined;
28297
28298   this.$$currentValidationRunId = 0;
28299
28300   this.$$scope = $scope;
28301   this.$$attr = $attr;
28302   this.$$element = $element;
28303   this.$$animate = $animate;
28304   this.$$timeout = $timeout;
28305   this.$$parse = $parse;
28306   this.$$q = $q;
28307   this.$$exceptionHandler = $exceptionHandler;
28308
28309   setupValidity(this);
28310   setupModelWatcher(this);
28311 }
28312
28313 NgModelController.prototype = {
28314   $$initGetterSetters: function() {
28315     if (this.$options.getOption('getterSetter')) {
28316       var invokeModelGetter = this.$$parse(this.$$attr.ngModel + '()'),
28317           invokeModelSetter = this.$$parse(this.$$attr.ngModel + '($$$p)');
28318
28319       this.$$ngModelGet = function($scope) {
28320         var modelValue = this.$$parsedNgModel($scope);
28321         if (isFunction(modelValue)) {
28322           modelValue = invokeModelGetter($scope);
28323         }
28324         return modelValue;
28325       };
28326       this.$$ngModelSet = function($scope, newValue) {
28327         if (isFunction(this.$$parsedNgModel($scope))) {
28328           invokeModelSetter($scope, {$$$p: newValue});
28329         } else {
28330           this.$$parsedNgModelAssign($scope, newValue);
28331         }
28332       };
28333     } else if (!this.$$parsedNgModel.assign) {
28334       throw ngModelMinErr('nonassign', 'Expression \'{0}\' is non-assignable. Element: {1}',
28335           this.$$attr.ngModel, startingTag(this.$$element));
28336     }
28337   },
28338
28339
28340   /**
28341    * @ngdoc method
28342    * @name ngModel.NgModelController#$render
28343    *
28344    * @description
28345    * Called when the view needs to be updated. It is expected that the user of the ng-model
28346    * directive will implement this method.
28347    *
28348    * The `$render()` method is invoked in the following situations:
28349    *
28350    * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
28351    *   committed value then `$render()` is called to update the input control.
28352    * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
28353    *   the `$viewValue` are different from last time.
28354    *
28355    * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
28356    * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
28357    * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
28358    * invoked if you only change a property on the objects.
28359    */
28360   $render: noop,
28361
28362   /**
28363    * @ngdoc method
28364    * @name ngModel.NgModelController#$isEmpty
28365    *
28366    * @description
28367    * This is called when we need to determine if the value of an input is empty.
28368    *
28369    * For instance, the required directive does this to work out if the input has data or not.
28370    *
28371    * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
28372    *
28373    * You can override this for input directives whose concept of being empty is different from the
28374    * default. The `checkboxInputType` directive does this because in its case a value of `false`
28375    * implies empty.
28376    *
28377    * @param {*} value The value of the input to check for emptiness.
28378    * @returns {boolean} True if `value` is "empty".
28379    */
28380   $isEmpty: function(value) {
28381     // eslint-disable-next-line no-self-compare
28382     return isUndefined(value) || value === '' || value === null || value !== value;
28383   },
28384
28385   $$updateEmptyClasses: function(value) {
28386     if (this.$isEmpty(value)) {
28387       this.$$animate.removeClass(this.$$element, NOT_EMPTY_CLASS);
28388       this.$$animate.addClass(this.$$element, EMPTY_CLASS);
28389     } else {
28390       this.$$animate.removeClass(this.$$element, EMPTY_CLASS);
28391       this.$$animate.addClass(this.$$element, NOT_EMPTY_CLASS);
28392     }
28393   },
28394
28395   /**
28396    * @ngdoc method
28397    * @name ngModel.NgModelController#$setPristine
28398    *
28399    * @description
28400    * Sets the control to its pristine state.
28401    *
28402    * This method can be called to remove the `ng-dirty` class and set the control to its pristine
28403    * state (`ng-pristine` class). A model is considered to be pristine when the control
28404    * has not been changed from when first compiled.
28405    */
28406   $setPristine: function() {
28407     this.$dirty = false;
28408     this.$pristine = true;
28409     this.$$animate.removeClass(this.$$element, DIRTY_CLASS);
28410     this.$$animate.addClass(this.$$element, PRISTINE_CLASS);
28411   },
28412
28413   /**
28414    * @ngdoc method
28415    * @name ngModel.NgModelController#$setDirty
28416    *
28417    * @description
28418    * Sets the control to its dirty state.
28419    *
28420    * This method can be called to remove the `ng-pristine` class and set the control to its dirty
28421    * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
28422    * from when first compiled.
28423    */
28424   $setDirty: function() {
28425     this.$dirty = true;
28426     this.$pristine = false;
28427     this.$$animate.removeClass(this.$$element, PRISTINE_CLASS);
28428     this.$$animate.addClass(this.$$element, DIRTY_CLASS);
28429     this.$$parentForm.$setDirty();
28430   },
28431
28432   /**
28433    * @ngdoc method
28434    * @name ngModel.NgModelController#$setUntouched
28435    *
28436    * @description
28437    * Sets the control to its untouched state.
28438    *
28439    * This method can be called to remove the `ng-touched` class and set the control to its
28440    * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
28441    * by default, however this function can be used to restore that state if the model has
28442    * already been touched by the user.
28443    */
28444   $setUntouched: function() {
28445     this.$touched = false;
28446     this.$untouched = true;
28447     this.$$animate.setClass(this.$$element, UNTOUCHED_CLASS, TOUCHED_CLASS);
28448   },
28449
28450   /**
28451    * @ngdoc method
28452    * @name ngModel.NgModelController#$setTouched
28453    *
28454    * @description
28455    * Sets the control to its touched state.
28456    *
28457    * This method can be called to remove the `ng-untouched` class and set the control to its
28458    * touched state (`ng-touched` class). A model is considered to be touched when the user has
28459    * first focused the control element and then shifted focus away from the control (blur event).
28460    */
28461   $setTouched: function() {
28462     this.$touched = true;
28463     this.$untouched = false;
28464     this.$$animate.setClass(this.$$element, TOUCHED_CLASS, UNTOUCHED_CLASS);
28465   },
28466
28467   /**
28468    * @ngdoc method
28469    * @name ngModel.NgModelController#$rollbackViewValue
28470    *
28471    * @description
28472    * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
28473    * which may be caused by a pending debounced event or because the input is waiting for some
28474    * future event.
28475    *
28476    * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
28477    * depend on special events such as `blur`, there can be a period when the `$viewValue` is out of
28478    * sync with the ngModel's `$modelValue`.
28479    *
28480    * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
28481    * and reset the input to the last committed view value.
28482    *
28483    * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
28484    * programmatically before these debounced/future events have resolved/occurred, because Angular's
28485    * dirty checking mechanism is not able to tell whether the model has actually changed or not.
28486    *
28487    * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
28488    * input which may have such events pending. This is important in order to make sure that the
28489    * input field will be updated with the new model value and any pending operations are cancelled.
28490    *
28491    * <example name="ng-model-cancel-update" module="cancel-update-example">
28492    *   <file name="app.js">
28493    *     angular.module('cancel-update-example', [])
28494    *
28495    *     .controller('CancelUpdateController', ['$scope', function($scope) {
28496    *       $scope.model = {value1: '', value2: ''};
28497    *
28498    *       $scope.setEmpty = function(e, value, rollback) {
28499    *         if (e.keyCode === 27) {
28500    *           e.preventDefault();
28501    *           if (rollback) {
28502    *             $scope.myForm[value].$rollbackViewValue();
28503    *           }
28504    *           $scope.model[value] = '';
28505    *         }
28506    *       };
28507    *     }]);
28508    *   </file>
28509    *   <file name="index.html">
28510    *     <div ng-controller="CancelUpdateController">
28511    *       <p>Both of these inputs are only updated if they are blurred. Hitting escape should
28512    *       empty them. Follow these steps and observe the difference:</p>
28513    *       <ol>
28514    *         <li>Type something in the input. You will see that the model is not yet updated</li>
28515    *         <li>Press the Escape key.
28516    *           <ol>
28517    *             <li> In the first example, nothing happens, because the model is already '', and no
28518    *             update is detected. If you blur the input, the model will be set to the current view.
28519    *             </li>
28520    *             <li> In the second example, the pending update is cancelled, and the input is set back
28521    *             to the last committed view value (''). Blurring the input does nothing.
28522    *             </li>
28523    *           </ol>
28524    *         </li>
28525    *       </ol>
28526    *
28527    *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
28528    *         <div>
28529    *           <p id="inputDescription1">Without $rollbackViewValue():</p>
28530    *           <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
28531    *                  ng-keydown="setEmpty($event, 'value1')">
28532    *           value1: "{{ model.value1 }}"
28533    *         </div>
28534    *
28535    *         <div>
28536    *           <p id="inputDescription2">With $rollbackViewValue():</p>
28537    *           <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
28538    *                  ng-keydown="setEmpty($event, 'value2', true)">
28539    *           value2: "{{ model.value2 }}"
28540    *         </div>
28541    *       </form>
28542    *     </div>
28543    *   </file>
28544        <file name="style.css">
28545           div {
28546             display: table-cell;
28547           }
28548           div:nth-child(1) {
28549             padding-right: 30px;
28550           }
28551
28552         </file>
28553    * </example>
28554    */
28555   $rollbackViewValue: function() {
28556     this.$$timeout.cancel(this.$$pendingDebounce);
28557     this.$viewValue = this.$$lastCommittedViewValue;
28558     this.$render();
28559   },
28560
28561   /**
28562    * @ngdoc method
28563    * @name ngModel.NgModelController#$validate
28564    *
28565    * @description
28566    * Runs each of the registered validators (first synchronous validators and then
28567    * asynchronous validators).
28568    * If the validity changes to invalid, the model will be set to `undefined`,
28569    * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
28570    * If the validity changes to valid, it will set the model to the last available valid
28571    * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
28572    */
28573   $validate: function() {
28574     // ignore $validate before model is initialized
28575     if (isNumberNaN(this.$modelValue)) {
28576       return;
28577     }
28578
28579     var viewValue = this.$$lastCommittedViewValue;
28580     // Note: we use the $$rawModelValue as $modelValue might have been
28581     // set to undefined during a view -> model update that found validation
28582     // errors. We can't parse the view here, since that could change
28583     // the model although neither viewValue nor the model on the scope changed
28584     var modelValue = this.$$rawModelValue;
28585
28586     var prevValid = this.$valid;
28587     var prevModelValue = this.$modelValue;
28588
28589     var allowInvalid = this.$options.getOption('allowInvalid');
28590
28591     var that = this;
28592     this.$$runValidators(modelValue, viewValue, function(allValid) {
28593       // If there was no change in validity, don't update the model
28594       // This prevents changing an invalid modelValue to undefined
28595       if (!allowInvalid && prevValid !== allValid) {
28596         // Note: Don't check this.$valid here, as we could have
28597         // external validators (e.g. calculated on the server),
28598         // that just call $setValidity and need the model value
28599         // to calculate their validity.
28600         that.$modelValue = allValid ? modelValue : undefined;
28601
28602         if (that.$modelValue !== prevModelValue) {
28603           that.$$writeModelToScope();
28604         }
28605       }
28606     });
28607   },
28608
28609   $$runValidators: function(modelValue, viewValue, doneCallback) {
28610     this.$$currentValidationRunId++;
28611     var localValidationRunId = this.$$currentValidationRunId;
28612     var that = this;
28613
28614     // check parser error
28615     if (!processParseErrors()) {
28616       validationDone(false);
28617       return;
28618     }
28619     if (!processSyncValidators()) {
28620       validationDone(false);
28621       return;
28622     }
28623     processAsyncValidators();
28624
28625     function processParseErrors() {
28626       var errorKey = that.$$parserName || 'parse';
28627       if (isUndefined(that.$$parserValid)) {
28628         setValidity(errorKey, null);
28629       } else {
28630         if (!that.$$parserValid) {
28631           forEach(that.$validators, function(v, name) {
28632             setValidity(name, null);
28633           });
28634           forEach(that.$asyncValidators, function(v, name) {
28635             setValidity(name, null);
28636           });
28637         }
28638         // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
28639         setValidity(errorKey, that.$$parserValid);
28640         return that.$$parserValid;
28641       }
28642       return true;
28643     }
28644
28645     function processSyncValidators() {
28646       var syncValidatorsValid = true;
28647       forEach(that.$validators, function(validator, name) {
28648         var result = Boolean(validator(modelValue, viewValue));
28649         syncValidatorsValid = syncValidatorsValid && result;
28650         setValidity(name, result);
28651       });
28652       if (!syncValidatorsValid) {
28653         forEach(that.$asyncValidators, function(v, name) {
28654           setValidity(name, null);
28655         });
28656         return false;
28657       }
28658       return true;
28659     }
28660
28661     function processAsyncValidators() {
28662       var validatorPromises = [];
28663       var allValid = true;
28664       forEach(that.$asyncValidators, function(validator, name) {
28665         var promise = validator(modelValue, viewValue);
28666         if (!isPromiseLike(promise)) {
28667           throw ngModelMinErr('nopromise',
28668             'Expected asynchronous validator to return a promise but got \'{0}\' instead.', promise);
28669         }
28670         setValidity(name, undefined);
28671         validatorPromises.push(promise.then(function() {
28672           setValidity(name, true);
28673         }, function() {
28674           allValid = false;
28675           setValidity(name, false);
28676         }));
28677       });
28678       if (!validatorPromises.length) {
28679         validationDone(true);
28680       } else {
28681         that.$$q.all(validatorPromises).then(function() {
28682           validationDone(allValid);
28683         }, noop);
28684       }
28685     }
28686
28687     function setValidity(name, isValid) {
28688       if (localValidationRunId === that.$$currentValidationRunId) {
28689         that.$setValidity(name, isValid);
28690       }
28691     }
28692
28693     function validationDone(allValid) {
28694       if (localValidationRunId === that.$$currentValidationRunId) {
28695
28696         doneCallback(allValid);
28697       }
28698     }
28699   },
28700
28701   /**
28702    * @ngdoc method
28703    * @name ngModel.NgModelController#$commitViewValue
28704    *
28705    * @description
28706    * Commit a pending update to the `$modelValue`.
28707    *
28708    * Updates may be pending by a debounced event or because the input is waiting for a some future
28709    * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
28710    * usually handles calling this in response to input events.
28711    */
28712   $commitViewValue: function() {
28713     var viewValue = this.$viewValue;
28714
28715     this.$$timeout.cancel(this.$$pendingDebounce);
28716
28717     // If the view value has not changed then we should just exit, except in the case where there is
28718     // a native validator on the element. In this case the validation state may have changed even though
28719     // the viewValue has stayed empty.
28720     if (this.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !this.$$hasNativeValidators)) {
28721       return;
28722     }
28723     this.$$updateEmptyClasses(viewValue);
28724     this.$$lastCommittedViewValue = viewValue;
28725
28726     // change to dirty
28727     if (this.$pristine) {
28728       this.$setDirty();
28729     }
28730     this.$$parseAndValidate();
28731   },
28732
28733   $$parseAndValidate: function() {
28734     var viewValue = this.$$lastCommittedViewValue;
28735     var modelValue = viewValue;
28736     var that = this;
28737
28738     this.$$parserValid = isUndefined(modelValue) ? undefined : true;
28739
28740     if (this.$$parserValid) {
28741       for (var i = 0; i < this.$parsers.length; i++) {
28742         modelValue = this.$parsers[i](modelValue);
28743         if (isUndefined(modelValue)) {
28744           this.$$parserValid = false;
28745           break;
28746         }
28747       }
28748     }
28749     if (isNumberNaN(this.$modelValue)) {
28750       // this.$modelValue has not been touched yet...
28751       this.$modelValue = this.$$ngModelGet(this.$$scope);
28752     }
28753     var prevModelValue = this.$modelValue;
28754     var allowInvalid = this.$options.getOption('allowInvalid');
28755     this.$$rawModelValue = modelValue;
28756
28757     if (allowInvalid) {
28758       this.$modelValue = modelValue;
28759       writeToModelIfNeeded();
28760     }
28761
28762     // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
28763     // This can happen if e.g. $setViewValue is called from inside a parser
28764     this.$$runValidators(modelValue, this.$$lastCommittedViewValue, function(allValid) {
28765       if (!allowInvalid) {
28766         // Note: Don't check this.$valid here, as we could have
28767         // external validators (e.g. calculated on the server),
28768         // that just call $setValidity and need the model value
28769         // to calculate their validity.
28770         that.$modelValue = allValid ? modelValue : undefined;
28771         writeToModelIfNeeded();
28772       }
28773     });
28774
28775     function writeToModelIfNeeded() {
28776       if (that.$modelValue !== prevModelValue) {
28777         that.$$writeModelToScope();
28778       }
28779     }
28780   },
28781
28782   $$writeModelToScope: function() {
28783     this.$$ngModelSet(this.$$scope, this.$modelValue);
28784     forEach(this.$viewChangeListeners, function(listener) {
28785       try {
28786         listener();
28787       } catch (e) {
28788         // eslint-disable-next-line no-invalid-this
28789         this.$$exceptionHandler(e);
28790       }
28791     }, this);
28792   },
28793
28794   /**
28795    * @ngdoc method
28796    * @name ngModel.NgModelController#$setViewValue
28797    *
28798    * @description
28799    * Update the view value.
28800    *
28801    * This method should be called when a control wants to change the view value; typically,
28802    * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
28803    * directive calls it when the value of the input changes and {@link ng.directive:select select}
28804    * calls it when an option is selected.
28805    *
28806    * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
28807    * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
28808    * value is sent directly for processing through the `$parsers` pipeline. After this, the `$validators` and
28809    * `$asyncValidators` are called and the value is applied to `$modelValue`.
28810    * Finally, the value is set to the **expression** specified in the `ng-model` attribute and
28811    * all the registered change listeners, in the `$viewChangeListeners` list are called.
28812    *
28813    * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
28814    * and the `default` trigger is not listed, all those actions will remain pending until one of the
28815    * `updateOn` events is triggered on the DOM element.
28816    * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
28817    * directive is used with a custom debounce for this particular event.
28818    * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
28819    * is specified, once the timer runs out.
28820    *
28821    * When used with standard inputs, the view value will always be a string (which is in some cases
28822    * parsed into another type, such as a `Date` object for `input[date]`.)
28823    * However, custom controls might also pass objects to this method. In this case, we should make
28824    * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
28825    * perform a deep watch of objects, it only looks for a change of identity. If you only change
28826    * the property of the object then ngModel will not realize that the object has changed and
28827    * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
28828    * not change properties of the copy once it has been passed to `$setViewValue`.
28829    * Otherwise you may cause the model value on the scope to change incorrectly.
28830    *
28831    * <div class="alert alert-info">
28832    * In any case, the value passed to the method should always reflect the current value
28833    * of the control. For example, if you are calling `$setViewValue` for an input element,
28834    * you should pass the input DOM value. Otherwise, the control and the scope model become
28835    * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
28836    * the control's DOM value in any way. If we want to change the control's DOM value
28837    * programmatically, we should update the `ngModel` scope expression. Its new value will be
28838    * picked up by the model controller, which will run it through the `$formatters`, `$render` it
28839    * to update the DOM, and finally call `$validate` on it.
28840    * </div>
28841    *
28842    * @param {*} value value from the view.
28843    * @param {string} trigger Event that triggered the update.
28844    */
28845   $setViewValue: function(value, trigger) {
28846     this.$viewValue = value;
28847     if (this.$options.getOption('updateOnDefault')) {
28848       this.$$debounceViewValueCommit(trigger);
28849     }
28850   },
28851
28852   $$debounceViewValueCommit: function(trigger) {
28853     var debounceDelay = this.$options.getOption('debounce');
28854
28855     if (isNumber(debounceDelay[trigger])) {
28856       debounceDelay = debounceDelay[trigger];
28857     } else if (isNumber(debounceDelay['default'])) {
28858       debounceDelay = debounceDelay['default'];
28859     }
28860
28861     this.$$timeout.cancel(this.$$pendingDebounce);
28862     var that = this;
28863     if (debounceDelay > 0) { // this fails if debounceDelay is an object
28864       this.$$pendingDebounce = this.$$timeout(function() {
28865         that.$commitViewValue();
28866       }, debounceDelay);
28867     } else if (this.$$scope.$root.$$phase) {
28868       this.$commitViewValue();
28869     } else {
28870       this.$$scope.$apply(function() {
28871         that.$commitViewValue();
28872       });
28873     }
28874   },
28875
28876   /**
28877    * @ngdoc method
28878    *
28879    * @name ngModel.NgModelController#$overrideModelOptions
28880    *
28881    * @description
28882    *
28883    * Override the current model options settings programmatically.
28884    *
28885    * The previous `ModelOptions` value will not be modified. Instead, a
28886    * new `ModelOptions` object will inherit from the previous one overriding
28887    * or inheriting settings that are defined in the given parameter.
28888    *
28889    * See {@link ngModelOptions} for information about what options can be specified
28890    * and how model option inheritance works.
28891    *
28892    * @param {Object} options a hash of settings to override the previous options
28893    *
28894    */
28895   $overrideModelOptions: function(options) {
28896     this.$options = this.$options.createChild(options);
28897   }
28898 };
28899
28900 function setupModelWatcher(ctrl) {
28901   // model -> value
28902   // Note: we cannot use a normal scope.$watch as we want to detect the following:
28903   // 1. scope value is 'a'
28904   // 2. user enters 'b'
28905   // 3. ng-change kicks in and reverts scope value to 'a'
28906   //    -> scope value did not change since the last digest as
28907   //       ng-change executes in apply phase
28908   // 4. view should be changed back to 'a'
28909   ctrl.$$scope.$watch(function ngModelWatch() {
28910     var modelValue = ctrl.$$ngModelGet(ctrl.$$scope);
28911
28912     // if scope model value and ngModel value are out of sync
28913     // TODO(perf): why not move this to the action fn?
28914     if (modelValue !== ctrl.$modelValue &&
28915        // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
28916         // eslint-disable-next-line no-self-compare
28917        (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
28918     ) {
28919       ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
28920       ctrl.$$parserValid = undefined;
28921
28922       var formatters = ctrl.$formatters,
28923           idx = formatters.length;
28924
28925       var viewValue = modelValue;
28926       while (idx--) {
28927         viewValue = formatters[idx](viewValue);
28928       }
28929       if (ctrl.$viewValue !== viewValue) {
28930         ctrl.$$updateEmptyClasses(viewValue);
28931         ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
28932         ctrl.$render();
28933
28934         // It is possible that model and view value have been updated during render
28935         ctrl.$$runValidators(ctrl.$modelValue, ctrl.$viewValue, noop);
28936       }
28937     }
28938
28939     return modelValue;
28940   });
28941 }
28942
28943 /**
28944  * @ngdoc method
28945  * @name ngModel.NgModelController#$setValidity
28946  *
28947  * @description
28948  * Change the validity state, and notify the form.
28949  *
28950  * This method can be called within $parsers/$formatters or a custom validation implementation.
28951  * However, in most cases it should be sufficient to use the `ngModel.$validators` and
28952  * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
28953  *
28954  * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
28955  *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
28956  *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
28957  *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
28958  *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
28959  *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
28960  * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
28961  *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
28962  *                          Skipped is used by Angular when validators do not run because of parse errors and
28963  *                          when `$asyncValidators` do not run because any of the `$validators` failed.
28964  */
28965 addSetValidityMethod({
28966   clazz: NgModelController,
28967   set: function(object, property) {
28968     object[property] = true;
28969   },
28970   unset: function(object, property) {
28971     delete object[property];
28972   }
28973 });
28974
28975
28976 /**
28977  * @ngdoc directive
28978  * @name ngModel
28979  *
28980  * @element input
28981  * @priority 1
28982  *
28983  * @description
28984  * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
28985  * property on the scope using {@link ngModel.NgModelController NgModelController},
28986  * which is created and exposed by this directive.
28987  *
28988  * `ngModel` is responsible for:
28989  *
28990  * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
28991  *   require.
28992  * - Providing validation behavior (i.e. required, number, email, url).
28993  * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
28994  * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
28995  *   `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
28996  * - Registering the control with its parent {@link ng.directive:form form}.
28997  *
28998  * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
28999  * current scope. If the property doesn't already exist on this scope, it will be created
29000  * implicitly and added to the scope.
29001  *
29002  * For best practices on using `ngModel`, see:
29003  *
29004  *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
29005  *
29006  * For basic examples, how to use `ngModel`, see:
29007  *
29008  *  - {@link ng.directive:input input}
29009  *    - {@link input[text] text}
29010  *    - {@link input[checkbox] checkbox}
29011  *    - {@link input[radio] radio}
29012  *    - {@link input[number] number}
29013  *    - {@link input[email] email}
29014  *    - {@link input[url] url}
29015  *    - {@link input[date] date}
29016  *    - {@link input[datetime-local] datetime-local}
29017  *    - {@link input[time] time}
29018  *    - {@link input[month] month}
29019  *    - {@link input[week] week}
29020  *  - {@link ng.directive:select select}
29021  *  - {@link ng.directive:textarea textarea}
29022  *
29023  * # Complex Models (objects or collections)
29024  *
29025  * By default, `ngModel` watches the model by reference, not value. This is important to know when
29026  * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
29027  * object or collection change, `ngModel` will not be notified and so the input will not be  re-rendered.
29028  *
29029  * The model must be assigned an entirely new object or collection before a re-rendering will occur.
29030  *
29031  * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
29032  * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
29033  * if the select is given the `multiple` attribute.
29034  *
29035  * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
29036  * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
29037  * not trigger a re-rendering of the model.
29038  *
29039  * # CSS classes
29040  * The following CSS classes are added and removed on the associated input/select/textarea element
29041  * depending on the validity of the model.
29042  *
29043  *  - `ng-valid`: the model is valid
29044  *  - `ng-invalid`: the model is invalid
29045  *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
29046  *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
29047  *  - `ng-pristine`: the control hasn't been interacted with yet
29048  *  - `ng-dirty`: the control has been interacted with
29049  *  - `ng-touched`: the control has been blurred
29050  *  - `ng-untouched`: the control hasn't been blurred
29051  *  - `ng-pending`: any `$asyncValidators` are unfulfilled
29052  *  - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
29053  *     by the {@link ngModel.NgModelController#$isEmpty} method
29054  *  - `ng-not-empty`: the view contains a non-empty value
29055  *
29056  * Keep in mind that ngAnimate can detect each of these classes when added and removed.
29057  *
29058  * ## Animation Hooks
29059  *
29060  * Animations within models are triggered when any of the associated CSS classes are added and removed
29061  * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
29062  * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
29063  * The animations that are triggered within ngModel are similar to how they work in ngClass and
29064  * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
29065  *
29066  * The following example shows a simple way to utilize CSS transitions to style an input element
29067  * that has been rendered as invalid after it has been validated:
29068  *
29069  * <pre>
29070  * //be sure to include ngAnimate as a module to hook into more
29071  * //advanced animations
29072  * .my-input {
29073  *   transition:0.5s linear all;
29074  *   background: white;
29075  * }
29076  * .my-input.ng-invalid {
29077  *   background: red;
29078  *   color:white;
29079  * }
29080  * </pre>
29081  *
29082  * @example
29083  * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample" name="ng-model">
29084      <file name="index.html">
29085        <script>
29086         angular.module('inputExample', [])
29087           .controller('ExampleController', ['$scope', function($scope) {
29088             $scope.val = '1';
29089           }]);
29090        </script>
29091        <style>
29092          .my-input {
29093            transition:all linear 0.5s;
29094            background: transparent;
29095          }
29096          .my-input.ng-invalid {
29097            color:white;
29098            background: red;
29099          }
29100        </style>
29101        <p id="inputDescription">
29102         Update input to see transitions when valid/invalid.
29103         Integer is a valid value.
29104        </p>
29105        <form name="testForm" ng-controller="ExampleController">
29106          <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
29107                 aria-describedby="inputDescription" />
29108        </form>
29109      </file>
29110  * </example>
29111  *
29112  * ## Binding to a getter/setter
29113  *
29114  * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
29115  * function that returns a representation of the model when called with zero arguments, and sets
29116  * the internal state of a model when called with an argument. It's sometimes useful to use this
29117  * for models that have an internal representation that's different from what the model exposes
29118  * to the view.
29119  *
29120  * <div class="alert alert-success">
29121  * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
29122  * frequently than other parts of your code.
29123  * </div>
29124  *
29125  * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
29126  * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
29127  * a `<form>`, which will enable this behavior for all `<input>`s within it. See
29128  * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
29129  *
29130  * The following example shows how to use `ngModel` with a getter/setter:
29131  *
29132  * @example
29133  * <example name="ngModel-getter-setter" module="getterSetterExample">
29134      <file name="index.html">
29135        <div ng-controller="ExampleController">
29136          <form name="userForm">
29137            <label>Name:
29138              <input type="text" name="userName"
29139                     ng-model="user.name"
29140                     ng-model-options="{ getterSetter: true }" />
29141            </label>
29142          </form>
29143          <pre>user.name = <span ng-bind="user.name()"></span></pre>
29144        </div>
29145      </file>
29146      <file name="app.js">
29147        angular.module('getterSetterExample', [])
29148          .controller('ExampleController', ['$scope', function($scope) {
29149            var _name = 'Brian';
29150            $scope.user = {
29151              name: function(newName) {
29152               // Note that newName can be undefined for two reasons:
29153               // 1. Because it is called as a getter and thus called with no arguments
29154               // 2. Because the property should actually be set to undefined. This happens e.g. if the
29155               //    input is invalid
29156               return arguments.length ? (_name = newName) : _name;
29157              }
29158            };
29159          }]);
29160      </file>
29161  * </example>
29162  */
29163 var ngModelDirective = ['$rootScope', function($rootScope) {
29164   return {
29165     restrict: 'A',
29166     require: ['ngModel', '^?form', '^?ngModelOptions'],
29167     controller: NgModelController,
29168     // Prelink needs to run before any input directive
29169     // so that we can set the NgModelOptions in NgModelController
29170     // before anyone else uses it.
29171     priority: 1,
29172     compile: function ngModelCompile(element) {
29173       // Setup initial state of the control
29174       element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
29175
29176       return {
29177         pre: function ngModelPreLink(scope, element, attr, ctrls) {
29178           var modelCtrl = ctrls[0],
29179               formCtrl = ctrls[1] || modelCtrl.$$parentForm,
29180               optionsCtrl = ctrls[2];
29181
29182           if (optionsCtrl) {
29183             modelCtrl.$options = optionsCtrl.$options;
29184           }
29185
29186           modelCtrl.$$initGetterSetters();
29187
29188           // notify others, especially parent forms
29189           formCtrl.$addControl(modelCtrl);
29190
29191           attr.$observe('name', function(newValue) {
29192             if (modelCtrl.$name !== newValue) {
29193               modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
29194             }
29195           });
29196
29197           scope.$on('$destroy', function() {
29198             modelCtrl.$$parentForm.$removeControl(modelCtrl);
29199           });
29200         },
29201         post: function ngModelPostLink(scope, element, attr, ctrls) {
29202           var modelCtrl = ctrls[0];
29203           if (modelCtrl.$options.getOption('updateOn')) {
29204             element.on(modelCtrl.$options.getOption('updateOn'), function(ev) {
29205               modelCtrl.$$debounceViewValueCommit(ev && ev.type);
29206             });
29207           }
29208
29209           function setTouched() {
29210             modelCtrl.$setTouched();
29211           }
29212
29213           element.on('blur', function() {
29214             if (modelCtrl.$touched) return;
29215
29216             if ($rootScope.$$phase) {
29217               scope.$evalAsync(setTouched);
29218             } else {
29219               scope.$apply(setTouched);
29220             }
29221           });
29222         }
29223       };
29224     }
29225   };
29226 }];
29227
29228 /* exported defaultModelOptions */
29229 var defaultModelOptions;
29230 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
29231
29232 /**
29233  * @ngdoc type
29234  * @name ModelOptions
29235  * @description
29236  * A container for the options set by the {@link ngModelOptions} directive
29237  */
29238 function ModelOptions(options) {
29239   this.$$options = options;
29240 }
29241
29242 ModelOptions.prototype = {
29243
29244   /**
29245    * @ngdoc method
29246    * @name ModelOptions#getOption
29247    * @param {string} name the name of the option to retrieve
29248    * @returns {*} the value of the option
29249    * @description
29250    * Returns the value of the given option
29251    */
29252   getOption: function(name) {
29253     return this.$$options[name];
29254   },
29255
29256   /**
29257    * @ngdoc method
29258    * @name ModelOptions#createChild
29259    * @param {Object} options a hash of options for the new child that will override the parent's options
29260    * @return {ModelOptions} a new `ModelOptions` object initialized with the given options.
29261    */
29262   createChild: function(options) {
29263     var inheritAll = false;
29264
29265     // make a shallow copy
29266     options = extend({}, options);
29267
29268     // Inherit options from the parent if specified by the value `"$inherit"`
29269     forEach(options, /* @this */ function(option, key) {
29270       if (option === '$inherit') {
29271         if (key === '*') {
29272           inheritAll = true;
29273         } else {
29274           options[key] = this.$$options[key];
29275           // `updateOn` is special so we must also inherit the `updateOnDefault` option
29276           if (key === 'updateOn') {
29277             options.updateOnDefault = this.$$options.updateOnDefault;
29278           }
29279         }
29280       } else {
29281         if (key === 'updateOn') {
29282           // If the `updateOn` property contains the `default` event then we have to remove
29283           // it from the event list and set the `updateOnDefault` flag.
29284           options.updateOnDefault = false;
29285           options[key] = trim(option.replace(DEFAULT_REGEXP, function() {
29286             options.updateOnDefault = true;
29287             return ' ';
29288           }));
29289         }
29290       }
29291     }, this);
29292
29293     if (inheritAll) {
29294       // We have a property of the form: `"*": "$inherit"`
29295       delete options['*'];
29296       defaults(options, this.$$options);
29297     }
29298
29299     // Finally add in any missing defaults
29300     defaults(options, defaultModelOptions.$$options);
29301
29302     return new ModelOptions(options);
29303   }
29304 };
29305
29306
29307 defaultModelOptions = new ModelOptions({
29308   updateOn: '',
29309   updateOnDefault: true,
29310   debounce: 0,
29311   getterSetter: false,
29312   allowInvalid: false,
29313   timezone: null
29314 });
29315
29316
29317 /**
29318  * @ngdoc directive
29319  * @name ngModelOptions
29320  *
29321  * @description
29322  * This directive allows you to modify the behaviour of {@link ngModel} directives within your
29323  * application. You can specify an `ngModelOptions` directive on any element. All {@link ngModel}
29324  * directives will use the options of their nearest `ngModelOptions` ancestor.
29325  *
29326  * The `ngModelOptions` settings are found by evaluating the value of the attribute directive as
29327  * an Angular expression. This expression should evaluate to an object, whose properties contain
29328  * the settings. For example: `<div "ng-model-options"="{ debounce: 100 }"`.
29329  *
29330  * ## Inheriting Options
29331  *
29332  * You can specify that an `ngModelOptions` setting should be inherited from a parent `ngModelOptions`
29333  * directive by giving it the value of `"$inherit"`.
29334  * Then it will inherit that setting from the first `ngModelOptions` directive found by traversing up the
29335  * DOM tree. If there is no ancestor element containing an `ngModelOptions` directive then default settings
29336  * will be used.
29337  *
29338  * For example given the following fragment of HTML
29339  *
29340  *
29341  * ```html
29342  * <div ng-model-options="{ allowInvalid: true, debounce: 200 }">
29343  *   <form ng-model-options="{ updateOn: 'blur', allowInvalid: '$inherit' }">
29344  *     <input ng-model-options="{ updateOn: 'default', allowInvalid: '$inherit' }" />
29345  *   </form>
29346  * </div>
29347  * ```
29348  *
29349  * the `input` element will have the following settings
29350  *
29351  * ```js
29352  * { allowInvalid: true, updateOn: 'default', debounce: 0 }
29353  * ```
29354  *
29355  * Notice that the `debounce` setting was not inherited and used the default value instead.
29356  *
29357  * You can specify that all undefined settings are automatically inherited from an ancestor by
29358  * including a property with key of `"*"` and value of `"$inherit"`.
29359  *
29360  * For example given the following fragment of HTML
29361  *
29362  *
29363  * ```html
29364  * <div ng-model-options="{ allowInvalid: true, debounce: 200 }">
29365  *   <form ng-model-options="{ updateOn: 'blur', "*": '$inherit' }">
29366  *     <input ng-model-options="{ updateOn: 'default', "*": '$inherit' }" />
29367  *   </form>
29368  * </div>
29369  * ```
29370  *
29371  * the `input` element will have the following settings
29372  *
29373  * ```js
29374  * { allowInvalid: true, updateOn: 'default', debounce: 200 }
29375  * ```
29376  *
29377  * Notice that the `debounce` setting now inherits the value from the outer `<div>` element.
29378  *
29379  * If you are creating a reusable component then you should be careful when using `"*": "$inherit"`
29380  * since you may inadvertently inherit a setting in the future that changes the behavior of your component.
29381  *
29382  *
29383  * ## Triggering and debouncing model updates
29384  *
29385  * The `updateOn` and `debounce` properties allow you to specify a custom list of events that will
29386  * trigger a model update and/or a debouncing delay so that the actual update only takes place when
29387  * a timer expires; this timer will be reset after another change takes place.
29388  *
29389  * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
29390  * be different from the value in the actual model. This means that if you update the model you
29391  * should also invoke {@link ngModel.NgModelController#$rollbackViewValue} on the relevant input field in
29392  * order to make sure it is synchronized with the model and that any debounced action is canceled.
29393  *
29394  * The easiest way to reference the control's {@link ngModel.NgModelController#$rollbackViewValue}
29395  * method is by making sure the input is placed inside a form that has a `name` attribute. This is
29396  * important because `form` controllers are published to the related scope under the name in their
29397  * `name` attribute.
29398  *
29399  * Any pending changes will take place immediately when an enclosing form is submitted via the
29400  * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
29401  * to have access to the updated model.
29402  *
29403  * The following example shows how to override immediate updates. Changes on the inputs within the
29404  * form will update the model only when the control loses focus (blur event). If `escape` key is
29405  * pressed while the input field is focused, the value is reset to the value in the current model.
29406  *
29407  * <example name="ngModelOptions-directive-blur" module="optionsExample">
29408  *   <file name="index.html">
29409  *     <div ng-controller="ExampleController">
29410  *       <form name="userForm">
29411  *         <label>
29412  *           Name:
29413  *           <input type="text" name="userName"
29414  *                  ng-model="user.name"
29415  *                  ng-model-options="{ updateOn: 'blur' }"
29416  *                  ng-keyup="cancel($event)" />
29417  *         </label><br />
29418  *         <label>
29419  *           Other data:
29420  *           <input type="text" ng-model="user.data" />
29421  *         </label><br />
29422  *       </form>
29423  *       <pre>user.name = <span ng-bind="user.name"></span></pre>
29424  *     </div>
29425  *   </file>
29426  *   <file name="app.js">
29427  *     angular.module('optionsExample', [])
29428  *       .controller('ExampleController', ['$scope', function($scope) {
29429  *         $scope.user = { name: 'say', data: '' };
29430  *
29431  *         $scope.cancel = function(e) {
29432  *           if (e.keyCode === 27) {
29433  *             $scope.userForm.userName.$rollbackViewValue();
29434  *           }
29435  *         };
29436  *       }]);
29437  *   </file>
29438  *   <file name="protractor.js" type="protractor">
29439  *     var model = element(by.binding('user.name'));
29440  *     var input = element(by.model('user.name'));
29441  *     var other = element(by.model('user.data'));
29442  *
29443  *     it('should allow custom events', function() {
29444  *       input.sendKeys(' hello');
29445  *       input.click();
29446  *       expect(model.getText()).toEqual('say');
29447  *       other.click();
29448  *       expect(model.getText()).toEqual('say hello');
29449  *     });
29450  *
29451  *     it('should $rollbackViewValue when model changes', function() {
29452  *       input.sendKeys(' hello');
29453  *       expect(input.getAttribute('value')).toEqual('say hello');
29454  *       input.sendKeys(protractor.Key.ESCAPE);
29455  *       expect(input.getAttribute('value')).toEqual('say');
29456  *       other.click();
29457  *       expect(model.getText()).toEqual('say');
29458  *     });
29459  *   </file>
29460  * </example>
29461  *
29462  * The next example shows how to debounce model changes. Model will be updated only 1 sec after last change.
29463  * If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
29464  *
29465  * <example name="ngModelOptions-directive-debounce" module="optionsExample">
29466  *   <file name="index.html">
29467  *     <div ng-controller="ExampleController">
29468  *       <form name="userForm">
29469  *         Name:
29470  *         <input type="text" name="userName"
29471  *                ng-model="user.name"
29472  *                ng-model-options="{ debounce: 1000 }" />
29473  *         <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br />
29474  *       </form>
29475  *       <pre>user.name = <span ng-bind="user.name"></span></pre>
29476  *     </div>
29477  *   </file>
29478  *   <file name="app.js">
29479  *     angular.module('optionsExample', [])
29480  *       .controller('ExampleController', ['$scope', function($scope) {
29481  *         $scope.user = { name: 'say' };
29482  *       }]);
29483  *   </file>
29484  * </example>
29485  *
29486  * ## Model updates and validation
29487  *
29488  * The default behaviour in `ngModel` is that the model value is set to `undefined` when the
29489  * validation determines that the value is invalid. By setting the `allowInvalid` property to true,
29490  * the model will still be updated even if the value is invalid.
29491  *
29492  *
29493  * ## Connecting to the scope
29494  *
29495  * By setting the `getterSetter` property to true you are telling ngModel that the `ngModel` expression
29496  * on the scope refers to a "getter/setter" function rather than the value itself.
29497  *
29498  * The following example shows how to bind to getter/setters:
29499  *
29500  * <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
29501  *   <file name="index.html">
29502  *     <div ng-controller="ExampleController">
29503  *       <form name="userForm">
29504  *         <label>
29505  *           Name:
29506  *           <input type="text" name="userName"
29507  *                  ng-model="user.name"
29508  *                  ng-model-options="{ getterSetter: true }" />
29509  *         </label>
29510  *       </form>
29511  *       <pre>user.name = <span ng-bind="user.name()"></span></pre>
29512  *     </div>
29513  *   </file>
29514  *   <file name="app.js">
29515  *     angular.module('getterSetterExample', [])
29516  *       .controller('ExampleController', ['$scope', function($scope) {
29517  *         var _name = 'Brian';
29518  *         $scope.user = {
29519  *           name: function(newName) {
29520  *             return angular.isDefined(newName) ? (_name = newName) : _name;
29521  *           }
29522  *         };
29523  *       }]);
29524  *   </file>
29525  * </example>
29526  *
29527  *
29528  * ## Specifying timezones
29529  *
29530  * You can specify the timezone that date/time input directives expect by providing its name in the
29531  * `timezone` property.
29532  *
29533  * @param {Object} ngModelOptions options to apply to {@link ngModel} directives on this element and
29534  *   and its descendents. Valid keys are:
29535  *   - `updateOn`: string specifying which event should the input be bound to. You can set several
29536  *     events using an space delimited list. There is a special event called `default` that
29537  *     matches the default events belonging to the control.
29538  *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
29539  *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
29540  *     custom value for each event. For example:
29541  *     ```
29542  *     ng-model-options="{
29543  *       updateOn: 'default blur',
29544  *       debounce: { 'default': 500, 'blur': 0 }
29545  *     }"
29546  *     ```
29547  *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
29548  *     not validate correctly instead of the default behavior of setting the model to undefined.
29549  *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
29550  *     `ngModel` as getters/setters.
29551  *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
29552  *     `<input type="date" />`, `<input type="time" />`, ... . It understands UTC/GMT and the
29553  *     continental US time zone abbreviations, but for general use, use a time zone offset, for
29554  *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
29555  *     If not specified, the timezone of the browser will be used.
29556  *
29557  */
29558 var ngModelOptionsDirective = function() {
29559   NgModelOptionsController.$inject = ['$attrs', '$scope'];
29560   function NgModelOptionsController($attrs, $scope) {
29561     this.$$attrs = $attrs;
29562     this.$$scope = $scope;
29563   }
29564   NgModelOptionsController.prototype = {
29565     $onInit: function() {
29566       var parentOptions = this.parentCtrl ? this.parentCtrl.$options : defaultModelOptions;
29567       var modelOptionsDefinition = this.$$scope.$eval(this.$$attrs.ngModelOptions);
29568
29569       this.$options = parentOptions.createChild(modelOptionsDefinition);
29570     }
29571   };
29572
29573   return {
29574     restrict: 'A',
29575     // ngModelOptions needs to run before ngModel and input directives
29576     priority: 10,
29577     require: {parentCtrl: '?^^ngModelOptions'},
29578     bindToController: true,
29579     controller: NgModelOptionsController
29580   };
29581 };
29582
29583
29584 // shallow copy over values from `src` that are not already specified on `dst`
29585 function defaults(dst, src) {
29586   forEach(src, function(value, key) {
29587     if (!isDefined(dst[key])) {
29588       dst[key] = value;
29589     }
29590   });
29591 }
29592
29593 /**
29594  * @ngdoc directive
29595  * @name ngNonBindable
29596  * @restrict AC
29597  * @priority 1000
29598  *
29599  * @description
29600  * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
29601  * DOM element. This is useful if the element contains what appears to be Angular directives and
29602  * bindings but which should be ignored by Angular. This could be the case if you have a site that
29603  * displays snippets of code, for instance.
29604  *
29605  * @element ANY
29606  *
29607  * @example
29608  * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
29609  * but the one wrapped in `ngNonBindable` is left alone.
29610  *
29611  * @example
29612     <example name="ng-non-bindable">
29613       <file name="index.html">
29614         <div>Normal: {{1 + 2}}</div>
29615         <div ng-non-bindable>Ignored: {{1 + 2}}</div>
29616       </file>
29617       <file name="protractor.js" type="protractor">
29618        it('should check ng-non-bindable', function() {
29619          expect(element(by.binding('1 + 2')).getText()).toContain('3');
29620          expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
29621        });
29622       </file>
29623     </example>
29624  */
29625 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
29626
29627 /* exported ngOptionsDirective */
29628
29629 /* global jqLiteRemove */
29630
29631 var ngOptionsMinErr = minErr('ngOptions');
29632
29633 /**
29634  * @ngdoc directive
29635  * @name ngOptions
29636  * @restrict A
29637  *
29638  * @description
29639  *
29640  * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
29641  * elements for the `<select>` element using the array or object obtained by evaluating the
29642  * `ngOptions` comprehension expression.
29643  *
29644  * In many cases, {@link ng.directive:ngRepeat ngRepeat} can be used on `<option>` elements instead of
29645  * `ngOptions` to achieve a similar result. However, `ngOptions` provides some benefits:
29646  * - more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
29647  * comprehension expression
29648  * - reduced memory consumption by not creating a new scope for each repeated instance
29649  * - increased render speed by creating the options in a documentFragment instead of individually
29650  *
29651  * When an item in the `<select>` menu is selected, the array element or object property
29652  * represented by the selected option will be bound to the model identified by the `ngModel`
29653  * directive.
29654  *
29655  * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
29656  * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
29657  * option. See example below for demonstration.
29658  *
29659  * ## Complex Models (objects or collections)
29660  *
29661  * By default, `ngModel` watches the model by reference, not value. This is important to know when
29662  * binding the select to a model that is an object or a collection.
29663  *
29664  * One issue occurs if you want to preselect an option. For example, if you set
29665  * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
29666  * because the objects are not identical. So by default, you should always reference the item in your collection
29667  * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
29668  *
29669  * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
29670  * of the item not by reference, but by the result of the `track by` expression. For example, if your
29671  * collection items have an id property, you would `track by item.id`.
29672  *
29673  * A different issue with objects or collections is that ngModel won't detect if an object property or
29674  * a collection item changes. For that reason, `ngOptions` additionally watches the model using
29675  * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
29676  * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
29677  * has not changed identity, but only a property on the object or an item in the collection changes.
29678  *
29679  * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
29680  * if the model is an array). This means that changing a property deeper than the first level inside the
29681  * object/collection will not trigger a re-rendering.
29682  *
29683  * ## `select` **`as`**
29684  *
29685  * Using `select` **`as`** will bind the result of the `select` expression to the model, but
29686  * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
29687  * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
29688  * is used, the result of that expression will be set as the value of the `option` and `select` elements.
29689  *
29690  *
29691  * ### `select` **`as`** and **`track by`**
29692  *
29693  * <div class="alert alert-warning">
29694  * Be careful when using `select` **`as`** and **`track by`** in the same expression.
29695  * </div>
29696  *
29697  * Given this array of items on the $scope:
29698  *
29699  * ```js
29700  * $scope.items = [{
29701  *   id: 1,
29702  *   label: 'aLabel',
29703  *   subItem: { name: 'aSubItem' }
29704  * }, {
29705  *   id: 2,
29706  *   label: 'bLabel',
29707  *   subItem: { name: 'bSubItem' }
29708  * }];
29709  * ```
29710  *
29711  * This will work:
29712  *
29713  * ```html
29714  * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
29715  * ```
29716  * ```js
29717  * $scope.selected = $scope.items[0];
29718  * ```
29719  *
29720  * but this will not work:
29721  *
29722  * ```html
29723  * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
29724  * ```
29725  * ```js
29726  * $scope.selected = $scope.items[0].subItem;
29727  * ```
29728  *
29729  * In both examples, the **`track by`** expression is applied successfully to each `item` in the
29730  * `items` array. Because the selected option has been set programmatically in the controller, the
29731  * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
29732  * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
29733  * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
29734  * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
29735  * is not matched against any `<option>` and the `<select>` appears as having no selected value.
29736  *
29737  *
29738  * @param {string} ngModel Assignable AngularJS expression to data-bind to.
29739  * @param {comprehension_expression} ngOptions in one of the following forms:
29740  *
29741  *   * for array data sources:
29742  *     * `label` **`for`** `value` **`in`** `array`
29743  *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
29744  *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
29745  *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
29746  *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
29747  *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
29748  *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
29749  *        (for including a filter with `track by`)
29750  *   * for object data sources:
29751  *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
29752  *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
29753  *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
29754  *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
29755  *     * `select` **`as`** `label` **`group by`** `group`
29756  *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
29757  *     * `select` **`as`** `label` **`disable when`** `disable`
29758  *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
29759  *
29760  * Where:
29761  *
29762  *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
29763  *   * `value`: local variable which will refer to each item in the `array` or each property value
29764  *      of `object` during iteration.
29765  *   * `key`: local variable which will refer to a property name in `object` during iteration.
29766  *   * `label`: The result of this expression will be the label for `<option>` element. The
29767  *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
29768  *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
29769  *      element. If not specified, `select` expression will default to `value`.
29770  *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
29771  *      DOM element.
29772  *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
29773  *      element. Return `true` to disable.
29774  *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
29775  *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
29776  *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
29777  *      even when the options are recreated (e.g. reloaded from the server).
29778  * @param {string=} name Property name of the form under which the control is published.
29779  * @param {string=} required The control is considered valid only if value is entered.
29780  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29781  *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29782  *    `required` when you want to data-bind to the `required` attribute.
29783  * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
29784  * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
29785  *
29786  * @example
29787     <example module="selectExample" name="select">
29788       <file name="index.html">
29789         <script>
29790         angular.module('selectExample', [])
29791           .controller('ExampleController', ['$scope', function($scope) {
29792             $scope.colors = [
29793               {name:'black', shade:'dark'},
29794               {name:'white', shade:'light', notAnOption: true},
29795               {name:'red', shade:'dark'},
29796               {name:'blue', shade:'dark', notAnOption: true},
29797               {name:'yellow', shade:'light', notAnOption: false}
29798             ];
29799             $scope.myColor = $scope.colors[2]; // red
29800           }]);
29801         </script>
29802         <div ng-controller="ExampleController">
29803           <ul>
29804             <li ng-repeat="color in colors">
29805               <label>Name: <input ng-model="color.name"></label>
29806               <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
29807               <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
29808             </li>
29809             <li>
29810               <button ng-click="colors.push({})">add</button>
29811             </li>
29812           </ul>
29813           <hr/>
29814           <label>Color (null not allowed):
29815             <select ng-model="myColor" ng-options="color.name for color in colors"></select>
29816           </label><br/>
29817           <label>Color (null allowed):
29818           <span  class="nullable">
29819             <select ng-model="myColor" ng-options="color.name for color in colors">
29820               <option value="">-- choose color --</option>
29821             </select>
29822           </span></label><br/>
29823
29824           <label>Color grouped by shade:
29825             <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
29826             </select>
29827           </label><br/>
29828
29829           <label>Color grouped by shade, with some disabled:
29830             <select ng-model="myColor"
29831                   ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
29832             </select>
29833           </label><br/>
29834
29835
29836
29837           Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
29838           <br/>
29839           <hr/>
29840           Currently selected: {{ {selected_color:myColor} }}
29841           <div style="border:solid 1px black; height:20px"
29842                ng-style="{'background-color':myColor.name}">
29843           </div>
29844         </div>
29845       </file>
29846       <file name="protractor.js" type="protractor">
29847          it('should check ng-options', function() {
29848            expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
29849            element.all(by.model('myColor')).first().click();
29850            element.all(by.css('select[ng-model="myColor"] option')).first().click();
29851            expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
29852            element(by.css('.nullable select[ng-model="myColor"]')).click();
29853            element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
29854            expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
29855          });
29856       </file>
29857     </example>
29858  */
29859
29860 /* eslint-disable max-len */
29861 //                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555000000000666666666666600000007777777777777000000000000000888888888800000000000000000009999999999
29862 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]+?))?$/;
29863                         // 1: value expression (valueFn)
29864                         // 2: label expression (displayFn)
29865                         // 3: group by expression (groupByFn)
29866                         // 4: disable when expression (disableWhenFn)
29867                         // 5: array item variable name
29868                         // 6: object item key variable name
29869                         // 7: object item value variable name
29870                         // 8: collection expression
29871                         // 9: track by expression
29872 /* eslint-enable */
29873
29874
29875 var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
29876
29877   function parseOptionsExpression(optionsExp, selectElement, scope) {
29878
29879     var match = optionsExp.match(NG_OPTIONS_REGEXP);
29880     if (!(match)) {
29881       throw ngOptionsMinErr('iexp',
29882         'Expected expression in form of ' +
29883         '\'_select_ (as _label_)? for (_key_,)?_value_ in _collection_\'' +
29884         ' but got \'{0}\'. Element: {1}',
29885         optionsExp, startingTag(selectElement));
29886     }
29887
29888     // Extract the parts from the ngOptions expression
29889
29890     // The variable name for the value of the item in the collection
29891     var valueName = match[5] || match[7];
29892     // The variable name for the key of the item in the collection
29893     var keyName = match[6];
29894
29895     // An expression that generates the viewValue for an option if there is a label expression
29896     var selectAs = / as /.test(match[0]) && match[1];
29897     // An expression that is used to track the id of each object in the options collection
29898     var trackBy = match[9];
29899     // An expression that generates the viewValue for an option if there is no label expression
29900     var valueFn = $parse(match[2] ? match[1] : valueName);
29901     var selectAsFn = selectAs && $parse(selectAs);
29902     var viewValueFn = selectAsFn || valueFn;
29903     var trackByFn = trackBy && $parse(trackBy);
29904
29905     // Get the value by which we are going to track the option
29906     // if we have a trackFn then use that (passing scope and locals)
29907     // otherwise just hash the given viewValue
29908     var getTrackByValueFn = trackBy ?
29909                               function(value, locals) { return trackByFn(scope, locals); } :
29910                               function getHashOfValue(value) { return hashKey(value); };
29911     var getTrackByValue = function(value, key) {
29912       return getTrackByValueFn(value, getLocals(value, key));
29913     };
29914
29915     var displayFn = $parse(match[2] || match[1]);
29916     var groupByFn = $parse(match[3] || '');
29917     var disableWhenFn = $parse(match[4] || '');
29918     var valuesFn = $parse(match[8]);
29919
29920     var locals = {};
29921     var getLocals = keyName ? function(value, key) {
29922       locals[keyName] = key;
29923       locals[valueName] = value;
29924       return locals;
29925     } : function(value) {
29926       locals[valueName] = value;
29927       return locals;
29928     };
29929
29930
29931     function Option(selectValue, viewValue, label, group, disabled) {
29932       this.selectValue = selectValue;
29933       this.viewValue = viewValue;
29934       this.label = label;
29935       this.group = group;
29936       this.disabled = disabled;
29937     }
29938
29939     function getOptionValuesKeys(optionValues) {
29940       var optionValuesKeys;
29941
29942       if (!keyName && isArrayLike(optionValues)) {
29943         optionValuesKeys = optionValues;
29944       } else {
29945         // if object, extract keys, in enumeration order, unsorted
29946         optionValuesKeys = [];
29947         for (var itemKey in optionValues) {
29948           if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
29949             optionValuesKeys.push(itemKey);
29950           }
29951         }
29952       }
29953       return optionValuesKeys;
29954     }
29955
29956     return {
29957       trackBy: trackBy,
29958       getTrackByValue: getTrackByValue,
29959       getWatchables: $parse(valuesFn, function(optionValues) {
29960         // Create a collection of things that we would like to watch (watchedArray)
29961         // so that they can all be watched using a single $watchCollection
29962         // that only runs the handler once if anything changes
29963         var watchedArray = [];
29964         optionValues = optionValues || [];
29965
29966         var optionValuesKeys = getOptionValuesKeys(optionValues);
29967         var optionValuesLength = optionValuesKeys.length;
29968         for (var index = 0; index < optionValuesLength; index++) {
29969           var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
29970           var value = optionValues[key];
29971
29972           var locals = getLocals(value, key);
29973           var selectValue = getTrackByValueFn(value, locals);
29974           watchedArray.push(selectValue);
29975
29976           // Only need to watch the displayFn if there is a specific label expression
29977           if (match[2] || match[1]) {
29978             var label = displayFn(scope, locals);
29979             watchedArray.push(label);
29980           }
29981
29982           // Only need to watch the disableWhenFn if there is a specific disable expression
29983           if (match[4]) {
29984             var disableWhen = disableWhenFn(scope, locals);
29985             watchedArray.push(disableWhen);
29986           }
29987         }
29988         return watchedArray;
29989       }),
29990
29991       getOptions: function() {
29992
29993         var optionItems = [];
29994         var selectValueMap = {};
29995
29996         // The option values were already computed in the `getWatchables` fn,
29997         // which must have been called to trigger `getOptions`
29998         var optionValues = valuesFn(scope) || [];
29999         var optionValuesKeys = getOptionValuesKeys(optionValues);
30000         var optionValuesLength = optionValuesKeys.length;
30001
30002         for (var index = 0; index < optionValuesLength; index++) {
30003           var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
30004           var value = optionValues[key];
30005           var locals = getLocals(value, key);
30006           var viewValue = viewValueFn(scope, locals);
30007           var selectValue = getTrackByValueFn(viewValue, locals);
30008           var label = displayFn(scope, locals);
30009           var group = groupByFn(scope, locals);
30010           var disabled = disableWhenFn(scope, locals);
30011           var optionItem = new Option(selectValue, viewValue, label, group, disabled);
30012
30013           optionItems.push(optionItem);
30014           selectValueMap[selectValue] = optionItem;
30015         }
30016
30017         return {
30018           items: optionItems,
30019           selectValueMap: selectValueMap,
30020           getOptionFromViewValue: function(value) {
30021             return selectValueMap[getTrackByValue(value)];
30022           },
30023           getViewValueFromOption: function(option) {
30024             // If the viewValue could be an object that may be mutated by the application,
30025             // we need to make a copy and not return the reference to the value on the option.
30026             return trackBy ? copy(option.viewValue) : option.viewValue;
30027           }
30028         };
30029       }
30030     };
30031   }
30032
30033
30034   // we can't just jqLite('<option>') since jqLite is not smart enough
30035   // to create it in <select> and IE barfs otherwise.
30036   var optionTemplate = window.document.createElement('option'),
30037       optGroupTemplate = window.document.createElement('optgroup');
30038
30039     function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
30040
30041       var selectCtrl = ctrls[0];
30042       var ngModelCtrl = ctrls[1];
30043       var multiple = attr.multiple;
30044
30045       // The emptyOption allows the application developer to provide their own custom "empty"
30046       // option when the viewValue does not match any of the option values.
30047       for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
30048         if (children[i].value === '') {
30049           selectCtrl.hasEmptyOption = true;
30050           selectCtrl.emptyOption = children.eq(i);
30051           break;
30052         }
30053       }
30054
30055       var providedEmptyOption = !!selectCtrl.emptyOption;
30056
30057       var unknownOption = jqLite(optionTemplate.cloneNode(false));
30058       unknownOption.val('?');
30059
30060       var options;
30061       var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
30062       // This stores the newly created options before they are appended to the select.
30063       // Since the contents are removed from the fragment when it is appended,
30064       // we only need to create it once.
30065       var listFragment = $document[0].createDocumentFragment();
30066
30067       // Overwrite the implementation. ngOptions doesn't use hashes
30068       selectCtrl.generateUnknownOptionValue = function(val) {
30069         return '?';
30070       };
30071
30072       // Update the controller methods for multiple selectable options
30073       if (!multiple) {
30074
30075         selectCtrl.writeValue = function writeNgOptionsValue(value) {
30076           var selectedOption = options.selectValueMap[selectElement.val()];
30077           var option = options.getOptionFromViewValue(value);
30078
30079           // Make sure to remove the selected attribute from the previously selected option
30080           // Otherwise, screen readers might get confused
30081           if (selectedOption) selectedOption.element.removeAttribute('selected');
30082
30083           if (option) {
30084             // Don't update the option when it is already selected.
30085             // For example, the browser will select the first option by default. In that case,
30086             // most properties are set automatically - except the `selected` attribute, which we
30087             // set always
30088
30089             if (selectElement[0].value !== option.selectValue) {
30090               selectCtrl.removeUnknownOption();
30091               selectCtrl.unselectEmptyOption();
30092
30093               selectElement[0].value = option.selectValue;
30094               option.element.selected = true;
30095             }
30096
30097             option.element.setAttribute('selected', 'selected');
30098           } else {
30099
30100             if (providedEmptyOption) {
30101               selectCtrl.selectEmptyOption();
30102             } else if (selectCtrl.unknownOption.parent().length) {
30103               selectCtrl.updateUnknownOption(value);
30104             } else {
30105               selectCtrl.renderUnknownOption(value);
30106             }
30107           }
30108         };
30109
30110         selectCtrl.readValue = function readNgOptionsValue() {
30111
30112           var selectedOption = options.selectValueMap[selectElement.val()];
30113
30114           if (selectedOption && !selectedOption.disabled) {
30115             selectCtrl.unselectEmptyOption();
30116             selectCtrl.removeUnknownOption();
30117             return options.getViewValueFromOption(selectedOption);
30118           }
30119           return null;
30120         };
30121
30122         // If we are using `track by` then we must watch the tracked value on the model
30123         // since ngModel only watches for object identity change
30124         // FIXME: When a user selects an option, this watch will fire needlessly
30125         if (ngOptions.trackBy) {
30126           scope.$watch(
30127             function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
30128             function() { ngModelCtrl.$render(); }
30129           );
30130         }
30131
30132       } else {
30133
30134         selectCtrl.writeValue = function writeNgOptionsMultiple(values) {
30135           // Only set `<option>.selected` if necessary, in order to prevent some browsers from
30136           // scrolling to `<option>` elements that are outside the `<select>` element's viewport.
30137
30138           var selectedOptions = values && values.map(getAndUpdateSelectedOption) || [];
30139
30140           options.items.forEach(function(option) {
30141             if (option.element.selected && !includes(selectedOptions, option)) {
30142               option.element.selected = false;
30143             }
30144           });
30145         };
30146
30147
30148         selectCtrl.readValue = function readNgOptionsMultiple() {
30149           var selectedValues = selectElement.val() || [],
30150               selections = [];
30151
30152           forEach(selectedValues, function(value) {
30153             var option = options.selectValueMap[value];
30154             if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
30155           });
30156
30157           return selections;
30158         };
30159
30160         // If we are using `track by` then we must watch these tracked values on the model
30161         // since ngModel only watches for object identity change
30162         if (ngOptions.trackBy) {
30163
30164           scope.$watchCollection(function() {
30165             if (isArray(ngModelCtrl.$viewValue)) {
30166               return ngModelCtrl.$viewValue.map(function(value) {
30167                 return ngOptions.getTrackByValue(value);
30168               });
30169             }
30170           }, function() {
30171             ngModelCtrl.$render();
30172           });
30173
30174         }
30175       }
30176
30177       if (providedEmptyOption) {
30178
30179         // we need to remove it before calling selectElement.empty() because otherwise IE will
30180         // remove the label from the element. wtf?
30181         selectCtrl.emptyOption.remove();
30182
30183         // compile the element since there might be bindings in it
30184         $compile(selectCtrl.emptyOption)(scope);
30185
30186         if (selectCtrl.emptyOption[0].nodeType === NODE_TYPE_COMMENT) {
30187           // This means the empty option has currently no actual DOM node, probably because
30188           // it has been modified by a transclusion directive.
30189           selectCtrl.hasEmptyOption = false;
30190
30191           // Redefine the registerOption function, which will catch
30192           // options that are added by ngIf etc. (rendering of the node is async because of
30193           // lazy transclusion)
30194           selectCtrl.registerOption = function(optionScope, optionEl) {
30195             if (optionEl.val() === '') {
30196               selectCtrl.hasEmptyOption = true;
30197               selectCtrl.emptyOption = optionEl;
30198               selectCtrl.emptyOption.removeClass('ng-scope');
30199               // This ensures the new empty option is selected if previously no option was selected
30200               ngModelCtrl.$render();
30201
30202               optionEl.on('$destroy', function() {
30203                 selectCtrl.hasEmptyOption = false;
30204                 selectCtrl.emptyOption = undefined;
30205               });
30206             }
30207           };
30208
30209         } else {
30210           // remove the class, which is added automatically because we recompile the element and it
30211           // becomes the compilation root
30212           selectCtrl.emptyOption.removeClass('ng-scope');
30213         }
30214
30215       }
30216
30217       selectElement.empty();
30218
30219       // We need to do this here to ensure that the options object is defined
30220       // when we first hit it in writeNgOptionsValue
30221       updateOptions();
30222
30223       // We will re-render the option elements if the option values or labels change
30224       scope.$watchCollection(ngOptions.getWatchables, updateOptions);
30225
30226       // ------------------------------------------------------------------ //
30227
30228       function addOptionElement(option, parent) {
30229         var optionElement = optionTemplate.cloneNode(false);
30230         parent.appendChild(optionElement);
30231         updateOptionElement(option, optionElement);
30232       }
30233
30234       function getAndUpdateSelectedOption(viewValue) {
30235         var option = options.getOptionFromViewValue(viewValue);
30236         var element = option && option.element;
30237
30238         if (element && !element.selected) element.selected = true;
30239
30240         return option;
30241       }
30242
30243       function updateOptionElement(option, element) {
30244         option.element = element;
30245         element.disabled = option.disabled;
30246         // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
30247         // selects in certain circumstances when multiple selects are next to each other and display
30248         // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
30249         // See https://github.com/angular/angular.js/issues/11314 for more info.
30250         // This is unfortunately untestable with unit / e2e tests
30251         if (option.label !== element.label) {
30252           element.label = option.label;
30253           element.textContent = option.label;
30254         }
30255         element.value = option.selectValue;
30256       }
30257
30258       function updateOptions() {
30259         var previousValue = options && selectCtrl.readValue();
30260
30261         // We must remove all current options, but cannot simply set innerHTML = null
30262         // since the providedEmptyOption might have an ngIf on it that inserts comments which we
30263         // must preserve.
30264         // Instead, iterate over the current option elements and remove them or their optgroup
30265         // parents
30266         if (options) {
30267
30268           for (var i = options.items.length - 1; i >= 0; i--) {
30269             var option = options.items[i];
30270             if (isDefined(option.group)) {
30271               jqLiteRemove(option.element.parentNode);
30272             } else {
30273               jqLiteRemove(option.element);
30274             }
30275           }
30276         }
30277
30278         options = ngOptions.getOptions();
30279
30280         var groupElementMap = {};
30281
30282         // Ensure that the empty option is always there if it was explicitly provided
30283         if (providedEmptyOption) {
30284           selectElement.prepend(selectCtrl.emptyOption);
30285         }
30286
30287         options.items.forEach(function addOption(option) {
30288           var groupElement;
30289
30290           if (isDefined(option.group)) {
30291
30292             // This option is to live in a group
30293             // See if we have already created this group
30294             groupElement = groupElementMap[option.group];
30295
30296             if (!groupElement) {
30297
30298               groupElement = optGroupTemplate.cloneNode(false);
30299               listFragment.appendChild(groupElement);
30300
30301               // Update the label on the group element
30302               // "null" is special cased because of Safari
30303               groupElement.label = option.group === null ? 'null' : option.group;
30304
30305               // Store it for use later
30306               groupElementMap[option.group] = groupElement;
30307             }
30308
30309             addOptionElement(option, groupElement);
30310
30311           } else {
30312
30313             // This option is not in a group
30314             addOptionElement(option, listFragment);
30315           }
30316         });
30317
30318         selectElement[0].appendChild(listFragment);
30319
30320         ngModelCtrl.$render();
30321
30322         // Check to see if the value has changed due to the update to the options
30323         if (!ngModelCtrl.$isEmpty(previousValue)) {
30324           var nextValue = selectCtrl.readValue();
30325           var isNotPrimitive = ngOptions.trackBy || multiple;
30326           if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
30327             ngModelCtrl.$setViewValue(nextValue);
30328             ngModelCtrl.$render();
30329           }
30330         }
30331
30332       }
30333   }
30334
30335   return {
30336     restrict: 'A',
30337     terminal: true,
30338     require: ['select', 'ngModel'],
30339     link: {
30340       pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
30341         // Deactivate the SelectController.register method to prevent
30342         // option directives from accidentally registering themselves
30343         // (and unwanted $destroy handlers etc.)
30344         ctrls[0].registerOption = noop;
30345       },
30346       post: ngOptionsPostLink
30347     }
30348   };
30349 }];
30350
30351 /**
30352  * @ngdoc directive
30353  * @name ngPluralize
30354  * @restrict EA
30355  *
30356  * @description
30357  * `ngPluralize` is a directive that displays messages according to en-US localization rules.
30358  * These rules are bundled with angular.js, but can be overridden
30359  * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
30360  * by specifying the mappings between
30361  * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
30362  * and the strings to be displayed.
30363  *
30364  * # Plural categories and explicit number rules
30365  * There are two
30366  * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
30367  * in Angular's default en-US locale: "one" and "other".
30368  *
30369  * While a plural category may match many numbers (for example, in en-US locale, "other" can match
30370  * any number that is not 1), an explicit number rule can only match one number. For example, the
30371  * explicit number rule for "3" matches the number 3. There are examples of plural categories
30372  * and explicit number rules throughout the rest of this documentation.
30373  *
30374  * # Configuring ngPluralize
30375  * You configure ngPluralize by providing 2 attributes: `count` and `when`.
30376  * You can also provide an optional attribute, `offset`.
30377  *
30378  * The value of the `count` attribute can be either a string or an {@link guide/expression
30379  * Angular expression}; these are evaluated on the current scope for its bound value.
30380  *
30381  * The `when` attribute specifies the mappings between plural categories and the actual
30382  * string to be displayed. The value of the attribute should be a JSON object.
30383  *
30384  * The following example shows how to configure ngPluralize:
30385  *
30386  * ```html
30387  * <ng-pluralize count="personCount"
30388                  when="{'0': 'Nobody is viewing.',
30389  *                      'one': '1 person is viewing.',
30390  *                      'other': '{} people are viewing.'}">
30391  * </ng-pluralize>
30392  *```
30393  *
30394  * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
30395  * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
30396  * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
30397  * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
30398  * show "a dozen people are viewing".
30399  *
30400  * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
30401  * into pluralized strings. In the previous example, Angular will replace `{}` with
30402  * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
30403  * for <span ng-non-bindable>{{numberExpression}}</span>.
30404  *
30405  * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
30406  * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
30407  *
30408  * # Configuring ngPluralize with offset
30409  * The `offset` attribute allows further customization of pluralized text, which can result in
30410  * a better user experience. For example, instead of the message "4 people are viewing this document",
30411  * you might display "John, Kate and 2 others are viewing this document".
30412  * The offset attribute allows you to offset a number by any desired value.
30413  * Let's take a look at an example:
30414  *
30415  * ```html
30416  * <ng-pluralize count="personCount" offset=2
30417  *               when="{'0': 'Nobody is viewing.',
30418  *                      '1': '{{person1}} is viewing.',
30419  *                      '2': '{{person1}} and {{person2}} are viewing.',
30420  *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
30421  *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
30422  * </ng-pluralize>
30423  * ```
30424  *
30425  * Notice that we are still using two plural categories(one, other), but we added
30426  * three explicit number rules 0, 1 and 2.
30427  * When one person, perhaps John, views the document, "John is viewing" will be shown.
30428  * When three people view the document, no explicit number rule is found, so
30429  * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
30430  * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
30431  * is shown.
30432  *
30433  * Note that when you specify offsets, you must provide explicit number rules for
30434  * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
30435  * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
30436  * plural categories "one" and "other".
30437  *
30438  * @param {string|expression} count The variable to be bound to.
30439  * @param {string} when The mapping between plural category to its corresponding strings.
30440  * @param {number=} offset Offset to deduct from the total number.
30441  *
30442  * @example
30443     <example module="pluralizeExample" name="ng-pluralize">
30444       <file name="index.html">
30445         <script>
30446           angular.module('pluralizeExample', [])
30447             .controller('ExampleController', ['$scope', function($scope) {
30448               $scope.person1 = 'Igor';
30449               $scope.person2 = 'Misko';
30450               $scope.personCount = 1;
30451             }]);
30452         </script>
30453         <div ng-controller="ExampleController">
30454           <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
30455           <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
30456           <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
30457
30458           <!--- Example with simple pluralization rules for en locale --->
30459           Without Offset:
30460           <ng-pluralize count="personCount"
30461                         when="{'0': 'Nobody is viewing.',
30462                                'one': '1 person is viewing.',
30463                                'other': '{} people are viewing.'}">
30464           </ng-pluralize><br>
30465
30466           <!--- Example with offset --->
30467           With Offset(2):
30468           <ng-pluralize count="personCount" offset=2
30469                         when="{'0': 'Nobody is viewing.',
30470                                '1': '{{person1}} is viewing.',
30471                                '2': '{{person1}} and {{person2}} are viewing.',
30472                                'one': '{{person1}}, {{person2}} and one other person are viewing.',
30473                                'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
30474           </ng-pluralize>
30475         </div>
30476       </file>
30477       <file name="protractor.js" type="protractor">
30478         it('should show correct pluralized string', function() {
30479           var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
30480           var withOffset = element.all(by.css('ng-pluralize')).get(1);
30481           var countInput = element(by.model('personCount'));
30482
30483           expect(withoutOffset.getText()).toEqual('1 person is viewing.');
30484           expect(withOffset.getText()).toEqual('Igor is viewing.');
30485
30486           countInput.clear();
30487           countInput.sendKeys('0');
30488
30489           expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
30490           expect(withOffset.getText()).toEqual('Nobody is viewing.');
30491
30492           countInput.clear();
30493           countInput.sendKeys('2');
30494
30495           expect(withoutOffset.getText()).toEqual('2 people are viewing.');
30496           expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
30497
30498           countInput.clear();
30499           countInput.sendKeys('3');
30500
30501           expect(withoutOffset.getText()).toEqual('3 people are viewing.');
30502           expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
30503
30504           countInput.clear();
30505           countInput.sendKeys('4');
30506
30507           expect(withoutOffset.getText()).toEqual('4 people are viewing.');
30508           expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
30509         });
30510         it('should show data-bound names', function() {
30511           var withOffset = element.all(by.css('ng-pluralize')).get(1);
30512           var personCount = element(by.model('personCount'));
30513           var person1 = element(by.model('person1'));
30514           var person2 = element(by.model('person2'));
30515           personCount.clear();
30516           personCount.sendKeys('4');
30517           person1.clear();
30518           person1.sendKeys('Di');
30519           person2.clear();
30520           person2.sendKeys('Vojta');
30521           expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
30522         });
30523       </file>
30524     </example>
30525  */
30526 var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
30527   var BRACE = /{}/g,
30528       IS_WHEN = /^when(Minus)?(.+)$/;
30529
30530   return {
30531     link: function(scope, element, attr) {
30532       var numberExp = attr.count,
30533           whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
30534           offset = attr.offset || 0,
30535           whens = scope.$eval(whenExp) || {},
30536           whensExpFns = {},
30537           startSymbol = $interpolate.startSymbol(),
30538           endSymbol = $interpolate.endSymbol(),
30539           braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
30540           watchRemover = angular.noop,
30541           lastCount;
30542
30543       forEach(attr, function(expression, attributeName) {
30544         var tmpMatch = IS_WHEN.exec(attributeName);
30545         if (tmpMatch) {
30546           var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
30547           whens[whenKey] = element.attr(attr.$attr[attributeName]);
30548         }
30549       });
30550       forEach(whens, function(expression, key) {
30551         whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
30552
30553       });
30554
30555       scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
30556         var count = parseFloat(newVal);
30557         var countIsNaN = isNumberNaN(count);
30558
30559         if (!countIsNaN && !(count in whens)) {
30560           // If an explicit number rule such as 1, 2, 3... is defined, just use it.
30561           // Otherwise, check it against pluralization rules in $locale service.
30562           count = $locale.pluralCat(count - offset);
30563         }
30564
30565         // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
30566         // In JS `NaN !== NaN`, so we have to explicitly check.
30567         if ((count !== lastCount) && !(countIsNaN && isNumberNaN(lastCount))) {
30568           watchRemover();
30569           var whenExpFn = whensExpFns[count];
30570           if (isUndefined(whenExpFn)) {
30571             if (newVal != null) {
30572               $log.debug('ngPluralize: no rule defined for \'' + count + '\' in ' + whenExp);
30573             }
30574             watchRemover = noop;
30575             updateElementText();
30576           } else {
30577             watchRemover = scope.$watch(whenExpFn, updateElementText);
30578           }
30579           lastCount = count;
30580         }
30581       });
30582
30583       function updateElementText(newText) {
30584         element.text(newText || '');
30585       }
30586     }
30587   };
30588 }];
30589
30590 /* exported ngRepeatDirective */
30591
30592 /**
30593  * @ngdoc directive
30594  * @name ngRepeat
30595  * @multiElement
30596  * @restrict A
30597  *
30598  * @description
30599  * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
30600  * instance gets its own scope, where the given loop variable is set to the current collection item,
30601  * and `$index` is set to the item index or key.
30602  *
30603  * Special properties are exposed on the local scope of each template instance, including:
30604  *
30605  * | Variable  | Type            | Details                                                                     |
30606  * |-----------|-----------------|-----------------------------------------------------------------------------|
30607  * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
30608  * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
30609  * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
30610  * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
30611  * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
30612  * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
30613  *
30614  * <div class="alert alert-info">
30615  *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
30616  *   This may be useful when, for instance, nesting ngRepeats.
30617  * </div>
30618  *
30619  *
30620  * # Iterating over object properties
30621  *
30622  * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
30623  * syntax:
30624  *
30625  * ```js
30626  * <div ng-repeat="(key, value) in myObj"> ... </div>
30627  * ```
30628  *
30629  * However, there are a few limitations compared to array iteration:
30630  *
30631  * - The JavaScript specification does not define the order of keys
30632  *   returned for an object, so Angular relies on the order returned by the browser
30633  *   when running `for key in myObj`. Browsers generally follow the strategy of providing
30634  *   keys in the order in which they were defined, although there are exceptions when keys are deleted
30635  *   and reinstated. See the
30636  *   [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
30637  *
30638  * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
30639  *   it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
30640  *
30641  * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
30642  *   objects, and will throw an error if used with one.
30643  *
30644  * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
30645  * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
30646  * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
30647  * or implement a `$watch` on the object yourself.
30648  *
30649  *
30650  * # Tracking and Duplicates
30651  *
30652  * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
30653  * the collection. When a change happens, `ngRepeat` then makes the corresponding changes to the DOM:
30654  *
30655  * * When an item is added, a new instance of the template is added to the DOM.
30656  * * When an item is removed, its template instance is removed from the DOM.
30657  * * When items are reordered, their respective templates are reordered in the DOM.
30658  *
30659  * To minimize creation of DOM elements, `ngRepeat` uses a function
30660  * to "keep track" of all items in the collection and their corresponding DOM elements.
30661  * For example, if an item is added to the collection, `ngRepeat` will know that all other items
30662  * already have DOM elements, and will not re-render them.
30663  *
30664  * The default tracking function (which tracks items by their identity) does not allow
30665  * duplicate items in arrays. This is because when there are duplicates, it is not possible
30666  * to maintain a one-to-one mapping between collection items and DOM elements.
30667  *
30668  * If you do need to repeat duplicate items, you can substitute the default tracking behavior
30669  * with your own using the `track by` expression.
30670  *
30671  * For example, you may track items by the index of each item in the collection, using the
30672  * special scope property `$index`:
30673  * ```html
30674  *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
30675  *      {{n}}
30676  *    </div>
30677  * ```
30678  *
30679  * You may also use arbitrary expressions in `track by`, including references to custom functions
30680  * on the scope:
30681  * ```html
30682  *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
30683  *      {{n}}
30684  *    </div>
30685  * ```
30686  *
30687  * <div class="alert alert-success">
30688  * If you are working with objects that have a unique identifier property, you should track
30689  * by this identifier instead of the object instance. Should you reload your data later, `ngRepeat`
30690  * will not have to rebuild the DOM elements for items it has already rendered, even if the
30691  * JavaScript objects in the collection have been substituted for new ones. For large collections,
30692  * this significantly improves rendering performance. If you don't have a unique identifier,
30693  * `track by $index` can also provide a performance boost.
30694  * </div>
30695  *
30696  * ```html
30697  *    <div ng-repeat="model in collection track by model.id">
30698  *      {{model.name}}
30699  *    </div>
30700  * ```
30701  *
30702  * <br />
30703  * <div class="alert alert-warning">
30704  * Avoid using `track by $index` when the repeated template contains
30705  * {@link guide/expression#one-time-binding one-time bindings}. In such cases, the `nth` DOM
30706  * element will always be matched with the `nth` item of the array, so the bindings on that element
30707  * will not be updated even when the corresponding item changes, essentially causing the view to get
30708  * out-of-sync with the underlying data.
30709  * </div>
30710  *
30711  * When no `track by` expression is provided, it is equivalent to tracking by the built-in
30712  * `$id` function, which tracks items by their identity:
30713  * ```html
30714  *    <div ng-repeat="obj in collection track by $id(obj)">
30715  *      {{obj.prop}}
30716  *    </div>
30717  * ```
30718  *
30719  * <br />
30720  * <div class="alert alert-warning">
30721  * **Note:** `track by` must always be the last expression:
30722  * </div>
30723  * ```
30724  *    <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
30725  *      {{model.name}}
30726  *    </div>
30727  * ```
30728  *
30729  *
30730  * # Special repeat start and end points
30731  * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
30732  * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
30733  * 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)
30734  * up to and including the ending HTML tag where **ng-repeat-end** is placed.
30735  *
30736  * The example below makes use of this feature:
30737  * ```html
30738  *   <header ng-repeat-start="item in items">
30739  *     Header {{ item }}
30740  *   </header>
30741  *   <div class="body">
30742  *     Body {{ item }}
30743  *   </div>
30744  *   <footer ng-repeat-end>
30745  *     Footer {{ item }}
30746  *   </footer>
30747  * ```
30748  *
30749  * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
30750  * ```html
30751  *   <header>
30752  *     Header A
30753  *   </header>
30754  *   <div class="body">
30755  *     Body A
30756  *   </div>
30757  *   <footer>
30758  *     Footer A
30759  *   </footer>
30760  *   <header>
30761  *     Header B
30762  *   </header>
30763  *   <div class="body">
30764  *     Body B
30765  *   </div>
30766  *   <footer>
30767  *     Footer B
30768  *   </footer>
30769  * ```
30770  *
30771  * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
30772  * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
30773  *
30774  * @animations
30775  * | Animation                        | Occurs                              |
30776  * |----------------------------------|-------------------------------------|
30777  * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
30778  * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
30779  * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
30780  *
30781  * See the example below for defining CSS animations with ngRepeat.
30782  *
30783  * @element ANY
30784  * @scope
30785  * @priority 1000
30786  * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
30787  *   formats are currently supported:
30788  *
30789  *   * `variable in expression` â€“ where variable is the user defined loop variable and `expression`
30790  *     is a scope expression giving the collection to enumerate.
30791  *
30792  *     For example: `album in artist.albums`.
30793  *
30794  *   * `(key, value) in expression` â€“ where `key` and `value` can be any user defined identifiers,
30795  *     and `expression` is the scope expression giving the collection to enumerate.
30796  *
30797  *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
30798  *
30799  *   * `variable in expression track by tracking_expression` â€“ You can also provide an optional tracking expression
30800  *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
30801  *     is specified, ng-repeat associates elements by identity. It is an error to have
30802  *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
30803  *     mapped to the same DOM element, which is not possible.)
30804  *
30805  *     Note that the tracking expression must come last, after any filters, and the alias expression.
30806  *
30807  *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
30808  *     will be associated by item identity in the array.
30809  *
30810  *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
30811  *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
30812  *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
30813  *     element in the same way in the DOM.
30814  *
30815  *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
30816  *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
30817  *     property is same.
30818  *
30819  *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
30820  *     to items in conjunction with a tracking expression.
30821  *
30822  *   * `variable in expression as alias_expression` â€“ You can also provide an optional alias expression which will then store the
30823  *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
30824  *     when a filter is active on the repeater, but the filtered result set is empty.
30825  *
30826  *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
30827  *     the items have been processed through the filter.
30828  *
30829  *     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
30830  *     (and not as operator, inside an expression).
30831  *
30832  *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
30833  *
30834  * @example
30835  * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
30836  * results by name or by age. New (entering) and removed (leaving) items are animated.
30837   <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true" name="ng-repeat">
30838     <file name="index.html">
30839       <div ng-controller="repeatController">
30840         I have {{friends.length}} friends. They are:
30841         <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
30842         <ul class="example-animate-container">
30843           <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
30844             [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
30845           </li>
30846           <li class="animate-repeat" ng-if="results.length === 0">
30847             <strong>No results found...</strong>
30848           </li>
30849         </ul>
30850       </div>
30851     </file>
30852     <file name="script.js">
30853       angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
30854         $scope.friends = [
30855           {name:'John', age:25, gender:'boy'},
30856           {name:'Jessie', age:30, gender:'girl'},
30857           {name:'Johanna', age:28, gender:'girl'},
30858           {name:'Joy', age:15, gender:'girl'},
30859           {name:'Mary', age:28, gender:'girl'},
30860           {name:'Peter', age:95, gender:'boy'},
30861           {name:'Sebastian', age:50, gender:'boy'},
30862           {name:'Erika', age:27, gender:'girl'},
30863           {name:'Patrick', age:40, gender:'boy'},
30864           {name:'Samantha', age:60, gender:'girl'}
30865         ];
30866       });
30867     </file>
30868     <file name="animations.css">
30869       .example-animate-container {
30870         background:white;
30871         border:1px solid black;
30872         list-style:none;
30873         margin:0;
30874         padding:0 10px;
30875       }
30876
30877       .animate-repeat {
30878         line-height:30px;
30879         list-style:none;
30880         box-sizing:border-box;
30881       }
30882
30883       .animate-repeat.ng-move,
30884       .animate-repeat.ng-enter,
30885       .animate-repeat.ng-leave {
30886         transition:all linear 0.5s;
30887       }
30888
30889       .animate-repeat.ng-leave.ng-leave-active,
30890       .animate-repeat.ng-move,
30891       .animate-repeat.ng-enter {
30892         opacity:0;
30893         max-height:0;
30894       }
30895
30896       .animate-repeat.ng-leave,
30897       .animate-repeat.ng-move.ng-move-active,
30898       .animate-repeat.ng-enter.ng-enter-active {
30899         opacity:1;
30900         max-height:30px;
30901       }
30902     </file>
30903     <file name="protractor.js" type="protractor">
30904       var friends = element.all(by.repeater('friend in friends'));
30905
30906       it('should render initial data set', function() {
30907         expect(friends.count()).toBe(10);
30908         expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
30909         expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
30910         expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
30911         expect(element(by.binding('friends.length')).getText())
30912             .toMatch("I have 10 friends. They are:");
30913       });
30914
30915        it('should update repeater when filter predicate changes', function() {
30916          expect(friends.count()).toBe(10);
30917
30918          element(by.model('q')).sendKeys('ma');
30919
30920          expect(friends.count()).toBe(2);
30921          expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
30922          expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
30923        });
30924       </file>
30925     </example>
30926  */
30927 var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
30928   var NG_REMOVED = '$$NG_REMOVED';
30929   var ngRepeatMinErr = minErr('ngRepeat');
30930
30931   var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
30932     // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
30933     scope[valueIdentifier] = value;
30934     if (keyIdentifier) scope[keyIdentifier] = key;
30935     scope.$index = index;
30936     scope.$first = (index === 0);
30937     scope.$last = (index === (arrayLength - 1));
30938     scope.$middle = !(scope.$first || scope.$last);
30939     // eslint-disable-next-line no-bitwise
30940     scope.$odd = !(scope.$even = (index & 1) === 0);
30941   };
30942
30943   var getBlockStart = function(block) {
30944     return block.clone[0];
30945   };
30946
30947   var getBlockEnd = function(block) {
30948     return block.clone[block.clone.length - 1];
30949   };
30950
30951
30952   return {
30953     restrict: 'A',
30954     multiElement: true,
30955     transclude: 'element',
30956     priority: 1000,
30957     terminal: true,
30958     $$tlb: true,
30959     compile: function ngRepeatCompile($element, $attr) {
30960       var expression = $attr.ngRepeat;
30961       var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
30962
30963       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*$/);
30964
30965       if (!match) {
30966         throw ngRepeatMinErr('iexp', 'Expected expression in form of \'_item_ in _collection_[ track by _id_]\' but got \'{0}\'.',
30967             expression);
30968       }
30969
30970       var lhs = match[1];
30971       var rhs = match[2];
30972       var aliasAs = match[3];
30973       var trackByExp = match[4];
30974
30975       match = lhs.match(/^(?:(\s*[$\w]+)|\(\s*([$\w]+)\s*,\s*([$\w]+)\s*\))$/);
30976
30977       if (!match) {
30978         throw ngRepeatMinErr('iidexp', '\'_item_\' in \'_item_ in _collection_\' should be an identifier or \'(_key_, _value_)\' expression, but got \'{0}\'.',
30979             lhs);
30980       }
30981       var valueIdentifier = match[3] || match[1];
30982       var keyIdentifier = match[2];
30983
30984       if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
30985           /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
30986         throw ngRepeatMinErr('badident', 'alias \'{0}\' is invalid --- must be a valid JS identifier which is not a reserved name.',
30987           aliasAs);
30988       }
30989
30990       var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
30991       var hashFnLocals = {$id: hashKey};
30992
30993       if (trackByExp) {
30994         trackByExpGetter = $parse(trackByExp);
30995       } else {
30996         trackByIdArrayFn = function(key, value) {
30997           return hashKey(value);
30998         };
30999         trackByIdObjFn = function(key) {
31000           return key;
31001         };
31002       }
31003
31004       return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
31005
31006         if (trackByExpGetter) {
31007           trackByIdExpFn = function(key, value, index) {
31008             // assign key, value, and $index to the locals so that they can be used in hash functions
31009             if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
31010             hashFnLocals[valueIdentifier] = value;
31011             hashFnLocals.$index = index;
31012             return trackByExpGetter($scope, hashFnLocals);
31013           };
31014         }
31015
31016         // Store a list of elements from previous run. This is a hash where key is the item from the
31017         // iterator, and the value is objects with following properties.
31018         //   - scope: bound scope
31019         //   - element: previous element.
31020         //   - index: position
31021         //
31022         // We are using no-proto object so that we don't need to guard against inherited props via
31023         // hasOwnProperty.
31024         var lastBlockMap = createMap();
31025
31026         //watch props
31027         $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
31028           var index, length,
31029               previousNode = $element[0],     // node that cloned nodes should be inserted after
31030                                               // initialized to the comment node anchor
31031               nextNode,
31032               // Same as lastBlockMap but it has the current state. It will become the
31033               // lastBlockMap on the next iteration.
31034               nextBlockMap = createMap(),
31035               collectionLength,
31036               key, value, // key/value of iteration
31037               trackById,
31038               trackByIdFn,
31039               collectionKeys,
31040               block,       // last object information {scope, element, id}
31041               nextBlockOrder,
31042               elementsToRemove;
31043
31044           if (aliasAs) {
31045             $scope[aliasAs] = collection;
31046           }
31047
31048           if (isArrayLike(collection)) {
31049             collectionKeys = collection;
31050             trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
31051           } else {
31052             trackByIdFn = trackByIdExpFn || trackByIdObjFn;
31053             // if object, extract keys, in enumeration order, unsorted
31054             collectionKeys = [];
31055             for (var itemKey in collection) {
31056               if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
31057                 collectionKeys.push(itemKey);
31058               }
31059             }
31060           }
31061
31062           collectionLength = collectionKeys.length;
31063           nextBlockOrder = new Array(collectionLength);
31064
31065           // locate existing items
31066           for (index = 0; index < collectionLength; index++) {
31067             key = (collection === collectionKeys) ? index : collectionKeys[index];
31068             value = collection[key];
31069             trackById = trackByIdFn(key, value, index);
31070             if (lastBlockMap[trackById]) {
31071               // found previously seen block
31072               block = lastBlockMap[trackById];
31073               delete lastBlockMap[trackById];
31074               nextBlockMap[trackById] = block;
31075               nextBlockOrder[index] = block;
31076             } else if (nextBlockMap[trackById]) {
31077               // if collision detected. restore lastBlockMap and throw an error
31078               forEach(nextBlockOrder, function(block) {
31079                 if (block && block.scope) lastBlockMap[block.id] = block;
31080               });
31081               throw ngRepeatMinErr('dupes',
31082                   'Duplicates in a repeater are not allowed. Use \'track by\' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}',
31083                   expression, trackById, value);
31084             } else {
31085               // new never before seen block
31086               nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
31087               nextBlockMap[trackById] = true;
31088             }
31089           }
31090
31091           // remove leftover items
31092           for (var blockKey in lastBlockMap) {
31093             block = lastBlockMap[blockKey];
31094             elementsToRemove = getBlockNodes(block.clone);
31095             $animate.leave(elementsToRemove);
31096             if (elementsToRemove[0].parentNode) {
31097               // if the element was not removed yet because of pending animation, mark it as deleted
31098               // so that we can ignore it later
31099               for (index = 0, length = elementsToRemove.length; index < length; index++) {
31100                 elementsToRemove[index][NG_REMOVED] = true;
31101               }
31102             }
31103             block.scope.$destroy();
31104           }
31105
31106           // we are not using forEach for perf reasons (trying to avoid #call)
31107           for (index = 0; index < collectionLength; index++) {
31108             key = (collection === collectionKeys) ? index : collectionKeys[index];
31109             value = collection[key];
31110             block = nextBlockOrder[index];
31111
31112             if (block.scope) {
31113               // if we have already seen this object, then we need to reuse the
31114               // associated scope/element
31115
31116               nextNode = previousNode;
31117
31118               // skip nodes that are already pending removal via leave animation
31119               do {
31120                 nextNode = nextNode.nextSibling;
31121               } while (nextNode && nextNode[NG_REMOVED]);
31122
31123               if (getBlockStart(block) !== nextNode) {
31124                 // existing item which got moved
31125                 $animate.move(getBlockNodes(block.clone), null, previousNode);
31126               }
31127               previousNode = getBlockEnd(block);
31128               updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
31129             } else {
31130               // new item which we don't know about
31131               $transclude(function ngRepeatTransclude(clone, scope) {
31132                 block.scope = scope;
31133                 // http://jsperf.com/clone-vs-createcomment
31134                 var endNode = ngRepeatEndComment.cloneNode(false);
31135                 clone[clone.length++] = endNode;
31136
31137                 $animate.enter(clone, null, previousNode);
31138                 previousNode = endNode;
31139                 // Note: We only need the first/last node of the cloned nodes.
31140                 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
31141                 // by a directive with templateUrl when its template arrives.
31142                 block.clone = clone;
31143                 nextBlockMap[block.id] = block;
31144                 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
31145               });
31146             }
31147           }
31148           lastBlockMap = nextBlockMap;
31149         });
31150       };
31151     }
31152   };
31153 }];
31154
31155 var NG_HIDE_CLASS = 'ng-hide';
31156 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
31157 /**
31158  * @ngdoc directive
31159  * @name ngShow
31160  * @multiElement
31161  *
31162  * @description
31163  * The `ngShow` directive shows or hides the given HTML element based on the expression provided to
31164  * the `ngShow` attribute.
31165  *
31166  * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
31167  * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
31168  * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
31169  * {@link ng.directive:ngCsp ngCsp}).
31170  *
31171  * ```html
31172  * <!-- when $scope.myValue is truthy (element is visible) -->
31173  * <div ng-show="myValue"></div>
31174  *
31175  * <!-- when $scope.myValue is falsy (element is hidden) -->
31176  * <div ng-show="myValue" class="ng-hide"></div>
31177  * ```
31178  *
31179  * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added
31180  * to the class attribute on the element causing it to become hidden. When truthy, the `.ng-hide`
31181  * CSS class is removed from the element causing the element not to appear hidden.
31182  *
31183  * ## Why is `!important` used?
31184  *
31185  * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
31186  * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
31187  * simple as changing the display style on a HTML list item would make hidden elements appear
31188  * visible. This also becomes a bigger issue when dealing with CSS frameworks.
31189  *
31190  * By using `!important`, the show and hide behavior will work as expected despite any clash between
31191  * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
31192  * developer chooses to override the styling to change how to hide an element then it is just a
31193  * matter of using `!important` in their own CSS code.
31194  *
31195  * ### Overriding `.ng-hide`
31196  *
31197  * By default, the `.ng-hide` class will style the element with `display: none !important`. If you
31198  * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
31199  * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
31200  * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
31201  *
31202  * ```css
31203  * .ng-hide:not(.ng-hide-animate) {
31204  *   /&#42; These are just alternative ways of hiding an element &#42;/
31205  *   display: block!important;
31206  *   position: absolute;
31207  *   top: -9999px;
31208  *   left: -9999px;
31209  * }
31210  * ```
31211  *
31212  * By default you don't need to override anything in CSS and the animations will work around the
31213  * display style.
31214  *
31215  * ## A note about animations with `ngShow`
31216  *
31217  * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
31218  * directive expression is true and false. This system works like the animation system present with
31219  * `ngClass` except that you must also include the `!important` flag to override the display
31220  * property so that the elements are not actually hidden during the animation.
31221  *
31222  * ```css
31223  * /&#42; A working example can be found at the bottom of this page. &#42;/
31224  * .my-element.ng-hide-add, .my-element.ng-hide-remove {
31225  *   transition: all 0.5s linear;
31226  * }
31227  *
31228  * .my-element.ng-hide-add { ... }
31229  * .my-element.ng-hide-add.ng-hide-add-active { ... }
31230  * .my-element.ng-hide-remove { ... }
31231  * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
31232  * ```
31233  *
31234  * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
31235  * to block during animation states - ngAnimate will automatically handle the style toggling for you.
31236  *
31237  * @animations
31238  * | Animation                                           | Occurs                                                                                                        |
31239  * |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
31240  * | {@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. |
31241  * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngShow` expression evaluates to a truthy value and just before contents are set to visible.        |
31242  *
31243  * @element ANY
31244  * @param {expression} ngShow If the {@link guide/expression expression} is truthy/falsy then the
31245  *                            element is shown/hidden respectively.
31246  *
31247  * @example
31248  * A simple example, animating the element's opacity:
31249  *
31250   <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-simple">
31251     <file name="index.html">
31252       Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
31253       <div class="check-element animate-show-hide" ng-show="checked">
31254         I show up when your checkbox is checked.
31255       </div>
31256     </file>
31257     <file name="animations.css">
31258       .animate-show-hide.ng-hide {
31259         opacity: 0;
31260       }
31261
31262       .animate-show-hide.ng-hide-add,
31263       .animate-show-hide.ng-hide-remove {
31264         transition: all linear 0.5s;
31265       }
31266
31267       .check-element {
31268         border: 1px solid black;
31269         opacity: 1;
31270         padding: 10px;
31271       }
31272     </file>
31273     <file name="protractor.js" type="protractor">
31274       it('should check ngShow', function() {
31275         var checkbox = element(by.model('checked'));
31276         var checkElem = element(by.css('.check-element'));
31277
31278         expect(checkElem.isDisplayed()).toBe(false);
31279         checkbox.click();
31280         expect(checkElem.isDisplayed()).toBe(true);
31281       });
31282     </file>
31283   </example>
31284  *
31285  * <hr />
31286  * @example
31287  * A more complex example, featuring different show/hide animations:
31288  *
31289   <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-complex">
31290     <file name="index.html">
31291       Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
31292       <div class="check-element funky-show-hide" ng-show="checked">
31293         I show up when your checkbox is checked.
31294       </div>
31295     </file>
31296     <file name="animations.css">
31297       body {
31298         overflow: hidden;
31299         perspective: 1000px;
31300       }
31301
31302       .funky-show-hide.ng-hide-add {
31303         transform: rotateZ(0);
31304         transform-origin: right;
31305         transition: all 0.5s ease-in-out;
31306       }
31307
31308       .funky-show-hide.ng-hide-add.ng-hide-add-active {
31309         transform: rotateZ(-135deg);
31310       }
31311
31312       .funky-show-hide.ng-hide-remove {
31313         transform: rotateY(90deg);
31314         transform-origin: left;
31315         transition: all 0.5s ease;
31316       }
31317
31318       .funky-show-hide.ng-hide-remove.ng-hide-remove-active {
31319         transform: rotateY(0);
31320       }
31321
31322       .check-element {
31323         border: 1px solid black;
31324         opacity: 1;
31325         padding: 10px;
31326       }
31327     </file>
31328     <file name="protractor.js" type="protractor">
31329       it('should check ngShow', function() {
31330         var checkbox = element(by.model('checked'));
31331         var checkElem = element(by.css('.check-element'));
31332
31333         expect(checkElem.isDisplayed()).toBe(false);
31334         checkbox.click();
31335         expect(checkElem.isDisplayed()).toBe(true);
31336       });
31337     </file>
31338   </example>
31339  */
31340 var ngShowDirective = ['$animate', function($animate) {
31341   return {
31342     restrict: 'A',
31343     multiElement: true,
31344     link: function(scope, element, attr) {
31345       scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
31346         // we're adding a temporary, animation-specific class for ng-hide since this way
31347         // we can control when the element is actually displayed on screen without having
31348         // to have a global/greedy CSS selector that breaks when other animations are run.
31349         // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
31350         $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
31351           tempClasses: NG_HIDE_IN_PROGRESS_CLASS
31352         });
31353       });
31354     }
31355   };
31356 }];
31357
31358
31359 /**
31360  * @ngdoc directive
31361  * @name ngHide
31362  * @multiElement
31363  *
31364  * @description
31365  * The `ngHide` directive shows or hides the given HTML element based on the expression provided to
31366  * the `ngHide` attribute.
31367  *
31368  * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
31369  * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
31370  * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
31371  * {@link ng.directive:ngCsp ngCsp}).
31372  *
31373  * ```html
31374  * <!-- when $scope.myValue is truthy (element is hidden) -->
31375  * <div ng-hide="myValue" class="ng-hide"></div>
31376  *
31377  * <!-- when $scope.myValue is falsy (element is visible) -->
31378  * <div ng-hide="myValue"></div>
31379  * ```
31380  *
31381  * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added
31382  * to the class attribute on the element causing it to become hidden. When falsy, the `.ng-hide`
31383  * CSS class is removed from the element causing the element not to appear hidden.
31384  *
31385  * ## Why is `!important` used?
31386  *
31387  * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
31388  * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
31389  * simple as changing the display style on a HTML list item would make hidden elements appear
31390  * visible. This also becomes a bigger issue when dealing with CSS frameworks.
31391  *
31392  * By using `!important`, the show and hide behavior will work as expected despite any clash between
31393  * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
31394  * developer chooses to override the styling to change how to hide an element then it is just a
31395  * matter of using `!important` in their own CSS code.
31396  *
31397  * ### Overriding `.ng-hide`
31398  *
31399  * By default, the `.ng-hide` class will style the element with `display: none !important`. If you
31400  * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
31401  * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
31402  * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
31403  *
31404  * ```css
31405  * .ng-hide:not(.ng-hide-animate) {
31406  *   /&#42; These are just alternative ways of hiding an element &#42;/
31407  *   display: block!important;
31408  *   position: absolute;
31409  *   top: -9999px;
31410  *   left: -9999px;
31411  * }
31412  * ```
31413  *
31414  * By default you don't need to override in CSS anything and the animations will work around the
31415  * display style.
31416  *
31417  * ## A note about animations with `ngHide`
31418  *
31419  * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
31420  * directive expression is true and false. This system works like the animation system present with
31421  * `ngClass` except that you must also include the `!important` flag to override the display
31422  * property so that the elements are not actually hidden during the animation.
31423  *
31424  * ```css
31425  * /&#42; A working example can be found at the bottom of this page. &#42;/
31426  * .my-element.ng-hide-add, .my-element.ng-hide-remove {
31427  *   transition: all 0.5s linear;
31428  * }
31429  *
31430  * .my-element.ng-hide-add { ... }
31431  * .my-element.ng-hide-add.ng-hide-add-active { ... }
31432  * .my-element.ng-hide-remove { ... }
31433  * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
31434  * ```
31435  *
31436  * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
31437  * to block during animation states - ngAnimate will automatically handle the style toggling for you.
31438  *
31439  * @animations
31440  * | Animation                                           | Occurs                                                                                                     |
31441  * |-----------------------------------------------------|------------------------------------------------------------------------------------------------------------|
31442  * | {@link $animate#addClass addClass} `.ng-hide`       | After the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden.  |
31443  * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible. |
31444  *
31445  *
31446  * @element ANY
31447  * @param {expression} ngHide If the {@link guide/expression expression} is truthy/falsy then the
31448  *                            element is hidden/shown respectively.
31449  *
31450  * @example
31451  * A simple example, animating the element's opacity:
31452  *
31453   <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-simple">
31454     <file name="index.html">
31455       Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
31456       <div class="check-element animate-show-hide" ng-hide="checked">
31457         I hide when your checkbox is checked.
31458       </div>
31459     </file>
31460     <file name="animations.css">
31461       .animate-show-hide.ng-hide {
31462         opacity: 0;
31463       }
31464
31465       .animate-show-hide.ng-hide-add,
31466       .animate-show-hide.ng-hide-remove {
31467         transition: all linear 0.5s;
31468       }
31469
31470       .check-element {
31471         border: 1px solid black;
31472         opacity: 1;
31473         padding: 10px;
31474       }
31475     </file>
31476     <file name="protractor.js" type="protractor">
31477       it('should check ngHide', function() {
31478         var checkbox = element(by.model('checked'));
31479         var checkElem = element(by.css('.check-element'));
31480
31481         expect(checkElem.isDisplayed()).toBe(true);
31482         checkbox.click();
31483         expect(checkElem.isDisplayed()).toBe(false);
31484       });
31485     </file>
31486   </example>
31487  *
31488  * <hr />
31489  * @example
31490  * A more complex example, featuring different show/hide animations:
31491  *
31492   <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-complex">
31493     <file name="index.html">
31494       Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
31495       <div class="check-element funky-show-hide" ng-hide="checked">
31496         I hide when your checkbox is checked.
31497       </div>
31498     </file>
31499     <file name="animations.css">
31500       body {
31501         overflow: hidden;
31502         perspective: 1000px;
31503       }
31504
31505       .funky-show-hide.ng-hide-add {
31506         transform: rotateZ(0);
31507         transform-origin: right;
31508         transition: all 0.5s ease-in-out;
31509       }
31510
31511       .funky-show-hide.ng-hide-add.ng-hide-add-active {
31512         transform: rotateZ(-135deg);
31513       }
31514
31515       .funky-show-hide.ng-hide-remove {
31516         transform: rotateY(90deg);
31517         transform-origin: left;
31518         transition: all 0.5s ease;
31519       }
31520
31521       .funky-show-hide.ng-hide-remove.ng-hide-remove-active {
31522         transform: rotateY(0);
31523       }
31524
31525       .check-element {
31526         border: 1px solid black;
31527         opacity: 1;
31528         padding: 10px;
31529       }
31530     </file>
31531     <file name="protractor.js" type="protractor">
31532       it('should check ngHide', function() {
31533         var checkbox = element(by.model('checked'));
31534         var checkElem = element(by.css('.check-element'));
31535
31536         expect(checkElem.isDisplayed()).toBe(true);
31537         checkbox.click();
31538         expect(checkElem.isDisplayed()).toBe(false);
31539       });
31540     </file>
31541   </example>
31542  */
31543 var ngHideDirective = ['$animate', function($animate) {
31544   return {
31545     restrict: 'A',
31546     multiElement: true,
31547     link: function(scope, element, attr) {
31548       scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
31549         // The comment inside of the ngShowDirective explains why we add and
31550         // remove a temporary class for the show/hide animation
31551         $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
31552           tempClasses: NG_HIDE_IN_PROGRESS_CLASS
31553         });
31554       });
31555     }
31556   };
31557 }];
31558
31559 /**
31560  * @ngdoc directive
31561  * @name ngStyle
31562  * @restrict AC
31563  *
31564  * @description
31565  * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
31566  *
31567  * @knownIssue
31568  * You should not use {@link guide/interpolation interpolation} in the value of the `style`
31569  * attribute, when using the `ngStyle` directive on the same element.
31570  * See {@link guide/interpolation#known-issues here} for more info.
31571  *
31572  * @element ANY
31573  * @param {expression} ngStyle
31574  *
31575  * {@link guide/expression Expression} which evals to an
31576  * object whose keys are CSS style names and values are corresponding values for those CSS
31577  * keys.
31578  *
31579  * Since some CSS style names are not valid keys for an object, they must be quoted.
31580  * See the 'background-color' style in the example below.
31581  *
31582  * @example
31583    <example name="ng-style">
31584      <file name="index.html">
31585         <input type="button" value="set color" ng-click="myStyle={color:'red'}">
31586         <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
31587         <input type="button" value="clear" ng-click="myStyle={}">
31588         <br/>
31589         <span ng-style="myStyle">Sample Text</span>
31590         <pre>myStyle={{myStyle}}</pre>
31591      </file>
31592      <file name="style.css">
31593        span {
31594          color: black;
31595        }
31596      </file>
31597      <file name="protractor.js" type="protractor">
31598        var colorSpan = element(by.css('span'));
31599
31600        it('should check ng-style', function() {
31601          expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
31602          element(by.css('input[value=\'set color\']')).click();
31603          expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
31604          element(by.css('input[value=clear]')).click();
31605          expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
31606        });
31607      </file>
31608    </example>
31609  */
31610 var ngStyleDirective = ngDirective(function(scope, element, attr) {
31611   scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
31612     if (oldStyles && (newStyles !== oldStyles)) {
31613       forEach(oldStyles, function(val, style) { element.css(style, '');});
31614     }
31615     if (newStyles) element.css(newStyles);
31616   }, true);
31617 });
31618
31619 /**
31620  * @ngdoc directive
31621  * @name ngSwitch
31622  * @restrict EA
31623  *
31624  * @description
31625  * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
31626  * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
31627  * as specified in the template.
31628  *
31629  * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
31630  * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
31631  * matches the value obtained from the evaluated expression. In other words, you define a container element
31632  * (where you place the directive), place an expression on the **`on="..."` attribute**
31633  * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
31634  * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
31635  * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
31636  * attribute is displayed.
31637  *
31638  * <div class="alert alert-info">
31639  * Be aware that the attribute values to match against cannot be expressions. They are interpreted
31640  * as literal string values to match against.
31641  * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
31642  * value of the expression `$scope.someVal`.
31643  * </div>
31644
31645  * @animations
31646  * | Animation                        | Occurs                              |
31647  * |----------------------------------|-------------------------------------|
31648  * | {@link ng.$animate#enter enter}  | after the ngSwitch contents change and the matched child element is placed inside the container |
31649  * | {@link ng.$animate#leave leave}  | after the ngSwitch contents change and just before the former contents are removed from the DOM |
31650  *
31651  * @usage
31652  *
31653  * ```
31654  * <ANY ng-switch="expression">
31655  *   <ANY ng-switch-when="matchValue1">...</ANY>
31656  *   <ANY ng-switch-when="matchValue2">...</ANY>
31657  *   <ANY ng-switch-default>...</ANY>
31658  * </ANY>
31659  * ```
31660  *
31661  *
31662  * @scope
31663  * @priority 1200
31664  * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
31665  * On child elements add:
31666  *
31667  * * `ngSwitchWhen`: the case statement to match against. If match then this
31668  *   case will be displayed. If the same match appears multiple times, all the
31669  *   elements will be displayed. It is possible to associate multiple values to
31670  *   the same `ngSwitchWhen` by defining the optional attribute
31671  *   `ngSwitchWhenSeparator`. The separator will be used to split the value of
31672  *   the `ngSwitchWhen` attribute into multiple tokens, and the element will show
31673  *   if any of the `ngSwitch` evaluates to any of these tokens.
31674  * * `ngSwitchDefault`: the default case when no other case match. If there
31675  *   are multiple default cases, all of them will be displayed when no other
31676  *   case match.
31677  *
31678  *
31679  * @example
31680   <example module="switchExample" deps="angular-animate.js" animations="true" name="ng-switch">
31681     <file name="index.html">
31682       <div ng-controller="ExampleController">
31683         <select ng-model="selection" ng-options="item for item in items">
31684         </select>
31685         <code>selection={{selection}}</code>
31686         <hr/>
31687         <div class="animate-switch-container"
31688           ng-switch on="selection">
31689             <div class="animate-switch" ng-switch-when="settings|options" ng-switch-when-separator="|">Settings Div</div>
31690             <div class="animate-switch" ng-switch-when="home">Home Span</div>
31691             <div class="animate-switch" ng-switch-default>default</div>
31692         </div>
31693       </div>
31694     </file>
31695     <file name="script.js">
31696       angular.module('switchExample', ['ngAnimate'])
31697         .controller('ExampleController', ['$scope', function($scope) {
31698           $scope.items = ['settings', 'home', 'options', 'other'];
31699           $scope.selection = $scope.items[0];
31700         }]);
31701     </file>
31702     <file name="animations.css">
31703       .animate-switch-container {
31704         position:relative;
31705         background:white;
31706         border:1px solid black;
31707         height:40px;
31708         overflow:hidden;
31709       }
31710
31711       .animate-switch {
31712         padding:10px;
31713       }
31714
31715       .animate-switch.ng-animate {
31716         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
31717
31718         position:absolute;
31719         top:0;
31720         left:0;
31721         right:0;
31722         bottom:0;
31723       }
31724
31725       .animate-switch.ng-leave.ng-leave-active,
31726       .animate-switch.ng-enter {
31727         top:-50px;
31728       }
31729       .animate-switch.ng-leave,
31730       .animate-switch.ng-enter.ng-enter-active {
31731         top:0;
31732       }
31733     </file>
31734     <file name="protractor.js" type="protractor">
31735       var switchElem = element(by.css('[ng-switch]'));
31736       var select = element(by.model('selection'));
31737
31738       it('should start in settings', function() {
31739         expect(switchElem.getText()).toMatch(/Settings Div/);
31740       });
31741       it('should change to home', function() {
31742         select.all(by.css('option')).get(1).click();
31743         expect(switchElem.getText()).toMatch(/Home Span/);
31744       });
31745       it('should change to settings via "options"', function() {
31746         select.all(by.css('option')).get(2).click();
31747         expect(switchElem.getText()).toMatch(/Settings Div/);
31748       });
31749       it('should select default', function() {
31750         select.all(by.css('option')).get(3).click();
31751         expect(switchElem.getText()).toMatch(/default/);
31752       });
31753     </file>
31754   </example>
31755  */
31756 var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
31757   return {
31758     require: 'ngSwitch',
31759
31760     // asks for $scope to fool the BC controller module
31761     controller: ['$scope', function NgSwitchController() {
31762      this.cases = {};
31763     }],
31764     link: function(scope, element, attr, ngSwitchController) {
31765       var watchExpr = attr.ngSwitch || attr.on,
31766           selectedTranscludes = [],
31767           selectedElements = [],
31768           previousLeaveAnimations = [],
31769           selectedScopes = [];
31770
31771       var spliceFactory = function(array, index) {
31772           return function(response) {
31773             if (response !== false) array.splice(index, 1);
31774           };
31775       };
31776
31777       scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
31778         var i, ii;
31779
31780         // Start with the last, in case the array is modified during the loop
31781         while (previousLeaveAnimations.length) {
31782           $animate.cancel(previousLeaveAnimations.pop());
31783         }
31784
31785         for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
31786           var selected = getBlockNodes(selectedElements[i].clone);
31787           selectedScopes[i].$destroy();
31788           var runner = previousLeaveAnimations[i] = $animate.leave(selected);
31789           runner.done(spliceFactory(previousLeaveAnimations, i));
31790         }
31791
31792         selectedElements.length = 0;
31793         selectedScopes.length = 0;
31794
31795         if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
31796           forEach(selectedTranscludes, function(selectedTransclude) {
31797             selectedTransclude.transclude(function(caseElement, selectedScope) {
31798               selectedScopes.push(selectedScope);
31799               var anchor = selectedTransclude.element;
31800               caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
31801               var block = { clone: caseElement };
31802
31803               selectedElements.push(block);
31804               $animate.enter(caseElement, anchor.parent(), anchor);
31805             });
31806           });
31807         }
31808       });
31809     }
31810   };
31811 }];
31812
31813 var ngSwitchWhenDirective = ngDirective({
31814   transclude: 'element',
31815   priority: 1200,
31816   require: '^ngSwitch',
31817   multiElement: true,
31818   link: function(scope, element, attrs, ctrl, $transclude) {
31819
31820     var cases = attrs.ngSwitchWhen.split(attrs.ngSwitchWhenSeparator).sort().filter(
31821       // Filter duplicate cases
31822       function(element, index, array) { return array[index - 1] !== element; }
31823     );
31824
31825     forEach(cases, function(whenCase) {
31826       ctrl.cases['!' + whenCase] = (ctrl.cases['!' + whenCase] || []);
31827       ctrl.cases['!' + whenCase].push({ transclude: $transclude, element: element });
31828     });
31829   }
31830 });
31831
31832 var ngSwitchDefaultDirective = ngDirective({
31833   transclude: 'element',
31834   priority: 1200,
31835   require: '^ngSwitch',
31836   multiElement: true,
31837   link: function(scope, element, attr, ctrl, $transclude) {
31838     ctrl.cases['?'] = (ctrl.cases['?'] || []);
31839     ctrl.cases['?'].push({ transclude: $transclude, element: element });
31840    }
31841 });
31842
31843 /**
31844  * @ngdoc directive
31845  * @name ngTransclude
31846  * @restrict EAC
31847  *
31848  * @description
31849  * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
31850  *
31851  * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
31852  * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
31853  *
31854  * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
31855  * content of this element will be removed before the transcluded content is inserted.
31856  * If the transcluded content is empty (or only whitespace), the existing content is left intact. This lets you provide fallback
31857  * content in the case that no transcluded content is provided.
31858  *
31859  * @element ANY
31860  *
31861  * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
31862  *                                               or its value is the same as the name of the attribute then the default slot is used.
31863  *
31864  * @example
31865  * ### Basic transclusion
31866  * This example demonstrates basic transclusion of content into a component directive.
31867  * <example name="simpleTranscludeExample" module="transcludeExample">
31868  *   <file name="index.html">
31869  *     <script>
31870  *       angular.module('transcludeExample', [])
31871  *        .directive('pane', function(){
31872  *           return {
31873  *             restrict: 'E',
31874  *             transclude: true,
31875  *             scope: { title:'@' },
31876  *             template: '<div style="border: 1px solid black;">' +
31877  *                         '<div style="background-color: gray">{{title}}</div>' +
31878  *                         '<ng-transclude></ng-transclude>' +
31879  *                       '</div>'
31880  *           };
31881  *       })
31882  *       .controller('ExampleController', ['$scope', function($scope) {
31883  *         $scope.title = 'Lorem Ipsum';
31884  *         $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
31885  *       }]);
31886  *     </script>
31887  *     <div ng-controller="ExampleController">
31888  *       <input ng-model="title" aria-label="title"> <br/>
31889  *       <textarea ng-model="text" aria-label="text"></textarea> <br/>
31890  *       <pane title="{{title}}"><span>{{text}}</span></pane>
31891  *     </div>
31892  *   </file>
31893  *   <file name="protractor.js" type="protractor">
31894  *      it('should have transcluded', function() {
31895  *        var titleElement = element(by.model('title'));
31896  *        titleElement.clear();
31897  *        titleElement.sendKeys('TITLE');
31898  *        var textElement = element(by.model('text'));
31899  *        textElement.clear();
31900  *        textElement.sendKeys('TEXT');
31901  *        expect(element(by.binding('title')).getText()).toEqual('TITLE');
31902  *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
31903  *      });
31904  *   </file>
31905  * </example>
31906  *
31907  * @example
31908  * ### Transclude fallback content
31909  * This example shows how to use `NgTransclude` with fallback content, that
31910  * is displayed if no transcluded content is provided.
31911  *
31912  * <example module="transcludeFallbackContentExample" name="ng-transclude">
31913  * <file name="index.html">
31914  * <script>
31915  * angular.module('transcludeFallbackContentExample', [])
31916  * .directive('myButton', function(){
31917  *             return {
31918  *               restrict: 'E',
31919  *               transclude: true,
31920  *               scope: true,
31921  *               template: '<button style="cursor: pointer;">' +
31922  *                           '<ng-transclude>' +
31923  *                             '<b style="color: red;">Button1</b>' +
31924  *                           '</ng-transclude>' +
31925  *                         '</button>'
31926  *             };
31927  *         });
31928  * </script>
31929  * <!-- fallback button content -->
31930  * <my-button id="fallback"></my-button>
31931  * <!-- modified button content -->
31932  * <my-button id="modified">
31933  *   <i style="color: green;">Button2</i>
31934  * </my-button>
31935  * </file>
31936  * <file name="protractor.js" type="protractor">
31937  * it('should have different transclude element content', function() {
31938  *          expect(element(by.id('fallback')).getText()).toBe('Button1');
31939  *          expect(element(by.id('modified')).getText()).toBe('Button2');
31940  *        });
31941  * </file>
31942  * </example>
31943  *
31944  * @example
31945  * ### Multi-slot transclusion
31946  * This example demonstrates using multi-slot transclusion in a component directive.
31947  * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
31948  *   <file name="index.html">
31949  *    <style>
31950  *      .title, .footer {
31951  *        background-color: gray
31952  *      }
31953  *    </style>
31954  *    <div ng-controller="ExampleController">
31955  *      <input ng-model="title" aria-label="title"> <br/>
31956  *      <textarea ng-model="text" aria-label="text"></textarea> <br/>
31957  *      <pane>
31958  *        <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
31959  *        <pane-body><p>{{text}}</p></pane-body>
31960  *      </pane>
31961  *    </div>
31962  *   </file>
31963  *   <file name="app.js">
31964  *    angular.module('multiSlotTranscludeExample', [])
31965  *     .directive('pane', function() {
31966  *        return {
31967  *          restrict: 'E',
31968  *          transclude: {
31969  *            'title': '?paneTitle',
31970  *            'body': 'paneBody',
31971  *            'footer': '?paneFooter'
31972  *          },
31973  *          template: '<div style="border: 1px solid black;">' +
31974  *                      '<div class="title" ng-transclude="title">Fallback Title</div>' +
31975  *                      '<div ng-transclude="body"></div>' +
31976  *                      '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
31977  *                    '</div>'
31978  *        };
31979  *    })
31980  *    .controller('ExampleController', ['$scope', function($scope) {
31981  *      $scope.title = 'Lorem Ipsum';
31982  *      $scope.link = 'https://google.com';
31983  *      $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
31984  *    }]);
31985  *   </file>
31986  *   <file name="protractor.js" type="protractor">
31987  *      it('should have transcluded the title and the body', function() {
31988  *        var titleElement = element(by.model('title'));
31989  *        titleElement.clear();
31990  *        titleElement.sendKeys('TITLE');
31991  *        var textElement = element(by.model('text'));
31992  *        textElement.clear();
31993  *        textElement.sendKeys('TEXT');
31994  *        expect(element(by.css('.title')).getText()).toEqual('TITLE');
31995  *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
31996  *        expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
31997  *      });
31998  *   </file>
31999  * </example>
32000  */
32001 var ngTranscludeMinErr = minErr('ngTransclude');
32002 var ngTranscludeDirective = ['$compile', function($compile) {
32003   return {
32004     restrict: 'EAC',
32005     terminal: true,
32006     compile: function ngTranscludeCompile(tElement) {
32007
32008       // Remove and cache any original content to act as a fallback
32009       var fallbackLinkFn = $compile(tElement.contents());
32010       tElement.empty();
32011
32012       return function ngTranscludePostLink($scope, $element, $attrs, controller, $transclude) {
32013
32014         if (!$transclude) {
32015           throw ngTranscludeMinErr('orphan',
32016           'Illegal use of ngTransclude directive in the template! ' +
32017           'No parent directive that requires a transclusion found. ' +
32018           'Element: {0}',
32019           startingTag($element));
32020         }
32021
32022
32023         // If the attribute is of the form: `ng-transclude="ng-transclude"` then treat it like the default
32024         if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
32025           $attrs.ngTransclude = '';
32026         }
32027         var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
32028
32029         // If the slot is required and no transclusion content is provided then this call will throw an error
32030         $transclude(ngTranscludeCloneAttachFn, null, slotName);
32031
32032         // If the slot is optional and no transclusion content is provided then use the fallback content
32033         if (slotName && !$transclude.isSlotFilled(slotName)) {
32034           useFallbackContent();
32035         }
32036
32037         function ngTranscludeCloneAttachFn(clone, transcludedScope) {
32038           if (clone.length && notWhitespace(clone)) {
32039             $element.append(clone);
32040           } else {
32041             useFallbackContent();
32042             // There is nothing linked against the transcluded scope since no content was available,
32043             // so it should be safe to clean up the generated scope.
32044             transcludedScope.$destroy();
32045           }
32046         }
32047
32048         function useFallbackContent() {
32049           // Since this is the fallback content rather than the transcluded content,
32050           // we link against the scope of this directive rather than the transcluded scope
32051           fallbackLinkFn($scope, function(clone) {
32052             $element.append(clone);
32053           });
32054         }
32055
32056         function notWhitespace(nodes) {
32057           for (var i = 0, ii = nodes.length; i < ii; i++) {
32058             var node = nodes[i];
32059             if (node.nodeType !== NODE_TYPE_TEXT || node.nodeValue.trim()) {
32060               return true;
32061             }
32062           }
32063         }
32064       };
32065     }
32066   };
32067 }];
32068
32069 /**
32070  * @ngdoc directive
32071  * @name script
32072  * @restrict E
32073  *
32074  * @description
32075  * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
32076  * template can be used by {@link ng.directive:ngInclude `ngInclude`},
32077  * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
32078  * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
32079  * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
32080  *
32081  * @param {string} type Must be set to `'text/ng-template'`.
32082  * @param {string} id Cache name of the template.
32083  *
32084  * @example
32085   <example  name="script-tag">
32086     <file name="index.html">
32087       <script type="text/ng-template" id="/tpl.html">
32088         Content of the template.
32089       </script>
32090
32091       <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
32092       <div id="tpl-content" ng-include src="currentTpl"></div>
32093     </file>
32094     <file name="protractor.js" type="protractor">
32095       it('should load template defined inside script tag', function() {
32096         element(by.css('#tpl-link')).click();
32097         expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
32098       });
32099     </file>
32100   </example>
32101  */
32102 var scriptDirective = ['$templateCache', function($templateCache) {
32103   return {
32104     restrict: 'E',
32105     terminal: true,
32106     compile: function(element, attr) {
32107       if (attr.type === 'text/ng-template') {
32108         var templateUrl = attr.id,
32109             text = element[0].text;
32110
32111         $templateCache.put(templateUrl, text);
32112       }
32113     }
32114   };
32115 }];
32116
32117 /* exported selectDirective, optionDirective */
32118
32119 var noopNgModelController = { $setViewValue: noop, $render: noop };
32120
32121 function setOptionSelectedStatus(optionEl, value) {
32122   optionEl.prop('selected', value); // needed for IE
32123   /**
32124    * When unselecting an option, setting the property to null / false should be enough
32125    * However, screenreaders might react to the selected attribute instead, see
32126    * https://github.com/angular/angular.js/issues/14419
32127    * Note: "selected" is a boolean attr and will be removed when the "value" arg in attr() is false
32128    * or null
32129    */
32130   optionEl.attr('selected', value);
32131 }
32132
32133 /**
32134  * @ngdoc type
32135  * @name  select.SelectController
32136  * @description
32137  * The controller for the `<select>` directive. This provides support for reading
32138  * and writing the selected value(s) of the control and also coordinates dynamically
32139  * added `<option>` elements, perhaps by an `ngRepeat` directive.
32140  */
32141 var SelectController =
32142         ['$element', '$scope', /** @this */ function($element, $scope) {
32143
32144   var self = this,
32145       optionsMap = new NgMap();
32146
32147   self.selectValueMap = {}; // Keys are the hashed values, values the original values
32148
32149   // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
32150   self.ngModelCtrl = noopNgModelController;
32151   self.multiple = false;
32152
32153   // The "unknown" option is one that is prepended to the list if the viewValue
32154   // does not match any of the options. When it is rendered the value of the unknown
32155   // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
32156   //
32157   // We can't just jqLite('<option>') since jqLite is not smart enough
32158   // to create it in <select> and IE barfs otherwise.
32159   self.unknownOption = jqLite(window.document.createElement('option'));
32160
32161   // The empty option is an option with the value '' that te application developer can
32162   // provide inside the select. When the model changes to a value that doesn't match an option,
32163   // it is selected - so if an empty option is provided, no unknown option is generated.
32164   // However, the empty option is not removed when the model matches an option. It is always selectable
32165   // and indicates that a "null" selection has been made.
32166   self.hasEmptyOption = false;
32167   self.emptyOption = undefined;
32168
32169   self.renderUnknownOption = function(val) {
32170     var unknownVal = self.generateUnknownOptionValue(val);
32171     self.unknownOption.val(unknownVal);
32172     $element.prepend(self.unknownOption);
32173     setOptionSelectedStatus(self.unknownOption, true);
32174     $element.val(unknownVal);
32175   };
32176
32177   self.updateUnknownOption = function(val) {
32178     var unknownVal = self.generateUnknownOptionValue(val);
32179     self.unknownOption.val(unknownVal);
32180     setOptionSelectedStatus(self.unknownOption, true);
32181     $element.val(unknownVal);
32182   };
32183
32184   self.generateUnknownOptionValue = function(val) {
32185     return '? ' + hashKey(val) + ' ?';
32186   };
32187
32188   self.removeUnknownOption = function() {
32189     if (self.unknownOption.parent()) self.unknownOption.remove();
32190   };
32191
32192   self.selectEmptyOption = function() {
32193     if (self.emptyOption) {
32194       $element.val('');
32195       setOptionSelectedStatus(self.emptyOption, true);
32196     }
32197   };
32198
32199   self.unselectEmptyOption = function() {
32200     if (self.hasEmptyOption) {
32201       self.emptyOption.removeAttr('selected');
32202     }
32203   };
32204
32205   $scope.$on('$destroy', function() {
32206     // disable unknown option so that we don't do work when the whole select is being destroyed
32207     self.renderUnknownOption = noop;
32208   });
32209
32210   // Read the value of the select control, the implementation of this changes depending
32211   // upon whether the select can have multiple values and whether ngOptions is at work.
32212   self.readValue = function readSingleValue() {
32213     var val = $element.val();
32214     // ngValue added option values are stored in the selectValueMap, normal interpolations are not
32215     var realVal = val in self.selectValueMap ? self.selectValueMap[val] : val;
32216
32217     if (self.hasOption(realVal)) {
32218       return realVal;
32219     }
32220
32221     return null;
32222   };
32223
32224
32225   // Write the value to the select control, the implementation of this changes depending
32226   // upon whether the select can have multiple values and whether ngOptions is at work.
32227   self.writeValue = function writeSingleValue(value) {
32228     // Make sure to remove the selected attribute from the previously selected option
32229     // Otherwise, screen readers might get confused
32230     var currentlySelectedOption = $element[0].options[$element[0].selectedIndex];
32231     if (currentlySelectedOption) setOptionSelectedStatus(jqLite(currentlySelectedOption), false);
32232
32233     if (self.hasOption(value)) {
32234       self.removeUnknownOption();
32235
32236       var hashedVal = hashKey(value);
32237       $element.val(hashedVal in self.selectValueMap ? hashedVal : value);
32238
32239       // Set selected attribute and property on selected option for screen readers
32240       var selectedOption = $element[0].options[$element[0].selectedIndex];
32241       setOptionSelectedStatus(jqLite(selectedOption), true);
32242     } else {
32243       if (value == null && self.emptyOption) {
32244         self.removeUnknownOption();
32245         self.selectEmptyOption();
32246       } else if (self.unknownOption.parent().length) {
32247         self.updateUnknownOption(value);
32248       } else {
32249         self.renderUnknownOption(value);
32250       }
32251     }
32252   };
32253
32254
32255   // Tell the select control that an option, with the given value, has been added
32256   self.addOption = function(value, element) {
32257     // Skip comment nodes, as they only pollute the `optionsMap`
32258     if (element[0].nodeType === NODE_TYPE_COMMENT) return;
32259
32260     assertNotHasOwnProperty(value, '"option value"');
32261     if (value === '') {
32262       self.hasEmptyOption = true;
32263       self.emptyOption = element;
32264     }
32265     var count = optionsMap.get(value) || 0;
32266     optionsMap.set(value, count + 1);
32267     // Only render at the end of a digest. This improves render performance when many options
32268     // are added during a digest and ensures all relevant options are correctly marked as selected
32269     scheduleRender();
32270   };
32271
32272   // Tell the select control that an option, with the given value, has been removed
32273   self.removeOption = function(value) {
32274     var count = optionsMap.get(value);
32275     if (count) {
32276       if (count === 1) {
32277         optionsMap.delete(value);
32278         if (value === '') {
32279           self.hasEmptyOption = false;
32280           self.emptyOption = undefined;
32281         }
32282       } else {
32283         optionsMap.set(value, count - 1);
32284       }
32285     }
32286   };
32287
32288   // Check whether the select control has an option matching the given value
32289   self.hasOption = function(value) {
32290     return !!optionsMap.get(value);
32291   };
32292
32293
32294   var renderScheduled = false;
32295   function scheduleRender() {
32296     if (renderScheduled) return;
32297     renderScheduled = true;
32298     $scope.$$postDigest(function() {
32299       renderScheduled = false;
32300       self.ngModelCtrl.$render();
32301     });
32302   }
32303
32304   var updateScheduled = false;
32305   function scheduleViewValueUpdate(renderAfter) {
32306     if (updateScheduled) return;
32307
32308     updateScheduled = true;
32309
32310     $scope.$$postDigest(function() {
32311       if ($scope.$$destroyed) return;
32312
32313       updateScheduled = false;
32314       self.ngModelCtrl.$setViewValue(self.readValue());
32315       if (renderAfter) self.ngModelCtrl.$render();
32316     });
32317   }
32318
32319
32320   self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
32321
32322     if (optionAttrs.$attr.ngValue) {
32323       // The value attribute is set by ngValue
32324       var oldVal, hashedVal = NaN;
32325       optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
32326
32327         var removal;
32328         var previouslySelected = optionElement.prop('selected');
32329
32330         if (isDefined(hashedVal)) {
32331           self.removeOption(oldVal);
32332           delete self.selectValueMap[hashedVal];
32333           removal = true;
32334         }
32335
32336         hashedVal = hashKey(newVal);
32337         oldVal = newVal;
32338         self.selectValueMap[hashedVal] = newVal;
32339         self.addOption(newVal, optionElement);
32340         // Set the attribute directly instead of using optionAttrs.$set - this stops the observer
32341         // from firing a second time. Other $observers on value will also get the result of the
32342         // ngValue expression, not the hashed value
32343         optionElement.attr('value', hashedVal);
32344
32345         if (removal && previouslySelected) {
32346           scheduleViewValueUpdate();
32347         }
32348
32349       });
32350     } else if (interpolateValueFn) {
32351       // The value attribute is interpolated
32352       optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
32353         // This method is overwritten in ngOptions and has side-effects!
32354         self.readValue();
32355
32356         var removal;
32357         var previouslySelected = optionElement.prop('selected');
32358
32359         if (isDefined(oldVal)) {
32360           self.removeOption(oldVal);
32361           removal = true;
32362         }
32363         oldVal = newVal;
32364         self.addOption(newVal, optionElement);
32365
32366         if (removal && previouslySelected) {
32367           scheduleViewValueUpdate();
32368         }
32369       });
32370     } else if (interpolateTextFn) {
32371       // The text content is interpolated
32372       optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
32373         optionAttrs.$set('value', newVal);
32374         var previouslySelected = optionElement.prop('selected');
32375         if (oldVal !== newVal) {
32376           self.removeOption(oldVal);
32377         }
32378         self.addOption(newVal, optionElement);
32379
32380         if (oldVal && previouslySelected) {
32381           scheduleViewValueUpdate();
32382         }
32383       });
32384     } else {
32385       // The value attribute is static
32386       self.addOption(optionAttrs.value, optionElement);
32387     }
32388
32389
32390     optionAttrs.$observe('disabled', function(newVal) {
32391
32392       // Since model updates will also select disabled options (like ngOptions),
32393       // we only have to handle options becoming disabled, not enabled
32394
32395       if (newVal === 'true' || newVal && optionElement.prop('selected')) {
32396         if (self.multiple) {
32397           scheduleViewValueUpdate(true);
32398         } else {
32399           self.ngModelCtrl.$setViewValue(null);
32400           self.ngModelCtrl.$render();
32401         }
32402       }
32403     });
32404
32405     optionElement.on('$destroy', function() {
32406       var currentValue = self.readValue();
32407       var removeValue = optionAttrs.value;
32408
32409       self.removeOption(removeValue);
32410       scheduleRender();
32411
32412       if (self.multiple && currentValue && currentValue.indexOf(removeValue) !== -1 ||
32413           currentValue === removeValue
32414       ) {
32415         // When multiple (selected) options are destroyed at the same time, we don't want
32416         // to run a model update for each of them. Instead, run a single update in the $$postDigest
32417         scheduleViewValueUpdate(true);
32418       }
32419     });
32420   };
32421 }];
32422
32423 /**
32424  * @ngdoc directive
32425  * @name select
32426  * @restrict E
32427  *
32428  * @description
32429  * HTML `select` element with angular data-binding.
32430  *
32431  * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
32432  * between the scope and the `<select>` control (including setting default values).
32433  * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
32434  * {@link ngOptions `ngOptions`} directives.
32435  *
32436  * When an item in the `<select>` menu is selected, the value of the selected option will be bound
32437  * to the model identified by the `ngModel` directive. With static or repeated options, this is
32438  * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
32439  * Value and textContent can be interpolated.
32440  *
32441  * ## Matching model and option values
32442  *
32443  * In general, the match between the model and an option is evaluated by strictly comparing the model
32444  * value against the value of the available options.
32445  *
32446  * If you are setting the option value with the option's `value` attribute, or textContent, the
32447  * value will always be a `string` which means that the model value must also be a string.
32448  * Otherwise the `select` directive cannot match them correctly.
32449  *
32450  * To bind the model to a non-string value, you can use one of the following strategies:
32451  * - the {@link ng.ngOptions `ngOptions`} directive
32452  *   ({@link ng.select#using-select-with-ngoptions-and-setting-a-default-value})
32453  * - the {@link ng.ngValue `ngValue`} directive, which allows arbitrary expressions to be
32454  *   option values ({@link ng.select#using-ngvalue-to-bind-the-model-to-an-array-of-objects Example})
32455  * - model $parsers / $formatters to convert the string value
32456  *   ({@link ng.select#binding-select-to-a-non-string-value-via-ngmodel-parsing-formatting Example})
32457  *
32458  * If the viewValue of `ngModel` does not match any of the options, then the control
32459  * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
32460  *
32461  * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
32462  * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
32463  * option. See example below for demonstration.
32464  *
32465  * ## Choosing between `ngRepeat` and `ngOptions`
32466  *
32467  * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
32468  * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits:
32469  * - more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
32470  * comprehension expression
32471  * - reduced memory consumption by not creating a new scope for each repeated instance
32472  * - increased render speed by creating the options in a documentFragment instead of individually
32473  *
32474  * Specifically, select with repeated options slows down significantly starting at 2000 options in
32475  * Chrome and Internet Explorer / Edge.
32476  *
32477  *
32478  * @param {string} ngModel Assignable angular expression to data-bind to.
32479  * @param {string=} name Property name of the form under which the control is published.
32480  * @param {string=} multiple Allows multiple options to be selected. The selected values will be
32481  *     bound to the model as an array.
32482  * @param {string=} required Sets `required` validation error key if the value is not entered.
32483  * @param {string=} ngRequired Adds required attribute and required validation constraint to
32484  * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
32485  * when you want to data-bind to the required attribute.
32486  * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
32487  *    interaction with the select element.
32488  * @param {string=} ngOptions sets the options that the select is populated with and defines what is
32489  * set on the model on selection. See {@link ngOptions `ngOptions`}.
32490  * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
32491  * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
32492  *
32493  * @example
32494  * ### Simple `select` elements with static options
32495  *
32496  * <example name="static-select" module="staticSelect">
32497  * <file name="index.html">
32498  * <div ng-controller="ExampleController">
32499  *   <form name="myForm">
32500  *     <label for="singleSelect"> Single select: </label><br>
32501  *     <select name="singleSelect" ng-model="data.singleSelect">
32502  *       <option value="option-1">Option 1</option>
32503  *       <option value="option-2">Option 2</option>
32504  *     </select><br>
32505  *
32506  *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
32507  *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
32508  *       <option value="">---Please select---</option> <!-- not selected / blank option -->
32509  *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
32510  *       <option value="option-2">Option 2</option>
32511  *     </select><br>
32512  *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
32513  *     <tt>singleSelect = {{data.singleSelect}}</tt>
32514  *
32515  *     <hr>
32516  *     <label for="multipleSelect"> Multiple select: </label><br>
32517  *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
32518  *       <option value="option-1">Option 1</option>
32519  *       <option value="option-2">Option 2</option>
32520  *       <option value="option-3">Option 3</option>
32521  *     </select><br>
32522  *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
32523  *   </form>
32524  * </div>
32525  * </file>
32526  * <file name="app.js">
32527  *  angular.module('staticSelect', [])
32528  *    .controller('ExampleController', ['$scope', function($scope) {
32529  *      $scope.data = {
32530  *       singleSelect: null,
32531  *       multipleSelect: [],
32532  *       option1: 'option-1'
32533  *      };
32534  *
32535  *      $scope.forceUnknownOption = function() {
32536  *        $scope.data.singleSelect = 'nonsense';
32537  *      };
32538  *   }]);
32539  * </file>
32540  *</example>
32541  *
32542  * ### Using `ngRepeat` to generate `select` options
32543  * <example name="select-ngrepeat" module="ngrepeatSelect">
32544  * <file name="index.html">
32545  * <div ng-controller="ExampleController">
32546  *   <form name="myForm">
32547  *     <label for="repeatSelect"> Repeat select: </label>
32548  *     <select name="repeatSelect" id="repeatSelect" ng-model="data.model">
32549  *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
32550  *     </select>
32551  *   </form>
32552  *   <hr>
32553  *   <tt>model = {{data.model}}</tt><br/>
32554  * </div>
32555  * </file>
32556  * <file name="app.js">
32557  *  angular.module('ngrepeatSelect', [])
32558  *    .controller('ExampleController', ['$scope', function($scope) {
32559  *      $scope.data = {
32560  *       model: null,
32561  *       availableOptions: [
32562  *         {id: '1', name: 'Option A'},
32563  *         {id: '2', name: 'Option B'},
32564  *         {id: '3', name: 'Option C'}
32565  *       ]
32566  *      };
32567  *   }]);
32568  * </file>
32569  *</example>
32570  *
32571  * ### Using `ngValue` to bind the model to an array of objects
32572  * <example name="select-ngvalue" module="ngvalueSelect">
32573  * <file name="index.html">
32574  * <div ng-controller="ExampleController">
32575  *   <form name="myForm">
32576  *     <label for="ngvalueselect"> ngvalue select: </label>
32577  *     <select size="6" name="ngvalueselect" ng-model="data.model" multiple>
32578  *       <option ng-repeat="option in data.availableOptions" ng-value="option.value">{{option.name}}</option>
32579  *     </select>
32580  *   </form>
32581  *   <hr>
32582  *   <pre>model = {{data.model | json}}</pre><br/>
32583  * </div>
32584  * </file>
32585  * <file name="app.js">
32586  *  angular.module('ngvalueSelect', [])
32587  *    .controller('ExampleController', ['$scope', function($scope) {
32588  *      $scope.data = {
32589  *       model: null,
32590  *       availableOptions: [
32591            {value: 'myString', name: 'string'},
32592            {value: 1, name: 'integer'},
32593            {value: true, name: 'boolean'},
32594            {value: null, name: 'null'},
32595            {value: {prop: 'value'}, name: 'object'},
32596            {value: ['a'], name: 'array'}
32597  *       ]
32598  *      };
32599  *   }]);
32600  * </file>
32601  *</example>
32602  *
32603  * ### Using `select` with `ngOptions` and setting a default value
32604  * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
32605  *
32606  * <example name="select-with-default-values" module="defaultValueSelect">
32607  * <file name="index.html">
32608  * <div ng-controller="ExampleController">
32609  *   <form name="myForm">
32610  *     <label for="mySelect">Make a choice:</label>
32611  *     <select name="mySelect" id="mySelect"
32612  *       ng-options="option.name for option in data.availableOptions track by option.id"
32613  *       ng-model="data.selectedOption"></select>
32614  *   </form>
32615  *   <hr>
32616  *   <tt>option = {{data.selectedOption}}</tt><br/>
32617  * </div>
32618  * </file>
32619  * <file name="app.js">
32620  *  angular.module('defaultValueSelect', [])
32621  *    .controller('ExampleController', ['$scope', function($scope) {
32622  *      $scope.data = {
32623  *       availableOptions: [
32624  *         {id: '1', name: 'Option A'},
32625  *         {id: '2', name: 'Option B'},
32626  *         {id: '3', name: 'Option C'}
32627  *       ],
32628  *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
32629  *       };
32630  *   }]);
32631  * </file>
32632  *</example>
32633  *
32634  *
32635  * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
32636  *
32637  * <example name="select-with-non-string-options" module="nonStringSelect">
32638  *   <file name="index.html">
32639  *     <select ng-model="model.id" convert-to-number>
32640  *       <option value="0">Zero</option>
32641  *       <option value="1">One</option>
32642  *       <option value="2">Two</option>
32643  *     </select>
32644  *     {{ model }}
32645  *   </file>
32646  *   <file name="app.js">
32647  *     angular.module('nonStringSelect', [])
32648  *       .run(function($rootScope) {
32649  *         $rootScope.model = { id: 2 };
32650  *       })
32651  *       .directive('convertToNumber', function() {
32652  *         return {
32653  *           require: 'ngModel',
32654  *           link: function(scope, element, attrs, ngModel) {
32655  *             ngModel.$parsers.push(function(val) {
32656  *               return parseInt(val, 10);
32657  *             });
32658  *             ngModel.$formatters.push(function(val) {
32659  *               return '' + val;
32660  *             });
32661  *           }
32662  *         };
32663  *       });
32664  *   </file>
32665  *   <file name="protractor.js" type="protractor">
32666  *     it('should initialize to model', function() {
32667  *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
32668  *     });
32669  *   </file>
32670  * </example>
32671  *
32672  */
32673 var selectDirective = function() {
32674
32675   return {
32676     restrict: 'E',
32677     require: ['select', '?ngModel'],
32678     controller: SelectController,
32679     priority: 1,
32680     link: {
32681       pre: selectPreLink,
32682       post: selectPostLink
32683     }
32684   };
32685
32686   function selectPreLink(scope, element, attr, ctrls) {
32687
32688       var selectCtrl = ctrls[0];
32689       var ngModelCtrl = ctrls[1];
32690
32691       // if ngModel is not defined, we don't need to do anything but set the registerOption
32692       // function to noop, so options don't get added internally
32693       if (!ngModelCtrl) {
32694         selectCtrl.registerOption = noop;
32695         return;
32696       }
32697
32698
32699       selectCtrl.ngModelCtrl = ngModelCtrl;
32700
32701       // When the selected item(s) changes we delegate getting the value of the select control
32702       // to the `readValue` method, which can be changed if the select can have multiple
32703       // selected values or if the options are being generated by `ngOptions`
32704       element.on('change', function() {
32705         selectCtrl.removeUnknownOption();
32706         scope.$apply(function() {
32707           ngModelCtrl.$setViewValue(selectCtrl.readValue());
32708         });
32709       });
32710
32711       // If the select allows multiple values then we need to modify how we read and write
32712       // values from and to the control; also what it means for the value to be empty and
32713       // we have to add an extra watch since ngModel doesn't work well with arrays - it
32714       // doesn't trigger rendering if only an item in the array changes.
32715       if (attr.multiple) {
32716         selectCtrl.multiple = true;
32717
32718         // Read value now needs to check each option to see if it is selected
32719         selectCtrl.readValue = function readMultipleValue() {
32720           var array = [];
32721           forEach(element.find('option'), function(option) {
32722             if (option.selected && !option.disabled) {
32723               var val = option.value;
32724               array.push(val in selectCtrl.selectValueMap ? selectCtrl.selectValueMap[val] : val);
32725             }
32726           });
32727           return array;
32728         };
32729
32730         // Write value now needs to set the selected property of each matching option
32731         selectCtrl.writeValue = function writeMultipleValue(value) {
32732           forEach(element.find('option'), function(option) {
32733             var shouldBeSelected = !!value && (includes(value, option.value) ||
32734                                                includes(value, selectCtrl.selectValueMap[option.value]));
32735             var currentlySelected = option.selected;
32736
32737             // IE and Edge, adding options to the selection via shift+click/UP/DOWN,
32738             // will de-select already selected options if "selected" on those options was set
32739             // more than once (i.e. when the options were already selected)
32740             // So we only modify the selected property if neccessary.
32741             // Note: this behavior cannot be replicated via unit tests because it only shows in the
32742             // actual user interface.
32743             if (shouldBeSelected !== currentlySelected) {
32744               setOptionSelectedStatus(jqLite(option), shouldBeSelected);
32745             }
32746
32747           });
32748         };
32749
32750         // we have to do it on each watch since ngModel watches reference, but
32751         // we need to work of an array, so we need to see if anything was inserted/removed
32752         var lastView, lastViewRef = NaN;
32753         scope.$watch(function selectMultipleWatch() {
32754           if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
32755             lastView = shallowCopy(ngModelCtrl.$viewValue);
32756             ngModelCtrl.$render();
32757           }
32758           lastViewRef = ngModelCtrl.$viewValue;
32759         });
32760
32761         // If we are a multiple select then value is now a collection
32762         // so the meaning of $isEmpty changes
32763         ngModelCtrl.$isEmpty = function(value) {
32764           return !value || value.length === 0;
32765         };
32766
32767       }
32768     }
32769
32770     function selectPostLink(scope, element, attrs, ctrls) {
32771       // if ngModel is not defined, we don't need to do anything
32772       var ngModelCtrl = ctrls[1];
32773       if (!ngModelCtrl) return;
32774
32775       var selectCtrl = ctrls[0];
32776
32777       // We delegate rendering to the `writeValue` method, which can be changed
32778       // if the select can have multiple selected values or if the options are being
32779       // generated by `ngOptions`.
32780       // This must be done in the postLink fn to prevent $render to be called before
32781       // all nodes have been linked correctly.
32782       ngModelCtrl.$render = function() {
32783         selectCtrl.writeValue(ngModelCtrl.$viewValue);
32784       };
32785     }
32786 };
32787
32788
32789 // The option directive is purely designed to communicate the existence (or lack of)
32790 // of dynamically created (and destroyed) option elements to their containing select
32791 // directive via its controller.
32792 var optionDirective = ['$interpolate', function($interpolate) {
32793   return {
32794     restrict: 'E',
32795     priority: 100,
32796     compile: function(element, attr) {
32797       var interpolateValueFn, interpolateTextFn;
32798
32799       if (isDefined(attr.ngValue)) {
32800         // Will be handled by registerOption
32801       } else if (isDefined(attr.value)) {
32802         // If the value attribute is defined, check if it contains an interpolation
32803         interpolateValueFn = $interpolate(attr.value, true);
32804       } else {
32805         // If the value attribute is not defined then we fall back to the
32806         // text content of the option element, which may be interpolated
32807         interpolateTextFn = $interpolate(element.text(), true);
32808         if (!interpolateTextFn) {
32809           attr.$set('value', element.text());
32810         }
32811       }
32812
32813       return function(scope, element, attr) {
32814         // This is an optimization over using ^^ since we don't want to have to search
32815         // all the way to the root of the DOM for every single option element
32816         var selectCtrlName = '$selectController',
32817             parent = element.parent(),
32818             selectCtrl = parent.data(selectCtrlName) ||
32819               parent.parent().data(selectCtrlName); // in case we are in optgroup
32820
32821         if (selectCtrl) {
32822           selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
32823         }
32824       };
32825     }
32826   };
32827 }];
32828
32829 /**
32830  * @ngdoc directive
32831  * @name ngRequired
32832  * @restrict A
32833  *
32834  * @description
32835  *
32836  * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
32837  * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
32838  * applied to custom controls.
32839  *
32840  * The directive sets the `required` attribute on the element if the Angular expression inside
32841  * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
32842  * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
32843  * for more info.
32844  *
32845  * The validator will set the `required` error key to true if the `required` attribute is set and
32846  * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
32847  * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
32848  * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
32849  * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
32850  *
32851  * @example
32852  * <example name="ngRequiredDirective" module="ngRequiredExample">
32853  *   <file name="index.html">
32854  *     <script>
32855  *       angular.module('ngRequiredExample', [])
32856  *         .controller('ExampleController', ['$scope', function($scope) {
32857  *           $scope.required = true;
32858  *         }]);
32859  *     </script>
32860  *     <div ng-controller="ExampleController">
32861  *       <form name="form">
32862  *         <label for="required">Toggle required: </label>
32863  *         <input type="checkbox" ng-model="required" id="required" />
32864  *         <br>
32865  *         <label for="input">This input must be filled if `required` is true: </label>
32866  *         <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
32867  *         <hr>
32868  *         required error set? = <code>{{form.input.$error.required}}</code><br>
32869  *         model = <code>{{model}}</code>
32870  *       </form>
32871  *     </div>
32872  *   </file>
32873  *   <file name="protractor.js" type="protractor">
32874        var required = element(by.binding('form.input.$error.required'));
32875        var model = element(by.binding('model'));
32876        var input = element(by.id('input'));
32877
32878        it('should set the required error', function() {
32879          expect(required.getText()).toContain('true');
32880
32881          input.sendKeys('123');
32882          expect(required.getText()).not.toContain('true');
32883          expect(model.getText()).toContain('123');
32884        });
32885  *   </file>
32886  * </example>
32887  */
32888 var requiredDirective = function() {
32889   return {
32890     restrict: 'A',
32891     require: '?ngModel',
32892     link: function(scope, elm, attr, ctrl) {
32893       if (!ctrl) return;
32894       attr.required = true; // force truthy in case we are on non input element
32895
32896       ctrl.$validators.required = function(modelValue, viewValue) {
32897         return !attr.required || !ctrl.$isEmpty(viewValue);
32898       };
32899
32900       attr.$observe('required', function() {
32901         ctrl.$validate();
32902       });
32903     }
32904   };
32905 };
32906
32907 /**
32908  * @ngdoc directive
32909  * @name ngPattern
32910  *
32911  * @description
32912  *
32913  * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
32914  * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
32915  *
32916  * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
32917  * does not match a RegExp which is obtained by evaluating the Angular expression given in the
32918  * `ngPattern` attribute value:
32919  * * If the expression evaluates to a RegExp object, then this is used directly.
32920  * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
32921  * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
32922  *
32923  * <div class="alert alert-info">
32924  * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
32925  * start at the index of the last search's match, thus not taking the whole input value into
32926  * account.
32927  * </div>
32928  *
32929  * <div class="alert alert-info">
32930  * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
32931  * differences:
32932  * <ol>
32933  *   <li>
32934  *     `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
32935  *     not available.
32936  *   </li>
32937  *   <li>
32938  *     The `ngPattern` attribute must be an expression, while the `pattern` value must be
32939  *     interpolated.
32940  *   </li>
32941  * </ol>
32942  * </div>
32943  *
32944  * @example
32945  * <example name="ngPatternDirective" module="ngPatternExample">
32946  *   <file name="index.html">
32947  *     <script>
32948  *       angular.module('ngPatternExample', [])
32949  *         .controller('ExampleController', ['$scope', function($scope) {
32950  *           $scope.regex = '\\d+';
32951  *         }]);
32952  *     </script>
32953  *     <div ng-controller="ExampleController">
32954  *       <form name="form">
32955  *         <label for="regex">Set a pattern (regex string): </label>
32956  *         <input type="text" ng-model="regex" id="regex" />
32957  *         <br>
32958  *         <label for="input">This input is restricted by the current pattern: </label>
32959  *         <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
32960  *         <hr>
32961  *         input valid? = <code>{{form.input.$valid}}</code><br>
32962  *         model = <code>{{model}}</code>
32963  *       </form>
32964  *     </div>
32965  *   </file>
32966  *   <file name="protractor.js" type="protractor">
32967        var model = element(by.binding('model'));
32968        var input = element(by.id('input'));
32969
32970        it('should validate the input with the default pattern', function() {
32971          input.sendKeys('aaa');
32972          expect(model.getText()).not.toContain('aaa');
32973
32974          input.clear().then(function() {
32975            input.sendKeys('123');
32976            expect(model.getText()).toContain('123');
32977          });
32978        });
32979  *   </file>
32980  * </example>
32981  */
32982 var patternDirective = function() {
32983   return {
32984     restrict: 'A',
32985     require: '?ngModel',
32986     link: function(scope, elm, attr, ctrl) {
32987       if (!ctrl) return;
32988
32989       var regexp, patternExp = attr.ngPattern || attr.pattern;
32990       attr.$observe('pattern', function(regex) {
32991         if (isString(regex) && regex.length > 0) {
32992           regex = new RegExp('^' + regex + '$');
32993         }
32994
32995         if (regex && !regex.test) {
32996           throw minErr('ngPattern')('noregexp',
32997             'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
32998             regex, startingTag(elm));
32999         }
33000
33001         regexp = regex || undefined;
33002         ctrl.$validate();
33003       });
33004
33005       ctrl.$validators.pattern = function(modelValue, viewValue) {
33006         // HTML5 pattern constraint validates the input value, so we validate the viewValue
33007         return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
33008       };
33009     }
33010   };
33011 };
33012
33013 /**
33014  * @ngdoc directive
33015  * @name ngMaxlength
33016  *
33017  * @description
33018  *
33019  * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
33020  * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
33021  *
33022  * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
33023  * is longer than the integer obtained by evaluating the Angular expression given in the
33024  * `ngMaxlength` attribute value.
33025  *
33026  * <div class="alert alert-info">
33027  * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
33028  * differences:
33029  * <ol>
33030  *   <li>
33031  *     `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
33032  *     validation is not available.
33033  *   </li>
33034  *   <li>
33035  *     The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
33036  *     interpolated.
33037  *   </li>
33038  * </ol>
33039  * </div>
33040  *
33041  * @example
33042  * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
33043  *   <file name="index.html">
33044  *     <script>
33045  *       angular.module('ngMaxlengthExample', [])
33046  *         .controller('ExampleController', ['$scope', function($scope) {
33047  *           $scope.maxlength = 5;
33048  *         }]);
33049  *     </script>
33050  *     <div ng-controller="ExampleController">
33051  *       <form name="form">
33052  *         <label for="maxlength">Set a maxlength: </label>
33053  *         <input type="number" ng-model="maxlength" id="maxlength" />
33054  *         <br>
33055  *         <label for="input">This input is restricted by the current maxlength: </label>
33056  *         <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
33057  *         <hr>
33058  *         input valid? = <code>{{form.input.$valid}}</code><br>
33059  *         model = <code>{{model}}</code>
33060  *       </form>
33061  *     </div>
33062  *   </file>
33063  *   <file name="protractor.js" type="protractor">
33064        var model = element(by.binding('model'));
33065        var input = element(by.id('input'));
33066
33067        it('should validate the input with the default maxlength', function() {
33068          input.sendKeys('abcdef');
33069          expect(model.getText()).not.toContain('abcdef');
33070
33071          input.clear().then(function() {
33072            input.sendKeys('abcde');
33073            expect(model.getText()).toContain('abcde');
33074          });
33075        });
33076  *   </file>
33077  * </example>
33078  */
33079 var maxlengthDirective = function() {
33080   return {
33081     restrict: 'A',
33082     require: '?ngModel',
33083     link: function(scope, elm, attr, ctrl) {
33084       if (!ctrl) return;
33085
33086       var maxlength = -1;
33087       attr.$observe('maxlength', function(value) {
33088         var intVal = toInt(value);
33089         maxlength = isNumberNaN(intVal) ? -1 : intVal;
33090         ctrl.$validate();
33091       });
33092       ctrl.$validators.maxlength = function(modelValue, viewValue) {
33093         return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
33094       };
33095     }
33096   };
33097 };
33098
33099 /**
33100  * @ngdoc directive
33101  * @name ngMinlength
33102  *
33103  * @description
33104  *
33105  * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
33106  * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
33107  *
33108  * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
33109  * is shorter than the integer obtained by evaluating the Angular expression given in the
33110  * `ngMinlength` attribute value.
33111  *
33112  * <div class="alert alert-info">
33113  * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
33114  * differences:
33115  * <ol>
33116  *   <li>
33117  *     `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
33118  *     validation is not available.
33119  *   </li>
33120  *   <li>
33121  *     The `ngMinlength` value must be an expression, while the `minlength` value must be
33122  *     interpolated.
33123  *   </li>
33124  * </ol>
33125  * </div>
33126  *
33127  * @example
33128  * <example name="ngMinlengthDirective" module="ngMinlengthExample">
33129  *   <file name="index.html">
33130  *     <script>
33131  *       angular.module('ngMinlengthExample', [])
33132  *         .controller('ExampleController', ['$scope', function($scope) {
33133  *           $scope.minlength = 3;
33134  *         }]);
33135  *     </script>
33136  *     <div ng-controller="ExampleController">
33137  *       <form name="form">
33138  *         <label for="minlength">Set a minlength: </label>
33139  *         <input type="number" ng-model="minlength" id="minlength" />
33140  *         <br>
33141  *         <label for="input">This input is restricted by the current minlength: </label>
33142  *         <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
33143  *         <hr>
33144  *         input valid? = <code>{{form.input.$valid}}</code><br>
33145  *         model = <code>{{model}}</code>
33146  *       </form>
33147  *     </div>
33148  *   </file>
33149  *   <file name="protractor.js" type="protractor">
33150        var model = element(by.binding('model'));
33151        var input = element(by.id('input'));
33152
33153        it('should validate the input with the default minlength', function() {
33154          input.sendKeys('ab');
33155          expect(model.getText()).not.toContain('ab');
33156
33157          input.sendKeys('abc');
33158          expect(model.getText()).toContain('abc');
33159        });
33160  *   </file>
33161  * </example>
33162  */
33163 var minlengthDirective = function() {
33164   return {
33165     restrict: 'A',
33166     require: '?ngModel',
33167     link: function(scope, elm, attr, ctrl) {
33168       if (!ctrl) return;
33169
33170       var minlength = 0;
33171       attr.$observe('minlength', function(value) {
33172         minlength = toInt(value) || 0;
33173         ctrl.$validate();
33174       });
33175       ctrl.$validators.minlength = function(modelValue, viewValue) {
33176         return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
33177       };
33178     }
33179   };
33180 };
33181
33182 if (window.angular.bootstrap) {
33183   // AngularJS is already loaded, so we can return here...
33184   if (window.console) {
33185     console.log('WARNING: Tried to load angular more than once.');
33186   }
33187   return;
33188 }
33189
33190 // try to bind to jquery now so that one can write jqLite(fn)
33191 // but we will rebind on bootstrap again.
33192 bindJQuery();
33193
33194 publishExternalAPI(angular);
33195
33196 angular.module("ngLocale", [], ["$provide", function($provide) {
33197 var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
33198 function getDecimals(n) {
33199   n = n + '';
33200   var i = n.indexOf('.');
33201   return (i == -1) ? 0 : n.length - i - 1;
33202 }
33203
33204 function getVF(n, opt_precision) {
33205   var v = opt_precision;
33206
33207   if (undefined === v) {
33208     v = Math.min(getDecimals(n), 3);
33209   }
33210
33211   var base = Math.pow(10, v);
33212   var f = ((n * base) | 0) % base;
33213   return {v: v, f: f};
33214 }
33215
33216 $provide.value("$locale", {
33217   "DATETIME_FORMATS": {
33218     "AMPMS": [
33219       "AM",
33220       "PM"
33221     ],
33222     "DAY": [
33223       "Sunday",
33224       "Monday",
33225       "Tuesday",
33226       "Wednesday",
33227       "Thursday",
33228       "Friday",
33229       "Saturday"
33230     ],
33231     "ERANAMES": [
33232       "Before Christ",
33233       "Anno Domini"
33234     ],
33235     "ERAS": [
33236       "BC",
33237       "AD"
33238     ],
33239     "FIRSTDAYOFWEEK": 6,
33240     "MONTH": [
33241       "January",
33242       "February",
33243       "March",
33244       "April",
33245       "May",
33246       "June",
33247       "July",
33248       "August",
33249       "September",
33250       "October",
33251       "November",
33252       "December"
33253     ],
33254     "SHORTDAY": [
33255       "Sun",
33256       "Mon",
33257       "Tue",
33258       "Wed",
33259       "Thu",
33260       "Fri",
33261       "Sat"
33262     ],
33263     "SHORTMONTH": [
33264       "Jan",
33265       "Feb",
33266       "Mar",
33267       "Apr",
33268       "May",
33269       "Jun",
33270       "Jul",
33271       "Aug",
33272       "Sep",
33273       "Oct",
33274       "Nov",
33275       "Dec"
33276     ],
33277     "STANDALONEMONTH": [
33278       "January",
33279       "February",
33280       "March",
33281       "April",
33282       "May",
33283       "June",
33284       "July",
33285       "August",
33286       "September",
33287       "October",
33288       "November",
33289       "December"
33290     ],
33291     "WEEKENDRANGE": [
33292       5,
33293       6
33294     ],
33295     "fullDate": "EEEE, MMMM d, y",
33296     "longDate": "MMMM d, y",
33297     "medium": "MMM d, y h:mm:ss a",
33298     "mediumDate": "MMM d, y",
33299     "mediumTime": "h:mm:ss a",
33300     "short": "M/d/yy h:mm a",
33301     "shortDate": "M/d/yy",
33302     "shortTime": "h:mm a"
33303   },
33304   "NUMBER_FORMATS": {
33305     "CURRENCY_SYM": "$",
33306     "DECIMAL_SEP": ".",
33307     "GROUP_SEP": ",",
33308     "PATTERNS": [
33309       {
33310         "gSize": 3,
33311         "lgSize": 3,
33312         "maxFrac": 3,
33313         "minFrac": 0,
33314         "minInt": 1,
33315         "negPre": "-",
33316         "negSuf": "",
33317         "posPre": "",
33318         "posSuf": ""
33319       },
33320       {
33321         "gSize": 3,
33322         "lgSize": 3,
33323         "maxFrac": 2,
33324         "minFrac": 2,
33325         "minInt": 1,
33326         "negPre": "-\u00a4",
33327         "negSuf": "",
33328         "posPre": "\u00a4",
33329         "posSuf": ""
33330       }
33331     ]
33332   },
33333   "id": "en-us",
33334   "localeID": "en_US",
33335   "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;}
33336 });
33337 }]);
33338
33339   jqLite(function() {
33340     angularInit(window.document, bootstrap);
33341   });
33342
33343 })(window);
33344
33345 !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>');