PageSourceSearch

https://fxboss.firstcitizens.com/app/assets/angularjs/angular.js?v=9.0.2

js firstcitizens.com collected 2026-09-24 21:50:38 UTC 1,377,909 bytes, 36,600 lines download raw bytes

vendor: 1,377,909 bytes, lines 1-36600
1/**
2 * @license AngularJS v1.8.3
3 * (c) 2010-2020 Google LLC. http://angularjs.org
4 * License: MIT
5 */
6(function(window) {'use strict';
7
8/* exported
9  minErrConfig,
10  errorHandlingConfig,
11  isValidObjectMaxDepth
12*/
13
14var minErrConfig = {
15  objectMaxDepth: 5,
16  urlErrorParamsEnabled: true
17};
18
19/**
20 * @ngdoc function
21 * @name angular.errorHandlingConfig
22 * @module ng
23 * @kind function
24 *
25 * @description
26 * Configure several aspects of error handling in AngularJS if used as a setter or return the
27 * current configuration if used as a getter. The following options are supported:
28 *
29 * - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages.
30 *
31 * Omitted or undefined options will leave the corresponding configuration values unchanged.
32 *
33 * @param {Object=} config - The configuration object. May only contain the options that need to be
34 *     updated. Supported keys:
35 *
36 * * `objectMaxDepth`  **{Number}** - The max depth for stringifying objects. Setting to a
37 *   non-positive or non-numeric value, removes the max depth limit.
38 *   Default: 5
39 *
40 * * `urlErrorParamsEnabled`  **{Boolean}** - Specifies whether the generated error url will
41 *   contain the parameters of the thrown error. Disabling the parameters can be useful if the
42 *   generated error url is very long.
43 *
44 *   Default: true. When used without argument, it returns the current value.
45 */
46function errorHandlingConfig(config) {
47  if (isObject(config)) {
48    if (isDefined(config.objectMaxDepth)) {
49      minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
50    }
51    if (isDefined(config.urlErrorParamsEnabled) && isBoolean(config.urlErrorParamsEnabled)) {
52      minErrConfig.urlErrorParamsEnabled = config.urlErrorParamsEnabled;
53    }
54  } else {
55    return minErrConfig;
56  }
57}
58
59/**
60 * @private
61 * @param {Number} maxDepth
62 * @return {boolean}
63 */
64function isValidObjectMaxDepth(maxDepth) {
65  return isNumber(maxDepth) && maxDepth > 0;
66}
67
68
69/**
70 * @description
71 *
72 * This object provides a utility for producing rich Error messages within
73 * AngularJS. It can be called as follows:
74 *
75 * var exampleMinErr = minErr('example');
76 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
77 *
78 * The above creates an instance of minErr in the example namespace. The
79 * resulting error will have a namespaced error code of example.one.  The
80 * resulting error will replace {0} with the value of foo, and {1} with the
81 * value of bar. The object is not restricted in the number of arguments it can
82 * take.
83 *
84 * If fewer arguments are specified than necessary for interpolation, the extra
85 * interpolation markers will be preserved in the final string.
86 *
87 * Since data will be parsed statically during a build step, some restrictions
88 * are applied with respect to how minErr instances are created and called.
89 * Instances should have names of the form namespaceMinErr for a minErr created
90 * using minErr('namespace'). Error codes, namespaces and template strings
91 * should all be static strings, not variables or general expressions.
92 *
93 * @param {string} module The namespace to use for the new minErr instance.
94 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
95 *   error from returned function, for cases when a particular type of error is useful.
96 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
97 */
98
99function minErr(module, ErrorConstructor) {
100  ErrorConstructor = ErrorConstructor || Error;
101
102  var url = 'https://errors.angularjs.org/1.8.3/';
103  var regex = url.replace('.', '\\.') + '[\\s\\S]*';
104  var errRegExp = new RegExp(regex, 'g');
105
106  return function() {
107    var code = arguments[0],
108      template = arguments[1],
109      message = '[' + (module ? module + ':' : '') + code + '] ',
110      templateArgs = sliceArgs(arguments, 2).map(function(arg) {
111        return toDebugString(arg, minErrConfig.objectMaxDepth);
112      }),
113      paramPrefix, i;
114
115    // A minErr message has two parts: the message itself and the url that contains the
116    // encoded message.
117    // The message's parameters can contain other error messages which also include error urls.
118    // To prevent the messages from getting too long, we strip the error urls from the parameters.
119
120    message += template.replace(/\{\d+\}/g, function(match) {
121      var index = +match.slice(1, -1);
122
123      if (index < templateArgs.length) {
124        return templateArgs[index].replace(errRegExp, '');
125      }
126
127      return match;
128    });
129
130    message += '\n' + url + (module ? module + '/' : '') + code;
131
132    if (minErrConfig.urlErrorParamsEnabled) {
133      for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
134        message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
135      }
136    }
137
138    return new ErrorConstructor(message);
139  };
140}
141
142/* We need to tell ESLint what variables are being exported */
143/* exported
144  angular,
145  msie,
146  jqLite,
147  jQuery,
148  slice,
149  splice,
150  push,
151  toString,
152  minErrConfig,
153  errorHandlingConfig,
154  isValidObjectMaxDepth,
155  ngMinErr,
156  angularModule,
157  uid,
158  REGEX_STRING_REGEXP,
159  VALIDITY_STATE_PROPERTY,
160
161  lowercase,
162  uppercase,
163  nodeName_,
164  isArrayLike,
165  forEach,
166  forEachSorted,
167  reverseParams,
168  nextUid,
169  setHashKey,
170  extend,
171  toInt,
172  inherit,
173  merge,
174  noop,
175  identity,
176  valueFn,
177  isUndefined,
178  isDefined,
179  isObject,
180  isBlankObject,
181  isString,
182  isNumber,
183  isNumberNaN,
184  isDate,
185  isError,
186  isArray,
187  isFunction,
188  isRegExp,
189  isWindow,
190  isScope,
191  isFile,
192  isFormData,
193  isBlob,
194  isBoolean,
195  isPromiseLike,
196  trim,
197  escapeForRegexp,
198  isElement,
199  makeMap,
200  includes,
201  arrayRemove,
202  copy,
203  simpleCompare,
204  equals,
205  csp,
206  jq,
207  concat,
208  sliceArgs,
209  bind,
210  toJsonReplacer,
211  toJson,
212  fromJson,
213  convertTimezoneToLocal,
214  timezoneToOffset,
215  addDateMinutes,
216  startingTag,
217  tryDecodeURIComponent,
218  parseKeyValue,
219  toKeyValue,
220  encodeUriSegment,
221  encodeUriQuery,
222  angularInit,
223  bootstrap,
224  getTestability,
225  snake_case,
226  bindJQuery,
227  assertArg,
228  assertArgFn,
229  assertNotHasOwnProperty,
230  getter,
231  getBlockNodes,
232  hasOwnProperty,
233  createMap,
234  stringify,
235  UNSAFE_restoreLegacyJqLiteXHTMLReplacement,
236
237  NODE_TYPE_ELEMENT,
238  NODE_TYPE_ATTRIBUTE,
239  NODE_TYPE_TEXT,
240  NODE_TYPE_COMMENT,
241  NODE_TYPE_DOCUMENT,
242  NODE_TYPE_DOCUMENT_FRAGMENT
243*/
244
245////////////////////////////////////
246
247/**
248 * @ngdoc module
249 * @name ng
250 * @module ng
251 * @installation
252 * @description
253 *
254 * The ng module is loaded by default when an AngularJS application is started. The module itself
255 * contains the essential components for an AngularJS application to function. The table below
256 * lists a high level breakdown of each of the services/factories, filters, directives and testing
257 * components available within this core module.
258 *
259 */
260
261var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
262
263// The name of a form control's ValidityState property.
264// This is used so that it's possible for internal tests to create mock ValidityStates.
265var VALIDITY_STATE_PROPERTY = 'validity';
266
267
268var hasOwnProperty = Object.prototype.hasOwnProperty;
269
270/**
271 * @private
272 *
273 * @description Converts the specified string to lowercase.
274 * @param {string} string String to be converted to lowercase.
275 * @returns {string} Lowercased string.
276 */
277var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
278
279/**
280 * @private
281 *
282 * @description Converts the specified string to uppercase.
283 * @param {string} string String to be converted to uppercase.
284 * @returns {string} Uppercased string.
285 */
286var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
287
288
289var
290    msie,             // holds major version number for IE, or NaN if UA is not IE.
291    jqLite,           // delay binding since jQuery could be loaded after us.
292    jQuery,           // delay binding
293    slice             = [].slice,
294    splice            = [].splice,
295    push              = [].push,
296    toString          = Object.prototype.toString,
297    getPrototypeOf    = Object.getPrototypeOf,
298    ngMinErr          = minErr('ng'),
299
300    /** @name angular */
301    angular           = window.angular || (window.angular = {}),
302    angularModule,
303    uid               = 0;
304
305// Support: IE 9-11 only
306/**
307 * documentMode is an IE-only property
308 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
309 */
310msie = window.document.documentMode;
311
312
313/**
314 * @private
315 * @param {*} obj
316 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
317 *                   String ...)
318 */
319function isArrayLike(obj) {
320
321  // `null`, `undefined` and `window` are not array-like
322  if (obj == null || isWindow(obj)) return false;
323
324  // arrays, strings and jQuery/jqLite objects are array like
325  // * jqLite is either the jQuery or jqLite constructor function
326  // * we have to check the existence of jqLite first as this method is called
327  //   via the forEach method when constructing the jqLite object in the first place
328  if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
329
330  // Support: iOS 8.2 (not reproducible in simulator)
331  // "length" in obj used to prevent JIT error (gh-11508)
332  var length = 'length' in Object(obj) && obj.length;
333
334  // NodeList objects (with `item` method) and
335  // other objects with suitable length characteristics are array-like
336  return isNumber(length) && (length >= 0 && (length - 1) in obj || typeof obj.item === 'function');
337
338}
339
340/**
341 * @ngdoc function
342 * @name angular.forEach
343 * @module ng
344 * @kind function
345 *
346 * @description
347 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
348 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
349 * is the value of an object property or an array element, `key` is the object property key or
350 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
351 *
352 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
353 * using the `hasOwnProperty` method.
354 *
355 * Unlike ES262's
356 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
357 * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
358 * return the value provided.
359 *
360   ```js
361     var values = {name: 'misko', gender: 'male'};
362     var log = [];
363     angular.forEach(values, function(value, key) {
364       this.push(key + ': ' + value);
365     }, log);
366     expect(log).toEqual(['name: misko', 'gender: male']);
367   ```
368 *
369 * @param {Object|Array} obj Object to iterate over.
370 * @param {Function} iterator Iterator function.
371 * @param {Object=} context Object to become context (`this`) for the iterator function.
372 * @returns {Object|Array} Reference to `obj`.
373 */
374
375function forEach(obj, iterator, context) {
376  var key, length;
377  if (obj) {
378    if (isFunction(obj)) {
379      for (key in obj) {
380        if (key !== 'prototype' && key !== 'length' && key !== 'name' && obj.hasOwnProperty(key)) {
381          iterator.call(context, obj[key], key, obj);
382        }
383      }
384    } else if (isArray(obj) || isArrayLike(obj)) {
385      var isPrimitive = typeof obj !== 'object';
386      for (key = 0, length = obj.length; key < length; key++) {
387        if (isPrimitive || key in obj) {
388          iterator.call(context, obj[key], key, obj);
389        }
390      }
391    } else if (obj.forEach && obj.forEach !== forEach) {
392        obj.forEach(iterator, context, obj);
393    } else if (isBlankObject(obj)) {
394      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
395      for (key in obj) {
396        iterator.call(context, obj[key], key, obj);
397      }
398    } else if (typeof obj.hasOwnProperty === 'function') {
399      // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
400      for (key in obj) {
401        if (obj.hasOwnProperty(key)) {
402          iterator.call(context, obj[key], key, obj);
403        }
404      }
405    } else {
406      // Slow path for objects which do not have a method `hasOwnProperty`
407      for (key in obj) {
408        if (hasOwnProperty.call(obj, key)) {
409          iterator.call(context, obj[key], key, obj);
410        }
411      }
412    }
413  }
414  return obj;
415}
416
417function forEachSorted(obj, iterator, context) {
418  var keys = Object.keys(obj).sort();
419  for (var i = 0; i < keys.length; i++) {
420    iterator.call(context, obj[keys[i]], keys[i]);
421  }
422  return keys;
423}
424
425
426/**
427 * when using forEach the params are value, key, but it is often useful to have key, value.
428 * @param {function(string, *)} iteratorFn
429 * @returns {function(*, string)}
430 */
431function reverseParams(iteratorFn) {
432  return function(value, key) {iteratorFn(key, value);};
433}
434
435/**
436 * A consistent way of creating unique IDs in angular.
437 *
438 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
439 * we hit number precision issues in JavaScript.
440 *
441 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
442 *
443 * @returns {number} an unique alpha-numeric string
444 */
445function nextUid() {
446  return ++uid;
447}
448
449
450/**
451 * Set or clear the hashkey for an object.
452 * @param obj object
453 * @param h the hashkey (!truthy to delete the hashkey)
454 */
455function setHashKey(obj, h) {
456  if (h) {
457    obj.$$hashKey = h;
458  } else {
459    delete obj.$$hashKey;
460  }
461}
462
463
464function baseExtend(dst, objs, deep) {
465  var h = dst.$$hashKey;
466
467  for (var i = 0, ii = objs.length; i < ii; ++i) {
468    var obj = objs[i];
469    if (!isObject(obj) && !isFunction(obj)) continue;
470    var keys = Object.keys(obj);
471    for (var j = 0, jj = keys.length; j < jj; j++) {
472      var key = keys[j];
473      var src = obj[key];
474
475      if (deep && isObject(src)) {
476        if (isDate(src)) {
477          dst[key] = new Date(src.valueOf());
478        } else if (isRegExp(src)) {
479          dst[key] = new RegExp(src);
480        } else if (src.nodeName) {
481          dst[key] = src.cloneNode(true);
482        } else if (isElement(src)) {
483          dst[key] = src.clone();
484        } else {
485          if (key !== '__proto__') {
486            if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
487            baseExtend(dst[key], [src], true);
488          }
489        }
490      } else {
491        dst[key] = src;
492      }
493    }
494  }
495
496  setHashKey(dst, h);
497  return dst;
498}
499
500/**
501 * @ngdoc function
502 * @name angular.extend
503 * @module ng
504 * @kind function
505 *
506 * @description
507 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
508 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
509 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
510 *
511 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
512 * {@link angular.merge} for this.
513 *
514 * @param {Object} dst Destination object.
515 * @param {...Object} src Source object(s).
516 * @returns {Object} Reference to `dst`.
517 */
518function extend(dst) {
519  return baseExtend(dst, slice.call(arguments, 1), false);
520}
521
522
523/**
524* @ngdoc function
525* @name angular.merge
526* @module ng
527* @kind function
528*
529* @description
530* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
531* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
532* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
533*
534* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
535* objects, performing a deep copy.
536*
537* @deprecated
538* sinceVersion="1.6.5"
539* This function is deprecated, but will not be removed in the 1.x lifecycle.
540* There are edge cases (see {@link angular.merge#known-issues known issues}) that are not
541* supported by this function. We suggest using another, similar library for all-purpose merging,
542* such as [lodash's merge()](https://lodash.com/docs/4.17.4#merge).
543*
544* @knownIssue
545* This is a list of (known) object types that are not handled correctly by this function:
546* - [`Blob`](https://developer.mozilla.org/docs/Web/API/Blob)
547* - [`MediaStream`](https://developer.mozilla.org/docs/Web/API/MediaStream)
548* - [`CanvasGradient`](https://developer.mozilla.org/docs/Web/API/CanvasGradient)
549* - AngularJS {@link $rootScope.Scope scopes};
550*
551* `angular.merge` also does not support merging objects with circular references.
552*
553* @param {Object} dst Destination object.
554* @param {...Object} src Source object(s).
555* @returns {Object} Reference to `dst`.
556*/
557function merge(dst) {
558  return baseExtend(dst, slice.call(arguments, 1), true);
559}
560
561
562
563function toInt(str) {
564  return parseInt(str, 10);
565}
566
567var isNumberNaN = Number.isNaN || function isNumberNaN(num) {
568  // eslint-disable-next-line no-self-compare
569  return num !== num;
570};
571
572
573function inherit(parent, extra) {
574  return extend(Object.create(parent), extra);
575}
576
577/**
578 * @ngdoc function
579 * @name angular.noop
580 * @module ng
581 * @kind function
582 *
583 * @description
584 * A function that performs no operations. This function can be useful when writing code in the
585 * functional style.
586   ```js
587     function foo(callback) {
588       var result = calculateResult();
589       (callback || angular.noop)(result);
590     }
591   ```
592 */
593function noop() {}
594noop.$inject = [];
595
596
597/**
598 * @ngdoc function
599 * @name angular.identity
600 * @module ng
601 * @kind function
602 *
603 * @description
604 * A function that returns its first argument. This function is useful when writing code in the
605 * functional style.
606 *
607   ```js
608   function transformer(transformationFn, value) {
609     return (transformationFn || angular.identity)(value);
610   };
611
612   // E.g.
613   function getResult(fn, input) {
614     return (fn || angular.identity)(input);
615   };
616
617   getResult(function(n) { return n * 2; }, 21);   // returns 42
618   getResult(null, 21);                            // returns 21
619   getResult(undefined, 21);                       // returns 21
620   ```
621 *
622 * @param {*} value to be returned.
623 * @returns {*} the value passed in.
624 */
625function identity($) {return $;}
626identity.$inject = [];
627
628
629function valueFn(value) {return function valueRef() {return value;};}
630
631function hasCustomToString(obj) {
632  return isFunction(obj.toString) && obj.toString !== toString;
633}
634
635
636/**
637 * @ngdoc function
638 * @name angular.isUndefined
639 * @module ng
640 * @kind function
641 *
642 * @description
643 * Determines if a reference is undefined.
644 *
645 * @param {*} value Reference to check.
646 * @returns {boolean} True if `value` is undefined.
647 */
648function isUndefined(value) {return typeof value === 'undefined';}
649
650
651/**
652 * @ngdoc function
653 * @name angular.isDefined
654 * @module ng
655 * @kind function
656 *
657 * @description
658 * Determines if a reference is defined.
659 *
660 * @param {*} value Reference to check.
661 * @returns {boolean} True if `value` is defined.
662 */
663function isDefined(value) {return typeof value !== 'undefined';}
664
665
666/**
667 * @ngdoc function
668 * @name angular.isObject
669 * @module ng
670 * @kind function
671 *
672 * @description
673 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
674 * considered to be objects. Note that JavaScript arrays are objects.
675 *
676 * @param {*} value Reference to check.
677 * @returns {boolean} True if `value` is an `Object` but not `null`.
678 */
679function isObject(value) {
680  // http://jsperf.com/isobject4
681  return value !== null && typeof value === 'object';
682}
683
684
685/**
686 * Determine if a value is an object with a null prototype
687 *
688 * @returns {boolean} True if `value` is an `Object` with a null prototype
689 */
690function isBlankObject(value) {
691  return value !== null && typeof value === 'object' && !getPrototypeOf(value);
692}
693
694
695/**
696 * @ngdoc function
697 * @name angular.isString
698 * @module ng
699 * @kind function
700 *
701 * @description
702 * Determines if a reference is a `String`.
703 *
704 * @param {*} value Reference to check.
705 * @returns {boolean} True if `value` is a `String`.
706 */
707function isString(value) {return typeof value === 'string';}
708
709
710/**
711 * @ngdoc function
712 * @name angular.isNumber
713 * @module ng
714 * @kind function
715 *
716 * @description
717 * Determines if a reference is a `Number`.
718 *
719 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
720 *
721 * If you wish to exclude these then you can use the native
722 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
723 * method.
724 *
725 * @param {*} value Reference to check.
726 * @returns {boolean} True if `value` is a `Number`.
727 */
728function isNumber(value) {return typeof value === 'number';}
729
730
731/**
732 * @ngdoc function
733 * @name angular.isDate
734 * @module ng
735 * @kind function
736 *
737 * @description
738 * Determines if a value is a date.
739 *
740 * @param {*} value Reference to check.
741 * @returns {boolean} True if `value` is a `Date`.
742 */
743function isDate(value) {
744  return toString.call(value) === '[object Date]';
745}
746
747
748/**
749 * @ngdoc function
750 * @name angular.isArray
751 * @module ng
752 * @kind function
753 *
754 * @description
755 * Determines if a reference is an `Array`.
756 *
757 * @param {*} value Reference to check.
758 * @returns {boolean} True if `value` is an `Array`.
759 */
760function isArray(arr) {
761  return Array.isArray(arr) || arr instanceof Array;
762}
763
764/**
765 * @description
766 * Determines if a reference is an `Error`.
767 * Loosely based on https://www.npmjs.com/package/iserror
768 *
769 * @param {*} value Reference to check.
770 * @returns {boolean} True if `value` is an `Error`.
771 */
772function isError(value) {
773  var tag = toString.call(value);
774  switch (tag) {
775    case '[object Error]': return true;
776    case '[object Exception]': return true;
777    case '[object DOMException]': return true;
778    default: return value instanceof Error;
779  }
780}
781
782/**
783 * @ngdoc function
784 * @name angular.isFunction
785 * @module ng
786 * @kind function
787 *
788 * @description
789 * Determines if a reference is a `Function`.
790 *
791 * @param {*} value Reference to check.
792 * @returns {boolean} True if `value` is a `Function`.
793 */
794function isFunction(value) {return typeof value === 'function';}
795
796
797/**
798 * Determines if a value is a regular expression object.
799 *
800 * @private
801 * @param {*} value Reference to check.
802 * @returns {boolean} True if `value` is a `RegExp`.
803 */
804function isRegExp(value) {
805  return toString.call(value) === '[object RegExp]';
806}
807
808
809/**
810 * Checks if `obj` is a window object.
811 *
812 * @private
813 * @param {*} obj Object to check
814 * @returns {boolean} True if `obj` is a window obj.
815 */
816function isWindow(obj) {
817  return obj && obj.window === obj;
818}
819
820
821function isScope(obj) {
822  return obj && obj.$evalAsync && obj.$watch;
823}
824
825
826function isFile(obj) {
827  return toString.call(obj) === '[object File]';
828}
829
830
831function isFormData(obj) {
832  return toString.call(obj) === '[object FormData]';
833}
834
835
836function isBlob(obj) {
837  return toString.call(obj) === '[object Blob]';
838}
839
840
841function isBoolean(value) {
842  return typeof value === 'boolean';
843}
844
845
846function isPromiseLike(obj) {
847  return obj && isFunction(obj.then);
848}
849
850
851var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array]$/;
852function isTypedArray(value) {
853  return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
854}
855
856function isArrayBuffer(obj) {
857  return toString.call(obj) === '[object ArrayBuffer]';
858}
859
860
861var trim = function(value) {
862  return isString(value) ? value.trim() : value;
863};
864
865// Copied from:
866// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
867// Prereq: s is a string.
868var escapeForRegexp = function(s) {
869  return s
870    .replace(/([-()[\]{}+?*.$^|,:#<!\\])/g, '\\$1')
871    // eslint-disable-next-line no-control-regex
872    .replace(/\x08/g, '\\x08');
873};
874
875
876/**
877 * @ngdoc function
878 * @name angular.isElement
879 * @module ng
880 * @kind function
881 *
882 * @description
883 * Determines if a reference is a DOM element (or wrapped jQuery element).
884 *
885 * @param {*} value Reference to check.
886 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
887 */
888function isElement(node) {
889  return !!(node &&
890    (node.nodeName  // We are a direct element.
891    || (node.prop && node.attr && node.find)));  // We have an on and find method part of jQuery API.
892}
893
894/**
895 * @param str 'key1,key2,...'
896 * @returns {object} in the form of {key1:true, key2:true, ...}
897 */
898function makeMap(str) {
899  var obj = {}, items = str.split(','), i;
900  for (i = 0; i < items.length; i++) {
901    obj[items[i]] = true;
902  }
903  return obj;
904}
905
906
907function nodeName_(element) {
908  return lowercase(element.nodeName || (element[0] && element[0].nodeName));
909}
910
911function includes(array, obj) {
912  return Array.prototype.indexOf.call(array, obj) !== -1;
913}
914
915function arrayRemove(array, value) {
916  var index = array.indexOf(value);
917  if (index >= 0) {
918    array.splice(index, 1);
919  }
920  return index;
921}
922
923/**
924 * @ngdoc function
925 * @name angular.copy
926 * @module ng
927 * @kind function
928 *
929 * @description
930 * Creates a deep copy of `source`, which should be an object or an array. This functions is used
931 * internally, mostly in the change-detection code. It is not intended as an all-purpose copy
932 * function, and has several limitations (see below).
933 *
934 * * If no destination is supplied, a copy of the object or array is created.
935 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
936 *   are deleted and then all elements/properties from the source are copied to it.
937 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
938 * * If `source` is identical to `destination` an exception will be thrown.
939 *
940 * <br />
941 *
942 * <div class="alert alert-warning">
943 *   Only enumerable properties are taken into account. Non-enumerable properties (both on `source`
944 *   and on `destination`) will be ignored.
945 * </div>
946 *
947 * <div class="alert alert-warning">
948 *   `angular.copy` does not check if destination and source are of the same type. It's the
949 *   developer's responsibility to make sure they are compatible.
950 * </div>
951 *
952 * @knownIssue
953 * This is a non-exhaustive list of object types / features that are not handled correctly by
954 * `angular.copy`. Note that since this functions is used by the change detection code, this
955 * means binding or watching objects of these types (or that include these types) might not work
956 * correctly.
957 * - [`File`](https://developer.mozilla.org/docs/Web/API/File)
958 * - [`Map`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map)
959 * - [`ImageData`](https://developer.mozilla.org/docs/Web/API/ImageData)
960 * - [`MediaStream`](https://developer.mozilla.org/docs/Web/API/MediaStream)
961 * - [`Set`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set)
962 * - [`WeakMap`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WeakMap)
963 * - [`getter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)/
964 *   [`setter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set)
965 *
966 * @param {*} source The source that will be used to make a copy. Can be any type, including
967 *     primitives, `null`, and `undefined`.
968 * @param {(Object|Array)=} destination Destination into which the source is copied. If provided,
969 *     must be of the same type as `source`.
970 * @returns {*} The copy or updated `destination`, if `destination` was specified.
971 *
972 * @example
973  <example module="copyExample" name="angular-copy">
974    <file name="index.html">
975      <div ng-controller="ExampleController">
976        <form novalidate class="simple-form">
977          <label>Name: <input type="text" ng-model="user.name" /></label><br />
978          <label>Age:  <input type="number" ng-model="user.age" /></label><br />
979          Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label>
980                  <label><input type="radio" ng-model="user.gender" value="female" />female</label><br />
981          <button ng-click="reset()">RESET</button>
982          <button ng-click="update(user)">SAVE</button>
983        </form>
984        <pre>form = {{user | json}}</pre>
985        <pre>leader = {{leader | json}}</pre>
986      </div>
987    </file>
988    <file name="script.js">
989      // Module: copyExample
990      angular.
991        module('copyExample', []).
992        controller('ExampleController', ['$scope', function($scope) {
993          $scope.leader = {};
994
995          $scope.reset = function() {
996            // Example with 1 argument
997            $scope.user = angular.copy($scope.leader);
998          };
999
1000          $scope.update = function(user) {
1001            // Example with 2 arguments
1002            angular.copy(user, $scope.leader);
1003          };
1004
1005          $scope.reset();
1006        }]);
1007    </file>
1008  </example>
1009 */
1010function copy(source, destination, maxDepth) {
1011  var stackSource = [];
1012  var stackDest = [];
1013  maxDepth = isValidObjectMaxDepth(maxDepth) ? maxDepth : NaN;
1014
1015  if (destination) {
1016    if (isTypedArray(destination) || isArrayBuffer(destination)) {
1017      throw ngMinErr('cpta', 'Can\'t copy! TypedArray destination cannot be mutated.');
1018    }
1019    if (source === destination) {
1020      throw ngMinErr('cpi', 'Can\'t copy! Source and destination are identical.');
1021    }
1022
1023    // Empty the destination object
1024    if (isArray(destination)) {
1025      destination.length = 0;
1026    } else {
1027      forEach(destination, function(value, key) {
1028        if (key !== '$$hashKey') {
1029          delete destination[key];
1030        }
1031      });
1032    }
1033
1034    stackSource.push(source);
1035    stackDest.push(destination);
1036    return copyRecurse(source, destination, maxDepth);
1037  }
1038
1039  return copyElement(source, maxDepth);
1040
1041  function copyRecurse(source, destination, maxDepth) {
1042    maxDepth--;
1043    if (maxDepth < 0) {
1044      return '...';
1045    }
1046    var h = destination.$$hashKey;
1047    var key;
1048    if (isArray(source)) {
1049      for (var i = 0, ii = source.length; i < ii; i++) {
1050        destination.push(copyElement(source[i], maxDepth));
1051      }
1052    } else if (isBlankObject(source)) {
1053      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
1054      for (key in source) {
1055        destination[key] = copyElement(source[key], maxDepth);
1056      }
1057    } else if (source && typeof source.hasOwnProperty === 'function') {
1058      // Slow path, which must rely on hasOwnProperty
1059      for (key in source) {
1060        if (source.hasOwnProperty(key)) {
1061          destination[key] = copyElement(source[key], maxDepth);
1062        }
1063      }
1064    } else {
1065      // Slowest path --- hasOwnProperty can't be called as a method
1066      for (key in source) {
1067        if (hasOwnProperty.call(source, key)) {
1068          destination[key] = copyElement(source[key], maxDepth);
1069        }
1070      }
1071    }
1072    setHashKey(destination, h);
1073    return destination;
1074  }
1075
1076  function copyElement(source, maxDepth) {
1077    // Simple values
1078    if (!isObject(source)) {
1079      return source;
1080    }
1081
1082    // Already copied values
1083    var index = stackSource.indexOf(source);
1084    if (index !== -1) {
1085      return stackDest[index];
1086    }
1087
1088    if (isWindow(source) || isScope(source)) {
1089      throw ngMinErr('cpws',
1090        'Can\'t copy! Making copies of Window or Scope instances is not supported.');
1091    }
1092
1093    var needsRecurse = false;
1094    var destination = copyType(source);
1095
1096    if (destination === undefined) {
1097      destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
1098      needsRecurse = true;
1099    }
1100
1101    stackSource.push(source);
1102    stackDest.push(destination);
1103
1104    return needsRecurse
1105      ? copyRecurse(source, destination, maxDepth)
1106      : destination;
1107  }
1108
1109  function copyType(source) {
1110    switch (toString.call(source)) {
1111      case '[object Int8Array]':
1112      case '[object Int16Array]':
1113      case '[object Int32Array]':
1114      case '[object Float32Array]':
1115      case '[object Float64Array]':
1116      case '[object Uint8Array]':
1117      case '[object Uint8ClampedArray]':
1118      case '[object Uint16Array]':
1119      case '[object Uint32Array]':
1120        return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length);
1121
1122      case '[object ArrayBuffer]':
1123        // Support: IE10
1124        if (!source.slice) {
1125          // If we're in this case we know the environment supports ArrayBuffer
1126          /* eslint-disable no-undef */
1127          var copied = new ArrayBuffer(source.byteLength);
1128          new Uint8Array(copied).set(new Uint8Array(source));
1129          /* eslint-enable */
1130          return copied;
1131        }
1132        return source.slice(0);
1133
1134      case '[object Boolean]':
1135      case '[object Number]':
1136      case '[object String]':
1137      case '[object Date]':
1138        return new source.constructor(source.valueOf());
1139
1140      case '[object RegExp]':
1141        var re = new RegExp(source.source, source.toString().match(/[^/]*$/)[0]);
1142        re.lastIndex = source.lastIndex;
1143        return re;
1144
1145      case '[object Blob]':
1146        return new source.constructor([source], {type: source.type});
1147    }
1148
1149    if (isFunction(source.cloneNode)) {
1150      return source.cloneNode(true);
1151    }
1152  }
1153}
1154
1155
1156// eslint-disable-next-line no-self-compare
1157function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }
1158
1159
1160/**
1161 * @ngdoc function
1162 * @name angular.equals
1163 * @module ng
1164 * @kind function
1165 *
1166 * @description
1167 * Determines if two objects or two values are equivalent. Supports value types, regular
1168 * expressions, arrays and objects.
1169 *
1170 * Two objects or values are considered equivalent if at least one of the following is true:
1171 *
1172 * * Both objects or values pass `===` comparison.
1173 * * Both objects or values are of the same type and all of their properties are equal by
1174 *   comparing them with `angular.equals`.
1175 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1176 * * Both values represent the same regular expression (In JavaScript,
1177 *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1178 *   representation matches).
1179 *
1180 * During a property comparison, properties of `function` type and properties with names
1181 * that begin with `$` are ignored.
1182 *
1183 * Scope and DOMWindow objects are being compared only by identify (`===`).
1184 *
1185 * @param {*} o1 Object or value to compare.
1186 * @param {*} o2 Object or value to compare.
1187 * @returns {boolean} True if arguments are equal.
1188 *
1189 * @example
1190   <example module="equalsExample" name="equalsExample">
1191     <file name="index.html">
1192      <div ng-controller="ExampleController">
1193        <form novalidate>
1194          <h3>User 1</h3>
1195          Name: <input type="text" ng-model="user1.name">
1196          Age: <input type="number" ng-model="user1.age">
1197
1198          <h3>User 2</h3>
1199          Name: <input type="text" ng-model="user2.name">
1200          Age: <input type="number" ng-model="user2.age">
1201
1202          <div>
1203            <br/>
1204            <input type="button" value="Compare" ng-click="compare()">
1205          </div>
1206          User 1: <pre>{{user1 | json}}</pre>
1207          User 2: <pre>{{user2 | json}}</pre>
1208          Equal: <pre>{{result}}</pre>
1209        </form>
1210      </div>
1211    </file>
1212    <file name="script.js">
1213        angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
1214          $scope.user1 = {};
1215          $scope.user2 = {};
1216          $scope.compare = function() {
1217            $scope.result = angular.equals($scope.user1, $scope.user2);
1218          };
1219        }]);
1220    </file>
1221  </example>
1222 */
1223function equals(o1, o2) {
1224  if (o1 === o2) return true;
1225  if (o1 === null || o2 === null) return false;
1226  // eslint-disable-next-line no-self-compare
1227  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1228  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1229  if (t1 === t2 && t1 === 'object') {
1230    if (isArray(o1)) {
1231      if (!isArray(o2)) return false;
1232      if ((length = o1.length) === o2.length) {
1233        for (key = 0; key < length; key++) {
1234          if (!equals(o1[key], o2[key])) return false;
1235        }
1236        return true;
1237      }
1238    } else if (isDate(o1)) {
1239      if (!isDate(o2)) return false;
1240      return simpleCompare(o1.getTime(), o2.getTime());
1241    } else if (isRegExp(o1)) {
1242      if (!isRegExp(o2)) return false;
1243      return o1.toString() === o2.toString();
1244    } else {
1245      if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1246        isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1247      keySet = createMap();
1248      for (key in o1) {
1249        if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1250        if (!equals(o1[key], o2[key])) return false;
1251        keySet[key] = true;
1252      }
1253      for (key in o2) {
1254        if (!(key in keySet) &&
1255            key.charAt(0) !== '$' &&
1256            isDefined(o2[key]) &&
1257            !isFunction(o2[key])) return false;
1258      }
1259      return true;
1260    }
1261  }
1262  return false;
1263}
1264
1265var csp = function() {
1266  if (!isDefined(csp.rules)) {
1267
1268
1269    var ngCspElement = (window.document.querySelector('[ng-csp]') ||
1270                    window.document.querySelector('[data-ng-csp]'));
1271
1272    if (ngCspElement) {
1273      var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1274                    ngCspElement.getAttribute('data-ng-csp');
1275      csp.rules = {
1276        noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1277        noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1278      };
1279    } else {
1280      csp.rules = {
1281        noUnsafeEval: noUnsafeEval(),
1282        noInlineStyle: false
1283      };
1284    }
1285  }
1286
1287  return csp.rules;
1288
1289  function noUnsafeEval() {
1290    try {
1291      // eslint-disable-next-line no-new, no-new-func
1292      new Function('');
1293      return false;
1294    } catch (e) {
1295      return true;
1296    }
1297  }
1298};
1299
1300/**
1301 * @ngdoc directive
1302 * @module ng
1303 * @name ngJq
1304 *
1305 * @element ANY
1306 * @param {string=} ngJq the name of the library available under `window`
1307 * to be used for angular.element
1308 * @description
1309 * Use this directive to force the angular.element library.  This should be
1310 * used to force either jqLite by leaving ng-jq blank or setting the name of
1311 * the jquery variable under window (eg. jQuery).
1312 *
1313 * Since AngularJS looks for this directive when it is loaded (doesn't wait for the
1314 * DOMContentLoaded event), it must be placed on an element that comes before the script
1315 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1316 * others ignored.
1317 *
1318 * @example
1319 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1320 ```html
1321 <!doctype html>
1322 <html ng-app ng-jq>
1323 ...
1324 ...
1325 </html>
1326 ```
1327 * @example
1328 * This example shows how to use a jQuery based library of a different name.
1329 * The library name must be available at the top most 'window'.
1330 ```html
1331 <!doctype html>
1332 <html ng-app ng-jq="jQueryLib">
1333 ...
1334 ...
1335 </html>
1336 ```
1337 */
1338var jq = function() {
1339  if (isDefined(jq.name_)) return jq.name_;
1340  var el;
1341  var i, ii = ngAttrPrefixes.length, prefix, name;
1342  for (i = 0; i < ii; ++i) {
1343    prefix = ngAttrPrefixes[i];
1344    el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]');
1345    if (el) {
1346      name = el.getAttribute(prefix + 'jq');
1347      break;
1348    }
1349  }
1350
1351  return (jq.name_ = name);
1352};
1353
1354function concat(array1, array2, index) {
1355  return array1.concat(slice.call(array2, index));
1356}
1357
1358function sliceArgs(args, startIndex) {
1359  return slice.call(args, startIndex || 0);
1360}
1361
1362
1363/**
1364 * @ngdoc function
1365 * @name angular.bind
1366 * @module ng
1367 * @kind function
1368 *
1369 * @description
1370 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1371 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1372 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1373 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1374 *
1375 * @param {Object} self Context which `fn` should be evaluated in.
1376 * @param {function()} fn Function to be bound.
1377 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1378 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1379 */
1380function bind(self, fn) {
1381  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1382  if (isFunction(fn) && !(fn instanceof RegExp)) {
1383    return curryArgs.length
1384      ? function() {
1385          return arguments.length
1386            ? fn.apply(self, concat(curryArgs, arguments, 0))
1387            : fn.apply(self, curryArgs);
1388        }
1389      : function() {
1390          return arguments.length
1391            ? fn.apply(self, arguments)
1392            : fn.call(self);
1393        };
1394  } else {
1395    // In IE, native methods are not functions so they cannot be bound (note: they don't need to be).
1396    return fn;
1397  }
1398}
1399
1400
1401function toJsonReplacer(key, value) {
1402  var val = value;
1403
1404  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1405    val = undefined;
1406  } else if (isWindow(value)) {
1407    val = '$WINDOW';
1408  } else if (value &&  window.document === value) {
1409    val = '$DOCUMENT';
1410  } else if (isScope(value)) {
1411    val = '$SCOPE';
1412  }
1413
1414  return val;
1415}
1416
1417
1418/**
1419 * @ngdoc function
1420 * @name angular.toJson
1421 * @module ng
1422 * @kind function
1423 *
1424 * @description
1425 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1426 * stripped since AngularJS uses this notation internally.
1427 *
1428 * @param {Object|Array|Date|string|number|boolean} obj Input to be serialized into JSON.
1429 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1430 *    If set to an integer, the JSON output will contain that many spaces per indentation.
1431 * @returns {string|undefined} JSON-ified string representing `obj`.
1432 * @knownIssue
1433 *
1434 * The Safari browser throws a `RangeError` instead of returning `null` when it tries to stringify a `Date`
1435 * object with an invalid date value. The only reliable way to prevent this is to monkeypatch the
1436 * `Date.prototype.toJSON` method as follows:
1437 *
1438 * ```
1439 * var _DatetoJSON = Date.prototype.toJSON;
1440 * Date.prototype.toJSON = function() {
1441 *   try {
1442 *     return _DatetoJSON.call(this);
1443 *   } catch(e) {
1444 *     if (e instanceof RangeError) {
1445 *       return null;
1446 *     }
1447 *     throw e;
1448 *   }
1449 * };
1450 * ```
1451 *
1452 * See https://github.com/angular/angular.js/pull/14221 for more information.
1453 */
1454function toJson(obj, pretty) {
1455  if (isUndefined(obj)) return undefined;
1456  if (!isNumber(pretty)) {
1457    pretty = pretty ? 2 : null;
1458  }
1459  return JSON.stringify(obj, toJsonReplacer, pretty);
1460}
1461
1462
1463/**
1464 * @ngdoc function
1465 * @name angular.fromJson
1466 * @module ng
1467 * @kind function
1468 *
1469 * @description
1470 * Deserializes a JSON string.
1471 *
1472 * @param {string} json JSON string to deserialize.
1473 * @returns {Object|Array|string|number} Deserialized JSON string.
1474 */
1475function fromJson(json) {
1476  return isString(json)
1477      ? JSON.parse(json)
1478      : json;
1479}
1480
1481
1482var ALL_COLONS = /:/g;
1483function timezoneToOffset(timezone, fallback) {
1484  // Support: IE 9-11 only, Edge 13-15+
1485  // IE/Edge do not "understand" colon (`:`) in timezone
1486  timezone = timezone.replace(ALL_COLONS, '');
1487  var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1488  return isNumberNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1489}
1490
1491
1492function addDateMinutes(date, minutes) {
1493  date = new Date(date.getTime());
1494  date.setMinutes(date.getMinutes() + minutes);
1495  return date;
1496}
1497
1498
1499function convertTimezoneToLocal(date, timezone, reverse) {
1500  reverse = reverse ? -1 : 1;
1501  var dateTimezoneOffset = date.getTimezoneOffset();
1502  var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
1503  return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
1504}
1505
1506
1507/**
1508 * @returns {string} Returns the string representation of the element.
1509 */
1510function startingTag(element) {
1511  element = jqLite(element).clone().empty();
1512  var elemHtml = jqLite('<div></div>').append(element).html();
1513  try {
1514    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1515        elemHtml.
1516          match(/^(<[^>]+>)/)[1].
1517          replace(/^<([\w-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
1518  } catch (e) {
1519    return lowercase(elemHtml);
1520  }
1521
1522}
1523
1524
1525/////////////////////////////////////////////////
1526
1527/**
1528 * Tries to decode the URI component without throwing an exception.
1529 *
1530 * @private
1531 * @param str value potential URI component to check.
1532 * @returns {boolean} True if `value` can be decoded
1533 * with the decodeURIComponent function.
1534 */
1535function tryDecodeURIComponent(value) {
1536  try {
1537    return decodeURIComponent(value);
1538  } catch (e) {
1539    // Ignore any invalid uri component.
1540  }
1541}
1542
1543
1544/**
1545 * Parses an escaped url query string into key-value pairs.
1546 * @returns {Object.<string,boolean|Array>}
1547 */
1548function parseKeyValue(/**string*/keyValue) {
1549  var obj = {};
1550  forEach((keyValue || '').split('&'), function(keyValue) {
1551    var splitPoint, key, val;
1552    if (keyValue) {
1553      key = keyValue = keyValue.replace(/\+/g,'%20');
1554      splitPoint = keyValue.indexOf('=');
1555      if (splitPoint !== -1) {
1556        key = keyValue.substring(0, splitPoint);
1557        val = keyValue.substring(splitPoint + 1);
1558      }
1559      key = tryDecodeURIComponent(key);
1560      if (isDefined(key)) {
1561        val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1562        if (!hasOwnProperty.call(obj, key)) {
1563          obj[key] = val;
1564        } else if (isArray(obj[key])) {
1565          obj[key].push(val);
1566        } else {
1567          obj[key] = [obj[key],val];
1568        }
1569      }
1570    }
1571  });
1572  return obj;
1573}
1574
1575function toKeyValue(obj) {
1576  var parts = [];
1577  forEach(obj, function(value, key) {
1578    if (isArray(value)) {
1579      forEach(value, function(arrayValue) {
1580        parts.push(encodeUriQuery(key, true) +
1581                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1582      });
1583    } else {
1584    parts.push(encodeUriQuery(key, true) +
1585               (value === true ? '' : '=' + encodeUriQuery(value, true)));
1586    }
1587  });
1588  return parts.length ? parts.join('&') : '';
1589}
1590
1591
1592/**
1593 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1594 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1595 * segments:
1596 *    segment       = *pchar
1597 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1598 *    pct-encoded   = "%" HEXDIG HEXDIG
1599 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1600 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1601 *                     / "*" / "+" / "," / ";" / "="
1602 */
1603function encodeUriSegment(val) {
1604  return encodeUriQuery(val, true).
1605             replace(/%26/gi, '&').
1606             replace(/%3D/gi, '=').
1607             replace(/%2B/gi, '+');
1608}
1609
1610
1611/**
1612 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1613 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1614 * encoded per http://tools.ietf.org/html/rfc3986:
1615 *    query         = *( pchar / "/" / "?" )
1616 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1617 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1618 *    pct-encoded   = "%" HEXDIG HEXDIG
1619 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1620 *                     / "*" / "+" / "," / ";" / "="
1621 */
1622function encodeUriQuery(val, pctEncodeSpaces) {
1623  return encodeURIComponent(val).
1624             replace(/%40/gi, '@').
1625             replace(/%3A/gi, ':').
1626             replace(/%24/g, '$').
1627             replace(/%2C/gi, ',').
1628             replace(/%3B/gi, ';').
1629             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1630}
1631
1632var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1633
1634function getNgAttribute(element, ngAttr) {
1635  var attr, i, ii = ngAttrPrefixes.length;
1636  for (i = 0; i < ii; ++i) {
1637    attr = ngAttrPrefixes[i] + ngAttr;
1638    if (isString(attr = element.getAttribute(attr))) {
1639      return attr;
1640    }
1641  }
1642  return null;
1643}
1644
1645function allowAutoBootstrap(document) {
1646  var script = document.currentScript;
1647
1648  if (!script) {
1649    // Support: IE 9-11 only
1650    // IE does not have `document.currentScript`
1651    return true;
1652  }
1653
1654  // If the `currentScript` property has been clobbered just return false, since this indicates a probable attack
1655  if (!(script instanceof window.HTMLScriptElement || script instanceof window.SVGScriptElement)) {
1656    return false;
1657  }
1658
1659  var attributes = script.attributes;
1660  var srcs = [attributes.getNamedItem('src'), attributes.getNamedItem('href'), attributes.getNamedItem('xlink:href')];
1661
1662  return srcs.every(function(src) {
1663    if (!src) {
1664      return true;
1665    }
1666    if (!src.value) {
1667      return false;
1668    }
1669
1670    var link = document.createElement('a');
1671    link.href = src.value;
1672
1673    if (document.location.origin === link.origin) {
1674      // Same-origin resources are always allowed, even for banned URL schemes.
1675      return true;
1676    }
1677    // Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
1678    // This is to prevent angular.js bundled with browser extensions from being used to bypass the
1679    // content security policy in web pages and other browser extensions.
1680    switch (link.protocol) {
1681      case 'http:':
1682      case 'https:':
1683      case 'ftp:':
1684      case 'blob:':
1685      case 'file:':
1686      case 'data:':
1687        return true;
1688      default:
1689        return false;
1690    }
1691  });
1692}
1693
1694// Cached as it has to run during loading so that document.currentScript is available.
1695var isAutoBootstrapAllowed = allowAutoBootstrap(window.document);
1696
1697/**
1698 * @ngdoc directive
1699 * @name ngApp
1700 * @module ng
1701 *
1702 * @element ANY
1703 * @param {angular.Module} ngApp an optional application
1704 *   {@link angular.module module} name to load.
1705 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1706 *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1707 *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1708 *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1709 *   tracking down the root of these bugs.
1710 *
1711 * @description
1712 *
1713 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1714 * designates the **root element** of the application and is typically placed near the root element
1715 * of the page - e.g. on the `<body>` or `<html>` tags.
1716 *
1717 * There are a few things to keep in mind when using `ngApp`:
1718 * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1719 *   found in the document will be used to define the root element to auto-bootstrap as an
1720 *   application. To run multiple applications in an HTML document you must manually bootstrap them using
1721 *   {@link angular.bootstrap} instead.
1722 * - AngularJS applications cannot be nested within each other.
1723 * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
1724 *   This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
1725 *   {@link ngRoute.ngView `ngView`}.
1726 *   Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1727 *   causing animations to stop working and making the injector inaccessible from outside the app.
1728 *
1729 * You can specify an **AngularJS module** to be used as the root module for the application.  This
1730 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1731 * should contain the application code needed or have dependencies on other modules that will
1732 * contain the code. See {@link angular.module} for more information.
1733 *
1734 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1735 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1736 * would not be resolved to `3`.
1737 *
1738 * @example
1739 *
1740 * ### Simple Usage
1741 *
1742 * `ngApp` is the easiest, and most common way to bootstrap an application.
1743 *
1744 <example module="ngAppDemo" name="ng-app">
1745   <file name="index.html">
1746   <div ng-controller="ngAppDemoController">
1747     I can add: {{a}} + {{b}} =  {{ a+b }}
1748   </div>
1749   </file>
1750   <file name="script.js">
1751   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1752     $scope.a = 1;
1753     $scope.b = 2;
1754   });
1755   </file>
1756 </example>
1757 *
1758 * @example
1759 *
1760 * ### With `ngStrictDi`
1761 *
1762 * Using `ngStrictDi`, you would see something like this:
1763 *
1764 <example ng-app-included="true" name="strict-di">
1765   <file name="index.html">
1766   <div ng-app="ngAppStrictDemo" ng-strict-di>
1767       <div ng-controller="GoodController1">
1768           I can add: {{a}} + {{b}} =  {{ a+b }}
1769
1770           <p>This renders because the controller does not fail to
1771              instantiate, by using explicit annotation style (see
1772              script.js for details)
1773           </p>
1774       </div>
1775
1776       <div ng-controller="GoodController2">
1777           Name: <input ng-model="name"><br />
1778           Hello, {{name}}!
1779
1780           <p>This renders because the controller does not fail to
1781              instantiate, by using explicit annotation style
1782              (see script.js for details)
1783           </p>
1784       </div>
1785
1786       <div ng-controller="BadController">
1787           I can add: {{a}} + {{b}} =  {{ a+b }}
1788
1789           <p>The controller could not be instantiated, due to relying
1790              on automatic function annotations (which are disabled in
1791              strict mode). As such, the content of this section is not
1792              interpolated, and there should be an error in your web console.
1793           </p>
1794       </div>
1795   </div>
1796   </file>
1797   <file name="script.js">
1798   angular.module('ngAppStrictDemo', [])
1799     // BadController will fail to instantiate, due to relying on automatic function annotation,
1800     // rather than an explicit annotation
1801     .controller('BadController', function($scope) {
1802       $scope.a = 1;
1803       $scope.b = 2;
1804     })
1805     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1806     // due to using explicit annotations using the array style and $inject property, respectively.
1807     .controller('GoodController1', ['$scope', function($scope) {
1808       $scope.a = 1;
1809       $scope.b = 2;
1810     }])
1811     .controller('GoodController2', GoodController2);
1812     function GoodController2($scope) {
1813       $scope.name = 'World';
1814     }
1815     GoodController2.$inject = ['$scope'];
1816   </file>
1817   <file name="style.css">
1818   div[ng-controller] {
1819       margin-bottom: 1em;
1820       -webkit-border-radius: 4px;
1821       border-radius: 4px;
1822       border: 1px solid;
1823       padding: .5em;
1824   }
1825   div[ng-controller^=Good] {
1826       border-color: #d6e9c6;
1827       background-color: #dff0d8;
1828       color: #3c763d;
1829   }
1830   div[ng-controller^=Bad] {
1831       border-color: #ebccd1;
1832       background-color: #f2dede;
1833       color: #a94442;
1834       margin-bottom: 0;
1835   }
1836   </file>
1837 </example>
1838 */
1839function angularInit(element, bootstrap) {
1840  var appElement,
1841      module,
1842      config = {};
1843
1844  // The element `element` has priority over any other element.
1845  forEach(ngAttrPrefixes, function(prefix) {
1846    var name = prefix + 'app';
1847
1848    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1849      appElement = element;
1850      module = element.getAttribute(name);
1851    }
1852  });
1853  forEach(ngAttrPrefixes, function(prefix) {
1854    var name = prefix + 'app';
1855    var candidate;
1856
1857    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1858      appElement = candidate;
1859      module = candidate.getAttribute(name);
1860    }
1861  });
1862  if (appElement) {
1863    if (!isAutoBootstrapAllowed) {
1864      window.console.error('AngularJS: disabling automatic bootstrap. <script> protocol indicates ' +
1865          'an extension, document.location.href does not match.');
1866      return;
1867    }
1868    config.strictDi = getNgAttribute(appElement, 'strict-di') !== null;
1869    bootstrap(appElement, module ? [module] : [], config);
1870  }
1871}
1872
1873/**
1874 * @ngdoc function
1875 * @name angular.bootstrap
1876 * @module ng
1877 * @description
1878 * Use this function to manually start up AngularJS application.
1879 *
1880 * For more information, see the {@link guide/bootstrap Bootstrap guide}.
1881 *
1882 * AngularJS will detect if it has been loaded into the browser more than once and only allow the
1883 * first loaded script to be bootstrapped and will report a warning to the browser console for
1884 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1885 * multiple instances of AngularJS try to work on the DOM.
1886 *
1887 * <div class="alert alert-warning">
1888 * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
1889 * They must use {@link ng.directive:ngApp ngApp}.
1890 * </div>
1891 *
1892 * <div class="alert alert-warning">
1893 * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
1894 * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
1895 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1896 * causing animations to stop working and making the injector inaccessible from outside the app.
1897 * </div>
1898 *
1899 * ```html
1900 * <!doctype html>
1901 * <html>
1902 * <body>
1903 * <div ng-controller="WelcomeController">
1904 *   {{greeting}}
1905 * </div>
1906 *
1907 * <script src="angular.js"></script>
1908 * <script>
1909 *   var app = angular.module('demo', [])
1910 *   .controller('WelcomeController', function($scope) {
1911 *       $scope.greeting = 'Welcome!';
1912 *   });
1913 *   angular.bootstrap(document, ['demo']);
1914 * </script>
1915 * </body>
1916 * </html>
1917 * ```
1918 *
1919 * @param {DOMElement} element DOM element which is the root of AngularJS application.
1920 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1921 *     Each item in the array should be the name of a predefined module or a (DI annotated)
1922 *     function that will be invoked by the injector as a `config` block.
1923 *     See: {@link angular.module modules}
1924 * @param {Object=} config an object for defining configuration options for the application. The
1925 *     following keys are supported:
1926 *
1927 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1928 *   assist in finding bugs which break minified code. Defaults to `false`.
1929 *
1930 * @returns {auto.$injector} Returns the newly created injector for this app.
1931 */
1932function bootstrap(element, modules, config) {
1933  if (!isObject(config)) config = {};
1934  var defaultConfig = {
1935    strictDi: false
1936  };
1937  config = extend(defaultConfig, config);
1938  var doBootstrap = function() {
1939    element = jqLite(element);
1940
1941    if (element.injector()) {
1942      var tag = (element[0] === window.document) ? 'document' : startingTag(element);
1943      // Encode angle brackets to prevent input from being sanitized to empty string #8683.
1944      throw ngMinErr(
1945          'btstrpd',
1946          'App already bootstrapped with this element \'{0}\'',
1947          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1948    }
1949
1950    modules = modules || [];
1951    modules.unshift(['$provide', function($provide) {
1952      $provide.value('$rootElement', element);
1953    }]);
1954
1955    if (config.debugInfoEnabled) {
1956      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1957      modules.push(['$compileProvider', function($compileProvider) {
1958        $compileProvider.debugInfoEnabled(true);
1959      }]);
1960    }
1961
1962    modules.unshift('ng');
1963    var injector = createInjector(modules, config.strictDi);
1964    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1965       function bootstrapApply(scope, element, compile, injector) {
1966        scope.$apply(function() {
1967          element.data('$injector', injector);
1968          compile(element)(scope);
1969        });
1970      }]
1971    );
1972    return injector;
1973  };
1974
1975  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1976  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1977
1978  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1979    config.debugInfoEnabled = true;
1980    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1981  }
1982
1983  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1984    return doBootstrap();
1985  }
1986
1987  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1988  angular.resumeBootstrap = function(extraModules) {
1989    forEach(extraModules, function(module) {
1990      modules.push(module);
1991    });
1992    return doBootstrap();
1993  };
1994
1995  if (isFunction(angular.resumeDeferredBootstrap)) {
1996    angular.resumeDeferredBootstrap();
1997  }
1998}
1999
2000/**
2001 * @ngdoc function
2002 * @name angular.reloadWithDebugInfo
2003 * @module ng
2004 * @description
2005 * Use this function to reload the current application with debug information turned on.
2006 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
2007 *
2008 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
2009 */
2010function reloadWithDebugInfo() {
2011  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
2012  window.location.reload();
2013}
2014
2015/**
2016 * @name angular.getTestability
2017 * @module ng
2018 * @description
2019 * Get the testability service for the instance of AngularJS on the given
2020 * element.
2021 * @param {DOMElement} element DOM element which is the root of AngularJS application.
2022 */
2023function getTestability(rootElement) {
2024  var injector = angular.element(rootElement).injector();
2025  if (!injector) {
2026    throw ngMinErr('test',
2027      'no injector found for element argument to getTestability');
2028  }
2029  return injector.get('$$testability');
2030}
2031
2032var SNAKE_CASE_REGEXP = /[A-Z]/g;
2033function snake_case(name, separator) {
2034  separator = separator || '_';
2035  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
2036    return (pos ? separator : '') + letter.toLowerCase();
2037  });
2038}
2039
2040var bindJQueryFired = false;
2041function bindJQuery() {
2042  var originalCleanData;
2043
2044  if (bindJQueryFired) {
2045    return;
2046  }
2047
2048  // bind to jQuery if present;
2049  var jqName = jq();
2050  jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
2051           !jqName             ? undefined     :   // use jqLite
2052                                 window[jqName];   // use jQuery specified by `ngJq`
2053
2054  // Use jQuery if it exists with proper functionality, otherwise default to us.
2055  // AngularJS 1.2+ requires jQuery 1.7+ for on()/off() support.
2056  // AngularJS 1.3+ technically requires at least jQuery 2.1+ but it may work with older
2057  // versions. It will not work for sure with jQuery <1.7, though.
2058  if (jQuery && jQuery.fn.on) {
2059    jqLite = jQuery;
2060    extend(jQuery.fn, {
2061      scope: JQLitePrototype.scope,
2062      isolateScope: JQLitePrototype.isolateScope,
2063      controller: /** @type {?} */ (JQLitePrototype).controller,
2064      injector: JQLitePrototype.injector,
2065      inheritedData: JQLitePrototype.inheritedData
2066    });
2067  } else {
2068    jqLite = JQLite;
2069  }
2070
2071  // All nodes removed from the DOM via various jqLite/jQuery APIs like .remove()
2072  // are passed through jqLite/jQuery.cleanData. Monkey-patch this method to fire
2073  // the $destroy event on all removed nodes.
2074  originalCleanData = jqLite.cleanData;
2075  jqLite.cleanData = function(elems) {
2076    var events;
2077    for (var i = 0, elem; (elem = elems[i]) != null; i++) {
2078      events = (jqLite._data(elem) || {}).events;
2079      if (events && events.$destroy) {
2080        jqLite(elem).triggerHandler('$destroy');
2081      }
2082    }
2083    originalCleanData(elems);
2084  };
2085
2086  angular.element = jqLite;
2087
2088  // Prevent double-proxying.
2089  bindJQueryFired = true;
2090}
2091
2092/**
2093 * @ngdoc function
2094 * @name angular.UNSAFE_restoreLegacyJqLiteXHTMLReplacement
2095 * @module ng
2096 * @kind function
2097 *
2098 * @description
2099 * Restores the pre-1.8 behavior of jqLite that turns XHTML-like strings like
2100 * `<div /><span />` to `<div></div><span></span>` instead of `<div><span></span></div>`.
2101 * The new behavior is a security fix. Thus, if you need to call this function, please try to adjust
2102 * your code for this change and remove your use of this function as soon as possible.
2103
2104 * Note that this only patches jqLite. If you use jQuery 3.5.0 or newer, please read the
2105 * [jQuery 3.5 upgrade guide](https://jquery.com/upgrade-guide/3.5/) for more details
2106 * about the workarounds.
2107 */
2108function UNSAFE_restoreLegacyJqLiteXHTMLReplacement() {
2109  JQLite.legacyXHTMLReplacement = true;
2110}
2111
2112/**
2113 * throw error if the argument is falsy.
2114 */
2115function assertArg(arg, name, reason) {
2116  if (!arg) {
2117    throw ngMinErr('areq', 'Argument \'{0}\' is {1}', (name || '?'), (reason || 'required'));
2118  }
2119  return arg;
2120}
2121
2122function assertArgFn(arg, name, acceptArrayAnnotation) {
2123  if (acceptArrayAnnotation && isArray(arg)) {
2124      arg = arg[arg.length - 1];
2125  }
2126
2127  assertArg(isFunction(arg), name, 'not a function, got ' +
2128      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
2129  return arg;
2130}
2131
2132/**
2133 * throw error if the name given is hasOwnProperty
2134 * @param  {String} name    the name to test
2135 * @param  {String} context the context in which the name is used, such as module or directive
2136 */
2137function assertNotHasOwnProperty(name, context) {
2138  if (name === 'hasOwnProperty') {
2139    throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2140  }
2141}
2142
2143/**
2144 * Return the value accessible from the object by path. Any undefined traversals are ignored
2145 * @param {Object} obj starting object
2146 * @param {String} path path to traverse
2147 * @param {boolean} [bindFnToScope=true]
2148 * @returns {Object} value as accessible by path
2149 */
2150//TODO(misko): this function needs to be removed
2151function getter(obj, path, bindFnToScope) {
2152  if (!path) return obj;
2153  var keys = path.split('.');
2154  var key;
2155  var lastInstance = obj;
2156  var len = keys.length;
2157
2158  for (var i = 0; i < len; i++) {
2159    key = keys[i];
2160    if (obj) {
2161      obj = (lastInstance = obj)[key];
2162    }
2163  }
2164  if (!bindFnToScope && isFunction(obj)) {
2165    return bind(lastInstance, obj);
2166  }
2167  return obj;
2168}
2169
2170/**
2171 * Return the DOM siblings between the first and last node in the given array.
2172 * @param {Array} array like object
2173 * @returns {Array} the inputted object or a jqLite collection containing the nodes
2174 */
2175function getBlockNodes(nodes) {
2176  // TODO(perf): update `nodes` instead of creating a new object?
2177  var node = nodes[0];
2178  var endNode = nodes[nodes.length - 1];
2179  var blockNodes;
2180
2181  for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
2182    if (blockNodes || nodes[i] !== node) {
2183      if (!blockNodes) {
2184        blockNodes = jqLite(slice.call(nodes, 0, i));
2185      }
2186      blockNodes.push(node);
2187    }
2188  }
2189
2190  return blockNodes || nodes;
2191}
2192
2193
2194/**
2195 * Creates a new object without a prototype. This object is useful for lookup without having to
2196 * guard against prototypically inherited properties via hasOwnProperty.
2197 *
2198 * Related micro-benchmarks:
2199 * - http://jsperf.com/object-create2
2200 * - http://jsperf.com/proto-map-lookup/2
2201 * - http://jsperf.com/for-in-vs-object-keys2
2202 *
2203 * @returns {Object}
2204 */
2205function createMap() {
2206  return Object.create(null);
2207}
2208
2209function stringify(value) {
2210  if (value == null) { // null || undefined
2211    return '';
2212  }
2213  switch (typeof value) {
2214    case 'string':
2215      break;
2216    case 'number':
2217      value = '' + value;
2218      break;
2219    default:
2220      if (hasCustomToString(value) && !isArray(value) && !isDate(value)) {
2221        value = value.toString();
2222      } else {
2223        value = toJson(value);
2224      }
2225  }
2226
2227  return value;
2228}
2229
2230var NODE_TYPE_ELEMENT = 1;
2231var NODE_TYPE_ATTRIBUTE = 2;
2232var NODE_TYPE_TEXT = 3;
2233var NODE_TYPE_COMMENT = 8;
2234var NODE_TYPE_DOCUMENT = 9;
2235var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
2236
2237/**
2238 * @ngdoc type
2239 * @name angular.Module
2240 * @module ng
2241 * @description
2242 *
2243 * Interface for configuring AngularJS {@link angular.module modules}.
2244 */
2245
2246function setupModuleLoader(window) {
2247
2248  var $injectorMinErr = minErr('$injector');
2249  var ngMinErr = minErr('ng');
2250
2251  function ensure(obj, name, factory) {
2252    return obj[name] || (obj[name] = factory());
2253  }
2254
2255  var angular = ensure(window, 'angular', Object);
2256
2257  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
2258  angular.$$minErr = angular.$$minErr || minErr;
2259
2260  return ensure(angular, 'module', function() {
2261    /** @type {Object.<string, angular.Module>} */
2262    var modules = {};
2263
2264    /**
2265     * @ngdoc function
2266     * @name angular.module
2267     * @module ng
2268     * @description
2269     *
2270     * The `angular.module` is a global place for creating, registering and retrieving AngularJS
2271     * modules.
2272     * All modules (AngularJS core or 3rd party) that should be available to an application must be
2273     * registered using this mechanism.
2274     *
2275     * Passing one argument retrieves an existing {@link angular.Module},
2276     * whereas passing more than one argument creates a new {@link angular.Module}
2277     *
2278     *
2279     * # Module
2280     *
2281     * A module is a collection of services, directives, controllers, filters, and configuration information.
2282     * `angular.module` is used to configure the {@link auto.$injector $injector}.
2283     *
2284     * ```js
2285     * // Create a new module
2286     * var myModule = angular.module('myModule', []);
2287     *
2288     * // register a new service
2289     * myModule.value('appName', 'MyCoolApp');
2290     *
2291     * // configure existing services inside initialization blocks.
2292     * myModule.config(['$locationProvider', function($locationProvider) {
2293     *   // Configure existing providers
2294     *   $locationProvider.hashPrefix('!');
2295     * }]);
2296     * ```
2297     *
2298     * Then you can create an injector and load your modules like this:
2299     *
2300     * ```js
2301     * var injector = angular.injector(['ng', 'myModule'])
2302     * ```
2303     *
2304     * However it's more likely that you'll just use
2305     * {@link ng.directive:ngApp ngApp} or
2306     * {@link angular.bootstrap} to simplify this process for you.
2307     *
2308     * @param {!string} name The name of the module to create or retrieve.
2309     * @param {!Array.<string>=} requires If specified then new module is being created. If
2310     *        unspecified then the module is being retrieved for further configuration.
2311     * @param {Function=} configFn Optional configuration function for the module. Same as
2312     *        {@link angular.Module#config Module#config()}.
2313     * @returns {angular.Module} new module with the {@link angular.Module} api.
2314     */
2315    return function module(name, requires, configFn) {
2316
2317      var info = {};
2318
2319      var assertNotHasOwnProperty = function(name, context) {
2320        if (name === 'hasOwnProperty') {
2321          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2322        }
2323      };
2324
2325      assertNotHasOwnProperty(name, 'module');
2326      if (requires && modules.hasOwnProperty(name)) {
2327        modules[name] = null;
2328      }
2329      return ensure(modules, name, function() {
2330        if (!requires) {
2331          throw $injectorMinErr('nomod', 'Module \'{0}\' is not available! You either misspelled ' +
2332             'the module name or forgot to load it. If registering a module ensure that you ' +
2333             'specify the dependencies as the second argument.', name);
2334        }
2335
2336        /** @type {!Array.<Array.<*>>} */
2337        var invokeQueue = [];
2338
2339        /** @type {!Array.<Function>} */
2340        var configBlocks = [];
2341
2342        /** @type {!Array.<Function>} */
2343        var runBlocks = [];
2344
2345        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2346
2347        /** @type {angular.Module} */
2348        var moduleInstance = {
2349          // Private state
2350          _invokeQueue: invokeQueue,
2351          _configBlocks: configBlocks,
2352          _runBlocks: runBlocks,
2353
2354          /**
2355           * @ngdoc method
2356           * @name angular.Module#info
2357           * @module ng
2358           *
2359           * @param {Object=} info Information about the module
2360           * @returns {Object|Module} The current info object for this module if called as a getter,
2361           *                          or `this` if called as a setter.
2362           *
2363           * @description
2364           * Read and write custom information about this module.
2365           * For example you could put the version of the module in here.
2366           *
2367           * ```js
2368           * angular.module('myModule', []).info({ version: '1.0.0' });
2369           * ```
2370           *
2371           * The version could then be read back out by accessing the module elsewhere:
2372           *
2373           * ```
2374           * var version = angular.module('myModule').info().version;
2375           * ```
2376           *
2377           * You can also retrieve this information during runtime via the
2378           * {@link $injector#modules `$injector.modules`} property:
2379           *
2380           * ```js
2381           * var version = $injector.modules['myModule'].info().version;
2382           * ```
2383           */
2384          info: function(value) {
2385            if (isDefined(value)) {
2386              if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value');
2387              info = value;
2388              return this;
2389            }
2390            return info;
2391          },
2392
2393          /**
2394           * @ngdoc property
2395           * @name angular.Module#requires
2396           * @module ng
2397           *
2398           * @description
2399           * Holds the list of modules which the injector will load before the current module is
2400           * loaded.
2401           */
2402          requires: requires,
2403
2404          /**
2405           * @ngdoc property
2406           * @name angular.Module#name
2407           * @module ng
2408           *
2409           * @description
2410           * Name of the module.
2411           */
2412          name: name,
2413
2414
2415          /**
2416           * @ngdoc method
2417           * @name angular.Module#provider
2418           * @module ng
2419           * @param {string} name service name
2420           * @param {Function} providerType Construction function for creating new instance of the
2421           *                                service.
2422           * @description
2423           * See {@link auto.$provide#provider $provide.provider()}.
2424           */
2425          provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2426
2427          /**
2428           * @ngdoc method
2429           * @name angular.Module#factory
2430           * @module ng
2431           * @param {string} name service name
2432           * @param {Function} providerFunction Function for creating new instance of the service.
2433           * @description
2434           * See {@link auto.$provide#factory $provide.factory()}.
2435           */
2436          factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2437
2438          /**
2439           * @ngdoc method
2440           * @name angular.Module#service
2441           * @module ng
2442           * @param {string} name service name
2443           * @param {Function} constructor A constructor function that will be instantiated.
2444           * @description
2445           * See {@link auto.$provide#service $provide.service()}.
2446           */
2447          service: invokeLaterAndSetModuleName('$provide', 'service'),
2448
2449          /**
2450           * @ngdoc method
2451           * @name angular.Module#value
2452           * @module ng
2453           * @param {string} name service name
2454           * @param {*} object Service instance object.
2455           * @description
2456           * See {@link auto.$provide#value $provide.value()}.
2457           */
2458          value: invokeLater('$provide', 'value'),
2459
2460          /**
2461           * @ngdoc method
2462           * @name angular.Module#constant
2463           * @module ng
2464           * @param {string} name constant name
2465           * @param {*} object Constant value.
2466           * @description
2467           * Because the constants are fixed, they get applied before other provide methods.
2468           * See {@link auto.$provide#constant $provide.constant()}.
2469           */
2470          constant: invokeLater('$provide', 'constant', 'unshift'),
2471
2472           /**
2473           * @ngdoc method
2474           * @name angular.Module#decorator
2475           * @module ng
2476           * @param {string} name The name of the service to decorate.
2477           * @param {Function} decorFn This function will be invoked when the service needs to be
2478           *                           instantiated and should return the decorated service instance.
2479           * @description
2480           * See {@link auto.$provide#decorator $provide.decorator()}.
2481           */
2482          decorator: invokeLaterAndSetModuleName('$provide', 'decorator', configBlocks),
2483
2484          /**
2485           * @ngdoc method
2486           * @name angular.Module#animation
2487           * @module ng
2488           * @param {string} name animation name
2489           * @param {Function} animationFactory Factory function for creating new instance of an
2490           *                                    animation.
2491           * @description
2492           *
2493           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2494           *
2495           *
2496           * Defines an animation hook that can be later used with
2497           * {@link $animate $animate} service and directives that use this service.
2498           *
2499           * ```js
2500           * module.animation('.animation-name', function($inject1, $inject2) {
2501           *   return {
2502           *     eventName : function(element, done) {
2503           *       //code to run the animation
2504           *       //once complete, then run done()
2505           *       return function cancellationFunction(element) {
2506           *         //code to cancel the animation
2507           *       }
2508           *     }
2509           *   }
2510           * })
2511           * ```
2512           *
2513           * See {@link ng.$animateProvider#register $animateProvider.register()} and
2514           * {@link ngAnimate ngAnimate module} for more information.
2515           */
2516          animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2517
2518          /**
2519           * @ngdoc method
2520           * @name angular.Module#filter
2521           * @module ng
2522           * @param {string} name Filter name - this must be a valid AngularJS expression identifier
2523           * @param {Function} filterFactory Factory function for creating new instance of filter.
2524           * @description
2525           * See {@link ng.$filterProvider#register $filterProvider.register()}.
2526           *
2527           * <div class="alert alert-warning">
2528           * **Note:** Filter names must be valid AngularJS {@link expression} identifiers, such as `uppercase` or `orderBy`.
2529           * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2530           * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2531           * (`myapp_subsection_filterx`).
2532           * </div>
2533           */
2534          filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2535
2536          /**
2537           * @ngdoc method
2538           * @name angular.Module#controller
2539           * @module ng
2540           * @param {string|Object} name Controller name, or an object map of controllers where the
2541           *    keys are the names and the values are the constructors.
2542           * @param {Function} constructor Controller constructor function.
2543           * @description
2544           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2545           */
2546          controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2547
2548          /**
2549           * @ngdoc method
2550           * @name angular.Module#directive
2551           * @module ng
2552           * @param {string|Object} name Directive name, or an object map of directives where the
2553           *    keys are the names and the values are the factories.
2554           * @param {Function} directiveFactory Factory function for creating new instance of
2555           * directives.
2556           * @description
2557           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2558           */
2559          directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2560
2561          /**
2562           * @ngdoc method
2563           * @name angular.Module#component
2564           * @module ng
2565           * @param {string|Object} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`),
2566           *    or an object map of components where the keys are the names and the values are the component definition objects.
2567           * @param {Object} options Component definition object (a simplified
2568           *    {@link ng.$compile#directive-definition-object directive definition object})
2569           *
2570           * @description
2571           * See {@link ng.$compileProvider#component $compileProvider.component()}.
2572           */
2573          component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
2574
2575          /**
2576           * @ngdoc method
2577           * @name angular.Module#config
2578           * @module ng
2579           * @param {Function} configFn Execute this function on module load. Useful for service
2580           *    configuration.
2581           * @description
2582           * Use this method to configure services by injecting their
2583           * {@link angular.Module#provider `providers`}, e.g. for adding routes to the
2584           * {@link ngRoute.$routeProvider $routeProvider}.
2585           *
2586           * Note that you can only inject {@link angular.Module#provider `providers`} and
2587           * {@link angular.Module#constant `constants`} into this function.
2588           *
2589           * For more about how to configure services, see
2590           * {@link providers#provider-recipe Provider Recipe}.
2591           */
2592          config: config,
2593
2594          /**
2595           * @ngdoc method
2596           * @name angular.Module#run
2597           * @module ng
2598           * @param {Function} initializationFn Execute this function after injector creation.
2599           *    Useful for application initialization.
2600           * @description
2601           * Use this method to register work which should be performed when the injector is done
2602           * loading all modules.
2603           */
2604          run: function(block) {
2605            runBlocks.push(block);
2606            return this;
2607          }
2608        };
2609
2610        if (configFn) {
2611          config(configFn);
2612        }
2613
2614        return moduleInstance;
2615
2616        /**
2617         * @param {string} provider
2618         * @param {string} method
2619         * @param {String=} insertMethod
2620         * @returns {angular.Module}
2621         */
2622        function invokeLater(provider, method, insertMethod, queue) {
2623          if (!queue) queue = invokeQueue;
2624          return function() {
2625            queue[insertMethod || 'push']([provider, method, arguments]);
2626            return moduleInstance;
2627          };
2628        }
2629
2630        /**
2631         * @param {string} provider
2632         * @param {string} method
2633         * @returns {angular.Module}
2634         */
2635        function invokeLaterAndSetModuleName(provider, method, queue) {
2636          if (!queue) queue = invokeQueue;
2637          return function(recipeName, factoryFunction) {
2638            if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2639            queue.push([provider, method, arguments]);
2640            return moduleInstance;
2641          };
2642        }
2643      });
2644    };
2645  });
2646
2647}
2648
2649/* global shallowCopy: true */
2650
2651/**
2652 * Creates a shallow copy of an object, an array or a primitive.
2653 *
2654 * Assumes that there are no proto properties for objects.
2655 */
2656function shallowCopy(src, dst) {
2657  if (isArray(src)) {
2658    dst = dst || [];
2659
2660    for (var i = 0, ii = src.length; i < ii; i++) {
2661      dst[i] = src[i];
2662    }
2663  } else if (isObject(src)) {
2664    dst = dst || {};
2665
2666    for (var key in src) {
2667      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
2668        dst[key] = src[key];
2669      }
2670    }
2671  }
2672
2673  return dst || src;
2674}
2675
2676/* exported toDebugString */
2677
2678function serializeObject(obj, maxDepth) {
2679  var seen = [];
2680
2681  // There is no direct way to stringify object until reaching a specific depth
2682  // and a very deep object can cause a performance issue, so we copy the object
2683  // based on this specific depth and then stringify it.
2684  if (isValidObjectMaxDepth(maxDepth)) {
2685    // This file is also included in `angular-loader`, so `copy()` might not always be available in
2686    // the closure. Therefore, it is lazily retrieved as `angular.copy()` when needed.
2687    obj = angular.copy(obj, null, maxDepth);
2688  }
2689  return JSON.stringify(obj, function(key, val) {
2690    val = toJsonReplacer(key, val);
2691    if (isObject(val)) {
2692
2693      if (seen.indexOf(val) >= 0) return '...';
2694
2695      seen.push(val);
2696    }
2697    return val;
2698  });
2699}
2700
2701function toDebugString(obj, maxDepth) {
2702  if (typeof obj === 'function') {
2703    return obj.toString().replace(/ \{[\s\S]*$/, '');
2704  } else if (isUndefined(obj)) {
2705    return 'undefined';
2706  } else if (typeof obj !== 'string') {
2707    return serializeObject(obj, maxDepth);
2708  }
2709  return obj;
2710}
2711
2712/* global angularModule: true,
2713  version: true,
2714
2715  $CompileProvider,
2716
2717  htmlAnchorDirective,
2718  inputDirective,
2719  hiddenInputBrowserCacheDirective,
2720  formDirective,
2721  scriptDirective,
2722  selectDirective,
2723  optionDirective,
2724  ngBindDirective,
2725  ngBindHtmlDirective,
2726  ngBindTemplateDirective,
2727  ngClassDirective,
2728  ngClassEvenDirective,
2729  ngClassOddDirective,
2730  ngCloakDirective,
2731  ngControllerDirective,
2732  ngFormDirective,
2733  ngHideDirective,
2734  ngIfDirective,
2735  ngIncludeDirective,
2736  ngIncludeFillContentDirective,
2737  ngInitDirective,
2738  ngNonBindableDirective,
2739  ngPluralizeDirective,
2740  ngRefDirective,
2741  ngRepeatDirective,
2742  ngShowDirective,
2743  ngStyleDirective,
2744  ngSwitchDirective,
2745  ngSwitchWhenDirective,
2746  ngSwitchDefaultDirective,
2747  ngOptionsDirective,
2748  ngTranscludeDirective,
2749  ngModelDirective,
2750  ngListDirective,
2751  ngChangeDirective,
2752  patternDirective,
2753  patternDirective,
2754  requiredDirective,
2755  requiredDirective,
2756  minlengthDirective,
2757  minlengthDirective,
2758  maxlengthDirective,
2759  maxlengthDirective,
2760  ngValueDirective,
2761  ngModelOptionsDirective,
2762  ngAttributeAliasDirectives,
2763  ngEventDirectives,
2764
2765  $AnchorScrollProvider,
2766  $AnimateProvider,
2767  $CoreAnimateCssProvider,
2768  $$CoreAnimateJsProvider,
2769  $$CoreAnimateQueueProvider,
2770  $$AnimateRunnerFactoryProvider,
2771  $$AnimateAsyncRunFactoryProvider,
2772  $BrowserProvider,
2773  $CacheFactoryProvider,
2774  $ControllerProvider,
2775  $DateProvider,
2776  $DocumentProvider,
2777  $$IsDocumentHiddenProvider,
2778  $ExceptionHandlerProvider,
2779  $FilterProvider,
2780  $$ForceReflowProvider,
2781  $InterpolateProvider,
2782  $$IntervalFactoryProvider,
2783  $IntervalProvider,
2784  $HttpProvider,
2785  $HttpParamSerializerProvider,
2786  $HttpParamSerializerJQLikeProvider,
2787  $HttpBackendProvider,
2788  $xhrFactoryProvider,
2789  $jsonpCallbacksProvider,
2790  $LocationProvider,
2791  $LogProvider,
2792  $$MapProvider,
2793  $ParseProvider,
2794  $RootScopeProvider,
2795  $QProvider,
2796  $$QProvider,
2797  $$SanitizeUriProvider,
2798  $SceProvider,
2799  $SceDelegateProvider,
2800  $SnifferProvider,
2801  $$TaskTrackerFactoryProvider,
2802  $TemplateCacheProvider,
2803  $TemplateRequestProvider,
2804  $$TestabilityProvider,
2805  $TimeoutProvider,
2806  $$RAFProvider,
2807  $WindowProvider,
2808  $$jqLiteProvider,
2809  $$CookieReaderProvider
2810*/
2811
2812
2813/**
2814 * @ngdoc object
2815 * @name angular.version
2816 * @module ng
2817 * @description
2818 * An object that contains information about the current AngularJS version.
2819 *
2820 * This object has the following properties:
2821 *
2822 * - `full` – `{string}` – Full version string, such as "0.9.18".
2823 * - `major` – `{number}` – Major version number, such as "0".
2824 * - `minor` – `{number}` – Minor version number, such as "9".
2825 * - `dot` – `{number}` – Dot version number, such as "18".
2826 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2827 */
2828var version = {
2829  // These placeholder strings will be replaced by grunt's `build` task.
2830  // They need to be double- or single-quoted.
2831  full: '1.8.3',
2832  major: 1,
2833  minor: 8,
2834  dot: 3,
2835  codeName: 'ultimate-farewell'
2836};
2837
2838
2839function publishExternalAPI(angular) {
2840  extend(angular, {
2841    'errorHandlingConfig': errorHandlingConfig,
2842    'bootstrap': bootstrap,
2843    'copy': copy,
2844    'extend': extend,
2845    'merge': merge,
2846    'equals': equals,
2847    'element': jqLite,
2848    'forEach': forEach,
2849    'injector': createInjector,
2850    'noop': noop,
2851    'bind': bind,
2852    'toJson': toJson,
2853    'fromJson': fromJson,
2854    'identity': identity,
2855    'isUndefined': isUndefined,
2856    'isDefined': isDefined,
2857    'isString': isString,
2858    'isFunction': isFunction,
2859    'isObject': isObject,
2860    'isNumber': isNumber,
2861    'isElement': isElement,
2862    'isArray': isArray,
2863    'version': version,
2864    'isDate': isDate,
2865    'callbacks': {$$counter: 0},
2866    'getTestability': getTestability,
2867    'reloadWithDebugInfo': reloadWithDebugInfo,
2868    'UNSAFE_restoreLegacyJqLiteXHTMLReplacement': UNSAFE_restoreLegacyJqLiteXHTMLReplacement,
2869    '$$minErr': minErr,
2870    '$$csp': csp,
2871    '$$encodeUriSegment': encodeUriSegment,
2872    '$$encodeUriQuery': encodeUriQuery,
2873    '$$lowercase': lowercase,
2874    '$$stringify': stringify,
2875    '$$uppercase': uppercase
2876  });
2877
2878  angularModule = setupModuleLoader(window);
2879
2880  angularModule('ng', ['ngLocale'], ['$provide',
2881    function ngModule($provide) {
2882      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2883      $provide.provider({
2884        $$sanitizeUri: $$SanitizeUriProvider
2885      });
2886      $provide.provider('$compile', $CompileProvider).
2887        directive({
2888            a: htmlAnchorDirective,
2889            input: inputDirective,
2890            textarea: inputDirective,
2891            form: formDirective,
2892            script: scriptDirective,
2893            select: selectDirective,
2894            option: optionDirective,
2895            ngBind: ngBindDirective,
2896            ngBindHtml: ngBindHtmlDirective,
2897            ngBindTemplate: ngBindTemplateDirective,
2898            ngClass: ngClassDirective,
2899            ngClassEven: ngClassEvenDirective,
2900            ngClassOdd: ngClassOddDirective,
2901            ngCloak: ngCloakDirective,
2902            ngController: ngControllerDirective,
2903            ngForm: ngFormDirective,
2904            ngHide: ngHideDirective,
2905            ngIf: ngIfDirective,
2906            ngInclude: ngIncludeDirective,
2907            ngInit: ngInitDirective,
2908            ngNonBindable: ngNonBindableDirective,
2909            ngPluralize: ngPluralizeDirective,
2910            ngRef: ngRefDirective,
2911            ngRepeat: ngRepeatDirective,
2912            ngShow: ngShowDirective,
2913            ngStyle: ngStyleDirective,
2914            ngSwitch: ngSwitchDirective,
2915            ngSwitchWhen: ngSwitchWhenDirective,
2916            ngSwitchDefault: ngSwitchDefaultDirective,
2917            ngOptions: ngOptionsDirective,
2918            ngTransclude: ngTranscludeDirective,
2919            ngModel: ngModelDirective,
2920            ngList: ngListDirective,
2921            ngChange: ngChangeDirective,
2922            pattern: patternDirective,
2923            ngPattern: patternDirective,
2924            required: requiredDirective,
2925            ngRequired: requiredDirective,
2926            minlength: minlengthDirective,
2927            ngMinlength: minlengthDirective,
2928            maxlength: maxlengthDirective,
2929            ngMaxlength: maxlengthDirective,
2930            ngValue: ngValueDirective,
2931            ngModelOptions: ngModelOptionsDirective
2932        }).
2933        directive({
2934          ngInclude: ngIncludeFillContentDirective,
2935          input: hiddenInputBrowserCacheDirective
2936        }).
2937        directive(ngAttributeAliasDirectives).
2938        directive(ngEventDirectives);
2939      $provide.provider({
2940        $anchorScroll: $AnchorScrollProvider,
2941        $animate: $AnimateProvider,
2942        $animateCss: $CoreAnimateCssProvider,
2943        $$animateJs: $$CoreAnimateJsProvider,
2944        $$animateQueue: $$CoreAnimateQueueProvider,
2945        $$AnimateRunner: $$AnimateRunnerFactoryProvider,
2946        $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
2947        $browser: $BrowserProvider,
2948        $cacheFactory: $CacheFactoryProvider,
2949        $controller: $ControllerProvider,
2950        $document: $DocumentProvider,
2951        $$isDocumentHidden: $$IsDocumentHiddenProvider,
2952        $exceptionHandler: $ExceptionHandlerProvider,
2953        $filter: $FilterProvider,
2954        $$forceReflow: $$ForceReflowProvider,
2955        $interpolate: $InterpolateProvider,
2956        $interval: $IntervalProvider,
2957        $$intervalFactory: $$IntervalFactoryProvider,
2958        $http: $HttpProvider,
2959        $httpParamSerializer: $HttpParamSerializerProvider,
2960        $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2961        $httpBackend: $HttpBackendProvider,
2962        $xhrFactory: $xhrFactoryProvider,
2963        $jsonpCallbacks: $jsonpCallbacksProvider,
2964        $location: $LocationProvider,
2965        $log: $LogProvider,
2966        $parse: $ParseProvider,
2967        $rootScope: $RootScopeProvider,
2968        $q: $QProvider,
2969        $$q: $$QProvider,
2970        $sce: $SceProvider,
2971        $sceDelegate: $SceDelegateProvider,
2972        $sniffer: $SnifferProvider,
2973        $$taskTrackerFactory: $$TaskTrackerFactoryProvider,
2974        $templateCache: $TemplateCacheProvider,
2975        $templateRequest: $TemplateRequestProvider,
2976        $$testability: $$TestabilityProvider,
2977        $timeout: $TimeoutProvider,
2978        $window: $WindowProvider,
2979        $$rAF: $$RAFProvider,
2980        $$jqLite: $$jqLiteProvider,
2981        $$Map: $$MapProvider,
2982        $$cookieReader: $$CookieReaderProvider
2983      });
2984    }
2985  ])
2986  .info({ angularVersion: '1.8.3' });
2987}
2988
2989/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2990 *     Any commits to this file should be reviewed with security in mind.  *
2991 *   Changes to this file can potentially create security vulnerabilities. *
2992 *          An approval from 2 Core members with history of modifying      *
2993 *                         this file is required.                          *
2994 *                                                                         *
2995 *  Does the change somehow allow for arbitrary javascript to be executed? *
2996 *    Or allows for someone to change the prototype of built-in objects?   *
2997 *     Or gives undesired access to variables likes document or window?    *
2998 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2999
3000/* global
3001  JQLitePrototype: true,
3002  BOOLEAN_ATTR: true,
3003  ALIASED_ATTR: true
3004*/
3005
3006//////////////////////////////////
3007//JQLite
3008//////////////////////////////////
3009
3010/**
3011 * @ngdoc function
3012 * @name angular.element
3013 * @module ng
3014 * @kind function
3015 *
3016 * @description
3017 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
3018 *
3019 * If jQuery is available, `angular.element` is an alias for the
3020 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
3021 * delegates to AngularJS's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
3022 *
3023 * jqLite is a tiny, API-compatible subset of jQuery that allows
3024 * AngularJS to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
3025 * commonly needed functionality with the goal of having a very small footprint.
3026 *
3027 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
3028 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
3029 * specific version of jQuery if multiple versions exist on the page.
3030 *
3031 * <div class="alert alert-info">**Note:** All element references in AngularJS are always wrapped with jQuery or
3032 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
3033 *
3034 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
3035 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
3036 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
3037 *
3038 * ## AngularJS's jqLite
3039 * jqLite provides only the following jQuery methods:
3040 *
3041 * - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument
3042 * - [`after()`](http://api.jquery.com/after/)
3043 * - [`append()`](http://api.jquery.com/append/) - Contrary to jQuery, this doesn't clone elements
3044 *   so will not work correctly when invoked on a jqLite object containing more than one DOM node
3045 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
3046 * - [`bind()`](http://api.jquery.com/bind/) (_deprecated_, use [`on()`](http://api.jquery.com/on/)) - Does not support namespaces, selectors or eventData
3047 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
3048 * - [`clone()`](http://api.jquery.com/clone/)
3049 * - [`contents()`](http://api.jquery.com/contents/)
3050 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
3051 *   As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
3052 * - [`data()`](http://api.jquery.com/data/)
3053 * - [`detach()`](http://api.jquery.com/detach/)
3054 * - [`empty()`](http://api.jquery.com/empty/)
3055 * - [`eq()`](http://api.jquery.com/eq/)
3056 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
3057 * - [`hasClass()`](http://api.jquery.com/hasClass/)
3058 * - [`html()`](http://api.jquery.com/html/)
3059 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
3060 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
3061 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
3062 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
3063 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
3064 * - [`prepend()`](http://api.jquery.com/prepend/)
3065 * - [`prop()`](http://api.jquery.com/prop/)
3066 * - [`ready()`](http://api.jquery.com/ready/) (_deprecated_, use `angular.element(callback)` instead of `angular.element(document).ready(callback)`)
3067 * - [`remove()`](http://api.jquery.com/remove/)
3068 * - [`removeAttr()`](http://api.jquery.com/removeAttr/) - Does not support multiple attributes
3069 * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument
3070 * - [`removeData()`](http://api.jquery.com/removeData/)
3071 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
3072 * - [`text()`](http://api.jquery.com/text/)
3073 * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument
3074 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers
3075 * - [`unbind()`](http://api.jquery.com/unbind/) (_deprecated_, use [`off()`](http://api.jquery.com/off/)) - Does not support namespaces or event object as parameter
3076 * - [`val()`](http://api.jquery.com/val/)
3077 * - [`wrap()`](http://api.jquery.com/wrap/)
3078 *
3079 * jqLite also provides a method restoring pre-1.8 insecure treatment of XHTML-like tags.
3080 * This legacy behavior turns input like `<div /><span />` to `<div></div><span></span>`
3081 * instead of `<div><span></span></div>` like version 1.8 & newer do. To restore it, invoke:
3082 * ```js
3083 * angular.UNSAFE_restoreLegacyJqLiteXHTMLReplacement();
3084 * ```
3085 * Note that this only patches jqLite. If you use jQuery 3.5.0 or newer, please read the
3086 * [jQuery 3.5 upgrade guide](https://jquery.com/upgrade-guide/3.5/) for more details
3087 * about the workarounds.
3088 *
3089 * ## jQuery/jqLite Extras
3090 * AngularJS also provides the following additional methods and events to both jQuery and jqLite:
3091 *
3092 * ### Events
3093 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
3094 *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
3095 *    element before it is removed.
3096 *
3097 * ### Methods
3098 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
3099 *   retrieves controller associated with the `ngController` directive. If `name` is provided as
3100 *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
3101 *   `'ngModel'`).
3102 * - `injector()` - retrieves the injector of the current element or its parent.
3103 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
3104 *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
3105 *   be enabled.
3106 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
3107 *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
3108 *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
3109 *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
3110 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
3111 *   parent element is reached.
3112 *
3113 * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
3114 * https://github.com/angular/angular.js/issues/14251 for more information.
3115 *
3116 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
3117 * @returns {Object} jQuery object.
3118 */
3119
3120JQLite.expando = 'ng339';
3121
3122var jqCache = JQLite.cache = {},
3123    jqId = 1;
3124
3125/*
3126 * !!! This is an undocumented "private" function !!!
3127 */
3128JQLite._data = function(node) {
3129  //jQuery always returns an object on cache miss
3130  return this.cache[node[this.expando]] || {};
3131};
3132
3133function jqNextId() { return ++jqId; }
3134
3135
3136var DASH_LOWERCASE_REGEXP = /-([a-z])/g;
3137var MS_HACK_REGEXP = /^-ms-/;
3138var MOUSE_EVENT_MAP = { mouseleave: 'mouseout', mouseenter: 'mouseover' };
3139var jqLiteMinErr = minErr('jqLite');
3140
3141/**
3142 * Converts kebab-case to camelCase.
3143 * There is also a special case for the ms prefix starting with a lowercase letter.
3144 * @param name Name to normalize
3145 */
3146function cssKebabToCamel(name) {
3147    return kebabToCamel(name.replace(MS_HACK_REGEXP, 'ms-'));
3148}
3149
3150function fnCamelCaseReplace(all, letter) {
3151  return letter.toUpperCase();
3152}
3153
3154/**
3155 * Converts kebab-case to camelCase.
3156 * @param name Name to normalize
3157 */
3158function kebabToCamel(name) {
3159  return name
3160    .replace(DASH_LOWERCASE_REGEXP, fnCamelCaseReplace);
3161}
3162
3163var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
3164var HTML_REGEXP = /<|&#?\w+;/;
3165var TAG_NAME_REGEXP = /<([\w:-]+)/;
3166var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
3167
3168// Table parts need to be wrapped with `<table>` or they're
3169// stripped to their contents when put in a div.
3170// XHTML parsers do not magically insert elements in the
3171// same way that tag soup parsers do, so we cannot shorten
3172// this by omitting <tbody> or other required elements.
3173var wrapMap = {
3174  thead: ['table'],
3175  col: ['colgroup', 'table'],
3176  tr: ['tbody', 'table'],
3177  td: ['tr', 'tbody', 'table']
3178};
3179
3180wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
3181wrapMap.th = wrapMap.td;
3182
3183// Support: IE <10 only
3184// IE 9 requires an option wrapper & it needs to have the whole table structure
3185// set up in advance; assigning `"<td></td>"` to `tr.innerHTML` doesn't work, etc.
3186var wrapMapIE9 = {
3187  option: [1, '<select multiple="multiple">', '</select>'],
3188  _default: [0, '', '']
3189};
3190
3191for (var key in wrapMap) {
3192  var wrapMapValueClosing = wrapMap[key];
3193  var wrapMapValue = wrapMapValueClosing.slice().reverse();
3194  wrapMapIE9[key] = [wrapMapValue.length, '<' + wrapMapValue.join('><') + '>', '</' + wrapMapValueClosing.join('></') + '>'];
3195}
3196
3197wrapMapIE9.optgroup = wrapMapIE9.option;
3198
3199function jqLiteIsTextNode(html) {
3200  return !HTML_REGEXP.test(html);
3201}
3202
3203function jqLiteAcceptsData(node) {
3204  // The window object can accept data but has no nodeType
3205  // Otherwise we are only interested in elements (1) and documents (9)
3206  var nodeType = node.nodeType;
3207  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
3208}
3209
3210function jqLiteHasData(node) {
3211  for (var key in jqCache[node.ng339]) {
3212    return true;
3213  }
3214  return false;
3215}
3216
3217function jqLiteBuildFragment(html, context) {
3218  var tmp, tag, wrap, finalHtml,
3219      fragment = context.createDocumentFragment(),
3220      nodes = [], i;
3221
3222  if (jqLiteIsTextNode(html)) {
3223    // Convert non-html into a text node
3224    nodes.push(context.createTextNode(html));
3225  } else {
3226    // Convert html into DOM nodes
3227    tmp = fragment.appendChild(context.createElement('div'));
3228    tag = (TAG_NAME_REGEXP.exec(html) || ['', ''])[1].toLowerCase();
3229    finalHtml = JQLite.legacyXHTMLReplacement ?
3230      html.replace(XHTML_TAG_REGEXP, '<$1></$2>') :
3231      html;
3232
3233    if (msie < 10) {
3234      wrap = wrapMapIE9[tag] || wrapMapIE9._default;
3235      tmp.innerHTML = wrap[1] + finalHtml + wrap[2];
3236
3237      // Descend through wrappers to the right content
3238      i = wrap[0];
3239      while (i--) {
3240        tmp = tmp.firstChild;
3241      }
3242    } else {
3243      wrap = wrapMap[tag] || [];
3244
3245      // Create wrappers & descend into them
3246      i = wrap.length;
3247      while (--i > -1) {
3248        tmp.appendChild(window.document.createElement(wrap[i]));
3249        tmp = tmp.firstChild;
3250      }
3251
3252      tmp.innerHTML = finalHtml;
3253    }
3254
3255    nodes = concat(nodes, tmp.childNodes);
3256
3257    tmp = fragment.firstChild;
3258    tmp.textContent = '';
3259  }
3260
3261  // Remove wrapper from fragment
3262  fragment.textContent = '';
3263  fragment.innerHTML = ''; // Clear inner HTML
3264  forEach(nodes, function(node) {
3265    fragment.appendChild(node);
3266  });
3267
3268  return fragment;
3269}
3270
3271function jqLiteParseHTML(html, context) {
3272  context = context || window.document;
3273  var parsed;
3274
3275  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
3276    return [context.createElement(parsed[1])];
3277  }
3278
3279  if ((parsed = jqLiteBuildFragment(html, context))) {
3280    return parsed.childNodes;
3281  }
3282
3283  return [];
3284}
3285
3286function jqLiteWrapNode(node, wrapper) {
3287  var parent = node.parentNode;
3288
3289  if (parent) {
3290    parent.replaceChild(wrapper, node);
3291  }
3292
3293  wrapper.appendChild(node);
3294}
3295
3296
3297// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
3298var jqLiteContains = window.Node.prototype.contains || /** @this */ function(arg) {
3299  // eslint-disable-next-line no-bitwise
3300  return !!(this.compareDocumentPosition(arg) & 16);
3301};
3302
3303/////////////////////////////////////////////
3304function JQLite(element) {
3305  if (element instanceof JQLite) {
3306    return element;
3307  }
3308
3309  var argIsString;
3310
3311  if (isString(element)) {
3312    element = trim(element);
3313    argIsString = true;
3314  }
3315  if (!(this instanceof JQLite)) {
3316    if (argIsString && element.charAt(0) !== '<') {
3317      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
3318    }
3319    return new JQLite(element);
3320  }
3321
3322  if (argIsString) {
3323    jqLiteAddNodes(this, jqLiteParseHTML(element));
3324  } else if (isFunction(element)) {
3325    jqLiteReady(element);
3326  } else {
3327    jqLiteAddNodes(this, element);
3328  }
3329}
3330
3331function jqLiteClone(element) {
3332  return element.cloneNode(true);
3333}
3334
3335function jqLiteDealoc(element, onlyDescendants) {
3336  if (!onlyDescendants && jqLiteAcceptsData(element)) jqLite.cleanData([element]);
3337
3338  if (element.querySelectorAll) {
3339    jqLite.cleanData(element.querySelectorAll('*'));
3340  }
3341}
3342
3343function isEmptyObject(obj) {
3344  var name;
3345
3346  for (name in obj) {
3347    return false;
3348  }
3349  return true;
3350}
3351
3352function removeIfEmptyData(element) {
3353  var expandoId = element.ng339;
3354  var expandoStore = expandoId && jqCache[expandoId];
3355
3356  var events = expandoStore && expandoStore.events;
3357  var data = expandoStore && expandoStore.data;
3358
3359  if ((!data || isEmptyObject(data)) && (!events || isEmptyObject(events))) {
3360    delete jqCache[expandoId];
3361    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
3362  }
3363}
3364
3365function jqLiteOff(element, type, fn, unsupported) {
3366  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
3367
3368  var expandoStore = jqLiteExpandoStore(element);
3369  var events = expandoStore && expandoStore.events;
3370  var handle = expandoStore && expandoStore.handle;
3371
3372  if (!handle) return; //no listeners registered
3373
3374  if (!type) {
3375    for (type in events) {
3376      if (type !== '$destroy') {
3377        element.removeEventListener(type, handle);
3378      }
3379      delete events[type];
3380    }
3381  } else {
3382
3383    var removeHandler = function(type) {
3384      var listenerFns = events[type];
3385      if (isDefined(fn)) {
3386        arrayRemove(listenerFns || [], fn);
3387      }
3388      if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
3389        element.removeEventListener(type, handle);
3390        delete events[type];
3391      }
3392    };
3393
3394    forEach(type.split(' '), function(type) {
3395      removeHandler(type);
3396      if (MOUSE_EVENT_MAP[type]) {
3397        removeHandler(MOUSE_EVENT_MAP[type]);
3398      }
3399    });
3400  }
3401
3402  removeIfEmptyData(element);
3403}
3404
3405function jqLiteRemoveData(element, name) {
3406  var expandoId = element.ng339;
3407  var expandoStore = expandoId && jqCache[expandoId];
3408
3409  if (expandoStore) {
3410    if (name) {
3411      delete expandoStore.data[name];
3412    } else {
3413      expandoStore.data = {};
3414    }
3415
3416    removeIfEmptyData(element);
3417  }
3418}
3419
3420
3421function jqLiteExpandoStore(element, createIfNecessary) {
3422  var expandoId = element.ng339,
3423      expandoStore = expandoId && jqCache[expandoId];
3424
3425  if (createIfNecessary && !expandoStore) {
3426    element.ng339 = expandoId = jqNextId();
3427    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
3428  }
3429
3430  return expandoStore;
3431}
3432
3433
3434function jqLiteData(element, key, value) {
3435  if (jqLiteAcceptsData(element)) {
3436    var prop;
3437
3438    var isSimpleSetter = isDefined(value);
3439    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
3440    var massGetter = !key;
3441    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
3442    var data = expandoStore && expandoStore.data;
3443
3444    if (isSimpleSetter) { // data('key', value)
3445      data[kebabToCamel(key)] = value;
3446    } else {
3447      if (massGetter) {  // data()
3448        return data;
3449      } else {
3450        if (isSimpleGetter) { // data('key')
3451          // don't force creation of expandoStore if it doesn't exist yet
3452          return data && data[kebabToCamel(key)];
3453        } else { // mass-setter: data({key1: val1, key2: val2})
3454          for (prop in key) {
3455            data[kebabToCamel(prop)] = key[prop];
3456          }
3457        }
3458      }
3459    }
3460  }
3461}
3462
3463function jqLiteHasClass(element, selector) {
3464  if (!element.getAttribute) return false;
3465  return ((' ' + (element.getAttribute('class') || '') + ' ').replace(/[\n\t]/g, ' ').
3466      indexOf(' ' + selector + ' ') > -1);
3467}
3468
3469function jqLiteRemoveClass(element, cssClasses) {
3470  if (cssClasses && element.setAttribute) {
3471    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3472                            .replace(/[\n\t]/g, ' ');
3473    var newClasses = existingClasses;
3474
3475    forEach(cssClasses.split(' '), function(cssClass) {
3476      cssClass = trim(cssClass);
3477      newClasses = newClasses.replace(' ' + cssClass + ' ', ' ');
3478    });
3479
3480    if (newClasses !== existingClasses) {
3481      element.setAttribute('class', trim(newClasses));
3482    }
3483  }
3484}
3485
3486function jqLiteAddClass(element, cssClasses) {
3487  if (cssClasses && element.setAttribute) {
3488    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3489                            .replace(/[\n\t]/g, ' ');
3490    var newClasses = existingClasses;
3491
3492    forEach(cssClasses.split(' '), function(cssClass) {
3493      cssClass = trim(cssClass);
3494      if (newClasses.indexOf(' ' + cssClass + ' ') === -1) {
3495        newClasses += cssClass + ' ';
3496      }
3497    });
3498
3499    if (newClasses !== existingClasses) {
3500      element.setAttribute('class', trim(newClasses));
3501    }
3502  }
3503}
3504
3505
3506function jqLiteAddNodes(root, elements) {
3507  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3508
3509  if (elements) {
3510
3511    // if a Node (the most common case)
3512    if (elements.nodeType) {
3513      root[root.length++] = elements;
3514    } else {
3515      var length = elements.length;
3516
3517      // if an Array or NodeList and not a Window
3518      if (typeof length === 'number' && elements.window !== elements) {
3519        if (length) {
3520          for (var i = 0; i < length; i++) {
3521            root[root.length++] = elements[i];
3522          }
3523        }
3524      } else {
3525        root[root.length++] = elements;
3526      }
3527    }
3528  }
3529}
3530
3531
3532function jqLiteController(element, name) {
3533  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3534}
3535
3536function jqLiteInheritedData(element, name, value) {
3537  // if element is the document object work with the html element instead
3538  // this makes $(document).scope() possible
3539  if (element.nodeType === NODE_TYPE_DOCUMENT) {
3540    element = element.documentElement;
3541  }
3542  var names = isArray(name) ? name : [name];
3543
3544  while (element) {
3545    for (var i = 0, ii = names.length; i < ii; i++) {
3546      if (isDefined(value = jqLite.data(element, names[i]))) return value;
3547    }
3548
3549    // If dealing with a document fragment node with a host element, and no parent, use the host
3550    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3551    // to lookup parent controllers.
3552    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3553  }
3554}
3555
3556function jqLiteEmpty(element) {
3557  jqLiteDealoc(element, true);
3558  while (element.firstChild) {
3559    element.removeChild(element.firstChild);
3560  }
3561}
3562
3563function jqLiteRemove(element, keepData) {
3564  if (!keepData) jqLiteDealoc(element);
3565  var parent = element.parentNode;
3566  if (parent) parent.removeChild(element);
3567}
3568
3569
3570function jqLiteDocumentLoaded(action, win) {
3571  win = win || window;
3572  if (win.document.readyState === 'complete') {
3573    // Force the action to be run async for consistent behavior
3574    // from the action's point of view
3575    // i.e. it will definitely not be in a $apply
3576    win.setTimeout(action);
3577  } else {
3578    // No need to unbind this handler as load is only ever called once
3579    jqLite(win).on('load', action);
3580  }
3581}
3582
3583function jqLiteReady(fn) {
3584  function trigger() {
3585    window.document.removeEventListener('DOMContentLoaded', trigger);
3586    window.removeEventListener('load', trigger);
3587    fn();
3588  }
3589
3590  // check if document is already loaded
3591  if (window.document.readyState === 'complete') {
3592    window.setTimeout(fn);
3593  } else {
3594    // We can not use jqLite since we are not done loading and jQuery could be loaded later.
3595
3596    // Works for modern browsers and IE9
3597    window.document.addEventListener('DOMContentLoaded', trigger);
3598
3599    // Fallback to window.onload for others
3600    window.addEventListener('load', trigger);
3601  }
3602}
3603
3604//////////////////////////////////////////
3605// Functions which are declared directly.
3606//////////////////////////////////////////
3607var JQLitePrototype = JQLite.prototype = {
3608  ready: jqLiteReady,
3609  toString: function() {
3610    var value = [];
3611    forEach(this, function(e) { value.push('' + e);});
3612    return '[' + value.join(', ') + ']';
3613  },
3614
3615  eq: function(index) {
3616      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3617  },
3618
3619  length: 0,
3620  push: push,
3621  sort: [].sort,
3622  splice: [].splice
3623};
3624
3625//////////////////////////////////////////
3626// Functions iterating getter/setters.
3627// these functions return self on setter and
3628// value on get.
3629//////////////////////////////////////////
3630var BOOLEAN_ATTR = {};
3631forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3632  BOOLEAN_ATTR[lowercase(value)] = value;
3633});
3634var BOOLEAN_ELEMENTS = {};
3635forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3636  BOOLEAN_ELEMENTS[value] = true;
3637});
3638var ALIASED_ATTR = {
3639  'ngMinlength': 'minlength',
3640  'ngMaxlength': 'maxlength',
3641  'ngMin': 'min',
3642  'ngMax': 'max',
3643  'ngPattern': 'pattern',
3644  'ngStep': 'step'
3645};
3646
3647function getBooleanAttrName(element, name) {
3648  // check dom last since we will most likely fail on name
3649  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3650
3651  // booleanAttr is here twice to minimize DOM access
3652  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3653}
3654
3655function getAliasedAttrName(name) {
3656  return ALIASED_ATTR[name];
3657}
3658
3659forEach({
3660  data: jqLiteData,
3661  removeData: jqLiteRemoveData,
3662  hasData: jqLiteHasData,
3663  cleanData: function jqLiteCleanData(nodes) {
3664    for (var i = 0, ii = nodes.length; i < ii; i++) {
3665      jqLiteRemoveData(nodes[i]);
3666      jqLiteOff(nodes[i]);
3667    }
3668  }
3669}, function(fn, name) {
3670  JQLite[name] = fn;
3671});
3672
3673forEach({
3674  data: jqLiteData,
3675  inheritedData: jqLiteInheritedData,
3676
3677  scope: function(element) {
3678    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3679    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3680  },
3681
3682  isolateScope: function(element) {
3683    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3684    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3685  },
3686
3687  controller: jqLiteController,
3688
3689  injector: function(element) {
3690    return jqLiteInheritedData(element, '$injector');
3691  },
3692
3693  removeAttr: function(element, name) {
3694    element.removeAttribute(name);
3695  },
3696
3697  hasClass: jqLiteHasClass,
3698
3699  css: function(element, name, value) {
3700    name = cssKebabToCamel(name);
3701
3702    if (isDefined(value)) {
3703      element.style[name] = value;
3704    } else {
3705      return element.style[name];
3706    }
3707  },
3708
3709  attr: function(element, name, value) {
3710    var ret;
3711    var nodeType = element.nodeType;
3712    if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT ||
3713      !element.getAttribute) {
3714      return;
3715    }
3716
3717    var lowercasedName = lowercase(name);
3718    var isBooleanAttr = BOOLEAN_ATTR[lowercasedName];
3719
3720    if (isDefined(value)) {
3721      // setter
3722
3723      if (value === null || (value === false && isBooleanAttr)) {
3724        element.removeAttribute(name);
3725      } else {
3726        element.setAttribute(name, isBooleanAttr ? lowercasedName : value);
3727      }
3728    } else {
3729      // getter
3730
3731      ret = element.getAttribute(name);
3732
3733      if (isBooleanAttr && ret !== null) {
3734        ret = lowercasedName;
3735      }
3736      // Normalize non-existing attributes to undefined (as jQuery).
3737      return ret === null ? undefined : ret;
3738    }
3739  },
3740
3741  prop: function(element, name, value) {
3742    if (isDefined(value)) {
3743      element[name] = value;
3744    } else {
3745      return element[name];
3746    }
3747  },
3748
3749  text: (function() {
3750    getText.$dv = '';
3751    return getText;
3752
3753    function getText(element, value) {
3754      if (isUndefined(value)) {
3755        var nodeType = element.nodeType;
3756        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3757      }
3758      element.textContent = value;
3759    }
3760  })(),
3761
3762  val: function(element, value) {
3763    if (isUndefined(value)) {
3764      if (element.multiple && nodeName_(element) === 'select') {
3765        var result = [];
3766        forEach(element.options, function(option) {
3767          if (option.selected) {
3768            result.push(option.value || option.text);
3769          }
3770        });
3771        return result;
3772      }
3773      return element.value;
3774    }
3775    element.value = value;
3776  },
3777
3778  html: function(element, value) {
3779    if (isUndefined(value)) {
3780      return element.innerHTML;
3781    }
3782    jqLiteDealoc(element, true);
3783    element.innerHTML = value;
3784  },
3785
3786  empty: jqLiteEmpty
3787}, function(fn, name) {
3788  /**
3789   * Properties: writes return selection, reads return first value
3790   */
3791  JQLite.prototype[name] = function(arg1, arg2) {
3792    var i, key;
3793    var nodeCount = this.length;
3794
3795    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3796    // in a way that survives minification.
3797    // jqLiteEmpty takes no arguments but is a setter.
3798    if (fn !== jqLiteEmpty &&
3799        (isUndefined((fn.length === 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3800      if (isObject(arg1)) {
3801
3802        // we are a write, but the object properties are the key/values
3803        for (i = 0; i < nodeCount; i++) {
3804          if (fn === jqLiteData) {
3805            // data() takes the whole object in jQuery
3806            fn(this[i], arg1);
3807          } else {
3808            for (key in arg1) {
3809              fn(this[i], key, arg1[key]);
3810            }
3811          }
3812        }
3813        // return self for chaining
3814        return this;
3815      } else {
3816        // we are a read, so read the first child.
3817        // TODO: do we still need this?
3818        var value = fn.$dv;
3819        // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3820        var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3821        for (var j = 0; j < jj; j++) {
3822          var nodeValue = fn(this[j], arg1, arg2);
3823          value = value ? value + nodeValue : nodeValue;
3824        }
3825        return value;
3826      }
3827    } else {
3828      // we are a write, so apply to all children
3829      for (i = 0; i < nodeCount; i++) {
3830        fn(this[i], arg1, arg2);
3831      }
3832      // return self for chaining
3833      return this;
3834    }
3835  };
3836});
3837
3838function createEventHandler(element, events) {
3839  var eventHandler = function(event, type) {
3840    // jQuery specific api
3841    event.isDefaultPrevented = function() {
3842      return event.defaultPrevented;
3843    };
3844
3845    var eventFns = events[type || event.type];
3846    var eventFnsLength = eventFns ? eventFns.length : 0;
3847
3848    if (!eventFnsLength) return;
3849
3850    if (isUndefined(event.immediatePropagationStopped)) {
3851      var originalStopImmediatePropagation = event.stopImmediatePropagation;
3852      event.stopImmediatePropagation = function() {
3853        event.immediatePropagationStopped = true;
3854
3855        if (event.stopPropagation) {
3856          event.stopPropagation();
3857        }
3858
3859        if (originalStopImmediatePropagation) {
3860          originalStopImmediatePropagation.call(event);
3861        }
3862      };
3863    }
3864
3865    event.isImmediatePropagationStopped = function() {
3866      return event.immediatePropagationStopped === true;
3867    };
3868
3869    // Some events have special handlers that wrap the real handler
3870    var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3871
3872    // Copy event handlers in case event handlers array is modified during execution.
3873    if ((eventFnsLength > 1)) {
3874      eventFns = shallowCopy(eventFns);
3875    }
3876
3877    for (var i = 0; i < eventFnsLength; i++) {
3878      if (!event.isImmediatePropagationStopped()) {
3879        handlerWrapper(element, event, eventFns[i]);
3880      }
3881    }
3882  };
3883
3884  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3885  //       events on `element`
3886  eventHandler.elem = element;
3887  return eventHandler;
3888}
3889
3890function defaultHandlerWrapper(element, event, handler) {
3891  handler.call(element, event);
3892}
3893
3894function specialMouseHandlerWrapper(target, event, handler) {
3895  // Refer to jQuery's implementation of mouseenter & mouseleave
3896  // Read about mouseenter and mouseleave:
3897  // http://www.quirksmode.org/js/events_mouse.html#link8
3898  var related = event.relatedTarget;
3899  // For mousenter/leave call the handler if related is outside the target.
3900  // NB: No relatedTarget if the mouse left/entered the browser window
3901  if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3902    handler.call(target, event);
3903  }
3904}
3905
3906//////////////////////////////////////////
3907// Functions iterating traversal.
3908// These functions chain results into a single
3909// selector.
3910//////////////////////////////////////////
3911forEach({
3912  removeData: jqLiteRemoveData,
3913
3914  on: function jqLiteOn(element, type, fn, unsupported) {
3915    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3916
3917    // Do not add event handlers to non-elements because they will not be cleaned up.
3918    if (!jqLiteAcceptsData(element)) {
3919      return;
3920    }
3921
3922    var expandoStore = jqLiteExpandoStore(element, true);
3923    var events = expandoStore.events;
3924    var handle = expandoStore.handle;
3925
3926    if (!handle) {
3927      handle = expandoStore.handle = createEventHandler(element, events);
3928    }
3929
3930    // http://jsperf.com/string-indexof-vs-split
3931    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3932    var i = types.length;
3933
3934    var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3935      var eventFns = events[type];
3936
3937      if (!eventFns) {
3938        eventFns = events[type] = [];
3939        eventFns.specialHandlerWrapper = specialHandlerWrapper;
3940        if (type !== '$destroy' && !noEventListener) {
3941          element.addEventListener(type, handle);
3942        }
3943      }
3944
3945      eventFns.push(fn);
3946    };
3947
3948    while (i--) {
3949      type = types[i];
3950      if (MOUSE_EVENT_MAP[type]) {
3951        addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3952        addHandler(type, undefined, true);
3953      } else {
3954        addHandler(type);
3955      }
3956    }
3957  },
3958
3959  off: jqLiteOff,
3960
3961  one: function(element, type, fn) {
3962    element = jqLite(element);
3963
3964    //add the listener twice so that when it is called
3965    //you can remove the original function and still be
3966    //able to call element.off(ev, fn) normally
3967    element.on(type, function onFn() {
3968      element.off(type, fn);
3969      element.off(type, onFn);
3970    });
3971    element.on(type, fn);
3972  },
3973
3974  replaceWith: function(element, replaceNode) {
3975    var index, parent = element.parentNode;
3976    jqLiteDealoc(element);
3977    forEach(new JQLite(replaceNode), function(node) {
3978      if (index) {
3979        parent.insertBefore(node, index.nextSibling);
3980      } else {
3981        parent.replaceChild(node, element);
3982      }
3983      index = node;
3984    });
3985  },
3986
3987  children: function(element) {
3988    var children = [];
3989    forEach(element.childNodes, function(element) {
3990      if (element.nodeType === NODE_TYPE_ELEMENT) {
3991        children.push(element);
3992      }
3993    });
3994    return children;
3995  },
3996
3997  contents: function(element) {
3998    return element.contentDocument || element.childNodes || [];
3999  },
4000
4001  append: function(element, node) {
4002    var nodeType = element.nodeType;
4003    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
4004
4005    node = new JQLite(node);
4006
4007    for (var i = 0, ii = node.length; i < ii; i++) {
4008      var child = node[i];
4009      element.appendChild(child);
4010    }
4011  },
4012
4013  prepend: function(element, node) {
4014    if (element.nodeType === NODE_TYPE_ELEMENT) {
4015      var index = element.firstChild;
4016      forEach(new JQLite(node), function(child) {
4017        element.insertBefore(child, index);
4018      });
4019    }
4020  },
4021
4022  wrap: function(element, wrapNode) {
4023    jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
4024  },
4025
4026  remove: jqLiteRemove,
4027
4028  detach: function(element) {
4029    jqLiteRemove(element, true);
4030  },
4031
4032  after: function(element, newElement) {
4033    var index = element, parent = element.parentNode;
4034
4035    if (parent) {
4036      newElement = new JQLite(newElement);
4037
4038      for (var i = 0, ii = newElement.length; i < ii; i++) {
4039        var node = newElement[i];
4040        parent.insertBefore(node, index.nextSibling);
4041        index = node;
4042      }
4043    }
4044  },
4045
4046  addClass: jqLiteAddClass,
4047  removeClass: jqLiteRemoveClass,
4048
4049  toggleClass: function(element, selector, condition) {
4050    if (selector) {
4051      forEach(selector.split(' '), function(className) {
4052        var classCondition = condition;
4053        if (isUndefined(classCondition)) {
4054          classCondition = !jqLiteHasClass(element, className);
4055        }
4056        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
4057      });
4058    }
4059  },
4060
4061  parent: function(element) {
4062    var parent = element.parentNode;
4063    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
4064  },
4065
4066  next: function(element) {
4067    return element.nextElementSibling;
4068  },
4069
4070  find: function(element, selector) {
4071    if (element.getElementsByTagName) {
4072      return element.getElementsByTagName(selector);
4073    } else {
4074      return [];
4075    }
4076  },
4077
4078  clone: jqLiteClone,
4079
4080  triggerHandler: function(element, event, extraParameters) {
4081
4082    var dummyEvent, eventFnsCopy, handlerArgs;
4083    var eventName = event.type || event;
4084    var expandoStore = jqLiteExpandoStore(element);
4085    var events = expandoStore && expandoStore.events;
4086    var eventFns = events && events[eventName];
4087
4088    if (eventFns) {
4089      // Create a dummy event to pass to the handlers
4090      dummyEvent = {
4091        preventDefault: function() { this.defaultPrevented = true; },
4092        isDefaultPrevented: function() { return this.defaultPrevented === true; },
4093        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
4094        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
4095        stopPropagation: noop,
4096        type: eventName,
4097        target: element
4098      };
4099
4100      // If a custom event was provided then extend our dummy event with it
4101      if (event.type) {
4102        dummyEvent = extend(dummyEvent, event);
4103      }
4104
4105      // Copy event handlers in case event handlers array is modified during execution.
4106      eventFnsCopy = shallowCopy(eventFns);
4107      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
4108
4109      forEach(eventFnsCopy, function(fn) {
4110        if (!dummyEvent.isImmediatePropagationStopped()) {
4111          fn.apply(element, handlerArgs);
4112        }
4113      });
4114    }
4115  }
4116}, function(fn, name) {
4117  /**
4118   * chaining functions
4119   */
4120  JQLite.prototype[name] = function(arg1, arg2, arg3) {
4121    var value;
4122
4123    for (var i = 0, ii = this.length; i < ii; i++) {
4124      if (isUndefined(value)) {
4125        value = fn(this[i], arg1, arg2, arg3);
4126        if (isDefined(value)) {
4127          // any function which returns a value needs to be wrapped
4128          value = jqLite(value);
4129        }
4130      } else {
4131        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
4132      }
4133    }
4134    return isDefined(value) ? value : this;
4135  };
4136});
4137
4138// bind legacy bind/unbind to on/off
4139JQLite.prototype.bind = JQLite.prototype.on;
4140JQLite.prototype.unbind = JQLite.prototype.off;
4141
4142
4143// Provider for private $$jqLite service
4144/** @this */
4145function $$jqLiteProvider() {
4146  this.$get = function $$jqLite() {
4147    return extend(JQLite, {
4148      hasClass: function(node, classes) {
4149        if (node.attr) node = node[0];
4150        return jqLiteHasClass(node, classes);
4151      },
4152      addClass: function(node, classes) {
4153        if (node.attr) node = node[0];
4154        return jqLiteAddClass(node, classes);
4155      },
4156      removeClass: function(node, classes) {
4157        if (node.attr) node = node[0];
4158        return jqLiteRemoveClass(node, classes);
4159      }
4160    });
4161  };
4162}
4163
4164/**
4165 * Computes a hash of an 'obj'.
4166 * Hash of a:
4167 *  string is string
4168 *  number is number as string
4169 *  object is either result of calling $$hashKey function on the object or uniquely generated id,
4170 *         that is also assigned to the $$hashKey property of the object.
4171 *
4172 * @param obj
4173 * @returns {string} hash string such that the same input will have the same hash string.
4174 *         The resulting string key is in 'type:hashKey' format.
4175 */
4176function hashKey(obj, nextUidFn) {
4177  var key = obj && obj.$$hashKey;
4178
4179  if (key) {
4180    if (typeof key === 'function') {
4181      key = obj.$$hashKey();
4182    }
4183    return key;
4184  }
4185
4186  var objType = typeof obj;
4187  if (objType === 'function' || (objType === 'object' && obj !== null)) {
4188    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
4189  } else {
4190    key = objType + ':' + obj;
4191  }
4192
4193  return key;
4194}
4195
4196// A minimal ES2015 Map implementation.
4197// Should be bug/feature equivalent to the native implementations of supported browsers
4198// (for the features required in Angular).
4199// See https://kangax.github.io/compat-table/es6/#test-Map
4200var nanKey = Object.create(null);
4201function NgMapShim() {
4202  this._keys = [];
4203  this._values = [];
4204  this._lastKey = NaN;
4205  this._lastIndex = -1;
4206}
4207NgMapShim.prototype = {
4208  _idx: function(key) {
4209    if (key !== this._lastKey) {
4210      this._lastKey = key;
4211      this._lastIndex = this._keys.indexOf(key);
4212    }
4213    return this._lastIndex;
4214  },
4215  _transformKey: function(key) {
4216    return isNumberNaN(key) ? nanKey : key;
4217  },
4218  get: function(key) {
4219    key = this._transformKey(key);
4220    var idx = this._idx(key);
4221    if (idx !== -1) {
4222      return this._values[idx];
4223    }
4224  },
4225  has: function(key) {
4226    key = this._transformKey(key);
4227    var idx = this._idx(key);
4228    return idx !== -1;
4229  },
4230  set: function(key, value) {
4231    key = this._transformKey(key);
4232    var idx = this._idx(key);
4233    if (idx === -1) {
4234      idx = this._lastIndex = this._keys.length;
4235    }
4236    this._keys[idx] = key;
4237    this._values[idx] = value;
4238
4239    // Support: IE11
4240    // Do not `return this` to simulate the partial IE11 implementation
4241  },
4242  delete: function(key) {
4243    key = this._transformKey(key);
4244    var idx = this._idx(key);
4245    if (idx === -1) {
4246      return false;
4247    }
4248    this._keys.splice(idx, 1);
4249    this._values.splice(idx, 1);
4250    this._lastKey = NaN;
4251    this._lastIndex = -1;
4252    return true;
4253  }
4254};
4255
4256// For now, always use `NgMapShim`, even if `window.Map` is available. Some native implementations
4257// are still buggy (often in subtle ways) and can cause hard-to-debug failures. When native `Map`
4258// implementations get more stable, we can reconsider switching to `window.Map` (when available).
4259var NgMap = NgMapShim;
4260
4261var $$MapProvider = [/** @this */function() {
4262  this.$get = [function() {
4263    return NgMap;
4264  }];
4265}];
4266
4267/**
4268 * @ngdoc function
4269 * @module ng
4270 * @name angular.injector
4271 * @kind function
4272 *
4273 * @description
4274 * Creates an injector object that can be used for retrieving services as well as for
4275 * dependency injection (see {@link guide/di dependency injection}).
4276 *
4277 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
4278 *     {@link angular.module}. The `ng` module must be explicitly added.
4279 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
4280 *     disallows argument name annotation inference.
4281 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
4282 *
4283 * @example
4284 * Typical usage
4285 * ```js
4286 *   // create an injector
4287 *   var $injector = angular.injector(['ng']);
4288 *
4289 *   // use the injector to kick off your application
4290 *   // use the type inference to auto inject arguments, or use implicit injection
4291 *   $injector.invoke(function($rootScope, $compile, $document) {
4292 *     $compile($document)($rootScope);
4293 *     $rootScope.$digest();
4294 *   });
4295 * ```
4296 *
4297 * Sometimes you want to get access to the injector of a currently running AngularJS app
4298 * from outside AngularJS. Perhaps, you want to inject and compile some markup after the
4299 * application has been bootstrapped. You can do this using the extra `injector()` added
4300 * to JQuery/jqLite elements. See {@link angular.element}.
4301 *
4302 * *This is fairly rare but could be the case if a third party library is injecting the
4303 * markup.*
4304 *
4305 * In the following example a new block of HTML containing a `ng-controller`
4306 * directive is added to the end of the document body by JQuery. We then compile and link
4307 * it into the current AngularJS scope.
4308 *
4309 * ```js
4310 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
4311 * $(document.body).append($div);
4312 *
4313 * angular.element(document).injector().invoke(function($compile) {
4314 *   var scope = angular.element($div).scope();
4315 *   $compile($div)(scope);
4316 * });
4317 * ```
4318 */
4319
4320
4321/**
4322 * @ngdoc module
4323 * @name auto
4324 * @installation
4325 * @description
4326 *
4327 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
4328 */
4329
4330var ARROW_ARG = /^([^(]+?)=>/;
4331var FN_ARGS = /^[^(]*\(\s*([^)]*)\)/m;
4332var FN_ARG_SPLIT = /,/;
4333var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
4334var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
4335var $injectorMinErr = minErr('$injector');
4336
4337function stringifyFn(fn) {
4338  return Function.prototype.toString.call(fn);
4339}
4340
4341function extractArgs(fn) {
4342  var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''),
4343      args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
4344  return args;
4345}
4346
4347function anonFn(fn) {
4348  // For anonymous functions, showing at the very least the function signature can help in
4349  // debugging.
4350  var args = extractArgs(fn);
4351  if (args) {
4352    return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
4353  }
4354  return 'fn';
4355}
4356
4357function annotate(fn, strictDi, name) {
4358  var $inject,
4359      argDecl,
4360      last;
4361
4362  if (typeof fn === 'function') {
4363    if (!($inject = fn.$inject)) {
4364      $inject = [];
4365      if (fn.length) {
4366        if (strictDi) {
4367          if (!isString(name) || !name) {
4368            name = fn.name || anonFn(fn);
4369          }
4370          throw $injectorMinErr('strictdi',
4371            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
4372        }
4373        argDecl = extractArgs(fn);
4374        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
4375          arg.replace(FN_ARG, function(all, underscore, name) {
4376            $inject.push(name);
4377          });
4378        });
4379      }
4380      fn.$inject = $inject;
4381    }
4382  } else if (isArray(fn)) {
4383    last = fn.length - 1;
4384    assertArgFn(fn[last], 'fn');
4385    $inject = fn.slice(0, last);
4386  } else {
4387    assertArgFn(fn, 'fn', true);
4388  }
4389  return $inject;
4390}
4391
4392///////////////////////////////////////
4393
4394/**
4395 * @ngdoc service
4396 * @name $injector
4397 *
4398 * @description
4399 *
4400 * `$injector` is used to retrieve object instances as defined by
4401 * {@link auto.$provide provider}, instantiate types, invoke methods,
4402 * and load modules.
4403 *
4404 * The following always holds true:
4405 *
4406 * ```js
4407 *   var $injector = angular.injector();
4408 *   expect($injector.get('$injector')).toBe($injector);
4409 *   expect($injector.invoke(function($injector) {
4410 *     return $injector;
4411 *   })).toBe($injector);
4412 * ```
4413 *
4414 * ## Injection Function Annotation
4415 *
4416 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
4417 * following are all valid ways of annotating function with injection arguments and are equivalent.
4418 *
4419 * ```js
4420 *   // inferred (only works if code not minified/obfuscated)
4421 *   $injector.invoke(function(serviceA){});
4422 *
4423 *   // annotated
4424 *   function explicit(serviceA) {};
4425 *   explicit.$inject = ['serviceA'];
4426 *   $injector.invoke(explicit);
4427 *
4428 *   // inline
4429 *   $injector.invoke(['serviceA', function(serviceA){}]);
4430 * ```
4431 *
4432 * ### Inference
4433 *
4434 * In JavaScript calling `toString()` on a function returns the function definition. The definition
4435 * can then be parsed and the function arguments can be extracted. This method of discovering
4436 * annotations is disallowed when the injector is in strict mode.
4437 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
4438 * argument names.
4439 *
4440 * ### `$inject` Annotation
4441 * By adding an `$inject` property onto a function the injection parameters can be specified.
4442 *
4443 * ### Inline
4444 * As an array of injection names, where the last item in the array is the function to call.
4445 */
4446
4447/**
4448 * @ngdoc property
4449 * @name $injector#modules
4450 * @type {Object}
4451 * @description
4452 * A hash containing all the modules that have been loaded into the
4453 * $injector.
4454 *
4455 * You can use this property to find out information about a module via the
4456 * {@link angular.Module#info `myModule.info(...)`} method.
4457 *
4458 * For example:
4459 *
4460 * ```
4461 * var info = $injector.modules['ngAnimate'].info();
4462 * ```
4463 *
4464 * **Do not use this property to attempt to modify the modules after the application
4465 * has been bootstrapped.**
4466 */
4467
4468
4469/**
4470 * @ngdoc method
4471 * @name $injector#get
4472 *
4473 * @description
4474 * Return an instance of the service.
4475 *
4476 * @param {string} name The name of the instance to retrieve.
4477 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
4478 * @return {*} The instance.
4479 */
4480
4481/**
4482 * @ngdoc method
4483 * @name $injector#invoke
4484 *
4485 * @description
4486 * Invoke the method and supply the method arguments from the `$injector`.
4487 *
4488 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
4489 *   injected according to the {@link guide/di $inject Annotation} rules.
4490 * @param {Object=} self The `this` for the invoked method.
4491 * @param {Object=} locals Optional object. If preset then any argument names are read from this
4492 *                         object first, before the `$injector` is consulted.
4493 * @returns {*} the value returned by the invoked `fn` function.
4494 */
4495
4496/**
4497 * @ngdoc method
4498 * @name $injector#has
4499 *
4500 * @description
4501 * Allows the user to query if the particular service exists.
4502 *
4503 * @param {string} name Name of the service to query.
4504 * @returns {boolean} `true` if injector has given service.
4505 */
4506
4507/**
4508 * @ngdoc method
4509 * @name $injector#instantiate
4510 * @description
4511 * Create a new instance of JS type. The method takes a constructor function, invokes the new
4512 * operator, and supplies all of the arguments to the constructor function as specified by the
4513 * constructor annotation.
4514 *
4515 * @param {Function} Type Annotated constructor function.
4516 * @param {Object=} locals Optional object. If preset then any argument names are read from this
4517 * object first, before the `$injector` is consulted.
4518 * @returns {Object} new instance of `Type`.
4519 */
4520
4521/**
4522 * @ngdoc method
4523 * @name $injector#annotate
4524 *
4525 * @description
4526 * Returns an array of service names which the function is requesting for injection. This API is
4527 * used by the injector to determine which services need to be injected into the function when the
4528 * function is invoked. There are three ways in which the function can be annotated with the needed
4529 * dependencies.
4530 *
4531 * #### Argument names
4532 *
4533 * The simplest form is to extract the dependencies from the arguments of the function. This is done
4534 * by converting the function into a string using `toString()` method and extracting the argument
4535 * names.
4536 * ```js
4537 *   // Given
4538 *   function MyController($scope, $route) {
4539 *     // ...
4540 *   }
4541 *
4542 *   // Then
4543 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4544 * ```
4545 *
4546 * You can disallow this method by using strict injection mode.
4547 *
4548 * This method does not work with code minification / obfuscation. For this reason the following
4549 * annotation strategies are supported.
4550 *
4551 * #### The `$inject` property
4552 *
4553 * If a function has an `$inject` property and its value is an array of strings, then the strings
4554 * represent names of services to be injected into the function.
4555 * ```js
4556 *   // Given
4557 *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
4558 *     // ...
4559 *   }
4560 *   // Define function dependencies
4561 *   MyController['$inject'] = ['$scope', '$route'];
4562 *
4563 *   // Then
4564 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4565 * ```
4566 *
4567 * #### The array notation
4568 *
4569 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4570 * is very inconvenient. In these situations using the array notation to specify the dependencies in
4571 * a way that survives minification is a better choice:
4572 *
4573 * ```js
4574 *   // We wish to write this (not minification / obfuscation safe)
4575 *   injector.invoke(function($compile, $rootScope) {
4576 *     // ...
4577 *   });
4578 *
4579 *   // We are forced to write break inlining
4580 *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4581 *     // ...
4582 *   };
4583 *   tmpFn.$inject = ['$compile', '$rootScope'];
4584 *   injector.invoke(tmpFn);
4585 *
4586 *   // To better support inline function the inline annotation is supported
4587 *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4588 *     // ...
4589 *   }]);
4590 *
4591 *   // Therefore
4592 *   expect(injector.annotate(
4593 *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4594 *    ).toEqual(['$compile', '$rootScope']);
4595 * ```
4596 *
4597 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4598 * be retrieved as described above.
4599 *
4600 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4601 *
4602 * @returns {Array.<string>} The names of the services which the function requires.
4603 */
4604/**
4605 * @ngdoc method
4606 * @name $injector#loadNewModules
4607 *
4608 * @description
4609 *
4610 * **This is a dangerous API, which you use at your own risk!**
4611 *
4612 * Add the specified modules to the current injector.
4613 *
4614 * This method will add each of the injectables to the injector and execute all of the config and run
4615 * blocks for each module passed to the method.
4616 *
4617 * If a module has already been loaded into the injector then it will not be loaded again.
4618 *
4619 * * The application developer is responsible for loading the code containing the modules; and for
4620 * ensuring that lazy scripts are not downloaded and executed more often that desired.
4621 * * Previously compiled HTML will not be affected by newly loaded directives, filters and components.
4622 * * Modules cannot be unloaded.
4623 *
4624 * You can use {@link $injector#modules `$injector.modules`} to check whether a module has been loaded
4625 * into the injector, which may indicate whether the script has been executed already.
4626 *
4627 * @example
4628 * Here is an example of loading a bundle of modules, with a utility method called `getScript`:
4629 *
4630 * ```javascript
4631 * app.factory('loadModule', function($injector) {
4632 *   return function loadModule(moduleName, bundleUrl) {
4633 *     return getScript(bundleUrl).then(function() { $injector.loadNewModules([moduleName]); });
4634 *   };
4635 * })
4636 * ```
4637 *
4638 * @param {Array<String|Function|Array>=} mods an array of modules to load into the application.
4639 *     Each item in the array should be the name of a predefined module or a (DI annotated)
4640 *     function that will be invoked by the injector as a `config` block.
4641 *     See: {@link angular.module modules}
4642 */
4643
4644
4645/**
4646 * @ngdoc service
4647 * @name $provide
4648 *
4649 * @description
4650 *
4651 * The {@link auto.$provide $provide} service has a number of methods for registering components
4652 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4653 * {@link angular.Module}.
4654 *
4655 * An AngularJS **service** is a singleton object created by a **service factory**.  These **service
4656 * factories** are functions which, in turn, are created by a **service provider**.
4657 * The **service providers** are constructor functions. When instantiated they must contain a
4658 * property called `$get`, which holds the **service factory** function.
4659 *
4660 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4661 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4662 * function to get the instance of the **service**.
4663 *
4664 * Often services have no configuration options and there is no need to add methods to the service
4665 * provider.  The provider will be no more than a constructor function with a `$get` property. For
4666 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4667 * services without specifying a provider.
4668 *
4669 * * {@link auto.$provide#provider provider(name, provider)} - registers a **service provider** with the
4670 *     {@link auto.$injector $injector}
4671 * * {@link auto.$provide#constant constant(name, obj)} - registers a value/object that can be accessed by
4672 *     providers and services.
4673 * * {@link auto.$provide#value value(name, obj)} - registers a value/object that can only be accessed by
4674 *     services, not providers.
4675 * * {@link auto.$provide#factory factory(name, fn)} - registers a service **factory function**
4676 *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
4677 *     given factory function.
4678 * * {@link auto.$provide#service service(name, Fn)} - registers a **constructor function**
4679 *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4680 *      a new object using the given constructor function.
4681 * * {@link auto.$provide#decorator decorator(name, decorFn)} - registers a **decorator function** that
4682 *      will be able to modify or replace the implementation of another service.
4683 *
4684 * See the individual methods for more information and examples.
4685 */
4686
4687/**
4688 * @ngdoc method
4689 * @name $provide#provider
4690 * @description
4691 *
4692 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4693 * are constructor functions, whose instances are responsible for "providing" a factory for a
4694 * service.
4695 *
4696 * Service provider names start with the name of the service they provide followed by `Provider`.
4697 * For example, the {@link ng.$log $log} service has a provider called
4698 * {@link ng.$logProvider $logProvider}.
4699 *
4700 * Service provider objects can have additional methods which allow configuration of the provider
4701 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4702 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4703 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4704 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4705 * console or not.
4706 *
4707 * It is possible to inject other providers into the provider function,
4708 * but the injected provider must have been defined before the one that requires it.
4709 *
4710 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4711                        'Provider'` key.
4712 * @param {(Object|function())} provider If the provider is:
4713 *
4714 *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4715 *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4716 *   - `Constructor`: a new instance of the provider will be created using
4717 *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4718 *
4719 * @returns {Object} registered provider instance
4720
4721 * @example
4722 *
4723 * The following example shows how to create a simple event tracking service and register it using
4724 * {@link auto.$provide#provider $provide.provider()}.
4725 *
4726 * ```js
4727 *  // Define the eventTracker provider
4728 *  function EventTrackerProvider() {
4729 *    var trackingUrl = '/track';
4730 *
4731 *    // A provider method for configuring where the tracked events should been saved
4732 *    this.setTrackingUrl = function(url) {
4733 *      trackingUrl = url;
4734 *    };
4735 *
4736 *    // The service factory function
4737 *    this.$get = ['$http', function($http) {
4738 *      var trackedEvents = {};
4739 *      return {
4740 *        // Call this to track an event
4741 *        event: function(event) {
4742 *          var count = trackedEvents[event] || 0;
4743 *          count += 1;
4744 *          trackedEvents[event] = count;
4745 *          return count;
4746 *        },
4747 *        // Call this to save the tracked events to the trackingUrl
4748 *        save: function() {
4749 *          $http.post(trackingUrl, trackedEvents);
4750 *        }
4751 *      };
4752 *    }];
4753 *  }
4754 *
4755 *  describe('eventTracker', function() {
4756 *    var postSpy;
4757 *
4758 *    beforeEach(module(function($provide) {
4759 *      // Register the eventTracker provider
4760 *      $provide.provider('eventTracker', EventTrackerProvider);
4761 *    }));
4762 *
4763 *    beforeEach(module(function(eventTrackerProvider) {
4764 *      // Configure eventTracker provider
4765 *      eventTrackerProvider.setTrackingUrl('/custom-track');
4766 *    }));
4767 *
4768 *    it('tracks events', inject(function(eventTracker) {
4769 *      expect(eventTracker.event('login')).toEqual(1);
4770 *      expect(eventTracker.event('login')).toEqual(2);
4771 *    }));
4772 *
4773 *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4774 *      postSpy = spyOn($http, 'post');
4775 *      eventTracker.event('login');
4776 *      eventTracker.save();
4777 *      expect(postSpy).toHaveBeenCalled();
4778 *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4779 *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4780 *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4781 *    }));
4782 *  });
4783 * ```
4784 */
4785
4786/**
4787 * @ngdoc method
4788 * @name $provide#factory
4789 * @description
4790 *
4791 * Register a **service factory**, which will be called to return the service instance.
4792 * This is short for registering a service where its provider consists of only a `$get` property,
4793 * which is the given service factory function.
4794 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4795 * configure your service in a provider.
4796 *
4797 * @param {string} name The name of the instance.
4798 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4799 *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4800 * @returns {Object} registered provider instance
4801 *
4802 * @example
4803 * Here is an example of registering a service
4804 * ```js
4805 *   $provide.factory('ping', ['$http', function($http) {
4806 *     return function ping() {
4807 *       return $http.send('/ping');
4808 *     };
4809 *   }]);
4810 * ```
4811 * You would then inject and use this service like this:
4812 * ```js
4813 *   someModule.controller('Ctrl', ['ping', function(ping) {
4814 *     ping();
4815 *   }]);
4816 * ```
4817 */
4818
4819
4820/**
4821 * @ngdoc method
4822 * @name $provide#service
4823 * @description
4824 *
4825 * Register a **service constructor**, which will be invoked with `new` to create the service
4826 * instance.
4827 * This is short for registering a service where its provider's `$get` property is a factory
4828 * function that returns an instance instantiated by the injector from the service constructor
4829 * function.
4830 *
4831 * Internally it looks a bit like this:
4832 *
4833 * ```
4834 * {
4835 *   $get: function() {
4836 *     return $injector.instantiate(constructor);
4837 *   }
4838 * }
4839 * ```
4840 *
4841 *
4842 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4843 * as a type/class.
4844 *
4845 * @param {string} name The name of the instance.
4846 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4847 *     that will be instantiated.
4848 * @returns {Object} registered provider instance
4849 *
4850 * @example
4851 * Here is an example of registering a service using
4852 * {@link auto.$provide#service $provide.service(class)}.
4853 * ```js
4854 *   var Ping = function($http) {
4855 *     this.$http = $http;
4856 *   };
4857 *
4858 *   Ping.$inject = ['$http'];
4859 *
4860 *   Ping.prototype.send = function() {
4861 *     return this.$http.get('/ping');
4862 *   };
4863 *   $provide.service('ping', Ping);
4864 * ```
4865 * You would then inject and use this service like this:
4866 * ```js
4867 *   someModule.controller('Ctrl', ['ping', function(ping) {
4868 *     ping.send();
4869 *   }]);
4870 * ```
4871 */
4872
4873
4874/**
4875 * @ngdoc method
4876 * @name $provide#value
4877 * @description
4878 *
4879 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4880 * number, an array, an object or a function. This is short for registering a service where its
4881 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4882 * service**. That also means it is not possible to inject other services into a value service.
4883 *
4884 * Value services are similar to constant services, except that they cannot be injected into a
4885 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4886 * an AngularJS {@link auto.$provide#decorator decorator}.
4887 *
4888 * @param {string} name The name of the instance.
4889 * @param {*} value The value.
4890 * @returns {Object} registered provider instance
4891 *
4892 * @example
4893 * Here are some examples of creating value services.
4894 * ```js
4895 *   $provide.value('ADMIN_USER', 'admin');
4896 *
4897 *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4898 *
4899 *   $provide.value('halfOf', function(value) {
4900 *     return value / 2;
4901 *   });
4902 * ```
4903 */
4904
4905
4906/**
4907 * @ngdoc method
4908 * @name $provide#constant
4909 * @description
4910 *
4911 * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
4912 * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
4913 * possible to inject other services into a constant.
4914 *
4915 * But unlike {@link auto.$provide#value value}, a constant can be
4916 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4917 * be overridden by an AngularJS {@link auto.$provide#decorator decorator}.
4918 *
4919 * @param {string} name The name of the constant.
4920 * @param {*} value The constant value.
4921 * @returns {Object} registered instance
4922 *
4923 * @example
4924 * Here a some examples of creating constants:
4925 * ```js
4926 *   $provide.constant('SHARD_HEIGHT', 306);
4927 *
4928 *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4929 *
4930 *   $provide.constant('double', function(value) {
4931 *     return value * 2;
4932 *   });
4933 * ```
4934 */
4935
4936
4937/**
4938 * @ngdoc method
4939 * @name $provide#decorator
4940 * @description
4941 *
4942 * Register a **decorator function** with the {@link auto.$injector $injector}. A decorator function
4943 * intercepts the creation of a service, allowing it to override or modify the behavior of the
4944 * service. The return value of the decorator function may be the original service, or a new service
4945 * that replaces (or wraps and delegates to) the original service.
4946 *
4947 * You can find out more about using decorators in the {@link guide/decorators} guide.
4948 *
4949 * @param {string} name The name of the service to decorate.
4950 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4951 *    provided and should return the decorated service instance. The function is called using
4952 *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4953 *    Local injection arguments:
4954 *
4955 *    * `$delegate` - The original service instance, which can be replaced, monkey patched, configured,
4956 *      decorated or delegated to.
4957 *
4958 * @example
4959 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4960 * calls to {@link ng.$log#error $log.warn()}.
4961 * ```js
4962 *   $provide.decorator('$log', ['$delegate', function($delegate) {
4963 *     $delegate.warn = $delegate.error;
4964 *     return $delegate;
4965 *   }]);
4966 * ```
4967 */
4968
4969
4970function createInjector(modulesToLoad, strictDi) {
4971  strictDi = (strictDi === true);
4972  var INSTANTIATING = {},
4973      providerSuffix = 'Provider',
4974      path = [],
4975      loadedModules = new NgMap(),
4976      providerCache = {
4977        $provide: {
4978            provider: supportObject(provider),
4979            factory: supportObject(factory),
4980            service: supportObject(service),
4981            value: supportObject(value),
4982            constant: supportObject(constant),
4983            decorator: decorator
4984          }
4985      },
4986      providerInjector = (providerCache.$injector =
4987          createInternalInjector(providerCache, function(serviceName, caller) {
4988            if (angular.isString(caller)) {
4989              path.push(caller);
4990            }
4991            throw $injectorMinErr('unpr', 'Unknown provider: {0}', path.join(' <- '));
4992          })),
4993      instanceCache = {},
4994      protoInstanceInjector =
4995          createInternalInjector(instanceCache, function(serviceName, caller) {
4996            var provider = providerInjector.get(serviceName + providerSuffix, caller);
4997            return instanceInjector.invoke(
4998                provider.$get, provider, undefined, serviceName);
4999          }),
5000      instanceInjector = protoInstanceInjector;
5001
5002  providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
5003  instanceInjector.modules = providerInjector.modules = createMap();
5004  var runBlocks = loadModules(modulesToLoad);
5005  instanceInjector = protoInstanceInjector.get('$injector');
5006  instanceInjector.strictDi = strictDi;
5007  forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
5008
5009  instanceInjector.loadNewModules = function(mods) {
5010    forEach(loadModules(mods), function(fn) { if (fn) instanceInjector.invoke(fn); });
5011  };
5012
5013
5014  return instanceInjector;
5015
5016  ////////////////////////////////////
5017  // $provider
5018  ////////////////////////////////////
5019
5020  function supportObject(delegate) {
5021    return function(key, value) {
5022      if (isObject(key)) {
5023        forEach(key, reverseParams(delegate));
5024      } else {
5025        return delegate(key, value);
5026      }
5027    };
5028  }
5029
5030  function provider(name, provider_) {
5031    assertNotHasOwnProperty(name, 'service');
5032    if (isFunction(provider_) || isArray(provider_)) {
5033      provider_ = providerInjector.instantiate(provider_);
5034    }
5035    if (!provider_.$get) {
5036      throw $injectorMinErr('pget', 'Provider \'{0}\' must define $get factory method.', name);
5037    }
5038    return (providerCache[name + providerSuffix] = provider_);
5039  }
5040
5041  function enforceReturnValue(name, factory) {
5042    return /** @this */ function enforcedReturnValue() {
5043      var result = instanceInjector.invoke(factory, this);
5044      if (isUndefined(result)) {
5045        throw $injectorMinErr('undef', 'Provider \'{0}\' must return a value from $get factory method.', name);
5046      }
5047      return result;
5048    };
5049  }
5050
5051  function factory(name, factoryFn, enforce) {
5052    return provider(name, {
5053      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
5054    });
5055  }
5056
5057  function service(name, constructor) {
5058    return factory(name, ['$injector', function($injector) {
5059      return $injector.instantiate(constructor);
5060    }]);
5061  }
5062
5063  function value(name, val) { return factory(name, valueFn(val), false); }
5064
5065  function constant(name, value) {
5066    assertNotHasOwnProperty(name, 'constant');
5067    providerCache[name] = value;
5068    instanceCache[name] = value;
5069  }
5070
5071  function decorator(serviceName, decorFn) {
5072    var origProvider = providerInjector.get(serviceName + providerSuffix),
5073        orig$get = origProvider.$get;
5074
5075    origProvider.$get = function() {
5076      var origInstance = instanceInjector.invoke(orig$get, origProvider);
5077      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
5078    };
5079  }
5080
5081  ////////////////////////////////////
5082  // Module Loading
5083  ////////////////////////////////////
5084  function loadModules(modulesToLoad) {
5085    assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
5086    var runBlocks = [], moduleFn;
5087    forEach(modulesToLoad, function(module) {
5088      if (loadedModules.get(module)) return;
5089      loadedModules.set(module, true);
5090
5091      function runInvokeQueue(queue) {
5092        var i, ii;
5093        for (i = 0, ii = queue.length; i < ii; i++) {
5094          var invokeArgs = queue[i],
5095              provider = providerInjector.get(invokeArgs[0]);
5096
5097          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
5098        }
5099      }
5100
5101      try {
5102        if (isString(module)) {
5103          moduleFn = angularModule(module);
5104          instanceInjector.modules[module] = moduleFn;
5105          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
5106          runInvokeQueue(moduleFn._invokeQueue);
5107          runInvokeQueue(moduleFn._configBlocks);
5108        } else if (isFunction(module)) {
5109            runBlocks.push(providerInjector.invoke(module));
5110        } else if (isArray(module)) {
5111            runBlocks.push(providerInjector.invoke(module));
5112        } else {
5113          assertArgFn(module, 'module');
5114        }
5115      } catch (e) {
5116        if (isArray(module)) {
5117          module = module[module.length - 1];
5118        }
5119        if (e.message && e.stack && e.stack.indexOf(e.message) === -1) {
5120          // Safari & FF's stack traces don't contain error.message content
5121          // unlike those of Chrome and IE
5122          // So if stack doesn't contain message, we create a new string that contains both.
5123          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
5124          // eslint-disable-next-line no-ex-assign
5125          e = e.message + '\n' + e.stack;
5126        }
5127        throw $injectorMinErr('modulerr', 'Failed to instantiate module {0} due to:\n{1}',
5128                  module, e.stack || e.message || e);
5129      }
5130    });
5131    return runBlocks;
5132  }
5133
5134  ////////////////////////////////////
5135  // internal Injector
5136  ////////////////////////////////////
5137
5138  function createInternalInjector(cache, factory) {
5139
5140    function getService(serviceName, caller) {
5141      if (cache.hasOwnProperty(serviceName)) {
5142        if (cache[serviceName] === INSTANTIATING) {
5143          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
5144                    serviceName + ' <- ' + path.join(' <- '));
5145        }
5146        return cache[serviceName];
5147      } else {
5148        try {
5149          path.unshift(serviceName);
5150          cache[serviceName] = INSTANTIATING;
5151          cache[serviceName] = factory(serviceName, caller);
5152          return cache[serviceName];
5153        } catch (err) {
5154          if (cache[serviceName] === INSTANTIATING) {
5155            delete cache[serviceName];
5156          }
5157          throw err;
5158        } finally {
5159          path.shift();
5160        }
5161      }
5162    }
5163
5164
5165    function injectionArgs(fn, locals, serviceName) {
5166      var args = [],
5167          $inject = createInjector.$$annotate(fn, strictDi, serviceName);
5168
5169      for (var i = 0, length = $inject.length; i < length; i++) {
5170        var key = $inject[i];
5171        if (typeof key !== 'string') {
5172          throw $injectorMinErr('itkn',
5173                  'Incorrect injection token! Expected service name as string, got {0}', key);
5174        }
5175        args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
5176                                                         getService(key, serviceName));
5177      }
5178      return args;
5179    }
5180
5181    function isClass(func) {
5182      // Support: IE 9-11 only
5183      // IE 9-11 do not support classes and IE9 leaks with the code below.
5184      if (msie || typeof func !== 'function') {
5185        return false;
5186      }
5187      var result = func.$$ngIsClass;
5188      if (!isBoolean(result)) {
5189        result = func.$$ngIsClass = /^class\b/.test(stringifyFn(func));
5190      }
5191      return result;
5192    }
5193
5194    function invoke(fn, self, locals, serviceName) {
5195      if (typeof locals === 'string') {
5196        serviceName = locals;
5197        locals = null;
5198      }
5199
5200      var args = injectionArgs(fn, locals, serviceName);
5201      if (isArray(fn)) {
5202        fn = fn[fn.length - 1];
5203      }
5204
5205      if (!isClass(fn)) {
5206        // http://jsperf.com/angularjs-invoke-apply-vs-switch
5207        // #5388
5208        return fn.apply(self, args);
5209      } else {
5210        args.unshift(null);
5211        return new (Function.prototype.bind.apply(fn, args))();
5212      }
5213    }
5214
5215
5216    function instantiate(Type, locals, serviceName) {
5217      // Check if Type is annotated and use just the given function at n-1 as parameter
5218      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
5219      var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
5220      var args = injectionArgs(Type, locals, serviceName);
5221      // Empty object at position 0 is ignored for invocation with `new`, but required.
5222      args.unshift(null);
5223      return new (Function.prototype.bind.apply(ctor, args))();
5224    }
5225
5226
5227    return {
5228      invoke: invoke,
5229      instantiate: instantiate,
5230      get: getService,
5231      annotate: createInjector.$$annotate,
5232      has: function(name) {
5233        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
5234      }
5235    };
5236  }
5237}
5238
5239createInjector.$$annotate = annotate;
5240
5241/**
5242 * @ngdoc provider
5243 * @name $anchorScrollProvider
5244 * @this
5245 *
5246 * @description
5247 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
5248 * {@link ng.$location#hash $location.hash()} changes.
5249 */
5250function $AnchorScrollProvider() {
5251
5252  var autoScrollingEnabled = true;
5253
5254  /**
5255   * @ngdoc method
5256   * @name $anchorScrollProvider#disableAutoScrolling
5257   *
5258   * @description
5259   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
5260   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
5261   * Use this method to disable automatic scrolling.
5262   *
5263   * If automatic scrolling is disabled, one must explicitly call
5264   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
5265   * current hash.
5266   */
5267  this.disableAutoScrolling = function() {
5268    autoScrollingEnabled = false;
5269  };
5270
5271  /**
5272   * @ngdoc service
5273   * @name $anchorScroll
5274   * @kind function
5275   * @requires $window
5276   * @requires $location
5277   * @requires $rootScope
5278   *
5279   * @description
5280   * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
5281   * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
5282   * in the
5283   * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document).
5284   *
5285   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
5286   * match any anchor whenever it changes. This can be disabled by calling
5287   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
5288   *
5289   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
5290   * vertical scroll-offset (either fixed or dynamic).
5291   *
5292   * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
5293   *                       {@link ng.$location#hash $location.hash()} will be used.
5294   *
5295   * @property {(number|function|jqLite)} yOffset
5296   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
5297   * positioned elements at the top of the page, such as navbars, headers etc.
5298   *
5299   * `yOffset` can be specified in various ways:
5300   * - **number**: A fixed number of pixels to be used as offset.<br /><br />
5301   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
5302   *   a number representing the offset (in pixels).<br /><br />
5303   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
5304   *   the top of the page to the element's bottom will be used as offset.<br />
5305   *   **Note**: The element will be taken into account only as long as its `position` is set to
5306   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
5307   *   their height and/or positioning according to the viewport's size.
5308   *
5309   * <br />
5310   * <div class="alert alert-warning">
5311   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
5312   * not some child element.
5313   * </div>
5314   *
5315   * @example
5316     <example module="anchorScrollExample" name="anchor-scroll">
5317       <file name="index.html">
5318         <div id="scrollArea" ng-controller="ScrollController">
5319           <a ng-click="gotoBottom()">Go to bottom</a>
5320           <a id="bottom"></a> You're at the bottom!
5321         </div>
5322       </file>
5323       <file name="script.js">
5324         angular.module('anchorScrollExample', [])
5325           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
5326             function($scope, $location, $anchorScroll) {
5327               $scope.gotoBottom = function() {
5328                 // set the location.hash to the id of
5329                 // the element you wish to scroll to.
5330                 $location.hash('bottom');
5331
5332                 // call $anchorScroll()
5333                 $anchorScroll();
5334               };
5335             }]);
5336       </file>
5337       <file name="style.css">
5338         #scrollArea {
5339           height: 280px;
5340           overflow: auto;
5341         }
5342
5343         #bottom {
5344           display: block;
5345           margin-top: 2000px;
5346         }
5347       </file>
5348     </example>
5349   *
5350   * <hr />
5351   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
5352   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
5353   *
5354   * @example
5355     <example module="anchorScrollOffsetExample" name="anchor-scroll-offset">
5356       <file name="index.html">
5357         <div class="fixed-header" ng-controller="headerCtrl">
5358           <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
5359             Go to anchor {{x}}
5360           </a>
5361         </div>
5362         <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
5363           Anchor {{x}} of 5
5364         </div>
5365       </file>
5366       <file name="script.js">
5367         angular.module('anchorScrollOffsetExample', [])
5368           .run(['$anchorScroll', function($anchorScroll) {
5369             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
5370           }])
5371           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
5372             function($anchorScroll, $location, $scope) {
5373               $scope.gotoAnchor = function(x) {
5374                 var newHash = 'anchor' + x;
5375                 if ($location.hash() !== newHash) {
5376                   // set the $location.hash to `newHash` and
5377                   // $anchorScroll will automatically scroll to it
5378                   $location.hash('anchor' + x);
5379                 } else {
5380                   // call $anchorScroll() explicitly,
5381                   // since $location.hash hasn't changed
5382                   $anchorScroll();
5383                 }
5384               };
5385             }
5386           ]);
5387       </file>
5388       <file name="style.css">
5389         body {
5390           padding-top: 50px;
5391         }
5392
5393         .anchor {
5394           border: 2px dashed DarkOrchid;
5395           padding: 10px 10px 200px 10px;
5396         }
5397
5398         .fixed-header {
5399           background-color: rgba(0, 0, 0, 0.2);
5400           height: 50px;
5401           position: fixed;
5402           top: 0; left: 0; right: 0;
5403         }
5404
5405         .fixed-header > a {
5406           display: inline-block;
5407           margin: 5px 15px;
5408         }
5409       </file>
5410     </example>
5411   */
5412  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
5413    var document = $window.document;
5414
5415    // Helper function to get first anchor from a NodeList
5416    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
5417    //  and working in all supported browsers.)
5418    function getFirstAnchor(list) {
5419      var result = null;
5420      Array.prototype.some.call(list, function(element) {
5421        if (nodeName_(element) === 'a') {
5422          result = element;
5423          return true;
5424        }
5425      });
5426      return result;
5427    }
5428
5429    function getYOffset() {
5430
5431      var offset = scroll.yOffset;
5432
5433      if (isFunction(offset)) {
5434        offset = offset();
5435      } else if (isElement(offset)) {
5436        var elem = offset[0];
5437        var style = $window.getComputedStyle(elem);
5438        if (style.position !== 'fixed') {
5439          offset = 0;
5440        } else {
5441          offset = elem.getBoundingClientRect().bottom;
5442        }
5443      } else if (!isNumber(offset)) {
5444        offset = 0;
5445      }
5446
5447      return offset;
5448    }
5449
5450    function scrollTo(elem) {
5451      if (elem) {
5452        elem.scrollIntoView();
5453
5454        var offset = getYOffset();
5455
5456        if (offset) {
5457          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
5458          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
5459          // top of the viewport.
5460          //
5461          // IF the number of pixels from the top of `elem` to the end of the page's content is less
5462          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
5463          // way down the page.
5464          //
5465          // This is often the case for elements near the bottom of the page.
5466          //
5467          // In such cases we do not need to scroll the whole `offset` up, just the difference between
5468          // the top of the element and the offset, which is enough to align the top of `elem` at the
5469          // desired position.
5470          var elemTop = elem.getBoundingClientRect().top;
5471          $window.scrollBy(0, elemTop - offset);
5472        }
5473      } else {
5474        $window.scrollTo(0, 0);
5475      }
5476    }
5477
5478    function scroll(hash) {
5479      // Allow numeric hashes
5480      hash = isString(hash) ? hash : isNumber(hash) ? hash.toString() : $location.hash();
5481      var elm;
5482
5483      // empty hash, scroll to the top of the page
5484      if (!hash) scrollTo(null);
5485
5486      // element with given id
5487      else if ((elm = document.getElementById(hash))) scrollTo(elm);
5488
5489      // first anchor with given name :-D
5490      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
5491
5492      // no element and hash === 'top', scroll to the top of the page
5493      else if (hash === 'top') scrollTo(null);
5494    }
5495
5496    // does not scroll when user clicks on anchor link that is currently on
5497    // (no url change, no $location.hash() change), browser native does scroll
5498    if (autoScrollingEnabled) {
5499      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
5500        function autoScrollWatchAction(newVal, oldVal) {
5501          // skip the initial scroll if $location.hash is empty
5502          if (newVal === oldVal && newVal === '') return;
5503
5504          jqLiteDocumentLoaded(function() {
5505            $rootScope.$evalAsync(scroll);
5506          });
5507        });
5508    }
5509
5510    return scroll;
5511  }];
5512}
5513
5514var $animateMinErr = minErr('$animate');
5515var ELEMENT_NODE = 1;
5516var NG_ANIMATE_CLASSNAME = 'ng-animate';
5517
5518function mergeClasses(a,b) {
5519  if (!a && !b) return '';
5520  if (!a) return b;
5521  if (!b) return a;
5522  if (isArray(a)) a = a.join(' ');
5523  if (isArray(b)) b = b.join(' ');
5524  return a + ' ' + b;
5525}
5526
5527function extractElementNode(element) {
5528  for (var i = 0; i < element.length; i++) {
5529    var elm = element[i];
5530    if (elm.nodeType === ELEMENT_NODE) {
5531      return elm;
5532    }
5533  }
5534}
5535
5536function splitClasses(classes) {
5537  if (isString(classes)) {
5538    classes = classes.split(' ');
5539  }
5540
5541  // Use createMap() to prevent class assumptions involving property names in
5542  // Object.prototype
5543  var obj = createMap();
5544  forEach(classes, function(klass) {
5545    // sometimes the split leaves empty string values
5546    // incase extra spaces were applied to the options
5547    if (klass.length) {
5548      obj[klass] = true;
5549    }
5550  });
5551  return obj;
5552}
5553
5554// if any other type of options value besides an Object value is
5555// passed into the $animate.method() animation then this helper code
5556// will be run which will ignore it. While this patch is not the
5557// greatest solution to this, a lot of existing plugins depend on
5558// $animate to either call the callback (< 1.2) or return a promise
5559// that can be changed. This helper function ensures that the options
5560// are wiped clean incase a callback function is provided.
5561function prepareAnimateOptions(options) {
5562  return isObject(options)
5563      ? options
5564      : {};
5565}
5566
5567var $$CoreAnimateJsProvider = /** @this */ function() {
5568  this.$get = noop;
5569};
5570
5571// this is prefixed with Core since it conflicts with
5572// the animateQueueProvider defined in ngAnimate/animateQueue.js
5573var $$CoreAnimateQueueProvider = /** @this */ function() {
5574  var postDigestQueue = new NgMap();
5575  var postDigestElements = [];
5576
5577  this.$get = ['$$AnimateRunner', '$rootScope',
5578       function($$AnimateRunner,   $rootScope) {
5579    return {
5580      enabled: noop,
5581      on: noop,
5582      off: noop,
5583      pin: noop,
5584
5585      push: function(element, event, options, domOperation) {
5586        if (domOperation) {
5587          domOperation();
5588        }
5589
5590        options = options || {};
5591        if (options.from) {
5592          element.css(options.from);
5593        }
5594        if (options.to) {
5595          element.css(options.to);
5596        }
5597
5598        if (options.addClass || options.removeClass) {
5599          addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
5600        }
5601
5602        var runner = new $$AnimateRunner();
5603
5604        // since there are no animations to run the runner needs to be
5605        // notified that the animation call is complete.
5606        runner.complete();
5607        return runner;
5608      }
5609    };
5610
5611
5612    function updateData(data, classes, value) {
5613      var changed = false;
5614      if (classes) {
5615        classes = isString(classes) ? classes.split(' ') :
5616                  isArray(classes) ? classes : [];
5617        forEach(classes, function(className) {
5618          if (className) {
5619            changed = true;
5620            data[className] = value;
5621          }
5622        });
5623      }
5624      return changed;
5625    }
5626
5627    function handleCSSClassChanges() {
5628      forEach(postDigestElements, function(element) {
5629        var data = postDigestQueue.get(element);
5630        if (data) {
5631          var existing = splitClasses(element.attr('class'));
5632          var toAdd = '';
5633          var toRemove = '';
5634          forEach(data, function(status, className) {
5635            var hasClass = !!existing[className];
5636            if (status !== hasClass) {
5637              if (status) {
5638                toAdd += (toAdd.length ? ' ' : '') + className;
5639              } else {
5640                toRemove += (toRemove.length ? ' ' : '') + className;
5641              }
5642            }
5643          });
5644
5645          forEach(element, function(elm) {
5646            if (toAdd) {
5647              jqLiteAddClass(elm, toAdd);
5648            }
5649            if (toRemove) {
5650              jqLiteRemoveClass(elm, toRemove);
5651            }
5652          });
5653          postDigestQueue.delete(element);
5654        }
5655      });
5656      postDigestElements.length = 0;
5657    }
5658
5659
5660    function addRemoveClassesPostDigest(element, add, remove) {
5661      var data = postDigestQueue.get(element) || {};
5662
5663      var classesAdded = updateData(data, add, true);
5664      var classesRemoved = updateData(data, remove, false);
5665
5666      if (classesAdded || classesRemoved) {
5667
5668        postDigestQueue.set(element, data);
5669        postDigestElements.push(element);
5670
5671        if (postDigestElements.length === 1) {
5672          $rootScope.$$postDigest(handleCSSClassChanges);
5673        }
5674      }
5675    }
5676  }];
5677};
5678
5679/**
5680 * @ngdoc provider
5681 * @name $animateProvider
5682 *
5683 * @description
5684 * Default implementation of $animate that doesn't perform any animations, instead just
5685 * synchronously performs DOM updates and resolves the returned runner promise.
5686 *
5687 * In order to enable animations the `ngAnimate` module has to be loaded.
5688 *
5689 * To see the functional implementation check out `src/ngAnimate/animate.js`.
5690 */
5691var $AnimateProvider = ['$provide', /** @this */ function($provide) {
5692  var provider = this;
5693  var classNameFilter = null;
5694  var customFilter = null;
5695
5696  this.$$registeredAnimations = Object.create(null);
5697
5698   /**
5699   * @ngdoc method
5700   * @name $animateProvider#register
5701   *
5702   * @description
5703   * Registers a new injectable animation factory function. The factory function produces the
5704   * animation object which contains callback functions for each event that is expected to be
5705   * animated.
5706   *
5707   *   * `eventFn`: `function(element, ... , doneFunction, options)`
5708   *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
5709   *   on the type of animation additional arguments will be injected into the animation function. The
5710   *   list below explains the function signatures for the different animation methods:
5711   *
5712   *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5713   *   - addClass: function(element, addedClasses, doneFunction, options)
5714   *   - removeClass: function(element, removedClasses, doneFunction, options)
5715   *   - enter, leave, move: function(element, doneFunction, options)
5716   *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
5717   *
5718   *   Make sure to trigger the `doneFunction` once the animation is fully complete.
5719   *
5720   * ```js
5721   *   return {
5722   *     //enter, leave, move signature
5723   *     eventFn : function(element, done, options) {
5724   *       //code to run the animation
5725   *       //once complete, then run done()
5726   *       return function endFunction(wasCancelled) {
5727   *         //code to cancel the animation
5728   *       }
5729   *     }
5730   *   }
5731   * ```
5732   *
5733   * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5734   * @param {Function} factory The factory function that will be executed to return the animation
5735   *                           object.
5736   */
5737  this.register = function(name, factory) {
5738    if (name && name.charAt(0) !== '.') {
5739      throw $animateMinErr('notcsel', 'Expecting class selector starting with \'.\' got \'{0}\'.', name);
5740    }
5741
5742    var key = name + '-animation';
5743    provider.$$registeredAnimations[name.substr(1)] = key;
5744    $provide.factory(key, factory);
5745  };
5746
5747  /**
5748   * @ngdoc method
5749   * @name $animateProvider#customFilter
5750   *
5751   * @description
5752   * Sets and/or returns the custom filter function that is used to "filter" animations, i.e.
5753   * determine if an animation is allowed or not. When no filter is specified (the default), no
5754   * animation will be blocked. Setting the `customFilter` value will only allow animations for
5755   * which the filter function's return value is truthy.
5756   *
5757   * This allows to easily create arbitrarily complex rules for filtering animations, such as
5758   * allowing specific events only, or enabling animations on specific subtrees of the DOM, etc.
5759   * Filtering animations can also boost performance for low-powered devices, as well as
5760   * applications containing a lot of structural operations.
5761   *
5762   * <div class="alert alert-success">
5763   *   **Best Practice:**
5764   *   Keep the filtering function as lean as possible, because it will be called for each DOM
5765   *   action (e.g. insertion, removal, class change) performed by "animation-aware" directives.
5766   *   See {@link guide/animations#which-directives-support-animations- here} for a list of built-in
5767   *   directives that support animations.
5768   *   Performing computationally expensive or time-consuming operations on each call of the
5769   *   filtering function can make your animations sluggish.
5770   * </div>
5771   *
5772   * **Note:** If present, `customFilter` will be checked before
5773   * {@link $animateProvider#classNameFilter classNameFilter}.
5774   *
5775   * @param {Function=} filterFn - The filter function which will be used to filter all animations.
5776   *   If a falsy value is returned, no animation will be performed. The function will be called
5777   *   with the following arguments:
5778   *   - **node** `{DOMElement}` - The DOM element to be animated.
5779   *   - **event** `{String}` - The name of the animation event (e.g. `enter`, `leave`, `addClass`
5780   *     etc).
5781   *   - **options** `{Object}` - A collection of options/styles used for the animation.
5782   * @return {Function} The current filter function or `null` if there is none set.
5783   */
5784  this.customFilter = function(filterFn) {
5785    if (arguments.length === 1) {
5786      customFilter = isFunction(filterFn) ? filterFn : null;
5787    }
5788
5789    return customFilter;
5790  };
5791
5792  /**
5793   * @ngdoc method
5794   * @name $animateProvider#classNameFilter
5795   *
5796   * @description
5797   * Sets and/or returns the CSS class regular expression that is checked when performing
5798   * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5799   * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5800   * When setting the `classNameFilter` value, animations will only be performed on elements
5801   * that successfully match the filter expression. This in turn can boost performance
5802   * for low-powered devices as well as applications containing a lot of structural operations.
5803   *
5804   * **Note:** If present, `classNameFilter` will be checked after
5805   * {@link $animateProvider#customFilter customFilter}. If `customFilter` is present and returns
5806   * false, `classNameFilter` will not be checked.
5807   *
5808   * @param {RegExp=} expression The className expression which will be checked against all animations
5809   * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5810   */
5811  this.classNameFilter = function(expression) {
5812    if (arguments.length === 1) {
5813      classNameFilter = (expression instanceof RegExp) ? expression : null;
5814      if (classNameFilter) {
5815        var reservedRegex = new RegExp('[(\\s|\\/)]' + NG_ANIMATE_CLASSNAME + '[(\\s|\\/)]');
5816        if (reservedRegex.test(classNameFilter.toString())) {
5817          classNameFilter = null;
5818          throw $animateMinErr('nongcls', '$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5819        }
5820      }
5821    }
5822    return classNameFilter;
5823  };
5824
5825  this.$get = ['$$animateQueue', function($$animateQueue) {
5826    function domInsert(element, parentElement, afterElement) {
5827      // if for some reason the previous element was removed
5828      // from the dom sometime before this code runs then let's
5829      // just stick to using the parent element as the anchor
5830      if (afterElement) {
5831        var afterNode = extractElementNode(afterElement);
5832        if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5833          afterElement = null;
5834        }
5835      }
5836      if (afterElement) {
5837        afterElement.after(element);
5838      } else {
5839        parentElement.prepend(element);
5840      }
5841    }
5842
5843    /**
5844     * @ngdoc service
5845     * @name $animate
5846     * @description The $animate service exposes a series of DOM utility methods that provide support
5847     * for animation hooks. The default behavior is the application of DOM operations, however,
5848     * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5849     * to ensure that animation runs with the triggered DOM operation.
5850     *
5851     * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5852     * included and only when it is active then the animation hooks that `$animate` triggers will be
5853     * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5854     * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5855     * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5856     *
5857     * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5858     *
5859     * To learn more about enabling animation support, click here to visit the
5860     * {@link ngAnimate ngAnimate module page}.
5861     */
5862    return {
5863      // we don't call it directly since non-existant arguments may
5864      // be interpreted as null within the sub enabled function
5865
5866      /**
5867       *
5868       * @ngdoc method
5869       * @name $animate#on
5870       * @kind function
5871       * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5872       *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5873       *    is fired with the following params:
5874       *
5875       * ```js
5876       * $animate.on('enter', container,
5877       *    function callback(element, phase) {
5878       *      // cool we detected an enter animation within the container
5879       *    }
5880       * );
5881       * ```
5882       *
5883       * <div class="alert alert-warning">
5884       * **Note**: Generally, the events that are fired correspond 1:1 to `$animate` method names,
5885       * e.g. {@link ng.$animate#addClass addClass()} will fire `addClass`, and {@link ng.ngClass}
5886       * will fire `addClass` if classes are added, and `removeClass` if classes are removed.
5887       * However, there are two exceptions:
5888       *
5889       * <ul>
5890       *   <li>if both an {@link ng.$animate#addClass addClass()} and a
5891       *   {@link ng.$animate#removeClass removeClass()} action are performed during the same
5892       *   animation, the event fired will be `setClass`. This is true even for `ngClass`.</li>
5893       *   <li>an {@link ng.$animate#animate animate()} call that adds and removes classes will fire
5894       *   the `setClass` event, but if it either removes or adds classes,
5895       *   it will fire `animate` instead.</li>
5896       * </ul>
5897       *
5898       * </div>
5899       *
5900       * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5901       * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5902       *     as well as among its children
5903       * @param {Function} callback the callback function that will be fired when the listener is triggered.
5904       *
5905       * The arguments present in the callback function are:
5906       * * `element` - The captured DOM element that the animation was fired on.
5907       * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5908       * * `data` - an object with these properties:
5909       *     * addClass - `{string|null}` - space-separated CSS classes to add to the element
5910       *     * removeClass - `{string|null}` - space-separated CSS classes to remove from the element
5911       *     * from - `{Object|null}` - CSS properties & values at the beginning of the animation
5912       *     * to - `{Object|null}` - CSS properties & values at the end of the animation
5913       *
5914       * Note that the callback does not trigger a scope digest. Wrap your call into a
5915       * {@link $rootScope.Scope#$apply scope.$apply} to propagate changes to the scope.
5916       */
5917      on: $$animateQueue.on,
5918
5919      /**
5920       *
5921       * @ngdoc method
5922       * @name $animate#off
5923       * @kind function
5924       * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5925       * can be used in three different ways depending on the arguments:
5926       *
5927       * ```js
5928       * // remove all the animation event listeners listening for `enter`
5929       * $animate.off('enter');
5930       *
5931       * // remove listeners for all animation events from the container element
5932       * $animate.off(container);
5933       *
5934       * // remove all the animation event listeners listening for `enter` on the given element and its children
5935       * $animate.off('enter', container);
5936       *
5937       * // remove the event listener function provided by `callback` that is set
5938       * // to listen for `enter` on the given `container` as well as its children
5939       * $animate.off('enter', container, callback);
5940       * ```
5941       *
5942       * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
5943       * addClass, removeClass, etc...), or the container element. If it is the element, all other
5944       * arguments are ignored.
5945       * @param {DOMElement=} container the container element the event listener was placed on
5946       * @param {Function=} callback the callback function that was registered as the listener
5947       */
5948      off: $$animateQueue.off,
5949
5950      /**
5951       * @ngdoc method
5952       * @name $animate#pin
5953       * @kind function
5954       * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5955       *    outside of the DOM structure of the AngularJS application. By doing so, any animation triggered via `$animate` can be issued on the
5956       *    element despite being outside the realm of the application or within another application. Say for example if the application
5957       *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5958       *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5959       *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5960       *
5961       *    Note that this feature is only active when the `ngAnimate` module is used.
5962       *
5963       * @param {DOMElement} element the external element that will be pinned
5964       * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5965       */
5966      pin: $$animateQueue.pin,
5967
5968      /**
5969       *
5970       * @ngdoc method
5971       * @name $animate#enabled
5972       * @kind function
5973       * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5974       * function can be called in four ways:
5975       *
5976       * ```js
5977       * // returns true or false
5978       * $animate.enabled();
5979       *
5980       * // changes the enabled state for all animations
5981       * $animate.enabled(false);
5982       * $animate.enabled(true);
5983       *
5984       * // returns true or false if animations are enabled for an element
5985       * $animate.enabled(element);
5986       *
5987       * // changes the enabled state for an element and its children
5988       * $animate.enabled(element, true);
5989       * $animate.enabled(element, false);
5990       * ```
5991       *
5992       * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5993       * @param {boolean=} enabled whether or not the animations will be enabled for the element
5994       *
5995       * @return {boolean} whether or not animations are enabled
5996       */
5997      enabled: $$animateQueue.enabled,
5998
5999      /**
6000       * @ngdoc method
6001       * @name $animate#cancel
6002       * @kind function
6003       * @description Cancels the provided animation and applies the end state of the animation.
6004       * Note that this does not cancel the underlying operation, e.g. the setting of classes or
6005       * adding the element to the DOM.
6006       *
6007       * @param {animationRunner} animationRunner An animation runner returned by an $animate function.
6008       *
6009       * @example
6010        <example module="animationExample" deps="angular-animate.js" animations="true" name="animate-cancel">
6011          <file name="app.js">
6012            angular.module('animationExample', ['ngAnimate']).component('cancelExample', {
6013              templateUrl: 'template.html',
6014              controller: function($element, $animate) {
6015                this.runner = null;
6016
6017                this.addClass = function() {
6018                  this.runner = $animate.addClass($element.find('div'), 'red');
6019                  var ctrl = this;
6020                  this.runner.finally(function() {
6021                    ctrl.runner = null;
6022                  });
6023                };
6024
6025                this.removeClass = function() {
6026                  this.runner = $animate.removeClass($element.find('div'), 'red');
6027                  var ctrl = this;
6028                  this.runner.finally(function() {
6029                    ctrl.runner = null;
6030                  });
6031                };
6032
6033                this.cancel = function() {
6034                  $animate.cancel(this.runner);
6035                };
6036              }
6037            });
6038          </file>
6039          <file name="template.html">
6040            <p>
6041              <button id="add" ng-click="$ctrl.addClass()">Add</button>
6042              <button ng-click="$ctrl.removeClass()">Remove</button>
6043              <br>
6044              <button id="cancel" ng-click="$ctrl.cancel()" ng-disabled="!$ctrl.runner">Cancel</button>
6045              <br>
6046              <div id="target">CSS-Animated Text</div>
6047            </p>
6048          </file>
6049          <file name="index.html">
6050            <cancel-example></cancel-example>
6051          </file>
6052          <file name="style.css">
6053            .red-add, .red-remove {
6054              transition: all 4s cubic-bezier(0.250, 0.460, 0.450, 0.940);
6055            }
6056
6057            .red,
6058            .red-add.red-add-active {
6059              color: #FF0000;
6060              font-size: 40px;
6061            }
6062
6063            .red-remove.red-remove-active {
6064              font-size: 10px;
6065              color: black;
6066            }
6067
6068          </file>
6069        </example>
6070       */
6071      cancel: function(runner) {
6072        if (runner.cancel) {
6073          runner.cancel();
6074        }
6075      },
6076
6077      /**
6078       *
6079       * @ngdoc method
6080       * @name $animate#enter
6081       * @kind function
6082       * @description Inserts the element into the DOM either after the `after` element (if provided) or
6083       *   as the first child within the `parent` element and then triggers an animation.
6084       *   A promise is returned that will be resolved during the next digest once the animation
6085       *   has completed.
6086       *
6087       * @param {DOMElement} element the element which will be inserted into the DOM
6088       * @param {DOMElement} parent the parent element which will append the element as
6089       *   a child (so long as the after element is not present)
6090       * @param {DOMElement=} after the sibling element after which the element will be appended
6091       * @param {object=} options an optional collection of options/styles that will be applied to the element.
6092       *   The object can have the following properties:
6093       *
6094       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
6095       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
6096       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
6097       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
6098       *
6099       * @return {Runner} the animation runner
6100       */
6101      enter: function(element, parent, after, options) {
6102        parent = parent && jqLite(parent);
6103        after = after && jqLite(after);
6104        parent = parent || after.parent();
6105        domInsert(element, parent, after);
6106        return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
6107      },
6108
6109      /**
6110       *
6111       * @ngdoc method
6112       * @name $animate#move
6113       * @kind function
6114       * @description Inserts (moves) the element into its new position in the DOM either after
6115       *   the `after` element (if provided) or as the first child within the `parent` element
6116       *   and then triggers an animation. A promise is returned that will be resolved
6117       *   during the next digest once the animation has completed.
6118       *
6119       * @param {DOMElement} element the element which will be moved into the new DOM position
6120       * @param {DOMElement} parent the parent element which will append the element as
6121       *   a child (so long as the after element is not present)
6122       * @param {DOMElement=} after the sibling element after which the element will be appended
6123       * @param {object=} options an optional collection of options/styles that will be applied to the element.
6124       *   The object can have the following properties:
6125       *
6126       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
6127       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
6128       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
6129       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
6130       *
6131       * @return {Runner} the animation runner
6132       */
6133      move: function(element, parent, after, options) {
6134        parent = parent && jqLite(parent);
6135        after = after && jqLite(after);
6136        parent = parent || after.parent();
6137        domInsert(element, parent, after);
6138        return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
6139      },
6140
6141      /**
6142       * @ngdoc method
6143       * @name $animate#leave
6144       * @kind function
6145       * @description Triggers an animation and then removes the element from the DOM.
6146       * When the function is called a promise is returned that will be resolved during the next
6147       * digest once the animation has completed.
6148       *
6149       * @param {DOMElement} element the element which will be removed from the DOM
6150       * @param {object=} options an optional collection of options/styles that will be applied to the element.
6151       *   The object can have the following properties:
6152       *
6153       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
6154       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
6155       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
6156       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
6157       *
6158       * @return {Runner} the animation runner
6159       */
6160      leave: function(element, options) {
6161        return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
6162          element.remove();
6163        });
6164      },
6165
6166      /**
6167       * @ngdoc method
6168       * @name $animate#addClass
6169       * @kind function
6170       *
6171       * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
6172       *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
6173       *   animation if element already contains the CSS class or if the class is removed at a later step.
6174       *   Note that class-based animations are treated differently compared to structural animations
6175       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
6176       *   depending if CSS or JavaScript animations are used.
6177       *
6178       * @param {DOMElement} element the element which the CSS classes will be applied to
6179       * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
6180       * @param {object=} options an optional collection of options/styles that will be applied to the element.
6181       *   The object can have the following properties:
6182       *
6183       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
6184       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
6185       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
6186       *
6187       * @return {Runner} animationRunner the animation runner
6188       */
6189      addClass: function(element, className, options) {
6190        options = prepareAnimateOptions(options);
6191        options.addClass = mergeClasses(options.addclass, className);
6192        return $$animateQueue.push(element, 'addClass', options);
6193      },
6194
6195      /**
6196       * @ngdoc method
6197       * @name $animate#removeClass
6198       * @kind function
6199       *
6200       * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
6201       *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
6202       *   animation if element does not contain the CSS class or if the class is added at a later step.
6203       *   Note that class-based animations are treated differently compared to structural animations
6204       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
6205       *   depending if CSS or JavaScript animations are used.
6206       *
6207       * @param {DOMElement} element the element which the CSS classes will be applied to
6208       * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
6209       * @param {object=} options an optional collection of options/styles that will be applied to the element.
6210       *   The object can have the following properties:
6211       *
6212       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
6213       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
6214       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
6215       *
6216       * @return {Runner} the animation runner
6217       */
6218      removeClass: function(element, className, options) {
6219        options = prepareAnimateOptions(options);
6220        options.removeClass = mergeClasses(options.removeClass, className);
6221        return $$animateQueue.push(element, 'removeClass', options);
6222      },
6223
6224      /**
6225       * @ngdoc method
6226       * @name $animate#setClass
6227       * @kind function
6228       *
6229       * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
6230       *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
6231       *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
6232       *    passed. Note that class-based animations are treated differently compared to structural animations
6233       *    (like enter, move and leave) since the CSS classes may be added/removed at different points
6234       *    depending if CSS or JavaScript animations are used.
6235       *
6236       * @param {DOMElement} element the element which the CSS classes will be applied to
6237       * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
6238       * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
6239       * @param {object=} options an optional collection of options/styles that will be applied to the element.
6240       *   The object can have the following properties:
6241       *
6242       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
6243       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
6244       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
6245       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
6246       *
6247       * @return {Runner} the animation runner
6248       */
6249      setClass: function(element, add, remove, options) {
6250        options = prepareAnimateOptions(options);
6251        options.addClass = mergeClasses(options.addClass, add);
6252        options.removeClass = mergeClasses(options.removeClass, remove);
6253        return $$animateQueue.push(element, 'setClass', options);
6254      },
6255
6256      /**
6257       * @ngdoc method
6258       * @name $animate#animate
6259       * @kind function
6260       *
6261       * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
6262       * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
6263       * on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and
6264       * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
6265       * style in `to`, the style in `from` is applied immediately, and no animation is run.
6266       * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
6267       * method (or as part of the `options` parameter):
6268       *
6269       * ```js
6270       * ngModule.animation('.my-inline-animation', function() {
6271       *   return {
6272       *     animate : function(element, from, to, done, options) {
6273       *       //animation
6274       *       done();
6275       *     }
6276       *   }
6277       * });
6278       * ```
6279       *
6280       * @param {DOMElement} element the element which the CSS styles will be applied to
6281       * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
6282       * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
6283       * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
6284       *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
6285       *    (Note that if no animation is detected then this value will not be applied to the element.)
6286       * @param {object=} options an optional collection of options/styles that will be applied to the element.
6287       *   The object can have the following properties:
6288       *
6289       *   - **addClass** - `{string}` - space-separated CSS classes to add to element
6290       *   - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
6291       *   - **removeClass** - `{string}` - space-separated CSS classes to remove from element
6292       *   - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
6293       *
6294       * @return {Runner} the animation runner
6295       */
6296      animate: function(element, from, to, className, options) {
6297        options = prepareAnimateOptions(options);
6298        options.from = options.from ? extend(options.from, from) : from;
6299        options.to   = options.to   ? extend(options.to, to)     : to;
6300
6301        className = className || 'ng-inline-animate';
6302        options.tempClasses = mergeClasses(options.tempClasses, className);
6303        return $$animateQueue.push(element, 'animate', options);
6304      }
6305    };
6306  }];
6307}];
6308
6309var $$AnimateAsyncRunFactoryProvider = /** @this */ function() {
6310  this.$get = ['$$rAF', function($$rAF) {
6311    var waitQueue = [];
6312
6313    function waitForTick(fn) {
6314      waitQueue.push(fn);
6315      if (waitQueue.length > 1) return;
6316      $$rAF(function() {
6317        for (var i = 0; i < waitQueue.length; i++) {
6318          waitQueue[i]();
6319        }
6320        waitQueue = [];
6321      });
6322    }
6323
6324    return function() {
6325      var passed = false;
6326      waitForTick(function() {
6327        passed = true;
6328      });
6329      return function(callback) {
6330        if (passed) {
6331          callback();
6332        } else {
6333          waitForTick(callback);
6334        }
6335      };
6336    };
6337  }];
6338};
6339
6340var $$AnimateRunnerFactoryProvider = /** @this */ function() {
6341  this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$$isDocumentHidden', '$timeout',
6342       function($q,   $sniffer,   $$animateAsyncRun,   $$isDocumentHidden,   $timeout) {
6343
6344    var INITIAL_STATE = 0;
6345    var DONE_PENDING_STATE = 1;
6346    var DONE_COMPLETE_STATE = 2;
6347
6348    AnimateRunner.chain = function(chain, callback) {
6349      var index = 0;
6350
6351      next();
6352      function next() {
6353        if (index === chain.length) {
6354          callback(true);
6355          return;
6356        }
6357
6358        chain[index](function(response) {
6359          if (response === false) {
6360            callback(false);
6361            return;
6362          }
6363          index++;
6364          next();
6365        });
6366      }
6367    };
6368
6369    AnimateRunner.all = function(runners, callback) {
6370      var count = 0;
6371      var status = true;
6372      forEach(runners, function(runner) {
6373        runner.done(onProgress);
6374      });
6375
6376      function onProgress(response) {
6377        status = status && response;
6378        if (++count === runners.length) {
6379          callback(status);
6380        }
6381      }
6382    };
6383
6384    function AnimateRunner(host) {
6385      this.setHost(host);
6386
6387      var rafTick = $$animateAsyncRun();
6388      var timeoutTick = function(fn) {
6389        $timeout(fn, 0, false);
6390      };
6391
6392      this._doneCallbacks = [];
6393      this._tick = function(fn) {
6394        if ($$isDocumentHidden()) {
6395          timeoutTick(fn);
6396        } else {
6397          rafTick(fn);
6398        }
6399      };
6400      this._state = 0;
6401    }
6402
6403    AnimateRunner.prototype = {
6404      setHost: function(host) {
6405        this.host = host || {};
6406      },
6407
6408      done: function(fn) {
6409        if (this._state === DONE_COMPLETE_STATE) {
6410          fn();
6411        } else {
6412          this._doneCallbacks.push(fn);
6413        }
6414      },
6415
6416      progress: noop,
6417
6418      getPromise: function() {
6419        if (!this.promise) {
6420          var self = this;
6421          this.promise = $q(function(resolve, reject) {
6422            self.done(function(status) {
6423              if (status === false) {
6424                reject();
6425              } else {
6426                resolve();
6427              }
6428            });
6429          });
6430        }
6431        return this.promise;
6432      },
6433
6434      then: function(resolveHandler, rejectHandler) {
6435        return this.getPromise().then(resolveHandler, rejectHandler);
6436      },
6437
6438      'catch': function(handler) {
6439        return this.getPromise()['catch'](handler);
6440      },
6441
6442      'finally': function(handler) {
6443        return this.getPromise()['finally'](handler);
6444      },
6445
6446      pause: function() {
6447        if (this.host.pause) {
6448          this.host.pause();
6449        }
6450      },
6451
6452      resume: function() {
6453        if (this.host.resume) {
6454          this.host.resume();
6455        }
6456      },
6457
6458      end: function() {
6459        if (this.host.end) {
6460          this.host.end();
6461        }
6462        this._resolve(true);
6463      },
6464
6465      cancel: function() {
6466        if (this.host.cancel) {
6467          this.host.cancel();
6468        }
6469        this._resolve(false);
6470      },
6471
6472      complete: function(response) {
6473        var self = this;
6474        if (self._state === INITIAL_STATE) {
6475          self._state = DONE_PENDING_STATE;
6476          self._tick(function() {
6477            self._resolve(response);
6478          });
6479        }
6480      },
6481
6482      _resolve: function(response) {
6483        if (this._state !== DONE_COMPLETE_STATE) {
6484          forEach(this._doneCallbacks, function(fn) {
6485            fn(response);
6486          });
6487          this._doneCallbacks.length = 0;
6488          this._state = DONE_COMPLETE_STATE;
6489        }
6490      }
6491    };
6492
6493    return AnimateRunner;
6494  }];
6495};
6496
6497/* exported $CoreAnimateCssProvider */
6498
6499/**
6500 * @ngdoc service
6501 * @name $animateCss
6502 * @kind object
6503 * @this
6504 *
6505 * @description
6506 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
6507 * then the `$animateCss` service will actually perform animations.
6508 *
6509 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
6510 */
6511var $CoreAnimateCssProvider = function() {
6512  this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
6513
6514    return function(element, initialOptions) {
6515      // all of the animation functions should create
6516      // a copy of the options data, however, if a
6517      // parent service has already created a copy then
6518      // we should stick to using that
6519      var options = initialOptions || {};
6520      if (!options.$$prepared) {
6521        options = copy(options);
6522      }
6523
6524      // there is no point in applying the styles since
6525      // there is no animation that goes on at all in
6526      // this version of $animateCss.
6527      if (options.cleanupStyles) {
6528        options.from = options.to = null;
6529      }
6530
6531      if (options.from) {
6532        element.css(options.from);
6533        options.from = null;
6534      }
6535
6536      var closed, runner = new $$AnimateRunner();
6537      return {
6538        start: run,
6539        end: run
6540      };
6541
6542      function run() {
6543        $$rAF(function() {
6544          applyAnimationContents();
6545          if (!closed) {
6546            runner.complete();
6547          }
6548          closed = true;
6549        });
6550        return runner;
6551      }
6552
6553      function applyAnimationContents() {
6554        if (options.addClass) {
6555          element.addClass(options.addClass);
6556          options.addClass = null;
6557        }
6558        if (options.removeClass) {
6559          element.removeClass(options.removeClass);
6560          options.removeClass = null;
6561        }
6562        if (options.to) {
6563          element.css(options.to);
6564          options.to = null;
6565        }
6566      }
6567    };
6568  }];
6569};
6570
6571/* global getHash: true, stripHash: false */
6572
6573function getHash(url) {
6574  var index = url.indexOf('#');
6575  return index === -1 ? '' : url.substr(index);
6576}
6577
6578function trimEmptyHash(url) {
6579  return url.replace(/#$/, '');
6580}
6581
6582/**
6583 * ! This is a private undocumented service !
6584 *
6585 * @name $browser
6586 * @requires $log
6587 * @description
6588 * This object has two goals:
6589 *
6590 * - hide all the global state in the browser caused by the window object
6591 * - abstract away all the browser specific features and inconsistencies
6592 *
6593 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
6594 * service, which can be used for convenient testing of the application without the interaction with
6595 * the real browser apis.
6596 */
6597/**
6598 * @param {object} window The global window object.
6599 * @param {object} document jQuery wrapped document.
6600 * @param {object} $log window.console or an object with the same interface.
6601 * @param {object} $sniffer $sniffer service
6602 */
6603function Browser(window, document, $log, $sniffer, $$taskTrackerFactory) {
6604  var self = this,
6605      location = window.location,
6606      history = window.history,
6607      setTimeout = window.setTimeout,
6608      clearTimeout = window.clearTimeout,
6609      pendingDeferIds = {},
6610      taskTracker = $$taskTrackerFactory($log);
6611
6612  self.isMock = false;
6613
6614  //////////////////////////////////////////////////////////////
6615  // Task-tracking API
6616  //////////////////////////////////////////////////////////////
6617
6618  // TODO(vojta): remove this temporary api
6619  self.$$completeOutstandingRequest = taskTracker.completeTask;
6620  self.$$incOutstandingRequestCount = taskTracker.incTaskCount;
6621
6622  // TODO(vojta): prefix this method with $$ ?
6623  self.notifyWhenNoOutstandingRequests = taskTracker.notifyWhenNoPendingTasks;
6624
6625  //////////////////////////////////////////////////////////////
6626  // URL API
6627  //////////////////////////////////////////////////////////////
6628
6629  var cachedState, lastHistoryState,
6630      lastBrowserUrl = location.href,
6631      baseElement = document.find('base'),
6632      pendingLocation = null,
6633      getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
6634        try {
6635          return history.state;
6636        } catch (e) {
6637          // MSIE can reportedly throw when there is no state (UNCONFIRMED).
6638        }
6639      };
6640
6641  cacheState();
6642
6643  /**
6644   * @name $browser#url
6645   *
6646   * @description
6647   * GETTER:
6648   * Without any argument, this method just returns current value of `location.href` (with a
6649   * trailing `#` stripped of if the hash is empty).
6650   *
6651   * SETTER:
6652   * With at least one argument, this method sets url to new value.
6653   * If html5 history api supported, `pushState`/`replaceState` is used, otherwise
6654   * `location.href`/`location.replace` is used.
6655   * Returns its own instance to allow chaining.
6656   *
6657   * NOTE: this api is intended for use only by the `$location` service. Please use the
6658   * {@link ng.$location $location service} to change url.
6659   *
6660   * @param {string} url New url (when used as setter)
6661   * @param {boolean=} replace Should new url replace current history record?
6662   * @param {object=} state State object to use with `pushState`/`replaceState`
6663   */
6664  self.url = function(url, replace, state) {
6665    // In modern browsers `history.state` is `null` by default; treating it separately
6666    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
6667    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
6668    if (isUndefined(state)) {
6669      state = null;
6670    }
6671
6672    // Android Browser BFCache causes location, history reference to become stale.
6673    if (location !== window.location) location = window.location;
6674    if (history !== window.history) history = window.history;
6675
6676    // setter
6677    if (url) {
6678      var sameState = lastHistoryState === state;
6679
6680      // Normalize the inputted URL
6681      url = urlResolve(url).href;
6682
6683      // Don't change anything if previous and current URLs and states match. This also prevents
6684      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
6685      // See https://github.com/angular/angular.js/commit/ffb2701
6686      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
6687        return self;
6688      }
6689      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
6690      lastBrowserUrl = url;
6691      lastHistoryState = state;
6692      // Don't use history API if only the hash changed
6693      // due to a bug in IE10/IE11 which leads
6694      // to not firing a `hashchange` nor `popstate` event
6695      // in some cases (see #9143).
6696      if ($sniffer.history && (!sameBase || !sameState)) {
6697        history[replace ? 'replaceState' : 'pushState'](state, '', url);
6698        cacheState();
6699      } else {
6700        if (!sameBase) {
6701          pendingLocation = url;
6702        }
6703        if (replace) {
6704          location.replace(url);
6705        } else if (!sameBase) {
6706          location.href = url;
6707        } else {
6708          location.hash = getHash(url);
6709        }
6710        if (location.href !== url) {
6711          pendingLocation = url;
6712        }
6713      }
6714      if (pendingLocation) {
6715        pendingLocation = url;
6716      }
6717      return self;
6718    // getter
6719    } else {
6720      // - pendingLocation is needed as browsers don't allow to read out
6721      //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
6722      //   https://openradar.appspot.com/22186109).
6723      return trimEmptyHash(pendingLocation || location.href);
6724    }
6725  };
6726
6727  /**
6728   * @name $browser#state
6729   *
6730   * @description
6731   * This method is a getter.
6732   *
6733   * Return history.state or null if history.state is undefined.
6734   *
6735   * @returns {object} state
6736   */
6737  self.state = function() {
6738    return cachedState;
6739  };
6740
6741  var urlChangeListeners = [],
6742      urlChangeInit = false;
6743
6744  function cacheStateAndFireUrlChange() {
6745    pendingLocation = null;
6746    fireStateOrUrlChange();
6747  }
6748
6749  // This variable should be used *only* inside the cacheState function.
6750  var lastCachedState = null;
6751  function cacheState() {
6752    // This should be the only place in $browser where `history.state` is read.
6753    cachedState = getCurrentState();
6754    cachedState = isUndefined(cachedState) ? null : cachedState;
6755
6756    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
6757    if (equals(cachedState, lastCachedState)) {
6758      cachedState = lastCachedState;
6759    }
6760
6761    lastCachedState = cachedState;
6762    lastHistoryState = cachedState;
6763  }
6764
6765  function fireStateOrUrlChange() {
6766    var prevLastHistoryState = lastHistoryState;
6767    cacheState();
6768
6769    if (lastBrowserUrl === self.url() && prevLastHistoryState === cachedState) {
6770      return;
6771    }
6772
6773    lastBrowserUrl = self.url();
6774    lastHistoryState = cachedState;
6775    forEach(urlChangeListeners, function(listener) {
6776      listener(self.url(), cachedState);
6777    });
6778  }
6779
6780  /**
6781   * @name $browser#onUrlChange
6782   *
6783   * @description
6784   * Register callback function that will be called, when url changes.
6785   *
6786   * It's only called when the url is changed from outside of AngularJS:
6787   * - user types different url into address bar
6788   * - user clicks on history (forward/back) button
6789   * - user clicks on a link
6790   *
6791   * It's not called when url is changed by $browser.url() method
6792   *
6793   * The listener gets called with new url as parameter.
6794   *
6795   * NOTE: this api is intended for use only by the $location service. Please use the
6796   * {@link ng.$location $location service} to monitor url changes in AngularJS apps.
6797   *
6798   * @param {function(string)} listener Listener function to be called when url changes.
6799   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
6800   */
6801  self.onUrlChange = function(callback) {
6802    // TODO(vojta): refactor to use node's syntax for events
6803    if (!urlChangeInit) {
6804      // We listen on both (hashchange/popstate) when available, as some browsers don't
6805      // fire popstate when user changes the address bar and don't fire hashchange when url
6806      // changed by push/replaceState
6807
6808      // html5 history api - popstate event
6809      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
6810      // hashchange event
6811      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
6812
6813      urlChangeInit = true;
6814    }
6815
6816    urlChangeListeners.push(callback);
6817    return callback;
6818  };
6819
6820  /**
6821   * @private
6822   * Remove popstate and hashchange handler from window.
6823   *
6824   * NOTE: this api is intended for use only by $rootScope.
6825   */
6826  self.$$applicationDestroyed = function() {
6827    jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
6828  };
6829
6830  /**
6831   * Checks whether the url has changed outside of AngularJS.
6832   * Needs to be exported to be able to check for changes that have been done in sync,
6833   * as hashchange/popstate events fire in async.
6834   */
6835  self.$$checkUrlChange = fireStateOrUrlChange;
6836
6837  //////////////////////////////////////////////////////////////
6838  // Misc API
6839  //////////////////////////////////////////////////////////////
6840
6841  /**
6842   * @name $browser#baseHref
6843   *
6844   * @description
6845   * Returns current <base href>
6846   * (always relative - without domain)
6847   *
6848   * @returns {string} The current base href
6849   */
6850  self.baseHref = function() {
6851    var href = baseElement.attr('href');
6852    return href ? href.replace(/^(https?:)?\/\/[^/]*/, '') : '';
6853  };
6854
6855  /**
6856   * @name $browser#defer
6857   * @param {function()} fn A function, who's execution should be deferred.
6858   * @param {number=} [delay=0] Number of milliseconds to defer the function execution.
6859   * @param {string=} [taskType=DEFAULT_TASK_TYPE] The type of task that is deferred.
6860   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
6861   *
6862   * @description
6863   * Executes a fn asynchronously via `setTimeout(fn, delay)`.
6864   *
6865   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
6866   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
6867   * via `$browser.defer.flush()`.
6868   *
6869   */
6870  self.defer = function(fn, delay, taskType) {
6871    var timeoutId;
6872
6873    delay = delay || 0;
6874    taskType = taskType || taskTracker.DEFAULT_TASK_TYPE;
6875
6876    taskTracker.incTaskCount(taskType);
6877    timeoutId = setTimeout(function() {
6878      delete pendingDeferIds[timeoutId];
6879      taskTracker.completeTask(fn, taskType);
6880    }, delay);
6881    pendingDeferIds[timeoutId] = taskType;
6882
6883    return timeoutId;
6884  };
6885
6886
6887  /**
6888   * @name $browser#defer.cancel
6889   *
6890   * @description
6891   * Cancels a deferred task identified with `deferId`.
6892   *
6893   * @param {*} deferId Token returned by the `$browser.defer` function.
6894   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
6895   *                    canceled.
6896   */
6897  self.defer.cancel = function(deferId) {
6898    if (pendingDeferIds.hasOwnProperty(deferId)) {
6899      var taskType = pendingDeferIds[deferId];
6900      delete pendingDeferIds[deferId];
6901      clearTimeout(deferId);
6902      taskTracker.completeTask(noop, taskType);
6903      return true;
6904    }
6905    return false;
6906  };
6907
6908}
6909
6910/** @this */
6911function $BrowserProvider() {
6912  this.$get = ['$window', '$log', '$sniffer', '$document', '$$taskTrackerFactory',
6913       function($window,   $log,   $sniffer,   $document,   $$taskTrackerFactory) {
6914    return new Browser($window, $document, $log, $sniffer, $$taskTrackerFactory);
6915  }];
6916}
6917
6918/**
6919 * @ngdoc service
6920 * @name $cacheFactory
6921 * @this
6922 *
6923 * @description
6924 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
6925 * them.
6926 *
6927 * ```js
6928 *
6929 *  var cache = $cacheFactory('cacheId');
6930 *  expect($cacheFactory.get('cacheId')).toBe(cache);
6931 *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
6932 *
6933 *  cache.put("key", "value");
6934 *  cache.put("another key", "another value");
6935 *
6936 *  // We've specified no options on creation
6937 *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
6938 *
6939 * ```
6940 *
6941 *
6942 * @param {string} cacheId Name or id of the newly created cache.
6943 * @param {object=} options Options object that specifies the cache behavior. Properties:
6944 *
6945 *   - `{number=}` `capacity` — turns the cache into LRU cache.
6946 *
6947 * @returns {object} Newly created cache object with the following set of methods:
6948 *
6949 * - `{object}` `info()` — Returns id, size, and options of cache.
6950 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
6951 *   it.
6952 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
6953 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
6954 * - `{void}` `removeAll()` — Removes all cached values.
6955 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
6956 *
6957 * @example
6958   <example module="cacheExampleApp" name="cache-factory">
6959     <file name="index.html">
6960       <div ng-controller="CacheController">
6961         <input ng-model="newCacheKey" placeholder="Key">
6962         <input ng-model="newCacheValue" placeholder="Value">
6963         <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
6964
6965         <p ng-if="keys.length">Cached Values</p>
6966         <div ng-repeat="key in keys">
6967           <span ng-bind="key"></span>
6968           <span>: </span>
6969           <b ng-bind="cache.get(key)"></b>
6970         </div>
6971
6972         <p>Cache Info</p>
6973         <div ng-repeat="(key, value) in cache.info()">
6974           <span ng-bind="key"></span>
6975           <span>: </span>
6976           <b ng-bind="value"></b>
6977         </div>
6978       </div>
6979     </file>
6980     <file name="script.js">
6981       angular.module('cacheExampleApp', []).
6982         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6983           $scope.keys = [];
6984           $scope.cache = $cacheFactory('cacheId');
6985           $scope.put = function(key, value) {
6986             if (angular.isUndefined($scope.cache.get(key))) {
6987               $scope.keys.push(key);
6988             }
6989             $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6990           };
6991         }]);
6992     </file>
6993     <file name="style.css">
6994       p {
6995         margin: 10px 0 3px;
6996       }
6997     </file>
6998   </example>
6999 */
7000function $CacheFactoryProvider() {
7001
7002  this.$get = function() {
7003    var caches = {};
7004
7005    function cacheFactory(cacheId, options) {
7006      if (cacheId in caches) {
7007        throw minErr('$cacheFactory')('iid', 'CacheId \'{0}\' is already taken!', cacheId);
7008      }
7009
7010      var size = 0,
7011          stats = extend({}, options, {id: cacheId}),
7012          data = createMap(),
7013          capacity = (options && options.capacity) || Number.MAX_VALUE,
7014          lruHash = createMap(),
7015          freshEnd = null,
7016          staleEnd = null;
7017
7018      /**
7019       * @ngdoc type
7020       * @name $cacheFactory.Cache
7021       *
7022       * @description
7023       * A cache object used to store and retrieve data, primarily used by
7024       * {@link $templateRequest $templateRequest} and the {@link ng.directive:script script}
7025       * directive to cache templates and other data.
7026       *
7027       * ```js
7028       *  angular.module('superCache')
7029       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
7030       *      return $cacheFactory('super-cache');
7031       *    }]);
7032       * ```
7033       *
7034       * Example test:
7035       *
7036       * ```js
7037       *  it('should behave like a cache', inject(function(superCache) {
7038       *    superCache.put('key', 'value');
7039       *    superCache.put('another key', 'another value');
7040       *
7041       *    expect(superCache.info()).toEqual({
7042       *      id: 'super-cache',
7043       *      size: 2
7044       *    });
7045       *
7046       *    superCache.remove('another key');
7047       *    expect(superCache.get('another key')).toBeUndefined();
7048       *
7049       *    superCache.removeAll();
7050       *    expect(superCache.info()).toEqual({
7051       *      id: 'super-cache',
7052       *      size: 0
7053       *    });
7054       *  }));
7055       * ```
7056       */
7057      return (caches[cacheId] = {
7058
7059        /**
7060         * @ngdoc method
7061         * @name $cacheFactory.Cache#put
7062         * @kind function
7063         *
7064         * @description
7065         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
7066         * retrieved later, and incrementing the size of the cache if the key was not already
7067         * present in the cache. If behaving like an LRU cache, it will also remove stale
7068         * entries from the set.
7069         *
7070         * It will not insert undefined values into the cache.
7071         *
7072         * @param {string} key the key under which the cached data is stored.
7073         * @param {*} value the value to store alongside the key. If it is undefined, the key
7074         *    will not be stored.
7075         * @returns {*} the value stored.
7076         */
7077        put: function(key, value) {
7078          if (isUndefined(value)) return;
7079          if (capacity < Number.MAX_VALUE) {
7080            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
7081
7082            refresh(lruEntry);
7083          }
7084
7085          if (!(key in data)) size++;
7086          data[key] = value;
7087
7088          if (size > capacity) {
7089            this.remove(staleEnd.key);
7090          }
7091
7092          return value;
7093        },
7094
7095        /**
7096         * @ngdoc method
7097         * @name $cacheFactory.Cache#get
7098         * @kind function
7099         *
7100         * @description
7101         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
7102         *
7103         * @param {string} key the key of the data to be retrieved
7104         * @returns {*} the value stored.
7105         */
7106        get: function(key) {
7107          if (capacity < Number.MAX_VALUE) {
7108            var lruEntry = lruHash[key];
7109
7110            if (!lruEntry) return;
7111
7112            refresh(lruEntry);
7113          }
7114
7115          return data[key];
7116        },
7117
7118
7119        /**
7120         * @ngdoc method
7121         * @name $cacheFactory.Cache#remove
7122         * @kind function
7123         *
7124         * @description
7125         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
7126         *
7127         * @param {string} key the key of the entry to be removed
7128         */
7129        remove: function(key) {
7130          if (capacity < Number.MAX_VALUE) {
7131            var lruEntry = lruHash[key];
7132
7133            if (!lruEntry) return;
7134
7135            if (lruEntry === freshEnd) freshEnd = lruEntry.p;
7136            if (lruEntry === staleEnd) staleEnd = lruEntry.n;
7137            link(lruEntry.n,lruEntry.p);
7138
7139            delete lruHash[key];
7140          }
7141
7142          if (!(key in data)) return;
7143
7144          delete data[key];
7145          size--;
7146        },
7147
7148
7149        /**
7150         * @ngdoc method
7151         * @name $cacheFactory.Cache#removeAll
7152         * @kind function
7153         *
7154         * @description
7155         * Clears the cache object of any entries.
7156         */
7157        removeAll: function() {
7158          data = createMap();
7159          size = 0;
7160          lruHash = createMap();
7161          freshEnd = staleEnd = null;
7162        },
7163
7164
7165        /**
7166         * @ngdoc method
7167         * @name $cacheFactory.Cache#destroy
7168         * @kind function
7169         *
7170         * @description
7171         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
7172         * removing it from the {@link $cacheFactory $cacheFactory} set.
7173         */
7174        destroy: function() {
7175          data = null;
7176          stats = null;
7177          lruHash = null;
7178          delete caches[cacheId];
7179        },
7180
7181
7182        /**
7183         * @ngdoc method
7184         * @name $cacheFactory.Cache#info
7185         * @kind function
7186         *
7187         * @description
7188         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
7189         *
7190         * @returns {object} an object with the following properties:
7191         *   <ul>
7192         *     <li>**id**: the id of the cache instance</li>
7193         *     <li>**size**: the number of entries kept in the cache instance</li>
7194         *     <li>**...**: any additional properties from the options object when creating the
7195         *       cache.</li>
7196         *   </ul>
7197         */
7198        info: function() {
7199          return extend({}, stats, {size: size});
7200        }
7201      });
7202
7203
7204      /**
7205       * makes the `entry` the freshEnd of the LRU linked list
7206       */
7207      function refresh(entry) {
7208        if (entry !== freshEnd) {
7209          if (!staleEnd) {
7210            staleEnd = entry;
7211          } else if (staleEnd === entry) {
7212            staleEnd = entry.n;
7213          }
7214
7215          link(entry.n, entry.p);
7216          link(entry, freshEnd);
7217          freshEnd = entry;
7218          freshEnd.n = null;
7219        }
7220      }
7221
7222
7223      /**
7224       * bidirectionally links two entries of the LRU linked list
7225       */
7226      function link(nextEntry, prevEntry) {
7227        if (nextEntry !== prevEntry) {
7228          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
7229          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
7230        }
7231      }
7232    }
7233
7234
7235  /**
7236   * @ngdoc method
7237   * @name $cacheFactory#info
7238   *
7239   * @description
7240   * Get information about all the caches that have been created
7241   *
7242   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
7243   */
7244    cacheFactory.info = function() {
7245      var info = {};
7246      forEach(caches, function(cache, cacheId) {
7247        info[cacheId] = cache.info();
7248      });
7249      return info;
7250    };
7251
7252
7253  /**
7254   * @ngdoc method
7255   * @name $cacheFactory#get
7256   *
7257   * @description
7258   * Get access to a cache object by the `cacheId` used when it was created.
7259   *
7260   * @param {string} cacheId Name or id of a cache to access.
7261   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
7262   */
7263    cacheFactory.get = function(cacheId) {
7264      return caches[cacheId];
7265    };
7266
7267
7268    return cacheFactory;
7269  };
7270}
7271
7272/**
7273 * @ngdoc service
7274 * @name $templateCache
7275 * @this
7276 *
7277 * @description
7278 * `$templateCache` is a {@link $cacheFactory.Cache Cache object} created by the
7279 * {@link ng.$cacheFactory $cacheFactory}.
7280 *
7281 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
7282 * can load templates directly into the cache in a `script` tag, by using {@link $templateRequest},
7283 * or by consuming the `$templateCache` service directly.
7284 *
7285 * Adding via the `script` tag:
7286 *
7287 * ```html
7288 *   <script type="text/ng-template" id="templateId.html">
7289 *     <p>This is the content of the template</p>
7290 *   </script>
7291 * ```
7292 *
7293 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
7294 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (e.g.
7295 * element with {@link ngApp} attribute), otherwise the template will be ignored.
7296 *
7297 * Adding via the `$templateCache` service:
7298 *
7299 * ```js
7300 * var myApp = angular.module('myApp', []);
7301 * myApp.run(function($templateCache) {
7302 *   $templateCache.put('templateId.html', 'This is the content of the template');
7303 * });
7304 * ```
7305 *
7306 * To retrieve the template later, simply use it in your component:
7307 * ```js
7308 * myApp.component('myComponent', {
7309 *    templateUrl: 'templateId.html'
7310 * });
7311 * ```
7312 *
7313 * or get it via the `$templateCache` service:
7314 * ```js
7315 * $templateCache.get('templateId.html')
7316 * ```
7317 *
7318 */
7319function $TemplateCacheProvider() {
7320  this.$get = ['$cacheFactory', function($cacheFactory) {
7321    return $cacheFactory('templates');
7322  }];
7323}
7324
7325/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7326 *     Any commits to this file should be reviewed with security in mind.  *
7327 *   Changes to this file can potentially create security vulnerabilities. *
7328 *          An approval from 2 Core members with history of modifying      *
7329 *                         this file is required.                          *
7330 *                                                                         *
7331 *  Does the change somehow allow for arbitrary javascript to be executed? *
7332 *    Or allows for someone to change the prototype of built-in objects?   *
7333 *     Or gives undesired access to variables like document or window?    *
7334 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
7335
7336/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
7337 *
7338 * DOM-related variables:
7339 *
7340 * - "node" - DOM Node
7341 * - "element" - DOM Element or Node
7342 * - "$node" or "$element" - jqLite-wrapped node or element
7343 *
7344 *
7345 * Compiler related stuff:
7346 *
7347 * - "linkFn" - linking fn of a single directive
7348 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
7349 * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
7350 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
7351 */
7352
7353
7354/**
7355 * @ngdoc service
7356 * @name $compile
7357 * @kind function
7358 *
7359 * @description
7360 * Compiles an HTML string or DOM into a template and produces a template function, which
7361 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
7362 *
7363 * The compilation is a process of walking the DOM tree and matching DOM elements to
7364 * {@link ng.$compileProvider#directive directives}.
7365 *
7366 * <div class="alert alert-warning">
7367 * **Note:** This document is an in-depth reference of all directive options.
7368 * For a gentle introduction to directives with examples of common use cases,
7369 * see the {@link guide/directive directive guide}.
7370 * </div>
7371 *
7372 * ## Comprehensive Directive API
7373 *
7374 * There are many different options for a directive.
7375 *
7376 * The difference resides in the return value of the factory function.
7377 * You can either return a {@link $compile#directive-definition-object Directive Definition Object (see below)}
7378 * that defines the directive properties, or just the `postLink` function (all other properties will have
7379 * the default values).
7380 *
7381 * <div class="alert alert-success">
7382 * **Best Practice:** It's recommended to use the "directive definition object" form.
7383 * </div>
7384 *
7385 * Here's an example directive declared with a Directive Definition Object:
7386 *
7387 * ```js
7388 *   var myModule = angular.module(...);
7389 *
7390 *   myModule.directive('directiveName', function factory(injectables) {
7391 *     var directiveDefinitionObject = {
7392 *       {@link $compile#-priority- priority}: 0,
7393 *       {@link $compile#-template- template}: '<div></div>', // or // function(tElement, tAttrs) { ... },
7394 *       // or
7395 *       // {@link $compile#-templateurl- templateUrl}: 'directive.html', // or // function(tElement, tAttrs) { ... },
7396 *       {@link $compile#-transclude- transclude}: false,
7397 *       {@link $compile#-restrict- restrict}: 'A',
7398 *       {@link $compile#-templatenamespace- templateNamespace}: 'html',
7399 *       {@link $compile#-scope- scope}: false,
7400 *       {@link $compile#-controller- controller}: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
7401 *       {@link $compile#-controlleras- controllerAs}: 'stringIdentifier',
7402 *       {@link $compile#-bindtocontroller- bindToController}: false,
7403 *       {@link $compile#-require- require}: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
7404 *       {@link $compile#-multielement- multiElement}: false,
7405 *       {@link $compile#-compile- compile}: function compile(tElement, tAttrs, transclude) {
7406 *         return {
7407 *            {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... },
7408 *            {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... }
7409 *         }
7410 *         // or
7411 *         // return function postLink( ... ) { ... }
7412 *       },
7413 *       // or
7414 *       // {@link $compile#-link- link}: {
7415 *       //  {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... },
7416 *       //  {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... }
7417 *       // }
7418 *       // or
7419 *       // {@link $compile#-link- link}: function postLink( ... ) { ... }
7420 *     };
7421 *     return directiveDefinitionObject;
7422 *   });
7423 * ```
7424 *
7425 * <div class="alert alert-warning">
7426 * **Note:** Any unspecified options will use the default value. You can see the default values below.
7427 * </div>
7428 *
7429 * Therefore the above can be simplified as:
7430 *
7431 * ```js
7432 *   var myModule = angular.module(...);
7433 *
7434 *   myModule.directive('directiveName', function factory(injectables) {
7435 *     var directiveDefinitionObject = {
7436 *       link: function postLink(scope, iElement, iAttrs) { ... }
7437 *     };
7438 *     return directiveDefinitionObject;
7439 *     // or
7440 *     // return function postLink(scope, iElement, iAttrs) { ... }
7441 *   });
7442 * ```
7443 *
7444 * ### Life-cycle hooks
7445 * Directive controllers can provide the following methods that are called by AngularJS at points in the life-cycle of the
7446 * directive:
7447 * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
7448 *   had their bindings initialized (and before the pre &amp; post linking functions for the directives on
7449 *   this element). This is a good place to put initialization code for your controller.
7450 * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
7451 *   `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
7452 *   object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
7453 *   component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will
7454 *   also be called when your bindings are initialized.
7455 * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on
7456 *   changes. Any actions that you wish to take in response to the changes that you detect must be
7457 *   invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
7458 *   could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not
7459 *   be detected by AngularJS's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments;
7460 *   if detecting changes, you must store the previous value(s) for comparison to the current values.
7461 * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
7462 *   external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
7463 *   the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
7464 *   components will have their `$onDestroy()` hook called before child components.
7465 * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
7466 *   function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
7467 *   Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
7468 *   they are waiting for their template to load asynchronously and their own compilation and linking has been
7469 *   suspended until that occurs.
7470 *
7471 * #### Comparison with life-cycle hooks in the new Angular
7472 * The new Angular also uses life-cycle hooks for its components. While the AngularJS life-cycle hooks are similar there are
7473 * some differences that you should be aware of, especially when it comes to moving your code from AngularJS to Angular:
7474 *
7475 * * AngularJS hooks are prefixed with `$`, such as `$onInit`. Angular hooks are prefixed with `ng`, such as `ngOnInit`.
7476 * * AngularJS hooks can be defined on the controller prototype or added to the controller inside its constructor.
7477 *   In Angular you can only define hooks on the prototype of the Component class.
7478 * * Due to the differences in change-detection, you may get many more calls to `$doCheck` in AngularJS than you would to
7479 *   `ngDoCheck` in Angular.
7480 * * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be
7481 *   propagated throughout the application.
7482 *   Angular does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an
7483 *   error or do nothing depending upon the state of `enableProdMode()`.
7484 *
7485 * #### Life-cycle hook examples
7486 *
7487 * This example shows how you can check for mutations to a Date object even though the identity of the object
7488 * has not changed.
7489 *
7490 * <example name="doCheckDateExample" module="do-check-module">
7491 *   <file name="app.js">
7492 *     angular.module('do-check-module', [])
7493 *       .component('app', {
7494 *         template:
7495 *           'Month: <input ng-model="$ctrl.month" ng-change="$ctrl.updateDate()">' +
7496 *           'Date: {{ $ctrl.date }}' +
7497 *           '<test date="$ctrl.date"></test>',
7498 *         controller: function() {
7499 *           this.date = new Date();
7500 *           this.month = this.date.getMonth();
7501 *           this.updateDate = function() {
7502 *             this.date.setMonth(this.month);
7503 *           };
7504 *         }
7505 *       })
7506 *       .component('test', {
7507 *         bindings: { date: '<' },
7508 *         template:
7509 *           '<pre>{{ $ctrl.log | json }}</pre>',
7510 *         controller: function() {
7511 *           var previousValue;
7512 *           this.log = [];
7513 *           this.$doCheck = function() {
7514 *             var currentValue = this.date && this.date.valueOf();
7515 *             if (previousValue !== currentValue) {
7516 *               this.log.push('doCheck: date mutated: ' + this.date);
7517 *               previousValue = currentValue;
7518 *             }
7519 *           };
7520 *         }
7521 *       });
7522 *   </file>
7523 *   <file name="index.html">
7524 *     <app></app>
7525 *   </file>
7526 * </example>
7527 *
7528 * This example show how you might use `$doCheck` to trigger changes in your component's inputs even if the
7529 * actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large
7530 * arrays or objects can have a negative impact on your application performance.)
7531 *
7532 * <example name="doCheckArrayExample" module="do-check-module">
7533 *   <file name="index.html">
7534 *     <div ng-init="items = []">
7535 *       <button ng-click="items.push(items.length)">Add Item</button>
7536 *       <button ng-click="items = []">Reset Items</button>
7537 *       <pre>{{ items }}</pre>
7538 *       <test items="items"></test>
7539 *     </div>
7540 *   </file>
7541 *   <file name="app.js">
7542 *      angular.module('do-check-module', [])
7543 *        .component('test', {
7544 *          bindings: { items: '<' },
7545 *          template:
7546 *            '<pre>{{ $ctrl.log | json }}</pre>',
7547 *          controller: function() {
7548 *            this.log = [];
7549 *
7550 *            this.$doCheck = function() {
7551 *              if (this.items_ref !== this.items) {
7552 *                this.log.push('doCheck: items changed');
7553 *                this.items_ref = this.items;
7554 *              }
7555 *              if (!angular.equals(this.items_clone, this.items)) {
7556 *                this.log.push('doCheck: items mutated');
7557 *                this.items_clone = angular.copy(this.items);
7558 *              }
7559 *            };
7560 *          }
7561 *        });
7562 *   </file>
7563 * </example>
7564 *
7565 *
7566 * ### Directive Definition Object
7567 *
7568 * The directive definition object provides instructions to the {@link ng.$compile
7569 * compiler}. The attributes are:
7570 *
7571 * #### `multiElement`
7572 * When this property is set to true (default is `false`), the HTML compiler will collect DOM nodes between
7573 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
7574 * together as the directive elements. It is recommended that this feature be used on directives
7575 * which are not strictly behavioral (such as {@link ngClick}), and which
7576 * do not manipulate or replace child nodes (such as {@link ngInclude}).
7577 *
7578 * #### `priority`
7579 * When there are multiple directives defined on a single DOM element, sometimes it
7580 * is necessary to specify the order in which the directives are applied. The `priority` is used
7581 * to sort the directives before their `compile` functions get called. Priority is defined as a
7582 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
7583 * are also run in priority order, but post-link functions are run in reverse order. The order
7584 * of directives with the same priority is undefined. The default priority is `0`.
7585 *
7586 * #### `terminal`
7587 * If set to true then the current `priority` will be the last set of directives
7588 * which will execute (any directives at the current priority will still execute
7589 * as the order of execution on same `priority` is undefined). Note that expressions
7590 * and other directives used in the directive's template will also be excluded from execution.
7591 *
7592 * #### `scope`
7593 * The scope property can be `false`, `true`, or an object:
7594 *
7595 * * **`false` (default):** No scope will be created for the directive. The directive will use its
7596 * parent's scope.
7597 *
7598 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
7599 * the directive's element. If multiple directives on the same element request a new scope,
7600 * only one new scope is created.
7601 *
7602 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's template.
7603 * The 'isolate' scope differs from normal scope in that it does not prototypically
7604 * inherit from its parent scope. This is useful when creating reusable components, which should not
7605 * accidentally read or modify data in the parent scope. Note that an isolate scope
7606 * directive without a `template` or `templateUrl` will not apply the isolate scope
7607 * to its children elements.
7608 *
7609 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
7610 * directive's element. These local properties are useful for aliasing values for templates. The keys in
7611 * the object hash map to the name of the property on the isolate scope; the values define how the property
7612 * is bound to the parent scope, via matching attributes on the directive's element:
7613 *
7614 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
7615 *   always a string since DOM attributes are strings. If no `attr` name is specified then the
7616 *   attribute name is assumed to be the same as the local name. Given `<my-component
7617 *   my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
7618 *   the directive's scope property `localName` will reflect the interpolated value of `hello
7619 *   {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
7620 *   scope. The `name` is read from the parent scope (not the directive's scope).
7621 *
7622 * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
7623 *   passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
7624 *   If no `attr` name is specified then the attribute name is assumed to be the same as the local
7625 *   name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
7626 *   localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
7627 *   value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
7628 *   `localModel` and vice versa. If the binding expression is non-assignable, or if the attribute
7629 *   isn't  optional and doesn't exist, an exception
7630 *   ({@link error/$compile/nonassign `$compile:nonassign`}) will be thrown upon discovering changes
7631 *   to the local value, since it will be impossible to sync them back to the parent scope.
7632 *
7633 *   By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
7634 *   method is used for tracking changes, and the equality check is based on object identity.
7635 *   However, if an object literal or an array literal is passed as the binding expression, the
7636 *   equality check is done by value (using the {@link angular.equals} function). It's also possible
7637 *   to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
7638 *   `$watchCollection`}: use `=*` or `=*attr`
7639 *
7640  * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
7641 *   expression passed via the attribute `attr`. The expression is evaluated in the context of the
7642 *   parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
7643 *   local name.
7644 *
7645 *   For example, given `<my-component my-attr="parentModel">` and directive definition of
7646 *   `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
7647 *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
7648 *   in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
7649 *   two caveats:
7650 *     1. one-way binding does not copy the value from the parent to the isolate scope, it simply
7651 *     sets the same value. That means if your bound value is an object, changes to its properties
7652 *     in the isolated scope will be reflected in the parent scope (because both reference the same object).
7653 *     2. one-way binding watches changes to the **identity** of the parent value. That means the
7654 *     {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
7655 *     to the value has changed. In most cases, this should not be of concern, but can be important
7656 *     to know if you one-way bind to an object, and then replace that object in the isolated scope.
7657 *     If you now change a property of the object in your parent scope, the change will not be
7658 *     propagated to the isolated scope, because the identity of the object on the parent scope
7659 *     has not changed. Instead you must assign a new object.
7660 *
7661 *   One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
7662 *   back to the parent. However, it does not make this completely impossible.
7663 *
7664 *   By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
7665 *   method is used for tracking changes, and the equality check is based on object identity.
7666 *   It's also possible to watch the evaluated value shallowly with
7667 *   {@link ng.$rootScope.Scope#$watchCollection `$watchCollection`}: use `<*` or `<*attr`
7668 *
7669 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
7670 *   no `attr` name is specified then the attribute name is assumed to be the same as the local name.
7671 *   Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
7672 *   localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
7673 *   the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
7674 *   via an expression to the parent scope. This can be done by passing a map of local variable names
7675 *   and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
7676 *   then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
7677 *
7678 * All 4 kinds of bindings (`@`, `=`, `<`, and `&`) can be made optional by adding `?` to the expression.
7679 * The marker must come after the mode and before the attribute name.
7680 * See the {@link error/$compile/iscp Invalid Isolate Scope Definition error} for definition examples.
7681 * This is useful to refine the interface directives provide.
7682 * One subtle difference between optional and non-optional happens **when the binding attribute is not
7683 * set**:
7684 * - the binding is optional: the property will not be defined
7685 * - the binding is not optional: the property is defined
7686 *
7687 * ```js
7688 *app.directive('testDir', function() {
7689    return {
7690      scope: {
7691        notoptional: '=',
7692        optional: '=?',
7693      },
7694      bindToController: true,
7695      controller: function() {
7696        this.$onInit = function() {
7697          console.log(this.hasOwnProperty('notoptional')) // true
7698          console.log(this.hasOwnProperty('optional')) // false
7699        }
7700      }
7701    }
7702  })
7703 *```
7704 *
7705 *
7706 * ##### Combining directives with different scope defintions
7707 *
7708 * In general it's possible to apply more than one directive to one element, but there might be limitations
7709 * depending on the type of scope required by the directives. The following points will help explain these limitations.
7710 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
7711 *
7712 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
7713 * * **child scope** + **no scope** =>  Both directives will share one single child scope
7714 * * **child scope** + **child scope** =>  Both directives will share one single child scope
7715 * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
7716 * its parent's scope
7717 * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
7718 * be applied to the same element.
7719 * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
7720 * cannot be applied to the same element.
7721 *
7722 *
7723 * #### `bindToController`
7724 * This property is used to bind scope properties directly to the controller. It can be either
7725 * `true` or an object hash with the same format as the `scope` property.
7726 *
7727 * When an isolate scope is used for a directive (see above), `bindToController: true` will
7728 * allow a component to have its properties bound to the controller, rather than to scope.
7729 *
7730 * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
7731 * properties. You can access these bindings once they have been initialized by providing a controller method called
7732 * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
7733 * initialized.
7734 *
7735 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
7736 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
7737 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
7738 * scope (useful for component directives).
7739 *
7740 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
7741 *
7742 *
7743 * #### `controller`
7744 * Controller constructor function. The controller is instantiated before the
7745 * pre-linking phase and can be accessed by other directives (see
7746 * `require` attribute). This allows the directives to communicate with each other and augment
7747 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
7748 *
7749 * * `$scope` - Current scope associated with the element
7750 * * `$element` - Current element
7751 * * `$attrs` - Current attributes object for the element
7752 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
7753 *   `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
7754 *    * `scope`: (optional) override the scope.
7755 *    * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
7756 *    * `futureParentElement` (optional):
7757 *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
7758 *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
7759 *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
7760 *          and when the `cloneLinkingFn` is passed,
7761 *          as those elements need to created and cloned in a special way when they are defined outside their
7762 *          usual containers (e.g. like `<svg>`).
7763 *        * See also the `directive.templateNamespace` property.
7764 *    * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
7765 *      then the default transclusion is provided.
7766 *    The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
7767 *    `true` if the specified slot contains content (i.e. one or more DOM nodes).
7768 *
7769 * #### `require`
7770 * Require another directive and inject its controller as the fourth argument to the linking function. The
7771 * `require` property can be a string, an array or an object:
7772 * * a **string** containing the name of the directive to pass to the linking function
7773 * * an **array** containing the names of directives to pass to the linking function. The argument passed to the
7774 * linking function will be an array of controllers in the same order as the names in the `require` property
7775 * * an **object** whose property values are the names of the directives to pass to the linking function. The argument
7776 * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
7777 * controllers.
7778 *
7779 * If the `require` property is an object and `bindToController` is truthy, then the required controllers are
7780 * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
7781 * have been constructed but before `$onInit` is called.
7782 * If the name of the required controller is the same as the local name (the key), the name can be
7783 * omitted. For example, `{parentDir: '^^'}` is equivalent to `{parentDir: '^^parentDir'}`.
7784 * See the {@link $compileProvider#component} helper for an example of how this can be used.
7785 * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
7786 * raised (unless no link function is specified and the required controllers are not being bound to the directive
7787 * controller, in which case error checking is skipped). The name can be prefixed with:
7788 *
7789 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
7790 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
7791 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
7792 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
7793 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
7794 *   `null` to the `link` fn if not found.
7795 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
7796 *   `null` to the `link` fn if not found.
7797 *
7798 *
7799 * #### `controllerAs`
7800 * Identifier name for a reference to the controller in the directive's scope.
7801 * This allows the controller to be referenced from the directive template. This is especially
7802 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
7803 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
7804 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
7805 *
7806 *
7807 * #### `restrict`
7808 * String of subset of `EACM` which restricts the directive to a specific directive
7809 * declaration style. If omitted, the defaults (elements and attributes) are used.
7810 *
7811 * * `E` - Element name (default): `<my-directive></my-directive>`
7812 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
7813 * * `C` - Class: `<div class="my-directive: exp;"></div>`
7814 * * `M` - Comment: `<!-- directive: my-directive exp -->`
7815 *
7816 *
7817 * #### `templateNamespace`
7818 * String representing the document type used by the markup in the template.
7819 * AngularJS needs this information as those elements need to be created and cloned
7820 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
7821 *
7822 * * `html` - All root nodes in the template are HTML. Root nodes may also be
7823 *   top-level elements such as `<svg>` or `<math>`.
7824 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
7825 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
7826 *
7827 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
7828 *
7829 * #### `template`
7830 * HTML markup that may:
7831 * * Replace the contents of the directive's element (default).
7832 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
7833 * * Wrap the contents of the directive's element (if `transclude` is true).
7834 *
7835 * Value may be:
7836 *
7837 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
7838 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
7839 *   function api below) and returns a string value.
7840 *
7841 *
7842 * #### `templateUrl`
7843 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
7844 *
7845 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
7846 * for later when the template has been resolved.  In the meantime it will continue to compile and link
7847 * sibling and parent elements as though this element had not contained any directives.
7848 *
7849 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
7850 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
7851 * case when only one deeply nested directive has `templateUrl`.
7852 *
7853 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}.
7854 *
7855 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
7856 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
7857 * a string value representing the url.  In either case, the template URL is passed through {@link
7858 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
7859 *
7860 *
7861 * #### `replace`
7862 * <div class="alert alert-danger">
7863 * **Note:** `replace` is deprecated in AngularJS and has been removed in the new Angular (v2+).
7864 * </div>
7865 *
7866 * Specifies what the template should replace. Defaults to `false`.
7867 *
7868 * * `true` - the template will replace the directive's element.
7869 * * `false` - the template will replace the contents of the directive's element.
7870 *
7871 * The replacement process migrates all of the attributes / classes from the old element to the new
7872 * one. See the {@link guide/directive#template-expanding-directive
7873 * Directives Guide} for an example.
7874 *
7875 * There are very few scenarios where element replacement is required for the application function,
7876 * the main one being reusable custom components that are used within SVG contexts
7877 * (because SVG doesn't work with custom elements in the DOM tree).
7878 *
7879 * #### `transclude`
7880 * Extract the contents of the element where the directive appears and make it available to the directive.
7881 * The contents are compiled and provided to the directive as a **transclusion function**. See the
7882 * {@link $compile#transclusion Transclusion} section below.
7883 *
7884 *
7885 * #### `compile`
7886 *
7887 * ```js
7888 *   function compile(tElement, tAttrs, transclude) { ... }
7889 * ```
7890 *
7891 * The compile function deals with transforming the template DOM. Since most directives do not do
7892 * template transformation, it is not used often. The compile function takes the following arguments:
7893 *
7894 *   * `tElement` - template element - The element where the directive has been declared. It is
7895 *     safe to do template transformation on the element and child elements only.
7896 *
7897 *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
7898 *     between all directive compile functions.
7899 *
7900 *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
7901 *
7902 * <div class="alert alert-warning">
7903 * **Note:** The template instance and the link instance may be different objects if the template has
7904 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
7905 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
7906 * should be done in a linking function rather than in a compile function.
7907 * </div>
7908
7909 * <div class="alert alert-warning">
7910 * **Note:** The compile function cannot handle directives that recursively use themselves in their
7911 * own templates or compile functions. Compiling these directives results in an infinite loop and
7912 * stack overflow errors.
7913 *
7914 * This can be avoided by manually using `$compile` in the postLink function to imperatively compile
7915 * a directive's template instead of relying on automatic template compilation via `template` or
7916 * `templateUrl` declaration or manual compilation inside the compile function.
7917 * </div>
7918 *
7919 * <div class="alert alert-danger">
7920 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
7921 *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
7922 *   to the link function instead.
7923 * </div>
7924
7925 * A compile function can have a return value which can be either a function or an object.
7926 *
7927 * * returning a (post-link) function - is equivalent to registering the linking function via the
7928 *   `link` property of the config object when the compile function is empty.
7929 *
7930 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
7931 *   control when a linking function should be called during the linking phase. See info about
7932 *   pre-linking and post-linking functions below.
7933 *
7934 *
7935 * #### `link`
7936 * This property is used only if the `compile` property is not defined.
7937 *
7938 * ```js
7939 *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
7940 * ```
7941 *
7942 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
7943 * executed after the template has been cloned. This is where most of the directive logic will be
7944 * put.
7945 *
7946 *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
7947 *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
7948 *
7949 *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
7950 *     manipulate the children of the element only in `postLink` function since the children have
7951 *     already been linked.
7952 *
7953 *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
7954 *     between all directive linking functions.
7955 *
7956 *   * `controller` - the directive's required controller instance(s) - Instances are shared
7957 *     among all directives, which allows the directives to use the controllers as a communication
7958 *     channel. The exact value depends on the directive's `require` property:
7959 *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
7960 *       * `string`: the controller instance
7961 *       * `array`: array of controller instances
7962 *
7963 *     If a required controller cannot be found, and it is optional, the instance is `null`,
7964 *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
7965 *
7966 *     Note that you can also require the directive's own controller - it will be made available like
7967 *     any other controller.
7968 *
7969 *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
7970 *     This is the same as the `$transclude` parameter of directive controllers,
7971 *     see {@link ng.$compile#-controller- the controller section for details}.
7972 *     `function([scope], cloneLinkingFn, futureParentElement)`.
7973 *
7974 * #### Pre-linking function
7975 *
7976 * Executed before the child elements are linked. Not safe to do DOM transformation since the
7977 * compiler linking function will fail to locate the correct elements for linking.
7978 *
7979 * #### Post-linking function
7980 *
7981 * Executed after the child elements are linked.
7982 *
7983 * Note that child elements that contain `templateUrl` directives will not have been compiled
7984 * and linked since they are waiting for their template to load asynchronously and their own
7985 * compilation and linking has been suspended until that occurs.
7986 *
7987 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
7988 * for their async templates to be resolved.
7989 *
7990 *
7991 * ### Transclusion
7992 *
7993 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
7994 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
7995 * scope from where they were taken.
7996 *
7997 * Transclusion is used (often with {@link ngTransclude}) to insert the
7998 * original contents of a directive's element into a specified place in the template of the directive.
7999 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
8000 * content has access to the properties on the scope from which it was taken, even if the directive
8001 * has isolated scope.
8002 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
8003 *
8004 * This makes it possible for the widget to have private state for its template, while the transcluded
8005 * content has access to its originating scope.
8006 *
8007 * <div class="alert alert-warning">
8008 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
8009 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
8010 * Testing Transclusion Directives}.
8011 * </div>
8012 *
8013 * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
8014 * directive's element, the entire element or multiple parts of the element contents:
8015 *
8016 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
8017 * * `'element'` - transclude the whole of the directive's element including any directives on this
8018 *   element that are defined at a lower priority than this directive. When used, the `template`
8019 *   property is ignored.
8020 * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
8021 *
8022 * **Multi-slot transclusion** is declared by providing an object for the `transclude` property.
8023 *
8024 * This object is a map where the keys are the name of the slot to fill and the value is an element selector
8025 * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
8026 * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
8027 *
8028 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}.
8029 *
8030 * If the element selector is prefixed with a `?` then that slot is optional.
8031 *
8032 * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
8033 * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
8034 *
8035 * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
8036 * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
8037 * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
8038 * injectable into the directive's controller.
8039 *
8040 *
8041 * #### Transclusion Functions
8042 *
8043 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
8044 * function** to the directive's `link` function and `controller`. This transclusion function is a special
8045 * **linking function** that will return the compiled contents linked to a new transclusion scope.
8046 *
8047 * <div class="alert alert-info">
8048 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
8049 * ngTransclude will deal with it for us.
8050 * </div>
8051 *
8052 * If you want to manually control the insertion and removal of the transcluded content in your directive
8053 * then you must use this transclude function. When you call a transclude function it returns a jqLite/JQuery
8054 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
8055 *
8056 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
8057 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
8058 * content and the `scope` is the newly created transclusion scope, which the clone will be linked to.
8059 *
8060 * <div class="alert alert-info">
8061 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
8062 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
8063 * </div>
8064 *
8065 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
8066 * attach function**:
8067 *
8068 * ```js
8069 * var transcludedContent, transclusionScope;
8070 *
8071 * $transclude(function(clone, scope) {
8072 *   element.append(clone);
8073 *   transcludedContent = clone;
8074 *   transclusionScope = scope;
8075 * });
8076 * ```
8077 *
8078 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
8079 * associated transclusion scope:
8080 *
8081 * ```js
8082 * transcludedContent.remove();
8083 * transclusionScope.$destroy();
8084 * ```
8085 *
8086 * <div class="alert alert-info">
8087 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
8088 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
8089 * then you are also responsible for calling `$destroy` on the transclusion scope.
8090 * </div>
8091 *
8092 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
8093 * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
8094 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
8095 *
8096 *
8097 * #### Transclusion Scopes
8098 *
8099 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
8100 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
8101 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
8102 * was taken.
8103 *
8104 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
8105 * like this:
8106 *
8107 * ```html
8108 * <div ng-app>
8109 *   <div isolate>
8110 *     <div transclusion>
8111 *     </div>
8112 *   </div>
8113 * </div>
8114 * ```
8115 *
8116 * The `$parent` scope hierarchy will look like this:
8117 *
8118   ```
8119   - $rootScope
8120     - isolate
8121       - transclusion
8122   ```
8123 *
8124 * but the scopes will inherit prototypically from different scopes to their `$parent`.
8125 *
8126   ```
8127   - $rootScope
8128     - transclusion
8129   - isolate
8130   ```
8131 *
8132 *
8133 * ### Attributes
8134 *
8135 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
8136 * `link()` or `compile()` functions. It has a variety of uses.
8137 *
8138 * * *Accessing normalized attribute names:* Directives like `ngBind` can be expressed in many ways:
8139 *   `ng:bind`, `data-ng-bind`, or `x-ng-bind`. The attributes object allows for normalized access
8140 *   to the attributes.
8141 *
8142 * * *Directive inter-communication:* All directives share the same instance of the attributes
8143 *   object which allows the directives to use the attributes object as inter directive
8144 *   communication.
8145 *
8146 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
8147 *   allowing other directives to read the interpolated value.
8148 *
8149 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
8150 *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
8151 *   the only way to easily get the actual value because during the linking phase the interpolation
8152 *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
8153 *
8154 * ```js
8155 * function linkingFn(scope, elm, attrs, ctrl) {
8156 *   // get the attribute value
8157 *   console.log(attrs.ngModel);
8158 *
8159 *   // change the attribute
8160 *   attrs.$set('ngModel', 'new value');
8161 *
8162 *   // observe changes to interpolated attribute
8163 *   attrs.$observe('ngModel', function(value) {
8164 *     console.log('ngModel has changed value to ' + value);
8165 *   });
8166 * }
8167 * ```
8168 *
8169 * ## Example
8170 *
8171 * <div class="alert alert-warning">
8172 * **Note**: Typically directives are registered with `module.directive`. The example below is
8173 * to illustrate how `$compile` works.
8174 * </div>
8175 *
8176 <example module="compileExample" name="compile">
8177   <file name="index.html">
8178    <script>
8179      angular.module('compileExample', [], function($compileProvider) {
8180        // Configure new 'compile' directive by passing a directive
8181        // factory function. The factory function injects '$compile'.
8182        $compileProvider.directive('compile', function($compile) {
8183          // The directive factory creates a link function.
8184          return function(scope, element, attrs) {
8185            scope.$watch(
8186              function(scope) {
8187                // Watch the 'compile' expression for changes.
8188                return scope.$eval(attrs.compile);
8189              },
8190              function(value) {
8191                // When the 'compile' expression changes
8192                // assign it into the current DOM.
8193                element.html(value);
8194
8195                // Compile the new DOM and link it to the current scope.
8196                // NOTE: we only compile '.childNodes' so that we
8197                // don't get into an infinite loop compiling ourselves.
8198                $compile(element.contents())(scope);
8199              }
8200            );
8201          };
8202        });
8203      })
8204      .controller('GreeterController', ['$scope', function($scope) {
8205        $scope.name = 'AngularJS';
8206        $scope.html = 'Hello {{name}}';
8207      }]);
8208    </script>
8209    <div ng-controller="GreeterController">
8210      <input ng-model="name"> <br/>
8211      <textarea ng-model="html"></textarea> <br/>
8212      <div compile="html"></div>
8213    </div>
8214   </file>
8215   <file name="protractor.js" type="protractor">
8216     it('should auto compile', function() {
8217       var textarea = $('textarea');
8218       var output = $('div[compile]');
8219       // The initial state reads 'Hello AngularJS'.
8220       expect(output.getText()).toBe('Hello AngularJS');
8221       textarea.clear();
8222       textarea.sendKeys('{{name}}!');
8223       expect(output.getText()).toBe('AngularJS!');
8224     });
8225   </file>
8226 </example>
8227
8228 *
8229 *
8230 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
8231 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
8232 *
8233 * <div class="alert alert-danger">
8234 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
8235 *   e.g. will not use the right outer scope. Please pass the transclude function as a
8236 *   `parentBoundTranscludeFn` to the link function instead.
8237 * </div>
8238 *
8239 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
8240 *                 root element(s), not their children)
8241 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
8242 * (a DOM element/tree) to a scope. Where:
8243 *
8244 *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
8245 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
8246 *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
8247 *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
8248 *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
8249 *
8250 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
8251 *      * `scope` - is the current scope with which the linking function is working with.
8252 *
8253 *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
8254 *  keys may be used to control linking behavior:
8255 *
8256 *      * `parentBoundTranscludeFn` - the transclude function made available to
8257 *        directives; if given, it will be passed through to the link functions of
8258 *        directives found in `element` during compilation.
8259 *      * `transcludeControllers` - an object hash with keys that map controller names
8260 *        to a hash with the key `instance`, which maps to the controller instance;
8261 *        if given, it will make the controllers available to directives on the compileNode:
8262 *        ```
8263 *        {
8264 *          parent: {
8265 *            instance: parentControllerInstance
8266 *          }
8267 *        }
8268 *        ```
8269 *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
8270 *        the cloned elements; only needed for transcludes that are allowed to contain non HTML
8271 *        elements (e.g. SVG elements). See also the `directive.controller` property.
8272 *
8273 * Calling the linking function returns the element of the template. It is either the original
8274 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
8275 *
8276 * After linking the view is not updated until after a call to `$digest`, which typically is done by
8277 * AngularJS automatically.
8278 *
8279 * If you need access to the bound view, there are two ways to do it:
8280 *
8281 * - If you are not asking the linking function to clone the template, create the DOM element(s)
8282 *   before you send them to the compiler and keep this reference around.
8283 *   ```js
8284 *     var element = angular.element('<p>{{total}}</p>');
8285 *     $compile(element)(scope);
8286 *   ```
8287 *
8288 * - if on the other hand, you need the element to be cloned, the view reference from the original
8289 *   example would not point to the clone, but rather to the original template that was cloned. In
8290 *   this case, you can access the clone either via the `cloneAttachFn` or the value returned by the
8291 *   linking function:
8292 *   ```js
8293 *     var templateElement = angular.element('<p>{{total}}</p>');
8294 *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
8295 *       // Attach the clone to DOM document at the right place.
8296 *     });
8297 *
8298 *     // Now we have reference to the cloned DOM via `clonedElement`.
8299 *     // NOTE: The `clonedElement` returned by the linking function is the same as the
8300 *     //       `clonedElement` passed to `cloneAttachFn`.
8301 *   ```
8302 *
8303 *
8304 * For information on how the compiler works, see the
8305 * {@link guide/compiler AngularJS HTML Compiler} section of the Developer Guide.
8306 *
8307 * @knownIssue
8308 *
8309 * ### Double Compilation
8310 *
8311   Double compilation occurs when an already compiled part of the DOM gets
8312   compiled again. This is an undesired effect and can lead to misbehaving directives, performance issues,
8313   and memory leaks. Refer to the Compiler Guide {@link guide/compiler#double-compilation-and-how-to-avoid-it
8314   section on double compilation} for an in-depth explanation and ways to avoid it.
8315
8316 * @knownIssue
8317
8318   ### Issues with `replace: true`
8319 *
8320 * <div class="alert alert-danger">
8321 *   **Note**: {@link $compile#-replace- `replace: true`} is deprecated and not recommended to use,
8322 *   mainly due to the issues listed here. It has been completely removed in the new Angular.
8323 * </div>
8324 *
8325 * #### Attribute values are not merged
8326 *
8327 * When a `replace` directive encounters the same attribute on the original and the replace node,
8328 * it will simply deduplicate the attribute and join the values with a space or with a `;` in case of
8329 * the `style` attribute.
8330 * ```html
8331 * Original Node: <span class="original" style="color: red;"></span>
8332 * Replace Template: <span class="replaced" style="background: blue;"></span>
8333 * Result: <span class="original replaced" style="color: red; background: blue;"></span>
8334 * ```
8335 *
8336 * That means attributes that contain AngularJS expressions will not be merged correctly, e.g.
8337 * {@link ngShow} or {@link ngClass} will cause a {@link $parse} error:
8338 *
8339 * ```html
8340 * Original Node: <span ng-class="{'something': something}" ng-show="!condition"></span>
8341 * Replace Template: <span ng-class="{'else': else}" ng-show="otherCondition"></span>
8342 * Result: <span ng-class="{'something': something} {'else': else}" ng-show="!condition otherCondition"></span>
8343 * ```
8344 *
8345 * See issue [#5695](https://github.com/angular/angular.js/issues/5695).
8346 *
8347 * #### Directives are not deduplicated before compilation
8348 *
8349 * When the original node and the replace template declare the same directive(s), they will be
8350 * {@link guide/compiler#double-compilation-and-how-to-avoid-it compiled twice} because the compiler
8351 * does not deduplicate them. In many cases, this is not noticeable, but e.g. {@link ngModel} will
8352 * attach `$formatters` and `$parsers` twice.
8353 *
8354 * See issue [#2573](https://github.com/angular/angular.js/issues/2573).
8355 *
8356 * #### `transclude: element` in the replace template root can have unexpected effects
8357 *
8358 * When the replace template has a directive at the root node that uses
8359 * {@link $compile#-transclude- `transclude: element`}, e.g.
8360 * {@link ngIf} or {@link ngRepeat}, the DOM structure or scope inheritance can be incorrect.
8361 * See the following issues:
8362 *
8363 * - Incorrect scope on replaced element:
8364 * [#9837](https://github.com/angular/angular.js/issues/9837)
8365 * - Different DOM between `template` and `templateUrl`:
8366 * [#10612](https://github.com/angular/angular.js/issues/14326)
8367 *
8368 */
8369
8370/**
8371 * @ngdoc directive
8372 * @name ngProp
8373 * @restrict A
8374 * @element ANY
8375 *
8376 * @usage
8377 *
8378 * ```html
8379 * <ANY ng-prop-propname="expression">
8380 * </ANY>
8381 * ```
8382 *
8383 * or with uppercase letters in property (e.g. "propName"):
8384 *
8385 *
8386 * ```html
8387 * <ANY ng-prop-prop_name="expression">
8388 * </ANY>
8389 * ```
8390 *
8391 *
8392 * @description
8393 * The `ngProp` directive binds an expression to a DOM element property.
8394 * `ngProp` allows writing to arbitrary properties by including
8395 * the property name in the attribute, e.g. `ng-prop-value="'my value'"` binds 'my value' to
8396 * the `value` property.
8397 *
8398 * Usually, it's not necessary to write to properties in AngularJS, as the built-in directives
8399 * handle the most common use cases (instead of the above example, you would use {@link ngValue}).
8400 *
8401 * However, [custom elements](https://developer.mozilla.org/docs/Web/Web_Components/Using_custom_elements)
8402 * often use custom properties to hold data, and `ngProp` can be used to provide input to these
8403 * custom elements.
8404 *
8405 * ## Binding to camelCase properties
8406 *
8407 * Since HTML attributes are case-insensitive, camelCase properties like `innerHTML` must be escaped.
8408 * AngularJS uses the underscore (_) in front of a character to indicate that it is uppercase, so
8409 * `innerHTML`  must be written as `ng-prop-inner_h_t_m_l="expression"` (Note that this is just an
8410 * example, and for binding HTML {@link ngBindHtml} should be used.
8411 *
8412 * ## Security
8413 *
8414 * Binding expressions to arbitrary properties poses a security risk, as  properties like `innerHTML`
8415 * can insert potentially dangerous HTML into the application, e.g. script tags that execute
8416 * malicious code.
8417 * For this reason, `ngProp` applies Strict Contextual Escaping with the {@link ng.$sce $sce service}.
8418 * This means vulnerable properties require their content to be "trusted", based on the
8419 * context of the property. For example, the `innerHTML` is in the `HTML` context, and the
8420 * `iframe.src` property is in the `RESOURCE_URL` context, which requires that values written to
8421 * this property are trusted as a `RESOURCE_URL`.
8422 *
8423 * This can be set explicitly by calling $sce.trustAs(type, value) on the value that is
8424 * trusted before passing it to the `ng-prop-*` directive. There are exist shorthand methods for
8425 * each context type in the form of {@link ng.$sce#trustAsResourceUrl $sce.trustAsResourceUrl()} et al.
8426 *
8427 * In some cases you can also rely upon automatic sanitization of untrusted values - see below.
8428 *
8429 * Based on the context, other options may exist to mark a value as trusted / configure the behavior
8430 * of {@link ng.$sce}. For example, to restrict the `RESOURCE_URL` context to specific origins, use
8431 * the {@link $sceDelegateProvider#trustedResourceUrlList trustedResourceUrlList()}
8432 * and {@link $sceDelegateProvider#bannedResourceUrlList bannedResourceUrlList()}.
8433 *
8434 * {@link ng.$sce#what-trusted-context-types-are-supported- Find out more about the different context types}.
8435 *
8436 * ### HTML Sanitization
8437 *
8438 * By default, `$sce` will throw an error if it detects untrusted HTML content, and will not bind the
8439 * content.
8440 * However, if you include the {@link ngSanitize ngSanitize module}, it will try to sanitize the
8441 * potentially dangerous HTML, e.g. strip non-trusted tags and attributes when binding to
8442 * `innerHTML`.
8443 *
8444 * @example
8445 * ### Binding to different contexts
8446 *
8447 * <example name="ngProp" module="exampleNgProp">
8448 *   <file name="app.js">
8449 *     angular.module('exampleNgProp', [])
8450 *       .component('main', {
8451 *         templateUrl: 'main.html',
8452 *         controller: function($sce) {
8453 *           this.safeContent = '<strong>Safe content</strong>';
8454 *           this.unsafeContent = '<button onclick="alert(\'Hello XSS!\')">Click for XSS</button>';
8455 *           this.trustedUnsafeContent = $sce.trustAsHtml(this.unsafeContent);
8456 *         }
8457 *       });
8458 *   </file>
8459 *   <file name="main.html">
8460 *     <div>
8461 *       <div class="prop-unit">
8462 *         Binding to a property without security context:
8463 *         <div class="prop-binding" ng-prop-inner_text="$ctrl.safeContent"></div>
8464 *         <span class="prop-note">innerText</span> (safeContent)
8465 *       </div>
8466 *
8467 *       <div class="prop-unit">
8468 *         "Safe" content that requires a security context will throw because the contents could potentially be dangerous ...
8469 *         <div class="prop-binding" ng-prop-inner_h_t_m_l="$ctrl.safeContent"></div>
8470 *         <span class="prop-note">innerHTML</span> (safeContent)
8471 *       </div>
8472 *
8473 *       <div class="prop-unit">
8474 *         ... so that actually dangerous content cannot be executed:
8475 *         <div class="prop-binding" ng-prop-inner_h_t_m_l="$ctrl.unsafeContent"></div>
8476 *         <span class="prop-note">innerHTML</span> (unsafeContent)
8477 *       </div>
8478 *
8479 *       <div class="prop-unit">
8480 *         ... but unsafe Content that has been trusted explicitly works - only do this if you are 100% sure!
8481 *         <div class="prop-binding" ng-prop-inner_h_t_m_l="$ctrl.trustedUnsafeContent"></div>
8482 *         <span class="prop-note">innerHTML</span> (trustedUnsafeContent)
8483 *       </div>
8484 *     </div>
8485 *   </file>
8486 *   <file name="index.html">
8487 *     <main></main>
8488 *   </file>
8489 *   <file name="styles.css">
8490 *     .prop-unit {
8491 *       margin-bottom: 10px;
8492 *     }
8493 *
8494 *     .prop-binding {
8495 *       min-height: 30px;
8496 *       border: 1px solid blue;
8497 *     }
8498 *
8499 *     .prop-note {
8500 *       font-family: Monospace;
8501 *     }
8502 *   </file>
8503 * </example>
8504 *
8505 *
8506 * @example
8507 * ### Binding to innerHTML with ngSanitize
8508 *
8509 * <example name="ngProp" module="exampleNgProp" deps="angular-sanitize.js">
8510 *   <file name="app.js">
8511 *     angular.module('exampleNgProp', ['ngSanitize'])
8512 *       .component('main', {
8513 *         templateUrl: 'main.html',
8514 *         controller: function($sce) {
8515 *           this.safeContent = '<strong>Safe content</strong>';
8516 *           this.unsafeContent = '<button onclick="alert(\'Hello XSS!\')">Click for XSS</button>';
8517 *           this.trustedUnsafeContent = $sce.trustAsHtml(this.unsafeContent);
8518 *         }
8519 *       });
8520 *   </file>
8521 *   <file name="main.html">
8522 *     <div>
8523 *       <div class="prop-unit">
8524 *         "Safe" content will be sanitized ...
8525 *         <div class="prop-binding" ng-prop-inner_h_t_m_l="$ctrl.safeContent"></div>
8526 *         <span class="prop-note">innerHTML</span> (safeContent)
8527 *       </div>
8528 *
8529 *       <div class="prop-unit">
8530 *         ... as will dangerous content:
8531 *         <div class="prop-binding" ng-prop-inner_h_t_m_l="$ctrl.unsafeContent"></div>
8532 *         <span class="prop-note">innerHTML</span> (unsafeContent)
8533 *       </div>
8534 *
8535 *       <div class="prop-unit">
8536 *         ... and content that has been trusted explicitly works the same as without ngSanitize:
8537 *         <div class="prop-binding" ng-prop-inner_h_t_m_l="$ctrl.trustedUnsafeContent"></div>
8538 *         <span class="prop-note">innerHTML</span> (trustedUnsafeContent)
8539 *       </div>
8540 *     </div>
8541 *   </file>
8542 *   <file name="index.html">
8543 *     <main></main>
8544 *   </file>
8545 *   <file name="styles.css">
8546 *     .prop-unit {
8547 *       margin-bottom: 10px;
8548 *     }
8549 *
8550 *     .prop-binding {
8551 *       min-height: 30px;
8552 *       border: 1px solid blue;
8553 *     }
8554 *
8555 *     .prop-note {
8556 *       font-family: Monospace;
8557 *     }
8558 *   </file>
8559 * </example>
8560 *
8561 */
8562
8563/** @ngdoc directive
8564 * @name ngOn
8565 * @restrict A
8566 * @element ANY
8567 *
8568 * @usage
8569 *
8570 * ```html
8571 * <ANY ng-on-eventname="expression">
8572 * </ANY>
8573 * ```
8574 *
8575 * or with uppercase letters in property (e.g. "eventName"):
8576 *
8577 *
8578 * ```html
8579 * <ANY ng-on-event_name="expression">
8580 * </ANY>
8581 * ```
8582 *
8583 * @description
8584 * The `ngOn` directive adds an event listener to a DOM element via
8585 * {@link angular.element angular.element().on()}, and evaluates an expression when the event is
8586 * fired.
8587 * `ngOn` allows adding listeners for arbitrary events by including
8588 * the event name in the attribute, e.g. `ng-on-drop="onDrop()"` executes the 'onDrop()' expression
8589 * when the `drop` event is fired.
8590 *
8591 * AngularJS provides specific directives for many events, such as {@link ngClick}, so in most
8592 * cases it is not necessary to use `ngOn`. However, AngularJS does not support all events
8593 * (e.g. the `drop` event in the example above), and new events might be introduced in later DOM
8594 * standards.
8595 *
8596 * Another use-case for `ngOn` is listening to
8597 * [custom events](https://developer.mozilla.org/docs/Web/Guide/Events/Creating_and_triggering_events)
8598 * fired by
8599 * [custom elements](https://developer.mozilla.org/docs/Web/Web_Components/Using_custom_elements).
8600 *
8601 * ## Binding to camelCase properties
8602 *
8603 * Since HTML attributes are case-insensitive, camelCase properties like `myEvent` must be escaped.
8604 * AngularJS uses the underscore (_) in front of a character to indicate that it is uppercase, so
8605 * `myEvent` must be written as `ng-on-my_event="expression"`.
8606 *
8607 * @example
8608 * ### Bind to built-in DOM events
8609 *
8610 * <example name="ngOn" module="exampleNgOn">
8611 *   <file name="app.js">
8612 *     angular.module('exampleNgOn', [])
8613 *       .component('main', {
8614 *         templateUrl: 'main.html',
8615 *         controller: function() {
8616 *           this.clickCount = 0;
8617 *           this.mouseoverCount = 0;
8618 *
8619 *           this.loadingState = 0;
8620 *         }
8621 *       });
8622 *   </file>
8623 *   <file name="main.html">
8624 *     <div>
8625 *       This is equivalent to `ngClick` and `ngMouseover`:<br>
8626 *       <button
8627 *         ng-on-click="$ctrl.clickCount = $ctrl.clickCount + 1"
8628 *         ng-on-mouseover="$ctrl.mouseoverCount = $ctrl.mouseoverCount + 1">Click or mouseover</button><br>
8629 *       clickCount: {{$ctrl.clickCount}}<br>
8630 *       mouseover: {{$ctrl.mouseoverCount}}
8631 *
8632 *       <hr>
8633 *
8634 *       For the `error` and `load` event on images no built-in AngularJS directives exist:<br>
8635 *       <img src="thisimagedoesnotexist.png" ng-on-error="$ctrl.loadingState = -1" ng-on-load="$ctrl.loadingState = 1"><br>
8636 *       <div ng-switch="$ctrl.loadingState">
8637 *         <span ng-switch-when="0">Image is loading</span>
8638 *         <span ng-switch-when="-1">Image load error</span>
8639 *         <span ng-switch-when="1">Image loaded successfully</span>
8640 *       </div>
8641 *     </div>
8642 *   </file>
8643 *   <file name="index.html">
8644 *     <main></main>
8645 *   </file>
8646 * </example>
8647 *
8648 *
8649 * @example
8650 * ### Bind to custom DOM events
8651 *
8652 * <example name="ngOnCustom" module="exampleNgOn">
8653 *   <file name="app.js">
8654 *     angular.module('exampleNgOn', [])
8655 *       .component('main', {
8656 *         templateUrl: 'main.html',
8657 *         controller: function() {
8658 *           this.eventLog = '';
8659 *
8660 *           this.listener = function($event) {
8661 *             this.eventLog = 'Event with type "' + $event.type + '" fired at ' + $event.detail;
8662 *           };
8663 *         }
8664 *       })
8665 *       .component('childComponent', {
8666 *         templateUrl: 'child.html',
8667 *         controller: function($element) {
8668 *           this.fireEvent = function() {
8669 *             var event = new CustomEvent('customtype', { detail: new Date()});
8670 *
8671 *             $element[0].dispatchEvent(event);
8672 *           };
8673 *         }
8674 *       });
8675 *   </file>
8676 *   <file name="main.html">
8677 *     <child-component ng-on-customtype="$ctrl.listener($event)"></child-component><br>
8678 *     <span>Event log: {{$ctrl.eventLog}}</span>
8679 *   </file>
8680 *   <file name="child.html">
8681      <button ng-click="$ctrl.fireEvent()">Fire custom event</button>
8682 *   </file>
8683 *   <file name="index.html">
8684 *     <main></main>
8685 *   </file>
8686 * </example>
8687 */
8688
8689var $compileMinErr = minErr('$compile');
8690
8691function UNINITIALIZED_VALUE() {}
8692var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
8693
8694/**
8695 * @ngdoc provider
8696 * @name $compileProvider
8697 *
8698 * @description
8699 */
8700$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
8701/** @this */
8702function $CompileProvider($provide, $$sanitizeUriProvider) {
8703  var hasDirectives = {},
8704      Suffix = 'Directive',
8705      COMMENT_DIRECTIVE_REGEXP = /^\s*directive:\s*([\w-]+)\s+(.*)$/,
8706      CLASS_DIRECTIVE_REGEXP = /(([\w-]+)(?::([^;]+))?;?)/,
8707      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
8708      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
8709
8710  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
8711  // The assumption is that future DOM event attribute names will begin with
8712  // 'on' and be composed of only English letters.
8713  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
8714  var bindingCache = createMap();
8715
8716  function parseIsolateBindings(scope, directiveName, isController) {
8717    var LOCAL_REGEXP = /^([@&]|[=<](\*?))(\??)\s*([\w$]*)$/;
8718
8719    var bindings = createMap();
8720
8721    forEach(scope, function(definition, scopeName) {
8722      definition = definition.trim();
8723
8724      if (definition in bindingCache) {
8725        bindings[scopeName] = bindingCache[definition];
8726        return;
8727      }
8728      var match = definition.match(LOCAL_REGEXP);
8729
8730      if (!match) {
8731        throw $compileMinErr('iscp',
8732            'Invalid {3} for directive \'{0}\'.' +
8733            ' Definition: {... {1}: \'{2}\' ...}',
8734            directiveName, scopeName, definition,
8735            (isController ? 'controller bindings definition' :
8736            'isolate scope definition'));
8737      }
8738
8739      bindings[scopeName] = {
8740        mode: match[1][0],
8741        collection: match[2] === '*',
8742        optional: match[3] === '?',
8743        attrName: match[4] || scopeName
8744      };
8745      if (match[4]) {
8746        bindingCache[definition] = bindings[scopeName];
8747      }
8748    });
8749
8750    return bindings;
8751  }
8752
8753  function parseDirectiveBindings(directive, directiveName) {
8754    var bindings = {
8755      isolateScope: null,
8756      bindToController: null
8757    };
8758    if (isObject(directive.scope)) {
8759      if (directive.bindToController === true) {
8760        bindings.bindToController = parseIsolateBindings(directive.scope,
8761                                                         directiveName, true);
8762        bindings.isolateScope = {};
8763      } else {
8764        bindings.isolateScope = parseIsolateBindings(directive.scope,
8765                                                     directiveName, false);
8766      }
8767    }
8768    if (isObject(directive.bindToController)) {
8769      bindings.bindToController =
8770          parseIsolateBindings(directive.bindToController, directiveName, true);
8771    }
8772    if (bindings.bindToController && !directive.controller) {
8773      // There is no controller
8774      throw $compileMinErr('noctrl',
8775            'Cannot bind to controller without directive \'{0}\'s controller.',
8776            directiveName);
8777    }
8778    return bindings;
8779  }
8780
8781  function assertValidDirectiveName(name) {
8782    var letter = name.charAt(0);
8783    if (!letter || letter !== lowercase(letter)) {
8784      throw $compileMinErr('baddir', 'Directive/Component name \'{0}\' is invalid. The first character must be a lowercase letter', name);
8785    }
8786    if (name !== name.trim()) {
8787      throw $compileMinErr('baddir',
8788            'Directive/Component name \'{0}\' is invalid. The name should not contain leading or trailing whitespaces',
8789            name);
8790    }
8791  }
8792
8793  function getDirectiveRequire(directive) {
8794    var require = directive.require || (directive.controller && directive.name);
8795
8796    if (!isArray(require) && isObject(require)) {
8797      forEach(require, function(value, key) {
8798        var match = value.match(REQUIRE_PREFIX_REGEXP);
8799        var name = value.substring(match[0].length);
8800        if (!name) require[key] = match[0] + key;
8801      });
8802    }
8803
8804    return require;
8805  }
8806
8807  function getDirectiveRestrict(restrict, name) {
8808    if (restrict && !(isString(restrict) && /[EACM]/.test(restrict))) {
8809      throw $compileMinErr('badrestrict',
8810          'Restrict property \'{0}\' of directive \'{1}\' is invalid',
8811          restrict,
8812          name);
8813    }
8814
8815    return restrict || 'EA';
8816  }
8817
8818  /**
8819   * @ngdoc method
8820   * @name $compileProvider#directive
8821   * @kind function
8822   *
8823   * @description
8824   * Register a new directive with the compiler.
8825   *
8826   * @param {string|Object} name Name of the directive in camel-case (i.e. `ngBind` which will match
8827   *    as `ng-bind`), or an object map of directives where the keys are the names and the values
8828   *    are the factories.
8829   * @param {Function|Array} directiveFactory An injectable directive factory function. See the
8830   *    {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
8831   * @returns {ng.$compileProvider} Self for chaining.
8832   */
8833  this.directive = function registerDirective(name, directiveFactory) {
8834    assertArg(name, 'name');
8835    assertNotHasOwnProperty(name, 'directive');
8836    if (isString(name)) {
8837      assertValidDirectiveName(name);
8838      assertArg(directiveFactory, 'directiveFactory');
8839      if (!hasDirectives.hasOwnProperty(name)) {
8840        hasDirectives[name] = [];
8841        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
8842          function($injector, $exceptionHandler) {
8843            var directives = [];
8844            forEach(hasDirectives[name], function(directiveFactory, index) {
8845              try {
8846                var directive = $injector.invoke(directiveFactory);
8847                if (isFunction(directive)) {
8848                  directive = { compile: valueFn(directive) };
8849                } else if (!directive.compile && directive.link) {
8850                  directive.compile = valueFn(directive.link);
8851                }
8852                directive.priority = directive.priority || 0;
8853                directive.index = index;
8854                directive.name = directive.name || name;
8855                directive.require = getDirectiveRequire(directive);
8856                directive.restrict = getDirectiveRestrict(directive.restrict, name);
8857                directive.$$moduleName = directiveFactory.$$moduleName;
8858                directives.push(directive);
8859              } catch (e) {
8860                $exceptionHandler(e);
8861              }
8862            });
8863            return directives;
8864          }]);
8865      }
8866      hasDirectives[name].push(directiveFactory);
8867    } else {
8868      forEach(name, reverseParams(registerDirective));
8869    }
8870    return this;
8871  };
8872
8873  /**
8874   * @ngdoc method
8875   * @name $compileProvider#component
8876   * @module ng
8877   * @param {string|Object} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`),
8878   *    or an object map of components where the keys are the names and the values are the component definition objects.
8879   * @param {Object} options Component definition object (a simplified
8880   *    {@link ng.$compile#directive-definition-object directive definition object}),
8881   *    with the following properties (all optional):
8882   *
8883   *    - `controller` – `{(string|function()=}` – controller constructor function that should be
8884   *      associated with newly created scope or the name of a {@link ng.$compile#-controller-
8885   *      registered controller} if passed as a string. An empty `noop` function by default.
8886   *    - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
8887   *      If present, the controller will be published to scope under the `controllerAs` name.
8888   *      If not present, this will default to be `$ctrl`.
8889   *    - `template` – `{string=|function()=}` – html template as a string or a function that
8890   *      returns an html template as a string which should be used as the contents of this component.
8891   *      Empty string by default.
8892   *
8893   *      If `template` is a function, then it is {@link auto.$injector#invoke injected} with
8894   *      the following locals:
8895   *
8896   *      - `$element` - Current element
8897   *      - `$attrs` - Current attributes object for the element
8898   *
8899   *    - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
8900   *      template that should be used  as the contents of this component.
8901   *
8902   *      If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
8903   *      the following locals:
8904   *
8905   *      - `$element` - Current element
8906   *      - `$attrs` - Current attributes object for the element
8907   *
8908   *    - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
8909   *      Component properties are always bound to the component controller and not to the scope.
8910   *      See {@link ng.$compile#-bindtocontroller- `bindToController`}.
8911   *    - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
8912   *      Disabled by default.
8913   *    - `require` - `{Object<string, string>=}` - requires the controllers of other directives and binds them to
8914   *      this component's controller. The object keys specify the property names under which the required
8915   *      controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}.
8916   *    - `$...` – additional properties to attach to the directive factory function and the controller
8917   *      constructor function. (This is used by the component router to annotate)
8918   *
8919   * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
8920   * @description
8921   * Register a **component definition** with the compiler. This is a shorthand for registering a special
8922   * type of directive, which represents a self-contained UI component in your application. Such components
8923   * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
8924   *
8925   * Component definitions are very simple and do not require as much configuration as defining general
8926   * directives. Component definitions usually consist only of a template and a controller backing it.
8927   *
8928   * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
8929   * `bindToController`. They always have **isolate scope** and are restricted to elements.
8930   *
8931   * Here are a few examples of how you would usually define components:
8932   *
8933   * ```js
8934   *   var myMod = angular.module(...);
8935   *   myMod.component('myComp', {
8936   *     template: '<div>My name is {{$ctrl.name}}</div>',
8937   *     controller: function() {
8938   *       this.name = 'shahar';
8939   *     }
8940   *   });
8941   *
8942   *   myMod.component('myComp', {
8943   *     template: '<div>My name is {{$ctrl.name}}</div>',
8944   *     bindings: {name: '@'}
8945   *   });
8946   *
8947   *   myMod.component('myComp', {
8948   *     templateUrl: 'views/my-comp.html',
8949   *     controller: 'MyCtrl',
8950   *     controllerAs: 'ctrl',
8951   *     bindings: {name: '@'}
8952   *   });
8953   *
8954   * ```
8955   * For more examples, and an in-depth guide, see the {@link guide/component component guide}.
8956   *
8957   * <br />
8958   * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
8959   */
8960  this.component = function registerComponent(name, options) {
8961    if (!isString(name)) {
8962      forEach(name, reverseParams(bind(this, registerComponent)));
8963      return this;
8964    }
8965
8966    var controller = options.controller || function() {};
8967
8968    function factory($injector) {
8969      function makeInjectable(fn) {
8970        if (isFunction(fn) || isArray(fn)) {
8971          return /** @this */ function(tElement, tAttrs) {
8972            return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
8973          };
8974        } else {
8975          return fn;
8976        }
8977      }
8978
8979      var template = (!options.template && !options.templateUrl ? '' : options.template);
8980      var ddo = {
8981        controller: controller,
8982        controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
8983        template: makeInjectable(template),
8984        templateUrl: makeInjectable(options.templateUrl),
8985        transclude: options.transclude,
8986        scope: {},
8987        bindToController: options.bindings || {},
8988        restrict: 'E',
8989        require: options.require
8990      };
8991
8992      // Copy annotations (starting with $) over to the DDO
8993      forEach(options, function(val, key) {
8994        if (key.charAt(0) === '$') ddo[key] = val;
8995      });
8996
8997      return ddo;
8998    }
8999
9000    // TODO(pete) remove the following `forEach` before we release 1.6.0
9001    // The [email protected] looks for the annotations on the controller constructor
9002    // Nothing in AngularJS looks for annotations on the factory function but we can't remove
9003    // it from 1.5.x yet.
9004
9005    // Copy any annotation properties (starting with $) over to the factory and controller constructor functions
9006    // These could be used by libraries such as the new component router
9007    forEach(options, function(val, key) {
9008      if (key.charAt(0) === '$') {
9009        factory[key] = val;
9010        // Don't try to copy over annotations to named controller
9011        if (isFunction(controller)) controller[key] = val;
9012      }
9013    });
9014
9015    factory.$inject = ['$injector'];
9016
9017    return this.directive(name, factory);
9018  };
9019
9020
9021  /**
9022   * @ngdoc method
9023   * @name $compileProvider#aHrefSanitizationTrustedUrlList
9024   * @kind function
9025   *
9026   * @description
9027   * Retrieves or overrides the default regular expression that is used for determining trusted safe
9028   * urls during a[href] sanitization.
9029   *
9030   * The sanitization is a security measure aimed at preventing XSS attacks via html links.
9031   *
9032   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
9033   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationTrustedUrlList`
9034   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
9035   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
9036   *
9037   * @param {RegExp=} regexp New regexp to trust urls with.
9038   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
9039   *    chaining otherwise.
9040   */
9041  this.aHrefSanitizationTrustedUrlList = function(regexp) {
9042    if (isDefined(regexp)) {
9043      $$sanitizeUriProvider.aHrefSanitizationTrustedUrlList(regexp);
9044      return this;
9045    } else {
9046      return $$sanitizeUriProvider.aHrefSanitizationTrustedUrlList();
9047    }
9048  };
9049
9050
9051  /**
9052   * @ngdoc method
9053   * @name $compileProvider#aHrefSanitizationWhitelist
9054   * @kind function
9055   *
9056   * @deprecated
9057   * sinceVersion="1.8.1"
9058   *
9059   * This method is deprecated. Use {@link $compileProvider#aHrefSanitizationTrustedUrlList
9060   * aHrefSanitizationTrustedUrlList} instead.
9061   */
9062  Object.defineProperty(this, 'aHrefSanitizationWhitelist', {
9063    get: function() {
9064      return this.aHrefSanitizationTrustedUrlList;
9065    },
9066    set: function(value) {
9067      this.aHrefSanitizationTrustedUrlList = value;
9068    }
9069  });
9070
9071
9072  /**
9073   * @ngdoc method
9074   * @name $compileProvider#imgSrcSanitizationTrustedUrlList
9075   * @kind function
9076   *
9077   * @description
9078   * Retrieves or overrides the default regular expression that is used for determining trusted safe
9079   * urls during img[src] sanitization.
9080   *
9081   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
9082   *
9083   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
9084   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationTrustedUrlList`
9085   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
9086   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
9087   *
9088   * @param {RegExp=} regexp New regexp to trust urls with.
9089   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
9090   *    chaining otherwise.
9091   */
9092  this.imgSrcSanitizationTrustedUrlList = function(regexp) {
9093    if (isDefined(regexp)) {
9094      $$sanitizeUriProvider.imgSrcSanitizationTrustedUrlList(regexp);
9095      return this;
9096    } else {
9097      return $$sanitizeUriProvider.imgSrcSanitizationTrustedUrlList();
9098    }
9099  };
9100
9101
9102  /**
9103   * @ngdoc method
9104   * @name $compileProvider#imgSrcSanitizationWhitelist
9105   * @kind function
9106   *
9107   * @deprecated
9108   * sinceVersion="1.8.1"
9109   *
9110   * This method is deprecated. Use {@link $compileProvider#imgSrcSanitizationTrustedUrlList
9111   * imgSrcSanitizationTrustedUrlList} instead.
9112   */
9113  Object.defineProperty(this, 'imgSrcSanitizationWhitelist', {
9114    get: function() {
9115      return this.imgSrcSanitizationTrustedUrlList;
9116    },
9117    set: function(value) {
9118      this.imgSrcSanitizationTrustedUrlList = value;
9119    }
9120  });
9121
9122  /**
9123   * @ngdoc method
9124   * @name  $compileProvider#debugInfoEnabled
9125   *
9126   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
9127   * current debugInfoEnabled state
9128   * @returns {*} current value if used as getter or itself (chaining) if used as setter
9129   *
9130   * @kind function
9131   *
9132   * @description
9133   * Call this method to enable/disable various debug runtime information in the compiler such as adding
9134   * binding information and a reference to the current scope on to DOM elements.
9135   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
9136   * * `ng-binding` CSS class
9137   * * `ng-scope` and `ng-isolated-scope` CSS classes
9138   * * `$binding` data property containing an array of the binding expressions
9139   * * Data properties used by the {@link angular.element#methods `scope()`/`isolateScope()` methods} to return
9140   *   the element's scope.
9141   * * Placeholder comments will contain information about what directive and binding caused the placeholder.
9142   *   E.g. `<!-- ngIf: shouldShow() -->`.
9143   *
9144   * You may want to disable this in production for a significant performance boost. See
9145   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
9146   *
9147   * The default value is true.
9148   */
9149  var debugInfoEnabled = true;
9150  this.debugInfoEnabled = function(enabled) {
9151    if (isDefined(enabled)) {
9152      debugInfoEnabled = enabled;
9153      return this;
9154    }
9155    return debugInfoEnabled;
9156  };
9157
9158  /**
9159   * @ngdoc method
9160   * @name  $compileProvider#strictComponentBindingsEnabled
9161   *
9162   * @param {boolean=} enabled update the strictComponentBindingsEnabled state if provided,
9163   * otherwise return the current strictComponentBindingsEnabled state.
9164   * @returns {*} current value if used as getter or itself (chaining) if used as setter
9165   *
9166   * @kind function
9167   *
9168   * @description
9169   * Call this method to enable / disable the strict component bindings check. If enabled, the
9170   * compiler will enforce that all scope / controller bindings of a
9171   * {@link $compileProvider#directive directive} / {@link $compileProvider#component component}
9172   * that are not set as optional with `?`, must be provided when the directive is instantiated.
9173   * If not provided, the compiler will throw the
9174   * {@link error/$compile/missingattr $compile:missingattr error}.
9175   *
9176   * The default value is false.
9177   */
9178  var strictComponentBindingsEnabled = false;
9179  this.strictComponentBindingsEnabled = function(enabled) {
9180    if (isDefined(enabled)) {
9181      strictComponentBindingsEnabled = enabled;
9182      return this;
9183    }
9184    return strictComponentBindingsEnabled;
9185  };
9186
9187  var TTL = 10;
9188  /**
9189   * @ngdoc method
9190   * @name $compileProvider#onChangesTtl
9191   * @description
9192   *
9193   * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
9194   * assuming that the model is unstable.
9195   *
9196   * The current default is 10 iterations.
9197   *
9198   * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
9199   * in several iterations of calls to these hooks. However if an application needs more than the default 10
9200   * iterations to stabilize then you should investigate what is causing the model to continuously change during
9201   * the `$onChanges` hook execution.
9202   *
9203   * Increasing the TTL could have performance implications, so you should not change it without proper justification.
9204   *
9205   * @param {number} limit The number of `$onChanges` hook iterations.
9206   * @returns {number|object} the current limit (or `this` if called as a setter for chaining)
9207   */
9208  this.onChangesTtl = function(value) {
9209    if (arguments.length) {
9210      TTL = value;
9211      return this;
9212    }
9213    return TTL;
9214  };
9215
9216  var commentDirectivesEnabledConfig = true;
9217  /**
9218   * @ngdoc method
9219   * @name $compileProvider#commentDirectivesEnabled
9220   * @description
9221   *
9222   * It indicates to the compiler
9223   * whether or not directives on comments should be compiled.
9224   * Defaults to `true`.
9225   *
9226   * Calling this function with false disables the compilation of directives
9227   * on comments for the whole application.
9228   * This results in a compilation performance gain,
9229   * as the compiler doesn't have to check comments when looking for directives.
9230   * This should however only be used if you are sure that no comment directives are used in
9231   * the application (including any 3rd party directives).
9232   *
9233   * @param {boolean} enabled `false` if the compiler may ignore directives on comments
9234   * @returns {boolean|object} the current value (or `this` if called as a setter for chaining)
9235   */
9236  this.commentDirectivesEnabled = function(value) {
9237    if (arguments.length) {
9238      commentDirectivesEnabledConfig = value;
9239      return this;
9240    }
9241    return commentDirectivesEnabledConfig;
9242  };
9243
9244
9245  var cssClassDirectivesEnabledConfig = true;
9246  /**
9247   * @ngdoc method
9248   * @name $compileProvider#cssClassDirectivesEnabled
9249   * @description
9250   *
9251   * It indicates to the compiler
9252   * whether or not directives on element classes should be compiled.
9253   * Defaults to `true`.
9254   *
9255   * Calling this function with false disables the compilation of directives
9256   * on element classes for the whole application.
9257   * This results in a compilation performance gain,
9258   * as the compiler doesn't have to check element classes when looking for directives.
9259   * This should however only be used if you are sure that no class directives are used in
9260   * the application (including any 3rd party directives).
9261   *
9262   * @param {boolean} enabled `false` if the compiler may ignore directives on element classes
9263   * @returns {boolean|object} the current value (or `this` if called as a setter for chaining)
9264   */
9265  this.cssClassDirectivesEnabled = function(value) {
9266    if (arguments.length) {
9267      cssClassDirectivesEnabledConfig = value;
9268      return this;
9269    }
9270    return cssClassDirectivesEnabledConfig;
9271  };
9272
9273
9274  /**
9275   * The security context of DOM Properties.
9276   * @private
9277   */
9278  var PROP_CONTEXTS = createMap();
9279
9280  /**
9281   * @ngdoc method
9282   * @name $compileProvider#addPropertySecurityContext
9283   * @description
9284   *
9285   * Defines the security context for DOM properties bound by ng-prop-*.
9286   *
9287   * @param {string} elementName The element name or '*' to match any element.
9288   * @param {string} propertyName The DOM property name.
9289   * @param {string} ctx The {@link $sce} security context in which this value is safe for use, e.g. `$sce.URL`
9290   * @returns {object} `this` for chaining
9291   */
9292  this.addPropertySecurityContext = function(elementName, propertyName, ctx) {
9293    var key = (elementName.toLowerCase() + '|' + propertyName.toLowerCase());
9294
9295    if (key in PROP_CONTEXTS && PROP_CONTEXTS[key] !== ctx) {
9296      throw $compileMinErr('ctxoverride', 'Property context \'{0}.{1}\' already set to \'{2}\', cannot override to \'{3}\'.', elementName, propertyName, PROP_CONTEXTS[key], ctx);
9297    }
9298
9299    PROP_CONTEXTS[key] = ctx;
9300    return this;
9301  };
9302
9303  /* Default property contexts.
9304   *
9305   * Copy of https://github.com/angular/angular/blob/6.0.6/packages/compiler/src/schema/dom_security_schema.ts#L31-L58
9306   * Changing:
9307   * - SecurityContext.* => SCE_CONTEXTS/$sce.*
9308   * - STYLE => CSS
9309   * - various URL => MEDIA_URL
9310   * - *|formAction, form|action URL => RESOURCE_URL (like the attribute)
9311   */
9312  (function registerNativePropertyContexts() {
9313    function registerContext(ctx, values) {
9314      forEach(values, function(v) { PROP_CONTEXTS[v.toLowerCase()] = ctx; });
9315    }
9316
9317    registerContext(SCE_CONTEXTS.HTML, [
9318      'iframe|srcdoc',
9319      '*|innerHTML',
9320      '*|outerHTML'
9321    ]);
9322    registerContext(SCE_CONTEXTS.CSS, ['*|style']);
9323    registerContext(SCE_CONTEXTS.URL, [
9324      'area|href',       'area|ping',
9325      'a|href',          'a|ping',
9326      'blockquote|cite',
9327      'body|background',
9328      'del|cite',
9329      'input|src',
9330      'ins|cite',
9331      'q|cite'
9332    ]);
9333    registerContext(SCE_CONTEXTS.MEDIA_URL, [
9334      'audio|src',
9335      'img|src',    'img|srcset',
9336      'source|src', 'source|srcset',
9337      'track|src',
9338      'video|src',  'video|poster'
9339    ]);
9340    registerContext(SCE_CONTEXTS.RESOURCE_URL, [
9341      '*|formAction',
9342      'applet|code',      'applet|codebase',
9343      'base|href',
9344      'embed|src',
9345      'frame|src',
9346      'form|action',
9347      'head|profile',
9348      'html|manifest',
9349      'iframe|src',
9350      'link|href',
9351      'media|src',
9352      'object|codebase',  'object|data',
9353      'script|src'
9354    ]);
9355  })();
9356
9357
9358  this.$get = [
9359            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
9360            '$controller', '$rootScope', '$sce', '$animate',
9361    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
9362             $controller,   $rootScope,   $sce,   $animate) {
9363
9364    var SIMPLE_ATTR_NAME = /^\w/;
9365    var specialAttrHolder = window.document.createElement('div');
9366
9367
9368    var commentDirectivesEnabled = commentDirectivesEnabledConfig;
9369    var cssClassDirectivesEnabled = cssClassDirectivesEnabledConfig;
9370
9371
9372    var onChangesTtl = TTL;
9373    // The onChanges hooks should all be run together in a single digest
9374    // When changes occur, the call to trigger their hooks will be added to this queue
9375    var onChangesQueue;
9376
9377    // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
9378    function flushOnChangesQueue() {
9379      try {
9380        if (!(--onChangesTtl)) {
9381          // We have hit the TTL limit so reset everything
9382          onChangesQueue = undefined;
9383          throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
9384        }
9385        // We must run this hook in an apply since the $$postDigest runs outside apply
9386        $rootScope.$apply(function() {
9387          for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
9388            try {
9389              onChangesQueue[i]();
9390            } catch (e) {
9391              $exceptionHandler(e);
9392            }
9393          }
9394          // Reset the queue to trigger a new schedule next time there is a change
9395          onChangesQueue = undefined;
9396        });
9397      } finally {
9398        onChangesTtl++;
9399      }
9400    }
9401
9402
9403    function sanitizeSrcset(value, invokeType) {
9404      if (!value) {
9405        return value;
9406      }
9407      if (!isString(value)) {
9408        throw $compileMinErr('srcset', 'Can\'t pass trusted values to `{0}`: "{1}"', invokeType, value.toString());
9409      }
9410
9411      // Such values are a bit too complex to handle automatically inside $sce.
9412      // Instead, we sanitize each of the URIs individually, which works, even dynamically.
9413
9414      // It's not possible to work around this using `$sce.trustAsMediaUrl`.
9415      // If you want to programmatically set explicitly trusted unsafe URLs, you should use
9416      // `$sce.trustAsHtml` on the whole `img` tag and inject it into the DOM using the
9417      // `ng-bind-html` directive.
9418
9419      var result = '';
9420
9421      // first check if there are spaces because it's not the same pattern
9422      var trimmedSrcset = trim(value);
9423      //                (   999x   ,|   999w   ,|   ,|,   )
9424      var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
9425      var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
9426
9427      // split srcset into tuple of uri and descriptor except for the last item
9428      var rawUris = trimmedSrcset.split(pattern);
9429
9430      // for each tuples
9431      var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
9432      for (var i = 0; i < nbrUrisWith2parts; i++) {
9433        var innerIdx = i * 2;
9434        // sanitize the uri
9435        result += $sce.getTrustedMediaUrl(trim(rawUris[innerIdx]));
9436        // add the descriptor
9437        result += ' ' + trim(rawUris[innerIdx + 1]);
9438      }
9439
9440      // split the last item into uri and descriptor
9441      var lastTuple = trim(rawUris[i * 2]).split(/\s/);
9442
9443      // sanitize the last uri
9444      result += $sce.getTrustedMediaUrl(trim(lastTuple[0]));
9445
9446      // and add the last descriptor if any
9447      if (lastTuple.length === 2) {
9448        result += (' ' + trim(lastTuple[1]));
9449      }
9450      return result;
9451    }
9452
9453
9454    function Attributes(element, attributesToCopy) {
9455      if (attributesToCopy) {
9456        var keys = Object.keys(attributesToCopy);
9457        var i, l, key;
9458
9459        for (i = 0, l = keys.length; i < l; i++) {
9460          key = keys[i];
9461          this[key] = attributesToCopy[key];
9462        }
9463      } else {
9464        this.$attr = {};
9465      }
9466
9467      this.$$element = element;
9468    }
9469
9470    Attributes.prototype = {
9471      /**
9472       * @ngdoc method
9473       * @name $compile.directive.Attributes#$normalize
9474       * @kind function
9475       *
9476       * @description
9477       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
9478       * `data-`) to its normalized, camelCase form.
9479       *
9480       * Also there is special case for Moz prefix starting with upper case letter.
9481       *
9482       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
9483       *
9484       * @param {string} name Name to normalize
9485       */
9486      $normalize: directiveNormalize,
9487
9488
9489      /**
9490       * @ngdoc method
9491       * @name $compile.directive.Attributes#$addClass
9492       * @kind function
9493       *
9494       * @description
9495       * Adds the CSS class value specified by the classVal parameter to the element. If animations
9496       * are enabled then an animation will be triggered for the class addition.
9497       *
9498       * @param {string} classVal The className value that will be added to the element
9499       */
9500      $addClass: function(classVal) {
9501        if (classVal && classVal.length > 0) {
9502          $animate.addClass(this.$$element, classVal);
9503        }
9504      },
9505
9506      /**
9507       * @ngdoc method
9508       * @name $compile.directive.Attributes#$removeClass
9509       * @kind function
9510       *
9511       * @description
9512       * Removes the CSS class value specified by the classVal parameter from the element. If
9513       * animations are enabled then an animation will be triggered for the class removal.
9514       *
9515       * @param {string} classVal The className value that will be removed from the element
9516       */
9517      $removeClass: function(classVal) {
9518        if (classVal && classVal.length > 0) {
9519          $animate.removeClass(this.$$element, classVal);
9520        }
9521      },
9522
9523      /**
9524       * @ngdoc method
9525       * @name $compile.directive.Attributes#$updateClass
9526       * @kind function
9527       *
9528       * @description
9529       * Adds and removes the appropriate CSS class values to the element based on the difference
9530       * between the new and old CSS class values (specified as newClasses and oldClasses).
9531       *
9532       * @param {string} newClasses The current CSS className value
9533       * @param {string} oldClasses The former CSS className value
9534       */
9535      $updateClass: function(newClasses, oldClasses) {
9536        var toAdd = tokenDifference(newClasses, oldClasses);
9537        if (toAdd && toAdd.length) {
9538          $animate.addClass(this.$$element, toAdd);
9539        }
9540
9541        var toRemove = tokenDifference(oldClasses, newClasses);
9542        if (toRemove && toRemove.length) {
9543          $animate.removeClass(this.$$element, toRemove);
9544        }
9545      },
9546
9547      /**
9548       * Set a normalized attribute on the element in a way such that all directives
9549       * can share the attribute. This function properly handles boolean attributes.
9550       * @param {string} key Normalized key. (ie ngAttribute)
9551       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
9552       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
9553       *     Defaults to true.
9554       * @param {string=} attrName Optional none normalized name. Defaults to key.
9555       */
9556      $set: function(key, value, writeAttr, attrName) {
9557        // TODO: decide whether or not to throw an error if "class"
9558        // is set through this function since it may cause $updateClass to
9559        // become unstable.
9560
9561        var node = this.$$element[0],
9562            booleanKey = getBooleanAttrName(node, key),
9563            aliasedKey = getAliasedAttrName(key),
9564            observer = key,
9565            nodeName;
9566
9567        if (booleanKey) {
9568          this.$$element.prop(key, value);
9569          attrName = booleanKey;
9570        } else if (aliasedKey) {
9571          this[aliasedKey] = value;
9572          observer = aliasedKey;
9573        }
9574
9575        this[key] = value;
9576
9577        // translate normalized key to actual key
9578        if (attrName) {
9579          this.$attr[key] = attrName;
9580        } else {
9581          attrName = this.$attr[key];
9582          if (!attrName) {
9583            this.$attr[key] = attrName = snake_case(key, '-');
9584          }
9585        }
9586
9587        nodeName = nodeName_(this.$$element);
9588
9589        // Sanitize img[srcset] values.
9590        if (nodeName === 'img' && key === 'srcset') {
9591          this[key] = value = sanitizeSrcset(value, '$set(\'srcset\', value)');
9592        }
9593
9594        if (writeAttr !== false) {
9595          if (value === null || isUndefined(value)) {
9596            this.$$element.removeAttr(attrName);
9597          } else {
9598            if (SIMPLE_ATTR_NAME.test(attrName)) {
9599              // jQuery skips special boolean attrs treatment in XML nodes for
9600              // historical reasons and hence AngularJS cannot freely call
9601              // `.attr(attrName, false) with such attributes. To avoid issues
9602              // in XHTML, call `removeAttr` in such cases instead.
9603              // See https://github.com/jquery/jquery/issues/4249
9604              if (booleanKey && value === false) {
9605                this.$$element.removeAttr(attrName);
9606              } else {
9607                this.$$element.attr(attrName, value);
9608              }
9609            } else {
9610              setSpecialAttr(this.$$element[0], attrName, value);
9611            }
9612          }
9613        }
9614
9615        // fire observers
9616        var $$observers = this.$$observers;
9617        if ($$observers) {
9618          forEach($$observers[observer], function(fn) {
9619            try {
9620              fn(value);
9621            } catch (e) {
9622              $exceptionHandler(e);
9623            }
9624          });
9625        }
9626      },
9627
9628
9629      /**
9630       * @ngdoc method
9631       * @name $compile.directive.Attributes#$observe
9632       * @kind function
9633       *
9634       * @description
9635       * Observes an interpolated attribute.
9636       *
9637       * The observer function will be invoked once during the next `$digest` following
9638       * compilation. The observer is then invoked whenever the interpolated value
9639       * changes.
9640       *
9641       * @param {string} key Normalized key. (ie ngAttribute) .
9642       * @param {function(interpolatedValue)} fn Function that will be called whenever
9643                the interpolated value of the attribute changes.
9644       *        See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
9645       *        guide} for more info.
9646       * @returns {function()} Returns a deregistration function for this observer.
9647       */
9648      $observe: function(key, fn) {
9649        var attrs = this,
9650            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
9651            listeners = ($$observers[key] || ($$observers[key] = []));
9652
9653        listeners.push(fn);
9654        $rootScope.$evalAsync(function() {
9655          if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
9656            // no one registered attribute interpolation function, so lets call it manually
9657            fn(attrs[key]);
9658          }
9659        });
9660
9661        return function() {
9662          arrayRemove(listeners, fn);
9663        };
9664      }
9665    };
9666
9667    function setSpecialAttr(element, attrName, value) {
9668      // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
9669      // so we have to jump through some hoops to get such an attribute
9670      // https://github.com/angular/angular.js/pull/13318
9671      specialAttrHolder.innerHTML = '<span ' + attrName + '>';
9672      var attributes = specialAttrHolder.firstChild.attributes;
9673      var attribute = attributes[0];
9674      // We have to remove the attribute from its container element before we can add it to the destination element
9675      attributes.removeNamedItem(attribute.name);
9676      attribute.value = value;
9677      element.attributes.setNamedItem(attribute);
9678    }
9679
9680    function safeAddClass($element, className) {
9681      try {
9682        $element.addClass(className);
9683      } catch (e) {
9684        // ignore, since it means that we are trying to set class on
9685        // SVG element, where class name is read-only.
9686      }
9687    }
9688
9689
9690    var startSymbol = $interpolate.startSymbol(),
9691        endSymbol = $interpolate.endSymbol(),
9692        denormalizeTemplate = (startSymbol === '{{' && endSymbol  === '}}')
9693            ? identity
9694            : function denormalizeTemplate(template) {
9695              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
9696        },
9697        NG_PREFIX_BINDING = /^ng(Attr|Prop|On)([A-Z].*)$/;
9698    var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
9699
9700    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
9701      var bindings = $element.data('$binding') || [];
9702
9703      if (isArray(binding)) {
9704        bindings = bindings.concat(binding);
9705      } else {
9706        bindings.push(binding);
9707      }
9708
9709      $element.data('$binding', bindings);
9710    } : noop;
9711
9712    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
9713      safeAddClass($element, 'ng-binding');
9714    } : noop;
9715
9716    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
9717      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
9718      $element.data(dataName, scope);
9719    } : noop;
9720
9721    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
9722      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
9723    } : noop;
9724
9725    compile.$$createComment = function(directiveName, comment) {
9726      var content = '';
9727      if (debugInfoEnabled) {
9728        content = ' ' + (directiveName || '') + ': ';
9729        if (comment) content += comment + ' ';
9730      }
9731      return window.document.createComment(content);
9732    };
9733
9734    return compile;
9735
9736    //================================
9737
9738    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
9739                        previousCompileContext) {
9740      if (!($compileNodes instanceof jqLite)) {
9741        // jquery always rewraps, whereas we need to preserve the original selector so that we can
9742        // modify it.
9743        $compileNodes = jqLite($compileNodes);
9744      }
9745      var compositeLinkFn =
9746              compileNodes($compileNodes, transcludeFn, $compileNodes,
9747                           maxPriority, ignoreDirective, previousCompileContext);
9748      compile.$$addScopeClass($compileNodes);
9749      var namespace = null;
9750      return function publicLinkFn(scope, cloneConnectFn, options) {
9751        if (!$compileNodes) {
9752          throw $compileMinErr('multilink', 'This element has already been linked.');
9753        }
9754        assertArg(scope, 'scope');
9755
9756        if (previousCompileContext && previousCompileContext.needsNewScope) {
9757          // A parent directive did a replace and a directive on this element asked
9758          // for transclusion, which caused us to lose a layer of element on which
9759          // we could hold the new transclusion scope, so we will create it manually
9760          // here.
9761          scope = scope.$parent.$new();
9762        }
9763
9764        options = options || {};
9765        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
9766          transcludeControllers = options.transcludeControllers,
9767          futureParentElement = options.futureParentElement;
9768
9769        // When `parentBoundTranscludeFn` is passed, it is a
9770        // `controllersBoundTransclude` function (it was previously passed
9771        // as `transclude` to directive.link) so we must unwrap it to get
9772        // its `boundTranscludeFn`
9773        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
9774          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
9775        }
9776
9777        if (!namespace) {
9778          namespace = detectNamespaceForChildElements(futureParentElement);
9779        }
9780        var $linkNode;
9781        if (namespace !== 'html') {
9782          // When using a directive with replace:true and templateUrl the $compileNodes
9783          // (or a child element inside of them)
9784          // might change, so we need to recreate the namespace adapted compileNodes
9785          // for call to the link function.
9786          // Note: This will already clone the nodes...
9787          $linkNode = jqLite(
9788            wrapTemplate(namespace, jqLite('<div></div>').append($compileNodes).html())
9789          );
9790        } else if (cloneConnectFn) {
9791          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
9792          // and sometimes changes the structure of the DOM.
9793          $linkNode = JQLitePrototype.clone.call($compileNodes);
9794        } else {
9795          $linkNode = $compileNodes;
9796        }
9797
9798        if (transcludeControllers) {
9799          for (var controllerName in transcludeControllers) {
9800            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
9801          }
9802        }
9803
9804        compile.$$addScopeInfo($linkNode, scope);
9805
9806        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
9807        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
9808
9809        if (!cloneConnectFn) {
9810          $compileNodes = compositeLinkFn = null;
9811        }
9812        return $linkNode;
9813      };
9814    }
9815
9816    function detectNamespaceForChildElements(parentElement) {
9817      // TODO: Make this detect MathML as well...
9818      var node = parentElement && parentElement[0];
9819      if (!node) {
9820        return 'html';
9821      } else {
9822        return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
9823      }
9824    }
9825
9826    /**
9827     * Compile function matches each node in nodeList against the directives. Once all directives
9828     * for a particular node are collected their compile functions are executed. The compile
9829     * functions return values - the linking functions - are combined into a composite linking
9830     * function, which is the a linking function for the node.
9831     *
9832     * @param {NodeList} nodeList an array of nodes or NodeList to compile
9833     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
9834     *        scope argument is auto-generated to the new child of the transcluded parent scope.
9835     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
9836     *        the rootElement must be set the jqLite collection of the compile root. This is
9837     *        needed so that the jqLite collection items can be replaced with widgets.
9838     * @param {number=} maxPriority Max directive priority.
9839     * @returns {Function} A composite linking function of all of the matched directives or null.
9840     */
9841    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
9842                            previousCompileContext) {
9843      var linkFns = [],
9844          // `nodeList` can be either an element's `.childNodes` (live NodeList)
9845          // or a jqLite/jQuery collection or an array
9846          notLiveList = isArray(nodeList) || (nodeList instanceof jqLite),
9847          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
9848
9849
9850      for (var i = 0; i < nodeList.length; i++) {
9851        attrs = new Attributes();
9852
9853        // Support: IE 11 only
9854        // Workaround for #11781 and #14924
9855        if (msie === 11) {
9856          mergeConsecutiveTextNodes(nodeList, i, notLiveList);
9857        }
9858
9859        // We must always refer to `nodeList[i]` hereafter,
9860        // since the nodes can be replaced underneath us.
9861        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
9862                                        ignoreDirective);
9863
9864        nodeLinkFn = (directives.length)
9865            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
9866                                      null, [], [], previousCompileContext)
9867            : null;
9868
9869        if (nodeLinkFn && nodeLinkFn.scope) {
9870          compile.$$addScopeClass(attrs.$$element);
9871        }
9872
9873        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
9874                      !(childNodes = nodeList[i].childNodes) ||
9875                      !childNodes.length)
9876            ? null
9877            : compileNodes(childNodes,
9878                 nodeLinkFn ? (
9879                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
9880                     && nodeLinkFn.transclude) : transcludeFn);
9881
9882        if (nodeLinkFn || childLinkFn) {
9883          linkFns.push(i, nodeLinkFn, childLinkFn);
9884          linkFnFound = true;
9885          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
9886        }
9887
9888        //use the previous context only for the first element in the virtual group
9889        previousCompileContext = null;
9890      }
9891
9892      // return a linking function if we have found anything, null otherwise
9893      return linkFnFound ? compositeLinkFn : null;
9894
9895      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
9896        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
9897        var stableNodeList;
9898
9899
9900        if (nodeLinkFnFound) {
9901          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
9902          // offsets don't get screwed up
9903          var nodeListLength = nodeList.length;
9904          stableNodeList = new Array(nodeListLength);
9905
9906          // create a sparse array by only copying the elements which have a linkFn
9907          for (i = 0; i < linkFns.length; i += 3) {
9908            idx = linkFns[i];
9909            stableNodeList[idx] = nodeList[idx];
9910          }
9911        } else {
9912          stableNodeList = nodeList;
9913        }
9914
9915        for (i = 0, ii = linkFns.length; i < ii;) {
9916          node = stableNodeList[linkFns[i++]];
9917          nodeLinkFn = linkFns[i++];
9918          childLinkFn = linkFns[i++];
9919
9920          if (nodeLinkFn) {
9921            if (nodeLinkFn.scope) {
9922              childScope = scope.$new();
9923              compile.$$addScopeInfo(jqLite(node), childScope);
9924            } else {
9925              childScope = scope;
9926            }
9927
9928            if (nodeLinkFn.transcludeOnThisElement) {
9929              childBoundTranscludeFn = createBoundTranscludeFn(
9930                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
9931
9932            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
9933              childBoundTranscludeFn = parentBoundTranscludeFn;
9934
9935            } else if (!parentBoundTranscludeFn && transcludeFn) {
9936              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
9937
9938            } else {
9939              childBoundTranscludeFn = null;
9940            }
9941
9942            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
9943
9944          } else if (childLinkFn) {
9945            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
9946          }
9947        }
9948      }
9949    }
9950
9951    function mergeConsecutiveTextNodes(nodeList, idx, notLiveList) {
9952      var node = nodeList[idx];
9953      var parent = node.parentNode;
9954      var sibling;
9955
9956      if (node.nodeType !== NODE_TYPE_TEXT) {
9957        return;
9958      }
9959
9960      while (true) {
9961        sibling = parent ? node.nextSibling : nodeList[idx + 1];
9962        if (!sibling || sibling.nodeType !== NODE_TYPE_TEXT) {
9963          break;
9964        }
9965
9966        node.nodeValue = node.nodeValue + sibling.nodeValue;
9967
9968        if (sibling.parentNode) {
9969          sibling.parentNode.removeChild(sibling);
9970        }
9971        if (notLiveList && sibling === nodeList[idx + 1]) {
9972          nodeList.splice(idx + 1, 1);
9973        }
9974      }
9975    }
9976
9977    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
9978      function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
9979
9980        if (!transcludedScope) {
9981          transcludedScope = scope.$new(false, containingScope);
9982          transcludedScope.$$transcluded = true;
9983        }
9984
9985        return transcludeFn(transcludedScope, cloneFn, {
9986          parentBoundTranscludeFn: previousBoundTranscludeFn,
9987          transcludeControllers: controllers,
9988          futureParentElement: futureParentElement
9989        });
9990      }
9991
9992      // We need  to attach the transclusion slots onto the `boundTranscludeFn`
9993      // so that they are available inside the `controllersBoundTransclude` function
9994      var boundSlots = boundTranscludeFn.$$slots = createMap();
9995      for (var slotName in transcludeFn.$$slots) {
9996        if (transcludeFn.$$slots[slotName]) {
9997          boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
9998        } else {
9999          boundSlots[slotName] = null;
10000        }
10001      }
10002
10003      return boundTranscludeFn;
10004    }
10005
10006    /**
10007     * Looks for directives on the given node and adds them to the directive collection which is
10008     * sorted.
10009     *
10010     * @param node Node to search.
10011     * @param directives An array to which the directives are added to. This array is sorted before
10012     *        the function returns.
10013     * @param attrs The shared attrs object which is used to populate the normalized attributes.
10014     * @param {number=} maxPriority Max directive priority.
10015     */
10016    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
10017      var nodeType = node.nodeType,
10018          attrsMap = attrs.$attr,
10019          match,
10020          nodeName,
10021          className;
10022
10023      switch (nodeType) {
10024        case NODE_TYPE_ELEMENT: /* Element */
10025
10026          nodeName = nodeName_(node);
10027
10028          // use the node name: <directive>
10029          addDirective(directives,
10030              directiveNormalize(nodeName), 'E', maxPriority, ignoreDirective);
10031
10032          // iterate over the attributes
10033          for (var attr, name, nName, value, ngPrefixMatch, nAttrs = node.attributes,
10034                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
10035            var attrStartName = false;
10036            var attrEndName = false;
10037
10038            var isNgAttr = false, isNgProp = false, isNgEvent = false;
10039            var multiElementMatch;
10040
10041            attr = nAttrs[j];
10042            name = attr.name;
10043            value = attr.value;
10044
10045            nName = directiveNormalize(name.toLowerCase());
10046
10047            // Support ng-attr-*, ng-prop-* and ng-on-*
10048            if ((ngPrefixMatch = nName.match(NG_PREFIX_BINDING))) {
10049              isNgAttr = ngPrefixMatch[1] === 'Attr';
10050              isNgProp = ngPrefixMatch[1] === 'Prop';
10051              isNgEvent = ngPrefixMatch[1] === 'On';
10052
10053              // Normalize the non-prefixed name
10054              name = name.replace(PREFIX_REGEXP, '')
10055                .toLowerCase()
10056                .substr(4 + ngPrefixMatch[1].length).replace(/_(.)/g, function(match, letter) {
10057                  return letter.toUpperCase();
10058                });
10059
10060            // Support *-start / *-end multi element directives
10061            } else if ((multiElementMatch = nName.match(MULTI_ELEMENT_DIR_RE)) && directiveIsMultiElement(multiElementMatch[1])) {
10062              attrStartName = name;
10063              attrEndName = name.substr(0, name.length - 5) + 'end';
10064              name = name.substr(0, name.length - 6);
10065            }
10066
10067            if (isNgProp || isNgEvent) {
10068              attrs[nName] = value;
10069              attrsMap[nName] = attr.name;
10070
10071              if (isNgProp) {
10072                addPropertyDirective(node, directives, nName, name);
10073              } else {
10074                addEventDirective(directives, nName, name);
10075              }
10076            } else {
10077              // Update nName for cases where a prefix was removed
10078              // NOTE: the .toLowerCase() is unnecessary and causes https://github.com/angular/angular.js/issues/16624 for ng-attr-*
10079              nName = directiveNormalize(name.toLowerCase());
10080              attrsMap[nName] = name;
10081
10082              if (isNgAttr || !attrs.hasOwnProperty(nName)) {
10083                attrs[nName] = value;
10084                if (getBooleanAttrName(node, nName)) {
10085                  attrs[nName] = true; // presence means true
10086                }
10087              }
10088
10089              addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
10090              addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
10091                            attrEndName);
10092            }
10093          }
10094
10095          if (nodeName === 'input' && node.getAttribute('type') === 'hidden') {
10096            // Hidden input elements can have strange behaviour when navigating back to the page
10097            // This tells the browser not to try to cache and reinstate previous values
10098            node.setAttribute('autocomplete', 'off');
10099          }
10100
10101          // use class as directive
10102          if (!cssClassDirectivesEnabled) break;
10103          className = node.className;
10104          if (isObject(className)) {
10105              // Maybe SVGAnimatedString
10106              className = className.animVal;
10107          }
10108          if (isString(className) && className !== '') {
10109            while ((match = CLASS_DIRECTIVE_REGEXP.exec(className))) {
10110              nName = directiveNormalize(match[2]);
10111              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
10112                attrs[nName] = trim(match[3]);
10113              }
10114              className = className.substr(match.index + match[0].length);
10115            }
10116          }
10117          break;
10118        case NODE_TYPE_TEXT: /* Text Node */
10119          addTextInterpolateDirective(directives, node.nodeValue);
10120          break;
10121        case NODE_TYPE_COMMENT: /* Comment */
10122          if (!commentDirectivesEnabled) break;
10123          collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective);
10124          break;
10125      }
10126
10127      directives.sort(byPriority);
10128      return directives;
10129    }
10130
10131    function collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
10132      // function created because of performance, try/catch disables
10133      // the optimization of the whole function #14848
10134      try {
10135        var match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
10136        if (match) {
10137          var nName = directiveNormalize(match[1]);
10138          if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
10139            attrs[nName] = trim(match[2]);
10140          }
10141        }
10142      } catch (e) {
10143        // turns out that under some circumstances IE9 throws errors when one attempts to read
10144        // comment's node value.
10145        // Just ignore it and continue. (Can't seem to reproduce in test case.)
10146      }
10147    }
10148
10149    /**
10150     * Given a node with a directive-start it collects all of the siblings until it finds
10151     * directive-end.
10152     * @param node
10153     * @param attrStart
10154     * @param attrEnd
10155     * @returns {*}
10156     */
10157    function groupScan(node, attrStart, attrEnd) {
10158      var nodes = [];
10159      var depth = 0;
10160      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
10161        do {
10162          if (!node) {
10163            throw $compileMinErr('uterdir',
10164                      'Unterminated attribute, found \'{0}\' but no matching \'{1}\' found.',
10165                      attrStart, attrEnd);
10166          }
10167          if (node.nodeType === NODE_TYPE_ELEMENT) {
10168            if (node.hasAttribute(attrStart)) depth++;
10169            if (node.hasAttribute(attrEnd)) depth--;
10170          }
10171          nodes.push(node);
10172          node = node.nextSibling;
10173        } while (depth > 0);
10174      } else {
10175        nodes.push(node);
10176      }
10177
10178      return jqLite(nodes);
10179    }
10180
10181    /**
10182     * Wrapper for linking function which converts normal linking function into a grouped
10183     * linking function.
10184     * @param linkFn
10185     * @param attrStart
10186     * @param attrEnd
10187     * @returns {Function}
10188     */
10189    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
10190      return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
10191        element = groupScan(element[0], attrStart, attrEnd);
10192        return linkFn(scope, element, attrs, controllers, transcludeFn);
10193      };
10194    }
10195
10196    /**
10197     * A function generator that is used to support both eager and lazy compilation
10198     * linking function.
10199     * @param eager
10200     * @param $compileNodes
10201     * @param transcludeFn
10202     * @param maxPriority
10203     * @param ignoreDirective
10204     * @param previousCompileContext
10205     * @returns {Function}
10206     */
10207    function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
10208      var compiled;
10209
10210      if (eager) {
10211        return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
10212      }
10213      return /** @this */ function lazyCompilation() {
10214        if (!compiled) {
10215          compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
10216
10217          // Null out all of these references in order to make them eligible for garbage collection
10218          // since this is a potentially long lived closure
10219          $compileNodes = transcludeFn = previousCompileContext = null;
10220        }
10221        return compiled.apply(this, arguments);
10222      };
10223    }
10224
10225    /**
10226     * Once the directives have been collected, their compile functions are executed. This method
10227     * is responsible for inlining directive templates as well as terminating the application
10228     * of the directives if the terminal directive has been reached.
10229     *
10230     * @param {Array} directives Array of collected directives to execute their compile function.
10231     *        this needs to be pre-sorted by priority order.
10232     * @param {Node} compileNode The raw DOM node to apply the compile functions to
10233     * @param {Object} templateAttrs The shared attribute function
10234     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
10235     *                                                  scope argument is auto-generated to the new
10236     *                                                  child of the transcluded parent scope.
10237     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
10238     *                              argument has the root jqLite array so that we can replace nodes
10239     *                              on it.
10240     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
10241     *                                           compiling the transclusion.
10242     * @param {Array.<Function>} preLinkFns
10243     * @param {Array.<Function>} postLinkFns
10244     * @param {Object} previousCompileContext Context used for previous compilation of the current
10245     *                                        node
10246     * @returns {Function} linkFn
10247     */
10248    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
10249                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
10250                                   previousCompileContext) {
10251      previousCompileContext = previousCompileContext || {};
10252
10253      var terminalPriority = -Number.MAX_VALUE,
10254          newScopeDirective = previousCompileContext.newScopeDirective,
10255          controllerDirectives = previousCompileContext.controllerDirectives,
10256          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
10257          templateDirective = previousCompileContext.templateDirective,
10258          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
10259          hasTranscludeDirective = false,
10260          hasTemplate = false,
10261          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
10262          $compileNode = templateAttrs.$$element = jqLite(compileNode),
10263          directive,
10264          directiveName,
10265          $template,
10266          replaceDirective = originalReplaceDirective,
10267          childTranscludeFn = transcludeFn,
10268          linkFn,
10269          didScanForMultipleTransclusion = false,
10270          mightHaveMultipleTransclusionError = false,
10271          directiveValue;
10272
10273      // executes all directives on the current element
10274      for (var i = 0, ii = directives.length; i < ii; i++) {
10275        directive = directives[i];
10276        var attrStart = directive.$$start;
10277        var attrEnd = directive.$$end;
10278
10279        // collect multiblock sections
10280        if (attrStart) {
10281          $compileNode = groupScan(compileNode, attrStart, attrEnd);
10282        }
10283        $template = undefined;
10284
10285        if (terminalPriority > directive.priority) {
10286          break; // prevent further processing of directives
10287        }
10288
10289        directiveValue = directive.scope;
10290
10291        if (directiveValue) {
10292
10293          // skip the check for directives with async templates, we'll check the derived sync
10294          // directive when the template arrives
10295          if (!directive.templateUrl) {
10296            if (isObject(directiveValue)) {
10297              // This directive is trying to add an isolated scope.
10298              // Check that there is no scope of any kind already
10299              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
10300                                directive, $compileNode);
10301              newIsolateScopeDirective = directive;
10302            } else {
10303              // This directive is trying to add a child scope.
10304              // Check that there is no isolated scope already
10305              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
10306                                $compileNode);
10307            }
10308          }
10309
10310          newScopeDirective = newScopeDirective || directive;
10311        }
10312
10313        directiveName = directive.name;
10314
10315        // If we encounter a condition that can result in transclusion on the directive,
10316        // then scan ahead in the remaining directives for others that may cause a multiple
10317        // transclusion error to be thrown during the compilation process.  If a matching directive
10318        // is found, then we know that when we encounter a transcluded directive, we need to eagerly
10319        // compile the `transclude` function rather than doing it lazily in order to throw
10320        // exceptions at the correct time
10321        if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
10322            || (directive.transclude && !directive.$$tlb))) {
10323                var candidateDirective;
10324
10325                for (var scanningIndex = i + 1; (candidateDirective = directives[scanningIndex++]);) {
10326                    if ((candidateDirective.transclude && !candidateDirective.$$tlb)
10327                        || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
10328                        mightHaveMultipleTransclusionError = true;
10329                        break;
10330                    }
10331                }
10332
10333                didScanForMultipleTransclusion = true;
10334        }
10335
10336        if (!directive.templateUrl && directive.controller) {
10337          controllerDirectives = controllerDirectives || createMap();
10338          assertNoDuplicate('\'' + directiveName + '\' controller',
10339              controllerDirectives[directiveName], directive, $compileNode);
10340          controllerDirectives[directiveName] = directive;
10341        }
10342
10343        directiveValue = directive.transclude;
10344
10345        if (directiveValue) {
10346          hasTranscludeDirective = true;
10347
10348          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
10349          // This option should only be used by directives that know how to safely handle element transclusion,
10350          // where the transcluded nodes are added or replaced after linking.
10351          if (!directive.$$tlb) {
10352            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
10353            nonTlbTranscludeDirective = directive;
10354          }
10355
10356          if (directiveValue === 'element') {
10357            hasElementTranscludeDirective = true;
10358            terminalPriority = directive.priority;
10359            $template = $compileNode;
10360            $compileNode = templateAttrs.$$element =
10361                jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
10362            compileNode = $compileNode[0];
10363            replaceWith(jqCollection, sliceArgs($template), compileNode);
10364
10365            childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
10366                                        replaceDirective && replaceDirective.name, {
10367                                          // Don't pass in:
10368                                          // - controllerDirectives - otherwise we'll create duplicates controllers
10369                                          // - newIsolateScopeDirective or templateDirective - combining templates with
10370                                          //   element transclusion doesn't make sense.
10371                                          //
10372                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
10373                                          // on the same element more than once.
10374                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective
10375                                        });
10376          } else {
10377
10378            var slots = createMap();
10379
10380            if (!isObject(directiveValue)) {
10381              $template = jqLite(jqLiteClone(compileNode)).contents();
10382            } else {
10383
10384              // We have transclusion slots,
10385              // collect them up, compile them and store their transclusion functions
10386              $template = window.document.createDocumentFragment();
10387
10388              var slotMap = createMap();
10389              var filledSlots = createMap();
10390
10391              // Parse the element selectors
10392              forEach(directiveValue, function(elementSelector, slotName) {
10393                // If an element selector starts with a ? then it is optional
10394                var optional = (elementSelector.charAt(0) === '?');
10395                elementSelector = optional ? elementSelector.substring(1) : elementSelector;
10396
10397                slotMap[elementSelector] = slotName;
10398
10399                // We explicitly assign `null` since this implies that a slot was defined but not filled.
10400                // Later when calling boundTransclusion functions with a slot name we only error if the
10401                // slot is `undefined`
10402                slots[slotName] = null;
10403
10404                // filledSlots contains `true` for all slots that are either optional or have been
10405                // filled. This is used to check that we have not missed any required slots
10406                filledSlots[slotName] = optional;
10407              });
10408
10409              // Add the matching elements into their slot
10410              forEach($compileNode.contents(), function(node) {
10411                var slotName = slotMap[directiveNormalize(nodeName_(node))];
10412                if (slotName) {
10413                  filledSlots[slotName] = true;
10414                  slots[slotName] = slots[slotName] || window.document.createDocumentFragment();
10415                  slots[slotName].appendChild(node);
10416                } else {
10417                  $template.appendChild(node);
10418                }
10419              });
10420
10421              // Check for required slots that were not filled
10422              forEach(filledSlots, function(filled, slotName) {
10423                if (!filled) {
10424                  throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
10425                }
10426              });
10427
10428              for (var slotName in slots) {
10429                if (slots[slotName]) {
10430                  // Only define a transclusion function if the slot was filled
10431                  var slotCompileNodes = jqLite(slots[slotName].childNodes);
10432                  slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slotCompileNodes, transcludeFn);
10433                }
10434              }
10435
10436              $template = jqLite($template.childNodes);
10437            }
10438
10439            $compileNode.empty(); // clear contents
10440            childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
10441                undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
10442            childTranscludeFn.$$slots = slots;
10443          }
10444        }
10445
10446        if (directive.template) {
10447          hasTemplate = true;
10448          assertNoDuplicate('template', templateDirective, directive, $compileNode);
10449          templateDirective = directive;
10450
10451          directiveValue = (isFunction(directive.template))
10452              ? directive.template($compileNode, templateAttrs)
10453              : directive.template;
10454
10455          directiveValue = denormalizeTemplate(directiveValue);
10456
10457          if (directive.replace) {
10458            replaceDirective = directive;
10459            if (jqLiteIsTextNode(directiveValue)) {
10460              $template = [];
10461            } else {
10462              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
10463            }
10464            compileNode = $template[0];
10465
10466            if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
10467              throw $compileMinErr('tplrt',
10468                  'Template for directive \'{0}\' must have exactly one root element. {1}',
10469                  directiveName, '');
10470            }
10471
10472            replaceWith(jqCollection, $compileNode, compileNode);
10473
10474            var newTemplateAttrs = {$attr: {}};
10475
10476            // combine directives from the original node and from the template:
10477            // - take the array of directives for this element
10478            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
10479            // - collect directives from the template and sort them by priority
10480            // - combine directives as: processed + template + unprocessed
10481            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
10482            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
10483
10484            if (newIsolateScopeDirective || newScopeDirective) {
10485              // The original directive caused the current element to be replaced but this element
10486              // also needs to have a new scope, so we need to tell the template directives
10487              // that they would need to get their scope from further up, if they require transclusion
10488              markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
10489            }
10490            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
10491            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
10492
10493            ii = directives.length;
10494          } else {
10495            $compileNode.html(directiveValue);
10496          }
10497        }
10498
10499        if (directive.templateUrl) {
10500          hasTemplate = true;
10501          assertNoDuplicate('template', templateDirective, directive, $compileNode);
10502          templateDirective = directive;
10503
10504          if (directive.replace) {
10505            replaceDirective = directive;
10506          }
10507
10508          // eslint-disable-next-line no-func-assign
10509          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
10510              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
10511                controllerDirectives: controllerDirectives,
10512                newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
10513                newIsolateScopeDirective: newIsolateScopeDirective,
10514                templateDirective: templateDirective,
10515                nonTlbTranscludeDirective: nonTlbTranscludeDirective
10516              });
10517          ii = directives.length;
10518        } else if (directive.compile) {
10519          try {
10520            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
10521            var context = directive.$$originalDirective || directive;
10522            if (isFunction(linkFn)) {
10523              addLinkFns(null, bind(context, linkFn), attrStart, attrEnd);
10524            } else if (linkFn) {
10525              addLinkFns(bind(context, linkFn.pre), bind(context, linkFn.post), attrStart, attrEnd);
10526            }
10527          } catch (e) {
10528            $exceptionHandler(e, startingTag($compileNode));
10529          }
10530        }
10531
10532        if (directive.terminal) {
10533          nodeLinkFn.terminal = true;
10534          terminalPriority = Math.max(terminalPriority, directive.priority);
10535        }
10536
10537      }
10538
10539      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
10540      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
10541      nodeLinkFn.templateOnThisElement = hasTemplate;
10542      nodeLinkFn.transclude = childTranscludeFn;
10543
10544      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
10545
10546      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
10547      return nodeLinkFn;
10548
10549      ////////////////////
10550
10551      function addLinkFns(pre, post, attrStart, attrEnd) {
10552        if (pre) {
10553          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
10554          pre.require = directive.require;
10555          pre.directiveName = directiveName;
10556          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
10557            pre = cloneAndAnnotateFn(pre, {isolateScope: true});
10558          }
10559          preLinkFns.push(pre);
10560        }
10561        if (post) {
10562          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
10563          post.require = directive.require;
10564          post.directiveName = directiveName;
10565          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
10566            post = cloneAndAnnotateFn(post, {isolateScope: true});
10567          }
10568          postLinkFns.push(post);
10569        }
10570      }
10571
10572      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
10573        var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
10574            attrs, scopeBindingInfo;
10575
10576        if (compileNode === linkNode) {
10577          attrs = templateAttrs;
10578          $element = templateAttrs.$$element;
10579        } else {
10580          $element = jqLite(linkNode);
10581          attrs = new Attributes($element, templateAttrs);
10582        }
10583
10584        controllerScope = scope;
10585        if (newIsolateScopeDirective) {
10586          isolateScope = scope.$new(true);
10587        } else if (newScopeDirective) {
10588          controllerScope = scope.$parent;
10589        }
10590
10591        if (boundTranscludeFn) {
10592          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
10593          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
10594          transcludeFn = controllersBoundTransclude;
10595          transcludeFn.$$boundTransclude = boundTranscludeFn;
10596          // expose the slots on the `$transclude` function
10597          transcludeFn.isSlotFilled = function(slotName) {
10598            return !!boundTranscludeFn.$$slots[slotName];
10599          };
10600        }
10601
10602        if (controllerDirectives) {
10603          elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
10604        }
10605
10606        if (newIsolateScopeDirective) {
10607          // Initialize isolate scope bindings for new isolate scope directive.
10608          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
10609              templateDirective === newIsolateScopeDirective.$$originalDirective)));
10610          compile.$$addScopeClass($element, true);
10611          isolateScope.$$isolateBindings =
10612              newIsolateScopeDirective.$$isolateBindings;
10613          scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
10614                                        isolateScope.$$isolateBindings,
10615                                        newIsolateScopeDirective);
10616          if (scopeBindingInfo.removeWatches) {
10617            isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
10618          }
10619        }
10620
10621        // Initialize bindToController bindings
10622        for (var name in elementControllers) {
10623          var controllerDirective = controllerDirectives[name];
10624          var controller = elementControllers[name];
10625          var bindings = controllerDirective.$$bindings.bindToController;
10626
10627          controller.instance = controller();
10628          $element.data('$' + controllerDirective.name + 'Controller', controller.instance);
10629          controller.bindingInfo =
10630            initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
10631          }
10632
10633        // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
10634        forEach(controllerDirectives, function(controllerDirective, name) {
10635          var require = controllerDirective.require;
10636          if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
10637            extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
10638          }
10639        });
10640
10641        // Handle the init and destroy lifecycle hooks on all controllers that have them
10642        forEach(elementControllers, function(controller) {
10643          var controllerInstance = controller.instance;
10644          if (isFunction(controllerInstance.$onChanges)) {
10645            try {
10646              controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
10647            } catch (e) {
10648              $exceptionHandler(e);
10649            }
10650          }
10651          if (isFunction(controllerInstance.$onInit)) {
10652            try {
10653              controllerInstance.$onInit();
10654            } catch (e) {
10655              $exceptionHandler(e);
10656            }
10657          }
10658          if (isFunction(controllerInstance.$doCheck)) {
10659            controllerScope.$watch(function() { controllerInstance.$doCheck(); });
10660            controllerInstance.$doCheck();
10661          }
10662          if (isFunction(controllerInstance.$onDestroy)) {
10663            controllerScope.$on('$destroy', function callOnDestroyHook() {
10664              controllerInstance.$onDestroy();
10665            });
10666          }
10667        });
10668
10669        // PRELINKING
10670        for (i = 0, ii = preLinkFns.length; i < ii; i++) {
10671          linkFn = preLinkFns[i];
10672          invokeLinkFn(linkFn,
10673              linkFn.isolateScope ? isolateScope : scope,
10674              $element,
10675              attrs,
10676              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
10677              transcludeFn
10678          );
10679        }
10680
10681        // RECURSION
10682        // We only pass the isolate scope, if the isolate directive has a template,
10683        // otherwise the child elements do not belong to the isolate directive.
10684        var scopeToChild = scope;
10685        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
10686          scopeToChild = isolateScope;
10687        }
10688        if (childLinkFn) {
10689          childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
10690        }
10691
10692        // POSTLINKING
10693        for (i = postLinkFns.length - 1; i >= 0; i--) {
10694          linkFn = postLinkFns[i];
10695          invokeLinkFn(linkFn,
10696              linkFn.isolateScope ? isolateScope : scope,
10697              $element,
10698              attrs,
10699              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
10700              transcludeFn
10701          );
10702        }
10703
10704        // Trigger $postLink lifecycle hooks
10705        forEach(elementControllers, function(controller) {
10706          var controllerInstance = controller.instance;
10707          if (isFunction(controllerInstance.$postLink)) {
10708            controllerInstance.$postLink();
10709          }
10710        });
10711
10712        // This is the function that is injected as `$transclude`.
10713        // Note: all arguments are optional!
10714        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
10715          var transcludeControllers;
10716          // No scope passed in:
10717          if (!isScope(scope)) {
10718            slotName = futureParentElement;
10719            futureParentElement = cloneAttachFn;
10720            cloneAttachFn = scope;
10721            scope = undefined;
10722          }
10723
10724          if (hasElementTranscludeDirective) {
10725            transcludeControllers = elementControllers;
10726          }
10727          if (!futureParentElement) {
10728            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
10729          }
10730          if (slotName) {
10731            // slotTranscludeFn can be one of three things:
10732            //  * a transclude function - a filled slot
10733            //  * `null` - an optional slot that was not filled
10734            //  * `undefined` - a slot that was not declared (i.e. invalid)
10735            var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
10736            if (slotTranscludeFn) {
10737              return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
10738            } else if (isUndefined(slotTranscludeFn)) {
10739              throw $compileMinErr('noslot',
10740               'No parent directive that requires a transclusion with slot name "{0}". ' +
10741               'Element: {1}',
10742               slotName, startingTag($element));
10743            }
10744          } else {
10745            return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
10746          }
10747        }
10748      }
10749    }
10750
10751    function getControllers(directiveName, require, $element, elementControllers) {
10752      var value;
10753
10754      if (isString(require)) {
10755        var match = require.match(REQUIRE_PREFIX_REGEXP);
10756        var name = require.substring(match[0].length);
10757        var inheritType = match[1] || match[3];
10758        var optional = match[2] === '?';
10759
10760        //If only parents then start at the parent element
10761        if (inheritType === '^^') {
10762          $element = $element.parent();
10763        //Otherwise attempt getting the controller from elementControllers in case
10764        //the element is transcluded (and has no data) and to avoid .data if possible
10765        } else {
10766          value = elementControllers && elementControllers[name];
10767          value = value && value.instance;
10768        }
10769
10770        if (!value) {
10771          var dataName = '$' + name + 'Controller';
10772
10773          if (inheritType === '^^' && $element[0] && $element[0].nodeType === NODE_TYPE_DOCUMENT) {
10774            // inheritedData() uses the documentElement when it finds the document, so we would
10775            // require from the element itself.
10776            value = null;
10777          } else {
10778            value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
10779          }
10780        }
10781
10782        if (!value && !optional) {
10783          throw $compileMinErr('ctreq',
10784              'Controller \'{0}\', required by directive \'{1}\', can\'t be found!',
10785              name, directiveName);
10786        }
10787      } else if (isArray(require)) {
10788        value = [];
10789        for (var i = 0, ii = require.length; i < ii; i++) {
10790          value[i] = getControllers(directiveName, require[i], $element, elementControllers);
10791        }
10792      } else if (isObject(require)) {
10793        value = {};
10794        forEach(require, function(controller, property) {
10795          value[property] = getControllers(directiveName, controller, $element, elementControllers);
10796        });
10797      }
10798
10799      return value || null;
10800    }
10801
10802    function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
10803      var elementControllers = createMap();
10804      for (var controllerKey in controllerDirectives) {
10805        var directive = controllerDirectives[controllerKey];
10806        var locals = {
10807          $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
10808          $element: $element,
10809          $attrs: attrs,
10810          $transclude: transcludeFn
10811        };
10812
10813        var controller = directive.controller;
10814        if (controller === '@') {
10815          controller = attrs[directive.name];
10816        }
10817
10818        var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
10819
10820        // For directives with element transclusion the element is a comment.
10821        // In this case .data will not attach any data.
10822        // Instead, we save the controllers for the element in a local hash and attach to .data
10823        // later, once we have the actual element.
10824        elementControllers[directive.name] = controllerInstance;
10825        $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
10826      }
10827      return elementControllers;
10828    }
10829
10830    // Depending upon the context in which a directive finds itself it might need to have a new isolated
10831    // or child scope created. For instance:
10832    // * if the directive has been pulled into a template because another directive with a higher priority
10833    // asked for element transclusion
10834    // * if the directive itself asks for transclusion but it is at the root of a template and the original
10835    // element was replaced. See https://github.com/angular/angular.js/issues/12936
10836    function markDirectiveScope(directives, isolateScope, newScope) {
10837      for (var j = 0, jj = directives.length; j < jj; j++) {
10838        directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
10839      }
10840    }
10841
10842    /**
10843     * looks up the directive and decorates it with exception handling and proper parameters. We
10844     * call this the boundDirective.
10845     *
10846     * @param {string} name name of the directive to look up.
10847     * @param {string} location The directive must be found in specific format.
10848     *   String containing any of theses characters:
10849     *
10850     *   * `E`: element name
10851     *   * `A': attribute
10852     *   * `C`: class
10853     *   * `M`: comment
10854     * @returns {boolean} true if directive was added.
10855     */
10856    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
10857                          endAttrName) {
10858      if (name === ignoreDirective) return null;
10859      var match = null;
10860      if (hasDirectives.hasOwnProperty(name)) {
10861        for (var directive, directives = $injector.get(name + Suffix),
10862            i = 0, ii = directives.length; i < ii; i++) {
10863          directive = directives[i];
10864          if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
10865               directive.restrict.indexOf(location) !== -1) {
10866            if (startAttrName) {
10867              directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
10868            }
10869            if (!directive.$$bindings) {
10870              var bindings = directive.$$bindings =
10871                  parseDirectiveBindings(directive, directive.name);
10872              if (isObject(bindings.isolateScope)) {
10873                directive.$$isolateBindings = bindings.isolateScope;
10874              }
10875            }
10876            tDirectives.push(directive);
10877            match = directive;
10878          }
10879        }
10880      }
10881      return match;
10882    }
10883
10884
10885    /**
10886     * looks up the directive and returns true if it is a multi-element directive,
10887     * and therefore requires DOM nodes between -start and -end markers to be grouped
10888     * together.
10889     *
10890     * @param {string} name name of the directive to look up.
10891     * @returns true if directive was registered as multi-element.
10892     */
10893    function directiveIsMultiElement(name) {
10894      if (hasDirectives.hasOwnProperty(name)) {
10895        for (var directive, directives = $injector.get(name + Suffix),
10896            i = 0, ii = directives.length; i < ii; i++) {
10897          directive = directives[i];
10898          if (directive.multiElement) {
10899            return true;
10900          }
10901        }
10902      }
10903      return false;
10904    }
10905
10906    /**
10907     * When the element is replaced with HTML template then the new attributes
10908     * on the template need to be merged with the existing attributes in the DOM.
10909     * The desired effect is to have both of the attributes present.
10910     *
10911     * @param {object} dst destination attributes (original DOM)
10912     * @param {object} src source attributes (from the directive template)
10913     */
10914    function mergeTemplateAttributes(dst, src) {
10915      var srcAttr = src.$attr,
10916          dstAttr = dst.$attr;
10917
10918      // reapply the old attributes to the new element
10919      forEach(dst, function(value, key) {
10920        if (key.charAt(0) !== '$') {
10921          if (src[key] && src[key] !== value) {
10922            if (value.length) {
10923              value += (key === 'style' ? ';' : ' ') + src[key];
10924            } else {
10925              value = src[key];
10926            }
10927          }
10928          dst.$set(key, value, true, srcAttr[key]);
10929        }
10930      });
10931
10932      // copy the new attributes on the old attrs object
10933      forEach(src, function(value, key) {
10934        // Check if we already set this attribute in the loop above.
10935        // `dst` will never contain hasOwnProperty as DOM parser won't let it.
10936        // You will get an "InvalidCharacterError: DOM Exception 5" error if you
10937        // have an attribute like "has-own-property" or "data-has-own-property", etc.
10938        if (!dst.hasOwnProperty(key) && key.charAt(0) !== '$') {
10939          dst[key] = value;
10940
10941          if (key !== 'class' && key !== 'style') {
10942            dstAttr[key] = srcAttr[key];
10943          }
10944        }
10945      });
10946    }
10947
10948
10949    function compileTemplateUrl(directives, $compileNode, tAttrs,
10950        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
10951      var linkQueue = [],
10952          afterTemplateNodeLinkFn,
10953          afterTemplateChildLinkFn,
10954          beforeTemplateCompileNode = $compileNode[0],
10955          origAsyncDirective = directives.shift(),
10956          derivedSyncDirective = inherit(origAsyncDirective, {
10957            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
10958          }),
10959          templateUrl = (isFunction(origAsyncDirective.templateUrl))
10960              ? origAsyncDirective.templateUrl($compileNode, tAttrs)
10961              : origAsyncDirective.templateUrl,
10962          templateNamespace = origAsyncDirective.templateNamespace;
10963
10964      $compileNode.empty();
10965
10966      $templateRequest(templateUrl)
10967        .then(function(content) {
10968          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
10969
10970          content = denormalizeTemplate(content);
10971
10972          if (origAsyncDirective.replace) {
10973            if (jqLiteIsTextNode(content)) {
10974              $template = [];
10975            } else {
10976              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
10977            }
10978            compileNode = $template[0];
10979
10980            if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
10981              throw $compileMinErr('tplrt',
10982                  'Template for directive \'{0}\' must have exactly one root element. {1}',
10983                  origAsyncDirective.name, templateUrl);
10984            }
10985
10986            tempTemplateAttrs = {$attr: {}};
10987            replaceWith($rootElement, $compileNode, compileNode);
10988            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
10989
10990            if (isObject(origAsyncDirective.scope)) {
10991              // the original directive that caused the template to be loaded async required
10992              // an isolate scope
10993              markDirectiveScope(templateDirectives, true);
10994            }
10995            directives = templateDirectives.concat(directives);
10996            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
10997          } else {
10998            compileNode = beforeTemplateCompileNode;
10999            $compileNode.html(content);
11000          }
11001
11002          directives.unshift(derivedSyncDirective);
11003
11004          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
11005              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
11006              previousCompileContext);
11007          forEach($rootElement, function(node, i) {
11008            if (node === compileNode) {
11009              $rootElement[i] = $compileNode[0];
11010            }
11011          });
11012          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
11013
11014          while (linkQueue.length) {
11015            var scope = linkQueue.shift(),
11016                beforeTemplateLinkNode = linkQueue.shift(),
11017                linkRootElement = linkQueue.shift(),
11018                boundTranscludeFn = linkQueue.shift(),
11019                linkNode = $compileNode[0];
11020
11021            if (scope.$$destroyed) continue;
11022
11023            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
11024              var oldClasses = beforeTemplateLinkNode.className;
11025
11026              if (!(previousCompileContext.hasElementTranscludeDirective &&
11027                  origAsyncDirective.replace)) {
11028                // it was cloned therefore we have to clone as well.
11029                linkNode = jqLiteClone(compileNode);
11030              }
11031              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
11032
11033              // Copy in CSS classes from original node
11034              safeAddClass(jqLite(linkNode), oldClasses);
11035            }
11036            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
11037              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
11038            } else {
11039              childBoundTranscludeFn = boundTranscludeFn;
11040            }
11041            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
11042              childBoundTranscludeFn);
11043          }
11044          linkQueue = null;
11045        }).catch(function(error) {
11046          if (isError(error)) {
11047            $exceptionHandler(error);
11048          }
11049        });
11050
11051      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
11052        var childBoundTranscludeFn = boundTranscludeFn;
11053        if (scope.$$destroyed) return;
11054        if (linkQueue) {
11055          linkQueue.push(scope,
11056                         node,
11057                         rootElement,
11058                         childBoundTranscludeFn);
11059        } else {
11060          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
11061            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
11062          }
11063          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
11064        }
11065      };
11066    }
11067
11068
11069    /**
11070     * Sorting function for bound directives.
11071     */
11072    function byPriority(a, b) {
11073      var diff = b.priority - a.priority;
11074      if (diff !== 0) return diff;
11075      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
11076      return a.index - b.index;
11077    }
11078
11079    function assertNoDuplicate(what, previousDirective, directive, element) {
11080
11081      function wrapModuleNameIfDefined(moduleName) {
11082        return moduleName ?
11083          (' (module: ' + moduleName + ')') :
11084          '';
11085      }
11086
11087      if (previousDirective) {
11088        throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
11089            previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
11090            directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
11091      }
11092    }
11093
11094
11095    function addTextInterpolateDirective(directives, text) {
11096      var interpolateFn = $interpolate(text, true);
11097      if (interpolateFn) {
11098        directives.push({
11099          priority: 0,
11100          compile: function textInterpolateCompileFn(templateNode) {
11101            var templateNodeParent = templateNode.parent(),
11102                hasCompileParent = !!templateNodeParent.length;
11103
11104            // When transcluding a template that has bindings in the root
11105            // we don't have a parent and thus need to add the class during linking fn.
11106            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
11107
11108            return function textInterpolateLinkFn(scope, node) {
11109              var parent = node.parent();
11110              if (!hasCompileParent) compile.$$addBindingClass(parent);
11111              compile.$$addBindingInfo(parent, interpolateFn.expressions);
11112              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
11113                node[0].nodeValue = value;
11114              });
11115            };
11116          }
11117        });
11118      }
11119    }
11120
11121
11122    function wrapTemplate(type, template) {
11123      type = lowercase(type || 'html');
11124      switch (type) {
11125      case 'svg':
11126      case 'math':
11127        var wrapper = window.document.createElement('div');
11128        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
11129        return wrapper.childNodes[0].childNodes;
11130      default:
11131        return template;
11132      }
11133    }
11134
11135
11136    function getTrustedAttrContext(nodeName, attrNormalizedName) {
11137      if (attrNormalizedName === 'srcdoc') {
11138        return $sce.HTML;
11139      }
11140      // All nodes with src attributes require a RESOURCE_URL value, except for
11141      // img and various html5 media nodes, which require the MEDIA_URL context.
11142      if (attrNormalizedName === 'src' || attrNormalizedName === 'ngSrc') {
11143        if (['img', 'video', 'audio', 'source', 'track'].indexOf(nodeName) === -1) {
11144          return $sce.RESOURCE_URL;
11145        }
11146        return $sce.MEDIA_URL;
11147      } else if (attrNormalizedName === 'xlinkHref') {
11148        // Some xlink:href are okay, most aren't
11149        if (nodeName === 'image') return $sce.MEDIA_URL;
11150        if (nodeName === 'a') return $sce.URL;
11151        return $sce.RESOURCE_URL;
11152      } else if (
11153          // Formaction
11154          (nodeName === 'form' && attrNormalizedName === 'action') ||
11155          // If relative URLs can go where they are not expected to, then
11156          // all sorts of trust issues can arise.
11157          (nodeName === 'base' && attrNormalizedName === 'href') ||
11158          // links can be stylesheets or imports, which can run script in the current origin
11159          (nodeName === 'link' && attrNormalizedName === 'href')
11160      ) {
11161        return $sce.RESOURCE_URL;
11162      } else if (nodeName === 'a' && (attrNormalizedName === 'href' ||
11163                                 attrNormalizedName === 'ngHref')) {
11164        return $sce.URL;
11165      }
11166    }
11167
11168    function getTrustedPropContext(nodeName, propNormalizedName) {
11169      var prop = propNormalizedName.toLowerCase();
11170      return PROP_CONTEXTS[nodeName + '|' + prop] || PROP_CONTEXTS['*|' + prop];
11171    }
11172
11173    function sanitizeSrcsetPropertyValue(value) {
11174      return sanitizeSrcset($sce.valueOf(value), 'ng-prop-srcset');
11175    }
11176    function addPropertyDirective(node, directives, attrName, propName) {
11177      if (EVENT_HANDLER_ATTR_REGEXP.test(propName)) {
11178        throw $compileMinErr('nodomevents', 'Property bindings for HTML DOM event properties are disallowed');
11179      }
11180
11181      var nodeName = nodeName_(node);
11182      var trustedContext = getTrustedPropContext(nodeName, propName);
11183
11184      var sanitizer = identity;
11185      // Sanitize img[srcset] + source[srcset] values.
11186      if (propName === 'srcset' && (nodeName === 'img' || nodeName === 'source')) {
11187        sanitizer = sanitizeSrcsetPropertyValue;
11188      } else if (trustedContext) {
11189        sanitizer = $sce.getTrusted.bind($sce, trustedContext);
11190      }
11191
11192      directives.push({
11193        priority: 100,
11194        compile: function ngPropCompileFn(_, attr) {
11195          var ngPropGetter = $parse(attr[attrName]);
11196          var ngPropWatch = $parse(attr[attrName], function sceValueOf(val) {
11197            // Unwrap the value to compare the actual inner safe value, not the wrapper object.
11198            return $sce.valueOf(val);
11199          });
11200
11201          return {
11202            pre: function ngPropPreLinkFn(scope, $element) {
11203              function applyPropValue() {
11204                var propValue = ngPropGetter(scope);
11205                $element[0][propName] = sanitizer(propValue);
11206              }
11207
11208              applyPropValue();
11209              scope.$watch(ngPropWatch, applyPropValue);
11210            }
11211          };
11212        }
11213      });
11214    }
11215
11216    function addEventDirective(directives, attrName, eventName) {
11217      directives.push(
11218        createEventDirective($parse, $rootScope, $exceptionHandler, attrName, eventName, /*forceAsync=*/false)
11219      );
11220    }
11221
11222    function addAttrInterpolateDirective(node, directives, value, name, isNgAttr) {
11223      var nodeName = nodeName_(node);
11224      var trustedContext = getTrustedAttrContext(nodeName, name);
11225      var mustHaveExpression = !isNgAttr;
11226      var allOrNothing = ALL_OR_NOTHING_ATTRS[name] || isNgAttr;
11227
11228      var interpolateFn = $interpolate(value, mustHaveExpression, trustedContext, allOrNothing);
11229
11230      // no interpolation found -> ignore
11231      if (!interpolateFn) return;
11232
11233      if (name === 'multiple' && nodeName === 'select') {
11234        throw $compileMinErr('selmulti',
11235            'Binding to the \'multiple\' attribute is not supported. Element: {0}',
11236            startingTag(node));
11237      }
11238
11239      if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
11240        throw $compileMinErr('nodomevents', 'Interpolations for HTML DOM event attributes are disallowed');
11241      }
11242
11243      directives.push({
11244        priority: 100,
11245        compile: function() {
11246            return {
11247              pre: function attrInterpolatePreLinkFn(scope, element, attr) {
11248                var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
11249
11250                // If the attribute has changed since last $interpolate()ed
11251                var newValue = attr[name];
11252                if (newValue !== value) {
11253                  // we need to interpolate again since the attribute value has been updated
11254                  // (e.g. by another directive's compile function)
11255                  // ensure unset/empty values make interpolateFn falsy
11256                  interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
11257                  value = newValue;
11258                }
11259
11260                // if attribute was updated so that there is no interpolation going on we don't want to
11261                // register any observers
11262                if (!interpolateFn) return;
11263
11264                // initialize attr object so that it's ready in case we need the value for isolate
11265                // scope initialization, otherwise the value would not be available from isolate
11266                // directive's linking fn during linking phase
11267                attr[name] = interpolateFn(scope);
11268
11269                ($$observers[name] || ($$observers[name] = [])).$$inter = true;
11270                (attr.$$observers && attr.$$observers[name].$$scope || scope).
11271                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
11272                    //special case for class attribute addition + removal
11273                    //so that class changes can tap into the animation
11274                    //hooks provided by the $animate service. Be sure to
11275                    //skip animations when the first digest occurs (when
11276                    //both the new and the old values are the same) since
11277                    //the CSS classes are the non-interpolated values
11278                    if (name === 'class' && newValue !== oldValue) {
11279                      attr.$updateClass(newValue, oldValue);
11280                    } else {
11281                      attr.$set(name, newValue);
11282                    }
11283                  });
11284              }
11285            };
11286          }
11287      });
11288    }
11289
11290
11291    /**
11292     * This is a special jqLite.replaceWith, which can replace items which
11293     * have no parents, provided that the containing jqLite collection is provided.
11294     *
11295     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
11296     *                               in the root of the tree.
11297     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
11298     *                                  the shell, but replace its DOM node reference.
11299     * @param {Node} newNode The new DOM node.
11300     */
11301    function replaceWith($rootElement, elementsToRemove, newNode) {
11302      var firstElementToRemove = elementsToRemove[0],
11303          removeCount = elementsToRemove.length,
11304          parent = firstElementToRemove.parentNode,
11305          i, ii;
11306
11307      if ($rootElement) {
11308        for (i = 0, ii = $rootElement.length; i < ii; i++) {
11309          if ($rootElement[i] === firstElementToRemove) {
11310            $rootElement[i++] = newNode;
11311            for (var j = i, j2 = j + removeCount - 1,
11312                     jj = $rootElement.length;
11313                 j < jj; j++, j2++) {
11314              if (j2 < jj) {
11315                $rootElement[j] = $rootElement[j2];
11316              } else {
11317                delete $rootElement[j];
11318              }
11319            }
11320            $rootElement.length -= removeCount - 1;
11321
11322            // If the replaced element is also the jQuery .context then replace it
11323            // .context is a deprecated jQuery api, so we should set it only when jQuery set it
11324            // http://api.jquery.com/context/
11325            if ($rootElement.context === firstElementToRemove) {
11326              $rootElement.context = newNode;
11327            }
11328            break;
11329          }
11330        }
11331      }
11332
11333      if (parent) {
11334        parent.replaceChild(newNode, firstElementToRemove);
11335      }
11336
11337      // Append all the `elementsToRemove` to a fragment. This will...
11338      // - remove them from the DOM
11339      // - allow them to still be traversed with .nextSibling
11340      // - allow a single fragment.qSA to fetch all elements being removed
11341      var fragment = window.document.createDocumentFragment();
11342      for (i = 0; i < removeCount; i++) {
11343        fragment.appendChild(elementsToRemove[i]);
11344      }
11345
11346      if (jqLite.hasData(firstElementToRemove)) {
11347        // Copy over user data (that includes AngularJS's $scope etc.). Don't copy private
11348        // data here because there's no public interface in jQuery to do that and copying over
11349        // event listeners (which is the main use of private data) wouldn't work anyway.
11350        jqLite.data(newNode, jqLite.data(firstElementToRemove));
11351
11352        // Remove $destroy event listeners from `firstElementToRemove`
11353        jqLite(firstElementToRemove).off('$destroy');
11354      }
11355
11356      // Cleanup any data/listeners on the elements and children.
11357      // This includes invoking the $destroy event on any elements with listeners.
11358      jqLite.cleanData(fragment.querySelectorAll('*'));
11359
11360      // Update the jqLite collection to only contain the `newNode`
11361      for (i = 1; i < removeCount; i++) {
11362        delete elementsToRemove[i];
11363      }
11364      elementsToRemove[0] = newNode;
11365      elementsToRemove.length = 1;
11366    }
11367
11368
11369    function cloneAndAnnotateFn(fn, annotation) {
11370      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
11371    }
11372
11373
11374    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
11375      try {
11376        linkFn(scope, $element, attrs, controllers, transcludeFn);
11377      } catch (e) {
11378        $exceptionHandler(e, startingTag($element));
11379      }
11380    }
11381
11382    function strictBindingsCheck(attrName, directiveName) {
11383      if (strictComponentBindingsEnabled) {
11384        throw $compileMinErr('missingattr',
11385          'Attribute \'{0}\' of \'{1}\' is non-optional and must be set!',
11386          attrName, directiveName);
11387      }
11388    }
11389
11390    // Set up $watches for isolate scope and controller bindings.
11391    function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
11392      var removeWatchCollection = [];
11393      var initialChanges = {};
11394      var changes;
11395
11396      forEach(bindings, function initializeBinding(definition, scopeName) {
11397        var attrName = definition.attrName,
11398        optional = definition.optional,
11399        mode = definition.mode, // @, =, <, or &
11400        lastValue,
11401        parentGet, parentSet, compare, removeWatch;
11402
11403        switch (mode) {
11404
11405          case '@':
11406            if (!optional && !hasOwnProperty.call(attrs, attrName)) {
11407              strictBindingsCheck(attrName, directive.name);
11408              destination[scopeName] = attrs[attrName] = undefined;
11409
11410            }
11411            removeWatch = attrs.$observe(attrName, function(value) {
11412              if (isString(value) || isBoolean(value)) {
11413                var oldValue = destination[scopeName];
11414                recordChanges(scopeName, value, oldValue);
11415                destination[scopeName] = value;
11416              }
11417            });
11418            attrs.$$observers[attrName].$$scope = scope;
11419            lastValue = attrs[attrName];
11420            if (isString(lastValue)) {
11421              // If the attribute has been provided then we trigger an interpolation to ensure
11422              // the value is there for use in the link fn
11423              destination[scopeName] = $interpolate(lastValue)(scope);
11424            } else if (isBoolean(lastValue)) {
11425              // If the attributes is one of the BOOLEAN_ATTR then AngularJS will have converted
11426              // the value to boolean rather than a string, so we special case this situation
11427              destination[scopeName] = lastValue;
11428            }
11429            initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
11430            removeWatchCollection.push(removeWatch);
11431            break;
11432
11433          case '=':
11434            if (!hasOwnProperty.call(attrs, attrName)) {
11435              if (optional) break;
11436              strictBindingsCheck(attrName, directive.name);
11437              attrs[attrName] = undefined;
11438            }
11439            if (optional && !attrs[attrName]) break;
11440
11441            parentGet = $parse(attrs[attrName]);
11442            if (parentGet.literal) {
11443              compare = equals;
11444            } else {
11445              compare = simpleCompare;
11446            }
11447            parentSet = parentGet.assign || function() {
11448              // reset the change, or we will throw this exception on every $digest
11449              lastValue = destination[scopeName] = parentGet(scope);
11450              throw $compileMinErr('nonassign',
11451                  'Expression \'{0}\' in attribute \'{1}\' used with directive \'{2}\' is non-assignable!',
11452                  attrs[attrName], attrName, directive.name);
11453            };
11454            lastValue = destination[scopeName] = parentGet(scope);
11455            var parentValueWatch = function parentValueWatch(parentValue) {
11456              if (!compare(parentValue, destination[scopeName])) {
11457                // we are out of sync and need to copy
11458                if (!compare(parentValue, lastValue)) {
11459                  // parent changed and it has precedence
11460                  destination[scopeName] = parentValue;
11461                } else {
11462                  // if the parent can be assigned then do so
11463                  parentSet(scope, parentValue = destination[scopeName]);
11464                }
11465              }
11466              lastValue = parentValue;
11467              return lastValue;
11468            };
11469            parentValueWatch.$stateful = true;
11470            if (definition.collection) {
11471              removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
11472            } else {
11473              removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
11474            }
11475            removeWatchCollection.push(removeWatch);
11476            break;
11477
11478          case '<':
11479            if (!hasOwnProperty.call(attrs, attrName)) {
11480              if (optional) break;
11481              strictBindingsCheck(attrName, directive.name);
11482              attrs[attrName] = undefined;
11483            }
11484            if (optional && !attrs[attrName]) break;
11485
11486            parentGet = $parse(attrs[attrName]);
11487            var isLiteral = parentGet.literal;
11488
11489            var initialValue = destination[scopeName] = parentGet(scope);
11490            initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
11491
11492            removeWatch = scope[definition.collection ? '$watchCollection' : '$watch'](parentGet, function parentValueWatchAction(newValue, oldValue) {
11493              if (oldValue === newValue) {
11494                if (oldValue === initialValue || (isLiteral && equals(oldValue, initialValue))) {
11495                  return;
11496                }
11497                oldValue = initialValue;
11498              }
11499              recordChanges(scopeName, newValue, oldValue);
11500              destination[scopeName] = newValue;
11501            });
11502
11503            removeWatchCollection.push(removeWatch);
11504            break;
11505
11506          case '&':
11507            if (!optional && !hasOwnProperty.call(attrs, attrName)) {
11508              strictBindingsCheck(attrName, directive.name);
11509            }
11510            // Don't assign Object.prototype method to scope
11511            parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
11512
11513            // Don't assign noop to destination if expression is not valid
11514            if (parentGet === noop && optional) break;
11515
11516            destination[scopeName] = function(locals) {
11517              return parentGet(scope, locals);
11518            };
11519            break;
11520        }
11521      });
11522
11523      function recordChanges(key, currentValue, previousValue) {
11524        if (isFunction(destination.$onChanges) && !simpleCompare(currentValue, previousValue)) {
11525          // If we have not already scheduled the top level onChangesQueue handler then do so now
11526          if (!onChangesQueue) {
11527            scope.$$postDigest(flushOnChangesQueue);
11528            onChangesQueue = [];
11529          }
11530          // If we have not already queued a trigger of onChanges for this controller then do so now
11531          if (!changes) {
11532            changes = {};
11533            onChangesQueue.push(triggerOnChangesHook);
11534          }
11535          // If the has been a change on this property already then we need to reuse the previous value
11536          if (changes[key]) {
11537            previousValue = changes[key].previousValue;
11538          }
11539          // Store this change
11540          changes[key] = new SimpleChange(previousValue, currentValue);
11541        }
11542      }
11543
11544      function triggerOnChangesHook() {
11545        destination.$onChanges(changes);
11546        // Now clear the changes so that we schedule onChanges when more changes arrive
11547        changes = undefined;
11548      }
11549
11550      return {
11551        initialChanges: initialChanges,
11552        removeWatches: removeWatchCollection.length && function removeWatches() {
11553          for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
11554            removeWatchCollection[i]();
11555          }
11556        }
11557      };
11558    }
11559  }];
11560}
11561
11562function SimpleChange(previous, current) {
11563  this.previousValue = previous;
11564  this.currentValue = current;
11565}
11566SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
11567
11568
11569var PREFIX_REGEXP = /^((?:x|data)[:\-_])/i;
11570var SPECIAL_CHARS_REGEXP = /[:\-_]+(.)/g;
11571
11572/**
11573 * Converts all accepted directives format into proper directive name.
11574 * @param name Name to normalize
11575 */
11576function directiveNormalize(name) {
11577  return name
11578    .replace(PREFIX_REGEXP, '')
11579    .replace(SPECIAL_CHARS_REGEXP, function(_, letter, offset) {
11580      return offset ? letter.toUpperCase() : letter;
11581    });
11582}
11583
11584/**
11585 * @ngdoc type
11586 * @name $compile.directive.Attributes
11587 *
11588 * @description
11589 * A shared object between directive compile / linking functions which contains normalized DOM
11590 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
11591 * needed since all of these are treated as equivalent in AngularJS:
11592 *
11593 * ```
11594 *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
11595 * ```
11596 */
11597
11598/**
11599 * @ngdoc property
11600 * @name $compile.directive.Attributes#$attr
11601 *
11602 * @description
11603 * A map of DOM element attribute names to the normalized name. This is
11604 * needed to do reverse lookup from normalized name back to actual name.
11605 */
11606
11607
11608/**
11609 * @ngdoc method
11610 * @name $compile.directive.Attributes#$set
11611 * @kind function
11612 *
11613 * @description
11614 * Set DOM element attribute value.
11615 *
11616 *
11617 * @param {string} name Normalized element attribute name of the property to modify. The name is
11618 *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
11619 *          property to the original name.
11620 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
11621 */
11622
11623
11624
11625/**
11626 * Closure compiler type information
11627 */
11628
11629function nodesetLinkingFn(
11630  /* angular.Scope */ scope,
11631  /* NodeList */ nodeList,
11632  /* Element */ rootElement,
11633  /* function(Function) */ boundTranscludeFn
11634) {}
11635
11636function directiveLinkingFn(
11637  /* nodesetLinkingFn */ nodesetLinkingFn,
11638  /* angular.Scope */ scope,
11639  /* Node */ node,
11640  /* Element */ rootElement,
11641  /* function(Function) */ boundTranscludeFn
11642) {}
11643
11644function tokenDifference(str1, str2) {
11645  var values = '',
11646      tokens1 = str1.split(/\s+/),
11647      tokens2 = str2.split(/\s+/);
11648
11649  outer:
11650  for (var i = 0; i < tokens1.length; i++) {
11651    var token = tokens1[i];
11652    for (var j = 0; j < tokens2.length; j++) {
11653      if (token === tokens2[j]) continue outer;
11654    }
11655    values += (values.length > 0 ? ' ' : '') + token;
11656  }
11657  return values;
11658}
11659
11660function removeComments(jqNodes) {
11661  jqNodes = jqLite(jqNodes);
11662  var i = jqNodes.length;
11663
11664  if (i <= 1) {
11665    return jqNodes;
11666  }
11667
11668  while (i--) {
11669    var node = jqNodes[i];
11670    if (node.nodeType === NODE_TYPE_COMMENT ||
11671       (node.nodeType === NODE_TYPE_TEXT && node.nodeValue.trim() === '')) {
11672         splice.call(jqNodes, i, 1);
11673    }
11674  }
11675  return jqNodes;
11676}
11677
11678var $controllerMinErr = minErr('$controller');
11679
11680
11681var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
11682function identifierForController(controller, ident) {
11683  if (ident && isString(ident)) return ident;
11684  if (isString(controller)) {
11685    var match = CNTRL_REG.exec(controller);
11686    if (match) return match[3];
11687  }
11688}
11689
11690
11691/**
11692 * @ngdoc provider
11693 * @name $controllerProvider
11694 * @this
11695 *
11696 * @description
11697 * The {@link ng.$controller $controller service} is used by AngularJS to create new
11698 * controllers.
11699 *
11700 * This provider allows controller registration via the
11701 * {@link ng.$controllerProvider#register register} method.
11702 */
11703function $ControllerProvider() {
11704  var controllers = {};
11705
11706  /**
11707   * @ngdoc method
11708   * @name $controllerProvider#has
11709   * @param {string} name Controller name to check.
11710   */
11711  this.has = function(name) {
11712    return controllers.hasOwnProperty(name);
11713  };
11714
11715  /**
11716   * @ngdoc method
11717   * @name $controllerProvider#register
11718   * @param {string|Object} name Controller name, or an object map of controllers where the keys are
11719   *    the names and the values are the constructors.
11720   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
11721   *    annotations in the array notation).
11722   */
11723  this.register = function(name, constructor) {
11724    assertNotHasOwnProperty(name, 'controller');
11725    if (isObject(name)) {
11726      extend(controllers, name);
11727    } else {
11728      controllers[name] = constructor;
11729    }
11730  };
11731
11732  this.$get = ['$injector', function($injector) {
11733
11734    /**
11735     * @ngdoc service
11736     * @name $controller
11737     * @requires $injector
11738     *
11739     * @param {Function|string} constructor If called with a function then it's considered to be the
11740     *    controller constructor function. Otherwise it's considered to be a string which is used
11741     *    to retrieve the controller constructor using the following steps:
11742     *
11743     *    * check if a controller with given name is registered via `$controllerProvider`
11744     *    * check if evaluating the string on the current scope returns a constructor
11745     *
11746     *    The string can use the `controller as property` syntax, where the controller instance is published
11747     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
11748     *    to work correctly.
11749     *
11750     * @param {Object} locals Injection locals for Controller.
11751     * @return {Object} Instance of given controller.
11752     *
11753     * @description
11754     * `$controller` service is responsible for instantiating controllers.
11755     *
11756     * It's just a simple call to {@link auto.$injector $injector}, but extracted into
11757     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
11758     */
11759    return function $controller(expression, locals, later, ident) {
11760      // PRIVATE API:
11761      //   param `later` --- indicates that the controller's constructor is invoked at a later time.
11762      //                     If true, $controller will allocate the object with the correct
11763      //                     prototype chain, but will not invoke the controller until a returned
11764      //                     callback is invoked.
11765      //   param `ident` --- An optional label which overrides the label parsed from the controller
11766      //                     expression, if any.
11767      var instance, match, constructor, identifier;
11768      later = later === true;
11769      if (ident && isString(ident)) {
11770        identifier = ident;
11771      }
11772
11773      if (isString(expression)) {
11774        match = expression.match(CNTRL_REG);
11775        if (!match) {
11776          throw $controllerMinErr('ctrlfmt',
11777            'Badly formed controller string \'{0}\'. ' +
11778            'Must match `__name__ as __id__` or `__name__`.', expression);
11779        }
11780        constructor = match[1];
11781        identifier = identifier || match[3];
11782        expression = controllers.hasOwnProperty(constructor)
11783            ? controllers[constructor]
11784            : getter(locals.$scope, constructor, true);
11785
11786        if (!expression) {
11787          throw $controllerMinErr('ctrlreg',
11788            'The controller with the name \'{0}\' is not registered.', constructor);
11789        }
11790
11791        assertArgFn(expression, constructor, true);
11792      }
11793
11794      if (later) {
11795        // Instantiate controller later:
11796        // This machinery is used to create an instance of the object before calling the
11797        // controller's constructor itself.
11798        //
11799        // This allows properties to be added to the controller before the constructor is
11800        // invoked. Primarily, this is used for isolate scope bindings in $compile.
11801        //
11802        // This feature is not intended for use by applications, and is thus not documented
11803        // publicly.
11804        // Object creation: http://jsperf.com/create-constructor/2
11805        var controllerPrototype = (isArray(expression) ?
11806          expression[expression.length - 1] : expression).prototype;
11807        instance = Object.create(controllerPrototype || null);
11808
11809        if (identifier) {
11810          addIdentifier(locals, identifier, instance, constructor || expression.name);
11811        }
11812
11813        return extend(function $controllerInit() {
11814          var result = $injector.invoke(expression, instance, locals, constructor);
11815          if (result !== instance && (isObject(result) || isFunction(result))) {
11816            instance = result;
11817            if (identifier) {
11818              // If result changed, re-assign controllerAs value to scope.
11819              addIdentifier(locals, identifier, instance, constructor || expression.name);
11820            }
11821          }
11822          return instance;
11823        }, {
11824          instance: instance,
11825          identifier: identifier
11826        });
11827      }
11828
11829      instance = $injector.instantiate(expression, locals, constructor);
11830
11831      if (identifier) {
11832        addIdentifier(locals, identifier, instance, constructor || expression.name);
11833      }
11834
11835      return instance;
11836    };
11837
11838    function addIdentifier(locals, identifier, instance, name) {
11839      if (!(locals && isObject(locals.$scope))) {
11840        throw minErr('$controller')('noscp',
11841          'Cannot export controller \'{0}\' as \'{1}\'! No $scope object provided via `locals`.',
11842          name, identifier);
11843      }
11844
11845      locals.$scope[identifier] = instance;
11846    }
11847  }];
11848}
11849
11850/**
11851 * @ngdoc service
11852 * @name $document
11853 * @requires $window
11854 * @this
11855 *
11856 * @description
11857 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
11858 *
11859 * @example
11860   <example module="documentExample" name="document">
11861     <file name="index.html">
11862       <div ng-controller="ExampleController">
11863         <p>$document title: <b ng-bind="title"></b></p>
11864         <p>window.document title: <b ng-bind="windowTitle"></b></p>
11865       </div>
11866     </file>
11867     <file name="script.js">
11868       angular.module('documentExample', [])
11869         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
11870           $scope.title = $document[0].title;
11871           $scope.windowTitle = angular.element(window.document)[0].title;
11872         }]);
11873     </file>
11874   </example>
11875 */
11876function $DocumentProvider() {
11877  this.$get = ['$window', function(window) {
11878    return jqLite(window.document);
11879  }];
11880}
11881
11882
11883/**
11884 * @private
11885 * @this
11886 * Listens for document visibility change and makes the current status accessible.
11887 */
11888function $$IsDocumentHiddenProvider() {
11889  this.$get = ['$document', '$rootScope', function($document, $rootScope) {
11890    var doc = $document[0];
11891    var hidden = doc && doc.hidden;
11892
11893    $document.on('visibilitychange', changeListener);
11894
11895    $rootScope.$on('$destroy', function() {
11896      $document.off('visibilitychange', changeListener);
11897    });
11898
11899    function changeListener() {
11900      hidden = doc.hidden;
11901    }
11902
11903    return function() {
11904      return hidden;
11905    };
11906  }];
11907}
11908
11909/**
11910 * @ngdoc service
11911 * @name $exceptionHandler
11912 * @requires ng.$log
11913 * @this
11914 *
11915 * @description
11916 * Any uncaught exception in AngularJS expressions is delegated to this service.
11917 * The default implementation simply delegates to `$log.error` which logs it into
11918 * the browser console.
11919 *
11920 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
11921 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
11922 *
11923 * ## Example:
11924 *
11925 * The example below will overwrite the default `$exceptionHandler` in order to (a) log uncaught
11926 * errors to the backend for later inspection by the developers and (b) to use `$log.warn()` instead
11927 * of `$log.error()`.
11928 *
11929 * ```js
11930 *   angular.
11931 *     module('exceptionOverwrite', []).
11932 *     factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) {
11933 *       return function myExceptionHandler(exception, cause) {
11934 *         logErrorsToBackend(exception, cause);
11935 *         $log.warn(exception, cause);
11936 *       };
11937 *     }]);
11938 * ```
11939 *
11940 * <hr />
11941 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
11942 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
11943 * (unless executed during a digest).
11944 *
11945 * If you wish, you can manually delegate exceptions, e.g.
11946 * `try { ... } catch(e) { $exceptionHandler(e); }`
11947 *
11948 * @param {Error} exception Exception associated with the error.
11949 * @param {string=} cause Optional information about the context in which
11950 *       the error was thrown.
11951 *
11952 */
11953function $ExceptionHandlerProvider() {
11954  this.$get = ['$log', function($log) {
11955    return function(exception, cause) {
11956      $log.error.apply($log, arguments);
11957    };
11958  }];
11959}
11960
11961var $$ForceReflowProvider = /** @this */ function() {
11962  this.$get = ['$document', function($document) {
11963    return function(domNode) {
11964      //the line below will force the browser to perform a repaint so
11965      //that all the animated elements within the animation frame will
11966      //be properly updated and drawn on screen. This is required to
11967      //ensure that the preparation animation is properly flushed so that
11968      //the active state picks up from there. DO NOT REMOVE THIS LINE.
11969      //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
11970      //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
11971      //WILL TAKE YEARS AWAY FROM YOUR LIFE.
11972      if (domNode) {
11973        if (!domNode.nodeType && domNode instanceof jqLite) {
11974          domNode = domNode[0];
11975        }
11976      } else {
11977        domNode = $document[0].body;
11978      }
11979      return domNode.offsetWidth + 1;
11980    };
11981  }];
11982};
11983
11984var APPLICATION_JSON = 'application/json';
11985var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
11986var JSON_START = /^\[|^\{(?!\{)/;
11987var JSON_ENDS = {
11988  '[': /]$/,
11989  '{': /}$/
11990};
11991var JSON_PROTECTION_PREFIX = /^\)]\}',?\n/;
11992var $httpMinErr = minErr('$http');
11993
11994function serializeValue(v) {
11995  if (isObject(v)) {
11996    return isDate(v) ? v.toISOString() : toJson(v);
11997  }
11998  return v;
11999}
12000
12001
12002/** @this */
12003function $HttpParamSerializerProvider() {
12004  /**
12005   * @ngdoc service
12006   * @name $httpParamSerializer
12007   * @description
12008   *
12009   * Default {@link $http `$http`} params serializer that converts objects to strings
12010   * according to the following rules:
12011   *
12012   * * `{'foo': 'bar'}` results in `foo=bar`
12013   * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
12014   * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
12015   * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D` (stringified and encoded representation of an object)
12016   *
12017   * Note that serializer will sort the request parameters alphabetically.
12018   */
12019
12020  this.$get = function() {
12021    return function ngParamSerializer(params) {
12022      if (!params) return '';
12023      var parts = [];
12024      forEachSorted(params, function(value, key) {
12025        if (value === null || isUndefined(value) || isFunction(value)) return;
12026        if (isArray(value)) {
12027          forEach(value, function(v) {
12028            parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
12029          });
12030        } else {
12031          parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
12032        }
12033      });
12034
12035      return parts.join('&');
12036    };
12037  };
12038}
12039
12040/** @this */
12041function $HttpParamSerializerJQLikeProvider() {
12042  /**
12043   * @ngdoc service
12044   * @name $httpParamSerializerJQLike
12045   *
12046   * @description
12047   *
12048   * Alternative {@link $http `$http`} params serializer that follows
12049   * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
12050   * The serializer will also sort the params alphabetically.
12051   *
12052   * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
12053   *
12054   * ```js
12055   * $http({
12056   *   url: myUrl,
12057   *   method: 'GET',
12058   *   params: myParams,
12059   *   paramSerializer: '$httpParamSerializerJQLike'
12060   * });
12061   * ```
12062   *
12063   * It is also possible to set it as the default `paramSerializer` in the
12064   * {@link $httpProvider#defaults `$httpProvider`}.
12065   *
12066   * Additionally, you can inject the serializer and use it explicitly, for example to serialize
12067   * form data for submission:
12068   *
12069   * ```js
12070   * .controller(function($http, $httpParamSerializerJQLike) {
12071   *   //...
12072   *
12073   *   $http({
12074   *     url: myUrl,
12075   *     method: 'POST',
12076   *     data: $httpParamSerializerJQLike(myData),
12077   *     headers: {
12078   *       'Content-Type': 'application/x-www-form-urlencoded'
12079   *     }
12080   *   });
12081   *
12082   * });
12083   * ```
12084   *
12085   */
12086  this.$get = function() {
12087    return function jQueryLikeParamSerializer(params) {
12088      if (!params) return '';
12089      var parts = [];
12090      serialize(params, '', true);
12091      return parts.join('&');
12092
12093      function serialize(toSerialize, prefix, topLevel) {
12094        if (isArray(toSerialize)) {
12095          forEach(toSerialize, function(value, index) {
12096            serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
12097          });
12098        } else if (isObject(toSerialize) && !isDate(toSerialize)) {
12099          forEachSorted(toSerialize, function(value, key) {
12100            serialize(value, prefix +
12101                (topLevel ? '' : '[') +
12102                key +
12103                (topLevel ? '' : ']'));
12104          });
12105        } else {
12106          if (isFunction(toSerialize)) {
12107            toSerialize = toSerialize();
12108          }
12109          parts.push(encodeUriQuery(prefix) + '=' +
12110              (toSerialize == null ? '' : encodeUriQuery(serializeValue(toSerialize))));
12111        }
12112      }
12113    };
12114  };
12115}
12116
12117function defaultHttpResponseTransform(data, headers) {
12118  if (isString(data)) {
12119    // Strip json vulnerability protection prefix and trim whitespace
12120    var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
12121
12122    if (tempData) {
12123      var contentType = headers('Content-Type');
12124      var hasJsonContentType = contentType && (contentType.indexOf(APPLICATION_JSON) === 0);
12125
12126      if (hasJsonContentType || isJsonLike(tempData)) {
12127        try {
12128          data = fromJson(tempData);
12129        } catch (e) {
12130          if (!hasJsonContentType) {
12131            return data;
12132          }
12133          throw $httpMinErr('baddata', 'Data must be a valid JSON object. Received: "{0}". ' +
12134          'Parse error: "{1}"', data, e);
12135        }
12136      }
12137    }
12138  }
12139
12140  return data;
12141}
12142
12143function isJsonLike(str) {
12144    var jsonStart = str.match(JSON_START);
12145    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
12146}
12147
12148/**
12149 * Parse headers into key value object
12150 *
12151 * @param {string} headers Raw headers as a string
12152 * @returns {Object} Parsed headers as key value object
12153 */
12154function parseHeaders(headers) {
12155  var parsed = createMap(), i;
12156
12157  function fillInParsed(key, val) {
12158    if (key) {
12159      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
12160    }
12161  }
12162
12163  if (isString(headers)) {
12164    forEach(headers.split('\n'), function(line) {
12165      i = line.indexOf(':');
12166      fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
12167    });
12168  } else if (isObject(headers)) {
12169    forEach(headers, function(headerVal, headerKey) {
12170      fillInParsed(lowercase(headerKey), trim(headerVal));
12171    });
12172  }
12173
12174  return parsed;
12175}
12176
12177
12178/**
12179 * Returns a function that provides access to parsed headers.
12180 *
12181 * Headers are lazy parsed when first requested.
12182 * @see parseHeaders
12183 *
12184 * @param {(string|Object)} headers Headers to provide access to.
12185 * @returns {function(string=)} Returns a getter function which if called with:
12186 *
12187 *   - if called with an argument returns a single header value or null
12188 *   - if called with no arguments returns an object containing all headers.
12189 */
12190function headersGetter(headers) {
12191  var headersObj;
12192
12193  return function(name) {
12194    if (!headersObj) headersObj =  parseHeaders(headers);
12195
12196    if (name) {
12197      var value = headersObj[lowercase(name)];
12198      if (value === undefined) {
12199        value = null;
12200      }
12201      return value;
12202    }
12203
12204    return headersObj;
12205  };
12206}
12207
12208
12209/**
12210 * Chain all given functions
12211 *
12212 * This function is used for both request and response transforming
12213 *
12214 * @param {*} data Data to transform.
12215 * @param {function(string=)} headers HTTP headers getter fn.
12216 * @param {number} status HTTP status code of the response.
12217 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
12218 * @returns {*} Transformed data.
12219 */
12220function transformData(data, headers, status, fns) {
12221  if (isFunction(fns)) {
12222    return fns(data, headers, status);
12223  }
12224
12225  forEach(fns, function(fn) {
12226    data = fn(data, headers, status);
12227  });
12228
12229  return data;
12230}
12231
12232
12233function isSuccess(status) {
12234  return 200 <= status && status < 300;
12235}
12236
12237
12238/**
12239 * @ngdoc provider
12240 * @name $httpProvider
12241 * @this
12242 *
12243 * @description
12244 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
12245 */
12246function $HttpProvider() {
12247  /**
12248   * @ngdoc property
12249   * @name $httpProvider#defaults
12250   * @description
12251   *
12252   * Object containing default values for all {@link ng.$http $http} requests.
12253   *
12254   * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
12255   * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
12256   * by default. See {@link $http#caching $http Caching} for more information.
12257   *
12258   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
12259   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
12260   * setting default headers.
12261   *     - **`defaults.headers.common`**
12262   *     - **`defaults.headers.post`**
12263   *     - **`defaults.headers.put`**
12264   *     - **`defaults.headers.patch`**
12265   *
12266   * - **`defaults.jsonpCallbackParam`** - `{string}` - the name of the query parameter that passes the name of the
12267   * callback in a JSONP request. The value of this parameter will be replaced with the expression generated by the
12268   * {@link $jsonpCallbacks} service. Defaults to `'callback'`.
12269   *
12270   * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
12271   *  used to the prepare string representation of request parameters (specified as an object).
12272   *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
12273   *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
12274   *
12275   * - **`defaults.transformRequest`** -
12276   * `{Array<function(data, headersGetter)>|function(data, headersGetter)}` -
12277   * An array of functions (or a single function) which are applied to the request data.
12278   * By default, this is an array with one request transformation function:
12279   *
12280   *   - If the `data` property of the request configuration object contains an object, serialize it
12281   *     into JSON format.
12282   *
12283   * - **`defaults.transformResponse`** -
12284   * `{Array<function(data, headersGetter, status)>|function(data, headersGetter, status)}` -
12285   * An array of functions (or a single function) which are applied to the response data. By default,
12286   * this is an array which applies one response transformation function that does two things:
12287   *
12288   *  - If XSRF prefix is detected, strip it
12289   *    (see {@link ng.$http#security-considerations Security Considerations in the $http docs}).
12290   *  - If the `Content-Type` is `application/json` or the response looks like JSON,
12291   *    deserialize it using a JSON parser.
12292   *
12293   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
12294   * Defaults value is `'XSRF-TOKEN'`.
12295   *
12296   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
12297   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
12298   *
12299   */
12300  var defaults = this.defaults = {
12301    // transform incoming response data
12302    transformResponse: [defaultHttpResponseTransform],
12303
12304    // transform outgoing request data
12305    transformRequest: [function(d) {
12306      return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
12307    }],
12308
12309    // default headers
12310    headers: {
12311      common: {
12312        'Accept': 'application/json, text/plain, */*'
12313      },
12314      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
12315      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
12316      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
12317    },
12318
12319    xsrfCookieName: 'XSRF-TOKEN',
12320    xsrfHeaderName: 'X-XSRF-TOKEN',
12321
12322    paramSerializer: '$httpParamSerializer',
12323
12324    jsonpCallbackParam: 'callback'
12325  };
12326
12327  var useApplyAsync = false;
12328  /**
12329   * @ngdoc method
12330   * @name $httpProvider#useApplyAsync
12331   * @description
12332   *
12333   * Configure $http service to combine processing of multiple http responses received at around
12334   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
12335   * significant performance improvement for bigger applications that make many HTTP requests
12336   * concurrently (common during application bootstrap).
12337   *
12338   * Defaults to false. If no value is specified, returns the current configured value.
12339   *
12340   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
12341   *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
12342   *    to load and share the same digest cycle.
12343   *
12344   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
12345   *    otherwise, returns the current configured value.
12346   */
12347  this.useApplyAsync = function(value) {
12348    if (isDefined(value)) {
12349      useApplyAsync = !!value;
12350      return this;
12351    }
12352    return useApplyAsync;
12353  };
12354
12355  /**
12356   * @ngdoc property
12357   * @name $httpProvider#interceptors
12358   * @description
12359   *
12360   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
12361   * pre-processing of request or postprocessing of responses.
12362   *
12363   * These service factories are ordered by request, i.e. they are applied in the same order as the
12364   * array, on request, but reverse order, on response.
12365   *
12366   * {@link ng.$http#interceptors Interceptors detailed info}
12367   */
12368  var interceptorFactories = this.interceptors = [];
12369
12370  /**
12371   * @ngdoc property
12372   * @name $httpProvider#xsrfTrustedOrigins
12373   * @description
12374   *
12375   * Array containing URLs whose origins are trusted to receive the XSRF token. See the
12376   * {@link ng.$http#security-considerations Security Considerations} sections for more details on
12377   * XSRF.
12378   *
12379   * **Note:** An "origin" consists of the [URI scheme](https://en.wikipedia.org/wiki/URI_scheme),
12380   * the [hostname](https://en.wikipedia.org/wiki/Hostname) and the
12381   * [port number](https://en.wikipedia.org/wiki/Port_(computer_networking). For `http:` and
12382   * `https:`, the port number can be omitted if using th default ports (80 and 443 respectively).
12383   * Examples: `http://example.com`, `https://api.example.com:9876`
12384   *
12385   * <div class="alert alert-warning">
12386   *   It is not possible to trust specific URLs/paths. The `path`, `query` and `fragment` parts
12387   *   of a URL will be ignored. For example, `https://foo.com/path/bar?query=baz#fragment` will be
12388   *   treated as `https://foo.com`, meaning that **all** requests to URLs starting with
12389   *   `https://foo.com/` will include the XSRF token.
12390   * </div>
12391   *
12392   * @example
12393   *
12394   * ```js
12395   * // App served from `https://example.com/`.
12396   * angular.
12397   *   module('xsrfTrustedOriginsExample', []).
12398   *   config(['$httpProvider', function($httpProvider) {
12399   *     $httpProvider.xsrfTrustedOrigins.push('https://api.example.com');
12400   *   }]).
12401   *   run(['$http', function($http) {
12402   *     // The XSRF token will be sent.
12403   *     $http.get('https://api.example.com/preferences').then(...);
12404   *
12405   *     // The XSRF token will NOT be sent.
12406   *     $http.get('https://stats.example.com/activity').then(...);
12407   *   }]);
12408   * ```
12409   */
12410  var xsrfTrustedOrigins = this.xsrfTrustedOrigins = [];
12411
12412  /**
12413   * @ngdoc property
12414   * @name $httpProvider#xsrfWhitelistedOrigins
12415   * @description
12416   *
12417   * @deprecated
12418   * sinceVersion="1.8.1"
12419   *
12420   * This property is deprecated. Use {@link $httpProvider#xsrfTrustedOrigins xsrfTrustedOrigins}
12421   * instead.
12422   */
12423  Object.defineProperty(this, 'xsrfWhitelistedOrigins', {
12424    get: function() {
12425      return this.xsrfTrustedOrigins;
12426    },
12427    set: function(origins) {
12428      this.xsrfTrustedOrigins = origins;
12429    }
12430  });
12431
12432  this.$get = ['$browser', '$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector', '$sce',
12433      function($browser, $httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector, $sce) {
12434
12435    var defaultCache = $cacheFactory('$http');
12436
12437    /**
12438     * Make sure that default param serializer is exposed as a function
12439     */
12440    defaults.paramSerializer = isString(defaults.paramSerializer) ?
12441      $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
12442
12443    /**
12444     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
12445     * The reversal is needed so that we can build up the interception chain around the
12446     * server request.
12447     */
12448    var reversedInterceptors = [];
12449
12450    forEach(interceptorFactories, function(interceptorFactory) {
12451      reversedInterceptors.unshift(isString(interceptorFactory)
12452          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
12453    });
12454
12455    /**
12456     * A function to check request URLs against a list of allowed origins.
12457     */
12458    var urlIsAllowedOrigin = urlIsAllowedOriginFactory(xsrfTrustedOrigins);
12459
12460    /**
12461     * @ngdoc service
12462     * @kind function
12463     * @name $http
12464     * @requires ng.$httpBackend
12465     * @requires $cacheFactory
12466     * @requires $rootScope
12467     * @requires $q
12468     * @requires $injector
12469     *
12470     * @description
12471     * The `$http` service is a core AngularJS service that facilitates communication with the remote
12472     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
12473     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
12474     *
12475     * For unit testing applications that use `$http` service, see
12476     * {@link ngMock.$httpBackend $httpBackend mock}.
12477     *
12478     * For a higher level of abstraction, please check out the {@link ngResource.$resource
12479     * $resource} service.
12480     *
12481     * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
12482     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
12483     * it is important to familiarize yourself with these APIs and the guarantees they provide.
12484     *
12485     *
12486     * ## General usage
12487     * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
12488     * that is used to generate an HTTP request and returns  a {@link ng.$q promise} that is
12489     * resolved (request success) or rejected (request failure) with a
12490     * {@link ng.$http#$http-returns response} object.
12491     *
12492     * ```js
12493     *   // Simple GET request example:
12494     *   $http({
12495     *     method: 'GET',
12496     *     url: '/someUrl'
12497     *   }).then(function successCallback(response) {
12498     *       // this callback will be called asynchronously
12499     *       // when the response is available
12500     *     }, function errorCallback(response) {
12501     *       // called asynchronously if an error occurs
12502     *       // or server returns response with an error status.
12503     *     });
12504     * ```
12505     *
12506     *
12507     * ## Shortcut methods
12508     *
12509     * Shortcut methods are also available. All shortcut methods require passing in the URL, and
12510     * request data must be passed in for POST/PUT requests. An optional config can be passed as the
12511     * last argument.
12512     *
12513     * ```js
12514     *   $http.get('/someUrl', config).then(successCallback, errorCallback);
12515     *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
12516     * ```
12517     *
12518     * Complete list of shortcut methods:
12519     *
12520     * - {@link ng.$http#get $http.get}
12521     * - {@link ng.$http#head $http.head}
12522     * - {@link ng.$http#post $http.post}
12523     * - {@link ng.$http#put $http.put}
12524     * - {@link ng.$http#delete $http.delete}
12525     * - {@link ng.$http#jsonp $http.jsonp}
12526     * - {@link ng.$http#patch $http.patch}
12527     *
12528     *
12529     * ## Writing Unit Tests that use $http
12530     * When unit testing (using {@link ngMock ngMock}), it is necessary to call
12531     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
12532     * request using trained responses.
12533     *
12534     * ```
12535     * $httpBackend.expectGET(...);
12536     * $http.get(...);
12537     * $httpBackend.flush();
12538     * ```
12539     *
12540     * ## Setting HTTP Headers
12541     *
12542     * The $http service will automatically add certain HTTP headers to all requests. These defaults
12543     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
12544     * object, which currently contains this default configuration:
12545     *
12546     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
12547     *   - <code>Accept: application/json, text/plain, \*&#65279;/&#65279;\*</code>
12548     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
12549     *   - `Content-Type: application/json`
12550     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
12551     *   - `Content-Type: application/json`
12552     *
12553     * To add or overwrite these defaults, simply add or remove a property from these configuration
12554     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
12555     * with the lowercased HTTP method name as the key, e.g.
12556     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
12557     *
12558     * The defaults can also be set at runtime via the `$http.defaults` object in the same
12559     * fashion. For example:
12560     *
12561     * ```
12562     * module.run(function($http) {
12563     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
12564     * });
12565     * ```
12566     *
12567     * In addition, you can supply a `headers` property in the config object passed when
12568     * calling `$http(config)`, which overrides the defaults without changing them globally.
12569     *
12570     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
12571     * Use the `headers` property, setting the desired header to `undefined`. For example:
12572     *
12573     * ```js
12574     * var req = {
12575     *  method: 'POST',
12576     *  url: 'http://example.com',
12577     *  headers: {
12578     *    'Content-Type': undefined
12579     *  },
12580     *  data: { test: 'test' }
12581     * }
12582     *
12583     * $http(req).then(function(){...}, function(){...});
12584     * ```
12585     *
12586     * ## Transforming Requests and Responses
12587     *
12588     * Both requests and responses can be transformed using transformation functions: `transformRequest`
12589     * and `transformResponse`. These properties can be a single function that returns
12590     * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
12591     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
12592     *
12593     * <div class="alert alert-warning">
12594     * **Note:** AngularJS does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
12595     * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
12596     * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
12597     * function will be reflected on the scope and in any templates where the object is data-bound.
12598     * To prevent this, transform functions should have no side-effects.
12599     * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
12600     * </div>
12601     *
12602     * ### Default Transformations
12603     *
12604     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
12605     * `defaults.transformResponse` properties. If a request does not provide its own transformations
12606     * then these will be applied.
12607     *
12608     * You can augment or replace the default transformations by modifying these properties by adding to or
12609     * replacing the array.
12610     *
12611     * AngularJS provides the following default transformations:
12612     *
12613     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`) is
12614     * an array with one function that does the following:
12615     *
12616     * - If the `data` property of the request configuration object contains an object, serialize it
12617     *   into JSON format.
12618     *
12619     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`) is
12620     * an array with one function that does the following:
12621     *
12622     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
12623     *  - If the `Content-Type` is `application/json` or the response looks like JSON,
12624   *      deserialize it using a JSON parser.
12625     *
12626     *
12627     * ### Overriding the Default Transformations Per Request
12628     *
12629     * If you wish to override the request/response transformations only for a single request then provide
12630     * `transformRequest` and/or `transformResponse` properties on the configuration object passed
12631     * into `$http`.
12632     *
12633     * Note that if you provide these properties on the config object the default transformations will be
12634     * overwritten. If you wish to augment the default transformations then you must include them in your
12635     * local transformation array.
12636     *
12637     * The following code demonstrates adding a new response transformation to be run after the default response
12638     * transformations have been run.
12639     *
12640     * ```js
12641     * function appendTransform(defaults, transform) {
12642     *
12643     *   // We can't guarantee that the default transformation is an array
12644     *   defaults = angular.isArray(defaults) ? defaults : [defaults];
12645     *
12646     *   // Append the new transformation to the defaults
12647     *   return defaults.concat(transform);
12648     * }
12649     *
12650     * $http({
12651     *   url: '...',
12652     *   method: 'GET',
12653     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
12654     *     return doTransform(value);
12655     *   })
12656     * });
12657     * ```
12658     *
12659     *
12660     * ## Caching
12661     *
12662     * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
12663     * set the config.cache value or the default cache value to TRUE or to a cache object (created
12664     * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
12665     * precedence over the default cache value.
12666     *
12667     * In order to:
12668     *   * cache all responses - set the default cache value to TRUE or to a cache object
12669     *   * cache a specific response - set config.cache value to TRUE or to a cache object
12670     *
12671     * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
12672     * then the default `$cacheFactory("$http")` object is used.
12673     *
12674     * The default cache value can be set by updating the
12675     * {@link ng.$http#defaults `$http.defaults.cache`} property or the
12676     * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
12677     *
12678     * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
12679     * the relevant cache object. The next time the same request is made, the response is returned
12680     * from the cache without sending a request to the server.
12681     *
12682     * Take note that:
12683     *
12684     *   * Only GET and JSONP requests are cached.
12685     *   * The cache key is the request URL including search parameters; headers are not considered.
12686     *   * Cached responses are returned asynchronously, in the same way as responses from the server.
12687     *   * If multiple identical requests are made using the same cache, which is not yet populated,
12688     *     one request will be made to the server and remaining requests will return the same response.
12689     *   * A cache-control header on the response does not affect if or how responses are cached.
12690     *
12691     *
12692     * ## Interceptors
12693     *
12694     * Before you start creating interceptors, be sure to understand the
12695     * {@link ng.$q $q and deferred/promise APIs}.
12696     *
12697     * For purposes of global error handling, authentication, or any kind of synchronous or
12698     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
12699     * able to intercept requests before they are handed to the server and
12700     * responses before they are handed over to the application code that
12701     * initiated these requests. The interceptors leverage the {@link ng.$q
12702     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
12703     *
12704     * The interceptors are service factories that are registered with the `$httpProvider` by
12705     * adding them to the `$httpProvider.interceptors` array. The factory is called and
12706     * injected with dependencies (if specified) and returns the interceptor.
12707     *
12708     * There are two kinds of interceptors (and two kinds of rejection interceptors):
12709     *
12710     *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
12711     *     modify the `config` object or create a new one. The function needs to return the `config`
12712     *     object directly, or a promise containing the `config` or a new `config` object.
12713     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
12714     *     resolved with a rejection.
12715     *   * `response`: interceptors get called with http `response` object. The function is free to
12716     *     modify the `response` object or create a new one. The function needs to return the `response`
12717     *     object directly, or as a promise containing the `response` or a new `response` object.
12718     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
12719     *     resolved with a rejection.
12720     *
12721     *
12722     * ```js
12723     *   // register the interceptor as a service
12724     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
12725     *     return {
12726     *       // optional method
12727     *       'request': function(config) {
12728     *         // do something on success
12729     *         return config;
12730     *       },
12731     *
12732     *       // optional method
12733     *      'requestError': function(rejection) {
12734     *         // do something on error
12735     *         if (canRecover(rejection)) {
12736     *           return responseOrNewPromise
12737     *         }
12738     *         return $q.reject(rejection);
12739     *       },
12740     *
12741     *
12742     *
12743     *       // optional method
12744     *       'response': function(response) {
12745     *         // do something on success
12746     *         return response;
12747     *       },
12748     *
12749     *       // optional method
12750     *      'responseError': function(rejection) {
12751     *         // do something on error
12752     *         if (canRecover(rejection)) {
12753     *           return responseOrNewPromise
12754     *         }
12755     *         return $q.reject(rejection);
12756     *       }
12757     *     };
12758     *   });
12759     *
12760     *   $httpProvider.interceptors.push('myHttpInterceptor');
12761     *
12762     *
12763     *   // alternatively, register the interceptor via an anonymous factory
12764     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
12765     *     return {
12766     *      'request': function(config) {
12767     *          // same as above
12768     *       },
12769     *
12770     *       'response': function(response) {
12771     *          // same as above
12772     *       }
12773     *     };
12774     *   });
12775     * ```
12776     *
12777     * ## Security Considerations
12778     *
12779     * When designing web applications, consider security threats from:
12780     *
12781     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
12782     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
12783     *
12784     * Both server and the client must cooperate in order to eliminate these threats. AngularJS comes
12785     * pre-configured with strategies that address these issues, but for this to work backend server
12786     * cooperation is required.
12787     *
12788     * ### JSON Vulnerability Protection
12789     *
12790     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
12791     * allows third party website to turn your JSON resource URL into
12792     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
12793     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
12794     * AngularJS will automatically strip the prefix before processing it as JSON.
12795     *
12796     * For example if your server needs to return:
12797     * ```js
12798     * ['one','two']
12799     * ```
12800     *
12801     * which is vulnerable to attack, your server can return:
12802     * ```js
12803     * )]}',
12804     * ['one','two']
12805     * ```
12806     *
12807     * AngularJS will strip the prefix, before processing the JSON.
12808     *
12809     *
12810     * ### Cross Site Request Forgery (XSRF) Protection
12811     *
12812     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
12813     * which the attacker can trick an authenticated user into unknowingly executing actions on your
12814     * website. AngularJS provides a mechanism to counter XSRF. When performing XHR requests, the
12815     * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
12816     * header (by default `X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read
12817     * the cookie, your server can be assured that the XHR came from JavaScript running on your
12818     * domain.
12819     *
12820     * To take advantage of this, your server needs to set a token in a JavaScript readable session
12821     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
12822     * server can verify that the cookie matches the `X-XSRF-TOKEN` HTTP header, and therefore be
12823     * sure that only JavaScript running on your domain could have sent the request. The token must
12824     * be unique for each user and must be verifiable by the server (to prevent the JavaScript from
12825     * making up its own tokens). We recommend that the token is a digest of your site's
12826     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
12827     * for added security.
12828     *
12829     * The header will &mdash; by default &mdash; **not** be set for cross-domain requests. This
12830     * prevents unauthorized servers (e.g. malicious or compromised 3rd-party APIs) from gaining
12831     * access to your users' XSRF tokens and exposing them to Cross Site Request Forgery. If you
12832     * want to, you can trust additional origins to also receive the XSRF token, by adding them
12833     * to {@link ng.$httpProvider#xsrfTrustedOrigins xsrfTrustedOrigins}. This might be
12834     * useful, for example, if your application, served from `example.com`, needs to access your API
12835     * at `api.example.com`.
12836     * See {@link ng.$httpProvider#xsrfTrustedOrigins $httpProvider.xsrfTrustedOrigins} for
12837     * more details.
12838     *
12839     * <div class="alert alert-danger">
12840     *   **Warning**<br />
12841     *   Only trusted origins that you have control over and make sure you understand the
12842     *   implications of doing so.
12843     * </div>
12844     *
12845     * The name of the cookie and the header can be specified using the `xsrfCookieName` and
12846     * `xsrfHeaderName` properties of either `$httpProvider.defaults` at config-time,
12847     * `$http.defaults` at run-time, or the per-request config object.
12848     *
12849     * In order to prevent collisions in environments where multiple AngularJS apps share the
12850     * same domain or subdomain, we recommend that each application uses a unique cookie name.
12851     *
12852     *
12853     * @param {object} config Object describing the request to be made and how it should be
12854     *    processed. The object has following properties:
12855     *
12856     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
12857     *    - **url** – `{string|TrustedObject}` – Absolute or relative URL of the resource that is being requested;
12858     *      or an object created by a call to `$sce.trustAsResourceUrl(url)`.
12859     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
12860     *      with the `paramSerializer` and appended as GET parameters.
12861     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
12862     *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
12863     *      HTTP headers to send to the server. If the return value of a function is null, the
12864     *      header will not be sent. Functions accept a config object as an argument.
12865     *    - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
12866     *      To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
12867     *      The handler will be called in the context of a `$apply` block.
12868     *    - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
12869     *      object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
12870     *      The handler will be called in the context of a `$apply` block.
12871     *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
12872     *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
12873     *    - **transformRequest** –
12874     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
12875     *      transform function or an array of such functions. The transform function takes the http
12876     *      request body and headers and returns its transformed (typically serialized) version.
12877     *      See {@link ng.$http#overriding-the-default-transformations-per-request
12878     *      Overriding the Default Transformations}
12879     *    - **transformResponse** –
12880     *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
12881     *      transform function or an array of such functions. The transform function takes the http
12882     *      response body, headers and status and returns its transformed (typically deserialized) version.
12883     *      See {@link ng.$http#overriding-the-default-transformations-per-request
12884     *      Overriding the Default Transformations}
12885     *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
12886     *      prepare the string representation of request parameters (specified as an object).
12887     *      If specified as string, it is interpreted as function registered with the
12888     *      {@link $injector $injector}, which means you can create your own serializer
12889     *      by registering it as a {@link auto.$provide#service service}.
12890     *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
12891     *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
12892     *    - **cache** – `{boolean|Object}` – A boolean value or object created with
12893     *      {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
12894     *      See {@link $http#caching $http Caching} for more information.
12895     *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
12896     *      that should abort the request when resolved.
12897     *
12898     *      A numerical timeout or a promise returned from {@link ng.$timeout $timeout}, will set
12899     *      the `xhrStatus` in the {@link $http#$http-returns response} to "timeout", and any other
12900     *      resolved promise will set it to "abort", following standard XMLHttpRequest behavior.
12901     *
12902     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
12903     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
12904     *      for more information.
12905     *    - **responseType** - `{string}` - see
12906     *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
12907     *
12908     * @returns {HttpPromise} A {@link ng.$q `Promise}` that will be resolved (request success)
12909     *   or rejected (request failure) with a response object.
12910     *
12911     *   The response object has these properties:
12912     *
12913     *   - **data** – `{string|Object}` – The response body transformed with
12914     *     the transform functions.
12915     *   - **status** – `{number}` – HTTP status code of the response.
12916     *   - **headers** – `{function([headerName])}` – Header getter function.
12917     *   - **config** – `{Object}` – The configuration object that was used
12918     *     to generate the request.
12919     *   - **statusText** – `{string}` – HTTP status text of the response.
12920     *   - **xhrStatus** – `{string}` – Status of the XMLHttpRequest
12921     *     (`complete`, `error`, `timeout` or `abort`).
12922     *
12923     *
12924     *   A response status code between 200 and 299 is considered a success status
12925     *   and will result in the success callback being called. Any response status
12926     *   code outside of that range is considered an error status and will result
12927     *   in the error callback being called.
12928     *   Also, status codes less than -1 are normalized to zero. -1 usually means
12929     *   the request was aborted, e.g. using a `config.timeout`. More information
12930     *   about the status might be available in the `xhrStatus` property.
12931     *
12932     *   Note that if the response is a redirect, XMLHttpRequest will transparently
12933     *   follow it, meaning that the outcome (success or error) will be determined
12934     *   by the final response status code.
12935     *
12936     *
12937     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
12938     *   requests. This is primarily meant to be used for debugging purposes.
12939     *
12940     *
12941     * @example
12942<example module="httpExample" name="http-service">
12943<file name="index.html">
12944  <div ng-controller="FetchController">
12945    <select ng-model="method" aria-label="Request method">
12946      <option>GET</option>
12947      <option>JSONP</option>
12948    </select>
12949    <input type="text" ng-model="url" size="80" aria-label="URL" />
12950    <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
12951    <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
12952    <button id="samplejsonpbtn"
12953      ng-click="updateModel('JSONP',
12954                    'https://angularjs.org/greet.php?name=Super%20Hero')">
12955      Sample JSONP
12956    </button>
12957    <button id="invalidjsonpbtn"
12958      ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist')">
12959        Invalid JSONP
12960      </button>
12961    <pre>http status code: {{status}}</pre>
12962    <pre>http response data: {{data}}</pre>
12963  </div>
12964</file>
12965<file name="script.js">
12966  angular.module('httpExample', [])
12967    .config(['$sceDelegateProvider', function($sceDelegateProvider) {
12968      // We must add the JSONP endpoint that we are using to the trusted list to show that we trust it
12969      $sceDelegateProvider.trustedResourceUrlList([
12970        'self',
12971        'https://angularjs.org/**'
12972      ]);
12973    }])
12974    .controller('FetchController', ['$scope', '$http', '$templateCache',
12975      function($scope, $http, $templateCache) {
12976        $scope.method = 'GET';
12977        $scope.url = 'http-hello.html';
12978
12979        $scope.fetch = function() {
12980          $scope.code = null;
12981          $scope.response = null;
12982
12983          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
12984            then(function(response) {
12985              $scope.status = response.status;
12986              $scope.data = response.data;
12987            }, function(response) {
12988              $scope.data = response.data || 'Request failed';
12989              $scope.status = response.status;
12990          });
12991        };
12992
12993        $scope.updateModel = function(method, url) {
12994          $scope.method = method;
12995          $scope.url = url;
12996        };
12997      }]);
12998</file>
12999<file name="http-hello.html">
13000  Hello, $http!
13001</file>
13002<file name="protractor.js" type="protractor">
13003  var status = element(by.binding('status'));
13004  var data = element(by.binding('data'));
13005  var fetchBtn = element(by.id('fetchbtn'));
13006  var sampleGetBtn = element(by.id('samplegetbtn'));
13007  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
13008
13009  it('should make an xhr GET request', function() {
13010    sampleGetBtn.click();
13011    fetchBtn.click();
13012    expect(status.getText()).toMatch('200');
13013    expect(data.getText()).toMatch(/Hello, \$http!/);
13014  });
13015
13016// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
13017// it('should make a JSONP request to angularjs.org', function() {
13018//   var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
13019//   sampleJsonpBtn.click();
13020//   fetchBtn.click();
13021//   expect(status.getText()).toMatch('200');
13022//   expect(data.getText()).toMatch(/Super Hero!/);
13023// });
13024
13025  it('should make JSONP request to invalid URL and invoke the error handler',
13026      function() {
13027    invalidJsonpBtn.click();
13028    fetchBtn.click();
13029    expect(status.getText()).toMatch('0');
13030    expect(data.getText()).toMatch('Request failed');
13031  });
13032</file>
13033</example>
13034     */
13035    function $http(requestConfig) {
13036
13037      if (!isObject(requestConfig)) {
13038        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
13039      }
13040
13041      if (!isString($sce.valueOf(requestConfig.url))) {
13042        throw minErr('$http')('badreq', 'Http request configuration url must be a string or a $sce trusted object.  Received: {0}', requestConfig.url);
13043      }
13044
13045      var config = extend({
13046        method: 'get',
13047        transformRequest: defaults.transformRequest,
13048        transformResponse: defaults.transformResponse,
13049        paramSerializer: defaults.paramSerializer,
13050        jsonpCallbackParam: defaults.jsonpCallbackParam
13051      }, requestConfig);
13052
13053      config.headers = mergeHeaders(requestConfig);
13054      config.method = uppercase(config.method);
13055      config.paramSerializer = isString(config.paramSerializer) ?
13056          $injector.get(config.paramSerializer) : config.paramSerializer;
13057
13058      $browser.$$incOutstandingRequestCount('$http');
13059
13060      var requestInterceptors = [];
13061      var responseInterceptors = [];
13062      var promise = $q.resolve(config);
13063
13064      // apply interceptors
13065      forEach(reversedInterceptors, function(interceptor) {
13066        if (interceptor.request || interceptor.requestError) {
13067          requestInterceptors.unshift(interceptor.request, interceptor.requestError);
13068        }
13069        if (interceptor.response || interceptor.responseError) {
13070          responseInterceptors.push(interceptor.response, interceptor.responseError);
13071        }
13072      });
13073
13074      promise = chainInterceptors(promise, requestInterceptors);
13075      promise = promise.then(serverRequest);
13076      promise = chainInterceptors(promise, responseInterceptors);
13077      promise = promise.finally(completeOutstandingRequest);
13078
13079      return promise;
13080
13081
13082      function chainInterceptors(promise, interceptors) {
13083        for (var i = 0, ii = interceptors.length; i < ii;) {
13084          var thenFn = interceptors[i++];
13085          var rejectFn = interceptors[i++];
13086
13087          promise = promise.then(thenFn, rejectFn);
13088        }
13089
13090        interceptors.length = 0;
13091
13092        return promise;
13093      }
13094
13095      function completeOutstandingRequest() {
13096        $browser.$$completeOutstandingRequest(noop, '$http');
13097      }
13098
13099      function executeHeaderFns(headers, config) {
13100        var headerContent, processedHeaders = {};
13101
13102        forEach(headers, function(headerFn, header) {
13103          if (isFunction(headerFn)) {
13104            headerContent = headerFn(config);
13105            if (headerContent != null) {
13106              processedHeaders[header] = headerContent;
13107            }
13108          } else {
13109            processedHeaders[header] = headerFn;
13110          }
13111        });
13112
13113        return processedHeaders;
13114      }
13115
13116      function mergeHeaders(config) {
13117        var defHeaders = defaults.headers,
13118            reqHeaders = extend({}, config.headers),
13119            defHeaderName, lowercaseDefHeaderName, reqHeaderName;
13120
13121        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
13122
13123        // using for-in instead of forEach to avoid unnecessary iteration after header has been found
13124        defaultHeadersIteration:
13125        for (defHeaderName in defHeaders) {
13126          lowercaseDefHeaderName = lowercase(defHeaderName);
13127
13128          for (reqHeaderName in reqHeaders) {
13129            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
13130              continue defaultHeadersIteration;
13131            }
13132          }
13133
13134          reqHeaders[defHeaderName] = defHeaders[defHeaderName];
13135        }
13136
13137        // execute if header value is a function for merged headers
13138        return executeHeaderFns(reqHeaders, shallowCopy(config));
13139      }
13140
13141      function serverRequest(config) {
13142        var headers = config.headers;
13143        var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
13144
13145        // strip content-type if data is undefined
13146        if (isUndefined(reqData)) {
13147          forEach(headers, function(value, header) {
13148            if (lowercase(header) === 'content-type') {
13149              delete headers[header];
13150            }
13151          });
13152        }
13153
13154        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
13155          config.withCredentials = defaults.withCredentials;
13156        }
13157
13158        // send request
13159        return sendReq(config, reqData).then(transformResponse, transformResponse);
13160      }
13161
13162      function transformResponse(response) {
13163        // make a copy since the response must be cacheable
13164        var resp = extend({}, response);
13165        resp.data = transformData(response.data, response.headers, response.status,
13166                                  config.transformResponse);
13167        return (isSuccess(response.status))
13168          ? resp
13169          : $q.reject(resp);
13170      }
13171    }
13172
13173    $http.pendingRequests = [];
13174
13175    /**
13176     * @ngdoc method
13177     * @name $http#get
13178     *
13179     * @description
13180     * Shortcut method to perform `GET` request.
13181     *
13182     * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
13183     *                                   or an object created by a call to `$sce.trustAsResourceUrl(url)`.
13184     * @param {Object=} config Optional configuration object. See {@link ng.$http#$http-arguments `$http()` arguments}.
13185     * @returns {HttpPromise}  A Promise that will be resolved or rejected with a response object.
13186     * See {@link ng.$http#$http-returns `$http()` return value}.
13187     */
13188
13189    /**
13190     * @ngdoc method
13191     * @name $http#delete
13192     *
13193     * @description
13194     * Shortcut method to perform `DELETE` request.
13195     *
13196     * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
13197     *                                   or an object created by a call to `$sce.trustAsResourceUrl(url)`.
13198     * @param {Object=} config Optional configuration object. See {@link ng.$http#$http-arguments `$http()` arguments}.
13199     * @returns {HttpPromise}  A Promise that will be resolved or rejected with a response object.
13200     * See {@link ng.$http#$http-returns `$http()` return value}.
13201     */
13202
13203    /**
13204     * @ngdoc method
13205     * @name $http#head
13206     *
13207     * @description
13208     * Shortcut method to perform `HEAD` request.
13209     *
13210     * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
13211     *                                   or an object created by a call to `$sce.trustAsResourceUrl(url)`.
13212     * @param {Object=} config Optional configuration object. See {@link ng.$http#$http-arguments `$http()` arguments}.
13213     * @returns {HttpPromise}  A Promise that will be resolved or rejected with a response object.
13214     * See {@link ng.$http#$http-returns `$http()` return value}.
13215     */
13216
13217    /**
13218     * @ngdoc method
13219     * @name $http#jsonp
13220     *
13221     * @description
13222     * Shortcut method to perform `JSONP` request.
13223     *
13224     * Note that, since JSONP requests are sensitive because the response is given full access to the browser,
13225     * the url must be declared, via {@link $sce} as a trusted resource URL.
13226     * You can trust a URL by adding it to the trusted resource URL list via
13227     * {@link $sceDelegateProvider#trustedResourceUrlList  `$sceDelegateProvider.trustedResourceUrlList`} or
13228     * by explicitly trusting the URL via {@link $sce#trustAsResourceUrl `$sce.trustAsResourceUrl(url)`}.
13229     *
13230     * You should avoid generating the URL for the JSONP request from user provided data.
13231     * Provide additional query parameters via `params` property of the `config` parameter, rather than
13232     * modifying the URL itself.
13233     *
13234     * JSONP requests must specify a callback to be used in the response from the server. This callback
13235     * is passed as a query parameter in the request. You must specify the name of this parameter by
13236     * setting the `jsonpCallbackParam` property on the request config object.
13237     *
13238     * ```
13239     * $http.jsonp('some/trusted/url', {jsonpCallbackParam: 'callback'})
13240     * ```
13241     *
13242     * You can also specify a default callback parameter name in `$http.defaults.jsonpCallbackParam`.
13243     * Initially this is set to `'callback'`.
13244     *
13245     * <div class="alert alert-danger">
13246     * You can no longer use the `JSON_CALLBACK` string as a placeholder for specifying where the callback
13247     * parameter value should go.
13248     * </div>
13249     *
13250     * If you would like to customise where and how the callbacks are stored then try overriding
13251     * or decorating the {@link $jsonpCallbacks} service.
13252     *
13253     * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
13254     *                                   or an object created by a call to `$sce.trustAsResourceUrl(url)`.
13255     * @param {Object=} config Optional configuration object. See {@link ng.$http#$http-arguments `$http()` arguments}.
13256     * @returns {HttpPromise}  A Promise that will be resolved or rejected with a response object.
13257     * See {@link ng.$http#$http-returns `$http()` return value}.
13258     */
13259    createShortMethods('get', 'delete', 'head', 'jsonp');
13260
13261    /**
13262     * @ngdoc method
13263     * @name $http#post
13264     *
13265     * @description
13266     * Shortcut method to perform `POST` request.
13267     *
13268     * @param {string} url Relative or absolute URL specifying the destination of the request
13269     * @param {*} data Request content
13270     * @param {Object=} config Optional configuration object. See {@link ng.$http#$http-arguments `$http()` arguments}.
13271     * @returns {HttpPromise}  A Promise that will be resolved or rejected with a response object.
13272     * See {@link ng.$http#$http-returns `$http()` return value}.
13273     */
13274
13275    /**
13276     * @ngdoc method
13277     * @name $http#put
13278     *
13279     * @description
13280     * Shortcut method to perform `PUT` request.
13281     *
13282     * @param {string} url Relative or absolute URL specifying the destination of the request
13283     * @param {*} data Request content
13284     * @param {Object=} config Optional configuration object. See {@link ng.$http#$http-arguments `$http()` arguments}.
13285     * @returns {HttpPromise}  A Promise that will be resolved or rejected with a response object.
13286     * See {@link ng.$http#$http-returns `$http()` return value}.
13287     */
13288
13289     /**
13290      * @ngdoc method
13291      * @name $http#patch
13292      *
13293      * @description
13294      * Shortcut method to perform `PATCH` request.
13295      *
13296      * @param {string} url Relative or absolute URL specifying the destination of the request
13297      * @param {*} data Request content
13298      * @param {Object=} config Optional configuration object. See {@link ng.$http#$http-arguments `$http()` arguments}.
13299      * @returns {HttpPromise}  A Promise that will be resolved or rejected with a response object.
13300      * See {@link ng.$http#$http-returns `$http()` return value}.
13301      */
13302    createShortMethodsWithData('post', 'put', 'patch');
13303
13304        /**
13305         * @ngdoc property
13306         * @name $http#defaults
13307         *
13308         * @description
13309         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
13310         * default headers, withCredentials as well as request and response transformations.
13311         *
13312         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
13313         */
13314    $http.defaults = defaults;
13315
13316
13317    return $http;
13318
13319
13320    function createShortMethods(names) {
13321      forEach(arguments, function(name) {
13322        $http[name] = function(url, config) {
13323          return $http(extend({}, config || {}, {
13324            method: name,
13325            url: url
13326          }));
13327        };
13328      });
13329    }
13330
13331
13332    function createShortMethodsWithData(name) {
13333      forEach(arguments, function(name) {
13334        $http[name] = function(url, data, config) {
13335          return $http(extend({}, config || {}, {
13336            method: name,
13337            url: url,
13338            data: data
13339          }));
13340        };
13341      });
13342    }
13343
13344
13345    /**
13346     * Makes the request.
13347     *
13348     * !!! ACCESSES CLOSURE VARS:
13349     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
13350     */
13351    function sendReq(config, reqData) {
13352      var deferred = $q.defer(),
13353          promise = deferred.promise,
13354          cache,
13355          cachedResp,
13356          reqHeaders = config.headers,
13357          isJsonp = lowercase(config.method) === 'jsonp',
13358          url = config.url;
13359
13360      if (isJsonp) {
13361        // JSONP is a pretty sensitive operation where we're allowing a script to have full access to
13362        // our DOM and JS space.  So we require that the URL satisfies SCE.RESOURCE_URL.
13363        url = $sce.getTrustedResourceUrl(url);
13364      } else if (!isString(url)) {
13365        // If it is not a string then the URL must be a $sce trusted object
13366        url = $sce.valueOf(url);
13367      }
13368
13369      url = buildUrl(url, config.paramSerializer(config.params));
13370
13371      if (isJsonp) {
13372        // Check the url and add the JSONP callback placeholder
13373        url = sanitizeJsonpCallbackParam(url, config.jsonpCallbackParam);
13374      }
13375
13376      $http.pendingRequests.push(config);
13377      promise.then(removePendingReq, removePendingReq);
13378
13379      if ((config.cache || defaults.cache) && config.cache !== false &&
13380          (config.method === 'GET' || config.method === 'JSONP')) {
13381        cache = isObject(config.cache) ? config.cache
13382            : isObject(/** @type {?} */ (defaults).cache)
13383              ? /** @type {?} */ (defaults).cache
13384              : defaultCache;
13385      }
13386
13387      if (cache) {
13388        cachedResp = cache.get(url);
13389        if (isDefined(cachedResp)) {
13390          if (isPromiseLike(cachedResp)) {
13391            // cached request has already been sent, but there is no response yet
13392            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
13393          } else {
13394            // serving from cache
13395            if (isArray(cachedResp)) {
13396              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3], cachedResp[4]);
13397            } else {
13398              resolvePromise(cachedResp, 200, {}, 'OK', 'complete');
13399            }
13400          }
13401        } else {
13402          // put the promise for the non-transformed response into cache as a placeholder
13403          cache.put(url, promise);
13404        }
13405      }
13406
13407
13408      // if we won't have the response in cache, set the xsrf headers and
13409      // send the request to the backend
13410      if (isUndefined(cachedResp)) {
13411        var xsrfValue = urlIsAllowedOrigin(config.url)
13412            ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
13413            : undefined;
13414        if (xsrfValue) {
13415          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
13416        }
13417
13418        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
13419            config.withCredentials, config.responseType,
13420            createApplyHandlers(config.eventHandlers),
13421            createApplyHandlers(config.uploadEventHandlers));
13422      }
13423
13424      return promise;
13425
13426      function createApplyHandlers(eventHandlers) {
13427        if (eventHandlers) {
13428          var applyHandlers = {};
13429          forEach(eventHandlers, function(eventHandler, key) {
13430            applyHandlers[key] = function(event) {
13431              if (useApplyAsync) {
13432                $rootScope.$applyAsync(callEventHandler);
13433              } else if ($rootScope.$$phase) {
13434                callEventHandler();
13435              } else {
13436                $rootScope.$apply(callEventHandler);
13437              }
13438
13439              function callEventHandler() {
13440                eventHandler(event);
13441              }
13442            };
13443          });
13444          return applyHandlers;
13445        }
13446      }
13447
13448
13449      /**
13450       * Callback registered to $httpBackend():
13451       *  - caches the response if desired
13452       *  - resolves the raw $http promise
13453       *  - calls $apply
13454       */
13455      function done(status, response, headersString, statusText, xhrStatus) {
13456        if (cache) {
13457          if (isSuccess(status)) {
13458            cache.put(url, [status, response, parseHeaders(headersString), statusText, xhrStatus]);
13459          } else {
13460            // remove promise from the cache
13461            cache.remove(url);
13462          }
13463        }
13464
13465        function resolveHttpPromise() {
13466          resolvePromise(response, status, headersString, statusText, xhrStatus);
13467        }
13468
13469        if (useApplyAsync) {
13470          $rootScope.$applyAsync(resolveHttpPromise);
13471        } else {
13472          resolveHttpPromise();
13473          if (!$rootScope.$$phase) $rootScope.$apply();
13474        }
13475      }
13476
13477
13478      /**
13479       * Resolves the raw $http promise.
13480       */
13481      function resolvePromise(response, status, headers, statusText, xhrStatus) {
13482        //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
13483        status = status >= -1 ? status : 0;
13484
13485        (isSuccess(status) ? deferred.resolve : deferred.reject)({
13486          data: response,
13487          status: status,
13488          headers: headersGetter(headers),
13489          config: config,
13490          statusText: statusText,
13491          xhrStatus: xhrStatus
13492        });
13493      }
13494
13495      function resolvePromiseWithResult(result) {
13496        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText, result.xhrStatus);
13497      }
13498
13499      function removePendingReq() {
13500        var idx = $http.pendingRequests.indexOf(config);
13501        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
13502      }
13503    }
13504
13505
13506    function buildUrl(url, serializedParams) {
13507      if (serializedParams.length > 0) {
13508        url += ((url.indexOf('?') === -1) ? '?' : '&') + serializedParams;
13509      }
13510      return url;
13511    }
13512
13513    function sanitizeJsonpCallbackParam(url, cbKey) {
13514      var parts = url.split('?');
13515      if (parts.length > 2) {
13516        // Throw if the url contains more than one `?` query indicator
13517        throw $httpMinErr('badjsonp', 'Illegal use more than one "?", in url, "{1}"', url);
13518      }
13519      var params = parseKeyValue(parts[1]);
13520      forEach(params, function(value, key) {
13521        if (value === 'JSON_CALLBACK') {
13522          // Throw if the url already contains a reference to JSON_CALLBACK
13523          throw $httpMinErr('badjsonp', 'Illegal use of JSON_CALLBACK in url, "{0}"', url);
13524        }
13525        if (key === cbKey) {
13526          // Throw if the callback param was already provided
13527          throw $httpMinErr('badjsonp', 'Illegal use of callback param, "{0}", in url, "{1}"', cbKey, url);
13528        }
13529      });
13530
13531      // Add in the JSON_CALLBACK callback param value
13532      url += ((url.indexOf('?') === -1) ? '?' : '&') + cbKey + '=JSON_CALLBACK';
13533
13534      return url;
13535    }
13536  }];
13537}
13538
13539/**
13540 * @ngdoc service
13541 * @name $xhrFactory
13542 * @this
13543 *
13544 * @description
13545 * Factory function used to create XMLHttpRequest objects.
13546 *
13547 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
13548 *
13549 * ```
13550 * angular.module('myApp', [])
13551 * .factory('$xhrFactory', function() {
13552 *   return function createXhr(method, url) {
13553 *     return new window.XMLHttpRequest({mozSystem: true});
13554 *   };
13555 * });
13556 * ```
13557 *
13558 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
13559 * @param {string} url URL of the request.
13560 */
13561function $xhrFactoryProvider() {
13562  this.$get = function() {
13563    return function createXhr() {
13564      return new window.XMLHttpRequest();
13565    };
13566  };
13567}
13568
13569/**
13570 * @ngdoc service
13571 * @name $httpBackend
13572 * @requires $jsonpCallbacks
13573 * @requires $document
13574 * @requires $xhrFactory
13575 * @this
13576 *
13577 * @description
13578 * HTTP backend used by the {@link ng.$http service} that delegates to
13579 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
13580 *
13581 * You should never need to use this service directly, instead use the higher-level abstractions:
13582 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
13583 *
13584 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
13585 * $httpBackend} which can be trained with responses.
13586 */
13587function $HttpBackendProvider() {
13588  this.$get = ['$browser', '$jsonpCallbacks', '$document', '$xhrFactory', function($browser, $jsonpCallbacks, $document, $xhrFactory) {
13589    return createHttpBackend($browser, $xhrFactory, $browser.defer, $jsonpCallbacks, $document[0]);
13590  }];
13591}
13592
13593function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
13594  // TODO(vojta): fix the signature
13595  return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
13596    url = url || $browser.url();
13597
13598    if (lowercase(method) === 'jsonp') {
13599      var callbackPath = callbacks.createCallback(url);
13600      var jsonpDone = jsonpReq(url, callbackPath, function(status, text) {
13601        // jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING)
13602        var response = (status === 200) && callbacks.getResponse(callbackPath);
13603        completeRequest(callback, status, response, '', text, 'complete');
13604        callbacks.removeCallback(callbackPath);
13605      });
13606    } else {
13607
13608      var xhr = createXhr(method, url);
13609      var abortedByTimeout = false;
13610
13611      xhr.open(method, url, true);
13612      forEach(headers, function(value, key) {
13613        if (isDefined(value)) {
13614            xhr.setRequestHeader(key, value);
13615        }
13616      });
13617
13618      xhr.onload = function requestLoaded() {
13619        var statusText = xhr.statusText || '';
13620
13621        // responseText is the old-school way of retrieving response (supported by IE9)
13622        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
13623        var response = ('response' in xhr) ? xhr.response : xhr.responseText;
13624
13625        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
13626        var status = xhr.status === 1223 ? 204 : xhr.status;
13627
13628        // fix status code when it is 0 (0 status is undocumented).
13629        // Occurs when accessing file resources or on Android 4.1 stock browser
13630        // while retrieving files from application cache.
13631        if (status === 0) {
13632          status = response ? 200 : urlResolve(url).protocol === 'file' ? 404 : 0;
13633        }
13634
13635        completeRequest(callback,
13636            status,
13637            response,
13638            xhr.getAllResponseHeaders(),
13639            statusText,
13640            'complete');
13641      };
13642
13643      var requestError = function() {
13644        // The response is always empty
13645        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
13646        completeRequest(callback, -1, null, null, '', 'error');
13647      };
13648
13649      var requestAborted = function() {
13650        completeRequest(callback, -1, null, null, '', abortedByTimeout ? 'timeout' : 'abort');
13651      };
13652
13653      var requestTimeout = function() {
13654        // The response is always empty
13655        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
13656        completeRequest(callback, -1, null, null, '', 'timeout');
13657      };
13658
13659      xhr.onerror = requestError;
13660      xhr.ontimeout = requestTimeout;
13661      xhr.onabort = requestAborted;
13662
13663      forEach(eventHandlers, function(value, key) {
13664        xhr.addEventListener(key, value);
13665      });
13666
13667      forEach(uploadEventHandlers, function(value, key) {
13668        xhr.upload.addEventListener(key, value);
13669      });
13670
13671      if (withCredentials) {
13672        xhr.withCredentials = true;
13673      }
13674
13675      if (responseType) {
13676        try {
13677          xhr.responseType = responseType;
13678        } catch (e) {
13679          // WebKit added support for the json responseType value on 09/03/2013
13680          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
13681          // known to throw when setting the value "json" as the response type. Other older
13682          // browsers implementing the responseType
13683          //
13684          // The json response type can be ignored if not supported, because JSON payloads are
13685          // parsed on the client-side regardless.
13686          if (responseType !== 'json') {
13687            throw e;
13688          }
13689        }
13690      }
13691
13692      xhr.send(isUndefined(post) ? null : post);
13693    }
13694
13695    // Since we are using xhr.abort() when a request times out, we have to set a flag that
13696    // indicates to requestAborted if the request timed out or was aborted.
13697    //
13698    // http.timeout = numerical timeout   timeout
13699    // http.timeout = $timeout            timeout
13700    // http.timeout = promise             abort
13701    // xhr.abort()                        abort (The xhr object is normally inaccessible, but
13702    //                                    can be exposed with the xhrFactory)
13703    if (timeout > 0) {
13704      var timeoutId = $browserDefer(function() {
13705        timeoutRequest('timeout');
13706      }, timeout);
13707    } else if (isPromiseLike(timeout)) {
13708      timeout.then(function() {
13709        timeoutRequest(isDefined(timeout.$$timeoutId) ? 'timeout' : 'abort');
13710      });
13711    }
13712
13713    function timeoutRequest(reason) {
13714      abortedByTimeout = reason === 'timeout';
13715      if (jsonpDone) {
13716        jsonpDone();
13717      }
13718      if (xhr) {
13719        xhr.abort();
13720      }
13721    }
13722
13723    function completeRequest(callback, status, response, headersString, statusText, xhrStatus) {
13724      // cancel timeout and subsequent timeout promise resolution
13725      if (isDefined(timeoutId)) {
13726        $browserDefer.cancel(timeoutId);
13727      }
13728      jsonpDone = xhr = null;
13729
13730      callback(status, response, headersString, statusText, xhrStatus);
13731    }
13732  };
13733
13734  function jsonpReq(url, callbackPath, done) {
13735    url = url.replace('JSON_CALLBACK', callbackPath);
13736    // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
13737    // - fetches local scripts via XHR and evals them
13738    // - adds and immediately removes script elements from the document
13739    var script = rawDocument.createElement('script'), callback = null;
13740    script.type = 'text/javascript';
13741    script.src = url;
13742    script.async = true;
13743
13744    callback = function(event) {
13745      script.removeEventListener('load', callback);
13746      script.removeEventListener('error', callback);
13747      rawDocument.body.removeChild(script);
13748      script = null;
13749      var status = -1;
13750      var text = 'unknown';
13751
13752      if (event) {
13753        if (event.type === 'load' && !callbacks.wasCalled(callbackPath)) {
13754          event = { type: 'error' };
13755        }
13756        text = event.type;
13757        status = event.type === 'error' ? 404 : 200;
13758      }
13759
13760      if (done) {
13761        done(status, text);
13762      }
13763    };
13764
13765    script.addEventListener('load', callback);
13766    script.addEventListener('error', callback);
13767    rawDocument.body.appendChild(script);
13768    return callback;
13769  }
13770}
13771
13772var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
13773$interpolateMinErr.throwNoconcat = function(text) {
13774  throw $interpolateMinErr('noconcat',
13775      'Error while interpolating: {0}\nStrict Contextual Escaping disallows ' +
13776      'interpolations that concatenate multiple expressions when a trusted value is ' +
13777      'required.  See http://docs.angularjs.org/api/ng.$sce', text);
13778};
13779
13780$interpolateMinErr.interr = function(text, err) {
13781  return $interpolateMinErr('interr', 'Can\'t interpolate: {0}\n{1}', text, err.toString());
13782};
13783
13784/**
13785 * @ngdoc provider
13786 * @name $interpolateProvider
13787 * @this
13788 *
13789 * @description
13790 *
13791 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
13792 *
13793 * <div class="alert alert-danger">
13794 * This feature is sometimes used to mix different markup languages, e.g. to wrap an AngularJS
13795 * template within a Python Jinja template (or any other template language). Mixing templating
13796 * languages is **very dangerous**. The embedding template language will not safely escape AngularJS
13797 * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
13798 * security bugs!
13799 * </div>
13800 *
13801 * @example
13802<example name="custom-interpolation-markup" module="customInterpolationApp">
13803<file name="index.html">
13804<script>
13805  var customInterpolationApp = angular.module('customInterpolationApp', []);
13806
13807  customInterpolationApp.config(function($interpolateProvider) {
13808    $interpolateProvider.startSymbol('//');
13809    $interpolateProvider.endSymbol('//');
13810  });
13811
13812
13813  customInterpolationApp.controller('DemoController', function() {
13814      this.label = "This binding is brought you by // interpolation symbols.";
13815  });
13816</script>
13817<div ng-controller="DemoController as demo">
13818    //demo.label//
13819</div>
13820</file>
13821<file name="protractor.js" type="protractor">
13822  it('should interpolate binding with custom symbols', function() {
13823    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
13824  });
13825</file>
13826</example>
13827 */
13828function $InterpolateProvider() {
13829  var startSymbol = '{{';
13830  var endSymbol = '}}';
13831
13832  /**
13833   * @ngdoc method
13834   * @name $interpolateProvider#startSymbol
13835   * @description
13836   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
13837   *
13838   * @param {string=} value new value to set the starting symbol to.
13839   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
13840   */
13841  this.startSymbol = function(value) {
13842    if (value) {
13843      startSymbol = value;
13844      return this;
13845    }
13846    return startSymbol;
13847  };
13848
13849  /**
13850   * @ngdoc method
13851   * @name $interpolateProvider#endSymbol
13852   * @description
13853   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
13854   *
13855   * @param {string=} value new value to set the ending symbol to.
13856   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
13857   */
13858  this.endSymbol = function(value) {
13859    if (value) {
13860      endSymbol = value;
13861      return this;
13862    }
13863    return endSymbol;
13864  };
13865
13866
13867  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
13868    var startSymbolLength = startSymbol.length,
13869        endSymbolLength = endSymbol.length,
13870        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
13871        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
13872
13873    function escape(ch) {
13874      return '\\\\\\' + ch;
13875    }
13876
13877    function unescapeText(text) {
13878      return text.replace(escapedStartRegexp, startSymbol).
13879        replace(escapedEndRegexp, endSymbol);
13880    }
13881
13882    // TODO: this is the same as the constantWatchDelegate in parse.js
13883    function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
13884      var unwatch = scope.$watch(function constantInterpolateWatch(scope) {
13885        unwatch();
13886        return constantInterp(scope);
13887      }, listener, objectEquality);
13888      return unwatch;
13889    }
13890
13891    /**
13892     * @ngdoc service
13893     * @name $interpolate
13894     * @kind function
13895     *
13896     * @requires $parse
13897     * @requires $sce
13898     *
13899     * @description
13900     *
13901     * Compiles a string with markup into an interpolation function. This service is used by the
13902     * HTML {@link ng.$compile $compile} service for data binding. See
13903     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
13904     * interpolation markup.
13905     *
13906     *
13907     * ```js
13908     *   var $interpolate = ...; // injected
13909     *   var exp = $interpolate('Hello {{name | uppercase}}!');
13910     *   expect(exp({name:'AngularJS'})).toEqual('Hello ANGULARJS!');
13911     * ```
13912     *
13913     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
13914     * `true`, the interpolation function will return `undefined` unless all embedded expressions
13915     * evaluate to a value other than `undefined`.
13916     *
13917     * ```js
13918     *   var $interpolate = ...; // injected
13919     *   var context = {greeting: 'Hello', name: undefined };
13920     *
13921     *   // default "forgiving" mode
13922     *   var exp = $interpolate('{{greeting}} {{name}}!');
13923     *   expect(exp(context)).toEqual('Hello !');
13924     *
13925     *   // "allOrNothing" mode
13926     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
13927     *   expect(exp(context)).toBeUndefined();
13928     *   context.name = 'AngularJS';
13929     *   expect(exp(context)).toEqual('Hello AngularJS!');
13930     * ```
13931     *
13932     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
13933     *
13934     * #### Escaped Interpolation
13935     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
13936     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
13937     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
13938     * or binding.
13939     *
13940     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
13941     * degree, while also enabling code examples to work without relying on the
13942     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
13943     *
13944     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
13945     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
13946     * interpolation start/end markers with their escaped counterparts.**
13947     *
13948     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
13949     * output when the $interpolate service processes the text. So, for HTML elements interpolated
13950     * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
13951     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
13952     * this is typically useful only when user-data is used in rendering a template from the server, or
13953     * when otherwise untrusted data is used by a directive.
13954     *
13955     * <example name="interpolation">
13956     *  <file name="index.html">
13957     *    <div ng-init="username='A user'">
13958     *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
13959     *        </p>
13960     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
13961     *        application, but fails to accomplish their task, because the server has correctly
13962     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
13963     *        characters.</p>
13964     *      <p>Instead, the result of the attempted script injection is visible, and can be removed
13965     *        from the database by an administrator.</p>
13966     *    </div>
13967     *  </file>
13968     * </example>
13969     *
13970     * @knownIssue
13971     * It is currently not possible for an interpolated expression to contain the interpolation end
13972     * symbol. For example, `{{ '}}' }}` will be incorrectly interpreted as `{{ ' }}` + `' }}`, i.e.
13973     * an interpolated expression consisting of a single-quote (`'`) and the `' }}` string.
13974     *
13975     * @knownIssue
13976     * All directives and components must use the standard `{{` `}}` interpolation symbols
13977     * in their templates. If you change the application interpolation symbols the {@link $compile}
13978     * service will attempt to denormalize the standard symbols to the custom symbols.
13979     * The denormalization process is not clever enough to know not to replace instances of the standard
13980     * symbols where they would not normally be treated as interpolation symbols. For example in the following
13981     * code snippet the closing braces of the literal object will get incorrectly denormalized:
13982     *
13983     * ```
13984     * <div data-context='{"context":{"id":3,"type":"page"}}">
13985     * ```
13986     *
13987     * The workaround is to ensure that such instances are separated by whitespace:
13988     * ```
13989     * <div data-context='{"context":{"id":3,"type":"page"} }">
13990     * ```
13991     *
13992     * See https://github.com/angular/angular.js/pull/14610#issuecomment-219401099 for more information.
13993     *
13994     * @param {string} text The text with markup to interpolate.
13995     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
13996     *    embedded expression in order to return an interpolation function. Strings with no
13997     *    embedded expression will return null for the interpolation function.
13998     * @param {string=} trustedContext when provided, the returned function passes the interpolated
13999     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
14000     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
14001     *    provides Strict Contextual Escaping for details.
14002     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
14003     *    unless all embedded expressions evaluate to a value other than `undefined`.
14004     * @returns {function(context)} an interpolation function which is used to compute the
14005     *    interpolated string. The function has these parameters:
14006     *
14007     * - `context`: evaluation context for all expressions embedded in the interpolated text
14008     */
14009    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
14010      var contextAllowsConcatenation = trustedContext === $sce.URL || trustedContext === $sce.MEDIA_URL;
14011
14012      // Provide a quick exit and simplified result function for text with no interpolation
14013      if (!text.length || text.indexOf(startSymbol) === -1) {
14014        if (mustHaveExpression) return;
14015
14016        var unescapedText = unescapeText(text);
14017        if (contextAllowsConcatenation) {
14018          unescapedText = $sce.getTrusted(trustedContext, unescapedText);
14019        }
14020        var constantInterp = valueFn(unescapedText);
14021        constantInterp.exp = text;
14022        constantInterp.expressions = [];
14023        constantInterp.$$watchDelegate = constantWatchDelegate;
14024
14025        return constantInterp;
14026      }
14027
14028      allOrNothing = !!allOrNothing;
14029      var startIndex,
14030          endIndex,
14031          index = 0,
14032          expressions = [],
14033          parseFns,
14034          textLength = text.length,
14035          exp,
14036          concat = [],
14037          expressionPositions = [],
14038          singleExpression;
14039
14040
14041      while (index < textLength) {
14042        if (((startIndex = text.indexOf(startSymbol, index)) !== -1) &&
14043             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) !== -1)) {
14044          if (index !== startIndex) {
14045            concat.push(unescapeText(text.substring(index, startIndex)));
14046          }
14047          exp = text.substring(startIndex + startSymbolLength, endIndex);
14048          expressions.push(exp);
14049          index = endIndex + endSymbolLength;
14050          expressionPositions.push(concat.length);
14051          concat.push(''); // Placeholder that will get replaced with the evaluated expression.
14052        } else {
14053          // we did not find an interpolation, so we have to add the remainder to the separators array
14054          if (index !== textLength) {
14055            concat.push(unescapeText(text.substring(index)));
14056          }
14057          break;
14058        }
14059      }
14060
14061      singleExpression = concat.length === 1 && expressionPositions.length === 1;
14062      // Intercept expression if we need to stringify concatenated inputs, which may be SCE trusted
14063      // objects rather than simple strings
14064      // (we don't modify the expression if the input consists of only a single trusted input)
14065      var interceptor = contextAllowsConcatenation && singleExpression ? undefined : parseStringifyInterceptor;
14066      parseFns = expressions.map(function(exp) { return $parse(exp, interceptor); });
14067
14068      // Concatenating expressions makes it hard to reason about whether some combination of
14069      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
14070      // single expression be used for some $sce-managed secure contexts (RESOURCE_URLs mostly),
14071      // we ensure that the value that's used is assigned or constructed by some JS code somewhere
14072      // that is more testable or make it obvious that you bound the value to some user controlled
14073      // value.  This helps reduce the load when auditing for XSS issues.
14074
14075      // Note that URL and MEDIA_URL $sce contexts do not need this, since `$sce` can sanitize the values
14076      // passed to it. In that case, `$sce.getTrusted` will be called on either the single expression
14077      // or on the overall concatenated string (losing trusted types used in the mix, by design).
14078      // Both these methods will sanitize plain strings. Also, HTML could be included, but since it's
14079      // only used in srcdoc attributes, this would not be very useful.
14080
14081      if (!mustHaveExpression || expressions.length) {
14082        var compute = function(values) {
14083          for (var i = 0, ii = expressions.length; i < ii; i++) {
14084            if (allOrNothing && isUndefined(values[i])) return;
14085            concat[expressionPositions[i]] = values[i];
14086          }
14087
14088          if (contextAllowsConcatenation) {
14089            // If `singleExpression` then `concat[0]` might be a "trusted" value or `null`, rather than a string
14090            return $sce.getTrusted(trustedContext, singleExpression ? concat[0] : concat.join(''));
14091          } else if (trustedContext && concat.length > 1) {
14092            // This context does not allow more than one part, e.g. expr + string or exp + exp.
14093            $interpolateMinErr.throwNoconcat(text);
14094          }
14095          // In an unprivileged context or only one part: just concatenate and return.
14096          return concat.join('');
14097        };
14098
14099        return extend(function interpolationFn(context) {
14100            var i = 0;
14101            var ii = expressions.length;
14102            var values = new Array(ii);
14103
14104            try {
14105              for (; i < ii; i++) {
14106                values[i] = parseFns[i](context);
14107              }
14108
14109              return compute(values);
14110            } catch (err) {
14111              $exceptionHandler($interpolateMinErr.interr(text, err));
14112            }
14113
14114          }, {
14115          // all of these properties are undocumented for now
14116          exp: text, //just for compatibility with regular watchers created via $watch
14117          expressions: expressions,
14118          $$watchDelegate: function(scope, listener) {
14119            var lastValue;
14120            return scope.$watchGroup(parseFns, /** @this */ function interpolateFnWatcher(values, oldValues) {
14121              var currValue = compute(values);
14122              listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
14123              lastValue = currValue;
14124            });
14125          }
14126        });
14127      }
14128
14129      function parseStringifyInterceptor(value) {
14130        try {
14131          // In concatenable contexts, getTrusted comes at the end, to avoid sanitizing individual
14132          // parts of a full URL. We don't care about losing the trustedness here.
14133          // In non-concatenable contexts, where there is only one expression, this interceptor is
14134          // not applied to the expression.
14135          value = (trustedContext && !contextAllowsConcatenation) ?
14136                    $sce.getTrusted(trustedContext, value) :
14137                    $sce.valueOf(value);
14138          return allOrNothing && !isDefined(value) ? value : stringify(value);
14139        } catch (err) {
14140          $exceptionHandler($interpolateMinErr.interr(text, err));
14141        }
14142      }
14143    }
14144
14145
14146    /**
14147     * @ngdoc method
14148     * @name $interpolate#startSymbol
14149     * @description
14150     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
14151     *
14152     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
14153     * the symbol.
14154     *
14155     * @returns {string} start symbol.
14156     */
14157    $interpolate.startSymbol = function() {
14158      return startSymbol;
14159    };
14160
14161
14162    /**
14163     * @ngdoc method
14164     * @name $interpolate#endSymbol
14165     * @description
14166     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
14167     *
14168     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
14169     * the symbol.
14170     *
14171     * @returns {string} end symbol.
14172     */
14173    $interpolate.endSymbol = function() {
14174      return endSymbol;
14175    };
14176
14177    return $interpolate;
14178  }];
14179}
14180
14181var $intervalMinErr = minErr('$interval');
14182
14183/** @this */
14184function $IntervalProvider() {
14185  this.$get = ['$$intervalFactory', '$window',
14186       function($$intervalFactory,   $window) {
14187    var intervals = {};
14188    var setIntervalFn = function(tick, delay, deferred) {
14189      var id = $window.setInterval(tick, delay);
14190      intervals[id] = deferred;
14191      return id;
14192    };
14193    var clearIntervalFn = function(id) {
14194      $window.clearInterval(id);
14195      delete intervals[id];
14196    };
14197
14198    /**
14199     * @ngdoc service
14200     * @name $interval
14201     *
14202     * @description
14203     * AngularJS's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
14204     * milliseconds.
14205     *
14206     * The return value of registering an interval function is a promise. This promise will be
14207     * notified upon each tick of the interval, and will be resolved after `count` iterations, or
14208     * run indefinitely if `count` is not defined. The value of the notification will be the
14209     * number of iterations that have run.
14210     * To cancel an interval, call `$interval.cancel(promise)`.
14211     *
14212     * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
14213     * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
14214     * time.
14215     *
14216     * <div class="alert alert-warning">
14217     * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
14218     * with them.  In particular they are not automatically destroyed when a controller's scope or a
14219     * directive's element are destroyed.
14220     * You should take this into consideration and make sure to always cancel the interval at the
14221     * appropriate moment.  See the example below for more details on how and when to do this.
14222     * </div>
14223     *
14224     * @param {function()} fn A function that should be called repeatedly. If no additional arguments
14225     *   are passed (see below), the function is called with the current iteration count.
14226     * @param {number} delay Number of milliseconds between each function call.
14227     * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
14228     *   indefinitely.
14229     * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
14230     *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
14231     * @param {...*=} Pass additional parameters to the executed function.
14232     * @returns {promise} A promise which will be notified on each iteration. It will resolve once all iterations of the interval complete.
14233     *
14234     * @example
14235     * <example module="intervalExample" name="interval-service">
14236     * <file name="index.html">
14237     *   <script>
14238     *     angular.module('intervalExample', [])
14239     *       .controller('ExampleController', ['$scope', '$interval',
14240     *         function($scope, $interval) {
14241     *           $scope.format = 'M/d/yy h:mm:ss a';
14242     *           $scope.blood_1 = 100;
14243     *           $scope.blood_2 = 120;
14244     *
14245     *           var stop;
14246     *           $scope.fight = function() {
14247     *             // Don't start a new fight if we are already fighting
14248     *             if ( angular.isDefined(stop) ) return;
14249     *
14250     *             stop = $interval(function() {
14251     *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
14252     *                 $scope.blood_1 = $scope.blood_1 - 3;
14253     *                 $scope.blood_2 = $scope.blood_2 - 4;
14254     *               } else {
14255     *                 $scope.stopFight();
14256     *               }
14257     *             }, 100);
14258     *           };
14259     *
14260     *           $scope.stopFight = function() {
14261     *             if (angular.isDefined(stop)) {
14262     *               $interval.cancel(stop);
14263     *               stop = undefined;
14264     *             }
14265     *           };
14266     *
14267     *           $scope.resetFight = function() {
14268     *             $scope.blood_1 = 100;
14269     *             $scope.blood_2 = 120;
14270     *           };
14271     *
14272     *           $scope.$on('$destroy', function() {
14273     *             // Make sure that the interval is destroyed too
14274     *             $scope.stopFight();
14275     *           });
14276     *         }])
14277     *       // Register the 'myCurrentTime' directive factory method.
14278     *       // We inject $interval and dateFilter service since the factory method is DI.
14279     *       .directive('myCurrentTime', ['$interval', 'dateFilter',
14280     *         function($interval, dateFilter) {
14281     *           // return the directive link function. (compile function not needed)
14282     *           return function(scope, element, attrs) {
14283     *             var format,  // date format
14284     *                 stopTime; // so that we can cancel the time updates
14285     *
14286     *             // used to update the UI
14287     *             function updateTime() {
14288     *               element.text(dateFilter(new Date(), format));
14289     *             }
14290     *
14291     *             // watch the expression, and update the UI on change.
14292     *             scope.$watch(attrs.myCurrentTime, function(value) {
14293     *               format = value;
14294     *               updateTime();
14295     *             });
14296     *
14297     *             stopTime = $interval(updateTime, 1000);
14298     *
14299     *             // listen on DOM destroy (removal) event, and cancel the next UI update
14300     *             // to prevent updating time after the DOM element was removed.
14301     *             element.on('$destroy', function() {
14302     *               $interval.cancel(stopTime);
14303     *             });
14304     *           }
14305     *         }]);
14306     *   </script>
14307     *
14308     *   <div>
14309     *     <div ng-controller="ExampleController">
14310     *       <label>Date format: <input ng-model="format"></label> <hr/>
14311     *       Current time is: <span my-current-time="format"></span>
14312     *       <hr/>
14313     *       Blood 1 : <font color='red'>{{blood_1}}</font>
14314     *       Blood 2 : <font color='red'>{{blood_2}}</font>
14315     *       <button type="button" data-ng-click="fight()">Fight</button>
14316     *       <button type="button" data-ng-click="stopFight()">StopFight</button>
14317     *       <button type="button" data-ng-click="resetFight()">resetFight</button>
14318     *     </div>
14319     *   </div>
14320     *
14321     * </file>
14322     * </example>
14323     */
14324    var interval = $$intervalFactory(setIntervalFn, clearIntervalFn);
14325
14326    /**
14327     * @ngdoc method
14328     * @name $interval#cancel
14329     *
14330     * @description
14331     * Cancels a task associated with the `promise`.
14332     *
14333     * @param {Promise=} promise returned by the `$interval` function.
14334     * @returns {boolean} Returns `true` if the task was successfully canceled.
14335     */
14336    interval.cancel = function(promise) {
14337      if (!promise) return false;
14338
14339      if (!promise.hasOwnProperty('$$intervalId')) {
14340        throw $intervalMinErr('badprom',
14341            '`$interval.cancel()` called with a promise that was not generated by `$interval()`.');
14342      }
14343
14344      if (!intervals.hasOwnProperty(promise.$$intervalId)) return false;
14345
14346      var id = promise.$$intervalId;
14347      var deferred = intervals[id];
14348
14349      // Interval cancels should not report an unhandled promise.
14350      markQExceptionHandled(deferred.promise);
14351      deferred.reject('canceled');
14352      clearIntervalFn(id);
14353
14354      return true;
14355    };
14356
14357    return interval;
14358  }];
14359}
14360
14361/** @this */
14362function $$IntervalFactoryProvider() {
14363  this.$get = ['$browser', '$q', '$$q', '$rootScope',
14364       function($browser,   $q,   $$q,   $rootScope) {
14365    return function intervalFactory(setIntervalFn, clearIntervalFn) {
14366      return function intervalFn(fn, delay, count, invokeApply) {
14367        var hasParams = arguments.length > 4,
14368            args = hasParams ? sliceArgs(arguments, 4) : [],
14369            iteration = 0,
14370            skipApply = isDefined(invokeApply) && !invokeApply,
14371            deferred = (skipApply ? $$q : $q).defer(),
14372            promise = deferred.promise;
14373
14374        count = isDefined(count) ? count : 0;
14375
14376        function callback() {
14377          if (!hasParams) {
14378            fn(iteration);
14379          } else {
14380            fn.apply(null, args);
14381          }
14382        }
14383
14384        function tick() {
14385          if (skipApply) {
14386            $browser.defer(callback);
14387          } else {
14388            $rootScope.$evalAsync(callback);
14389          }
14390          deferred.notify(iteration++);
14391
14392          if (count > 0 && iteration >= count) {
14393            deferred.resolve(iteration);
14394            clearIntervalFn(promise.$$intervalId);
14395          }
14396
14397          if (!skipApply) $rootScope.$apply();
14398        }
14399
14400        promise.$$intervalId = setIntervalFn(tick, delay, deferred, skipApply);
14401
14402        return promise;
14403      };
14404    };
14405  }];
14406}
14407
14408/**
14409 * @ngdoc service
14410 * @name $jsonpCallbacks
14411 * @requires $window
14412 * @description
14413 * This service handles the lifecycle of callbacks to handle JSONP requests.
14414 * Override this service if you wish to customise where the callbacks are stored and
14415 * how they vary compared to the requested url.
14416 */
14417var $jsonpCallbacksProvider = /** @this */ function() {
14418  this.$get = function() {
14419    var callbacks = angular.callbacks;
14420    var callbackMap = {};
14421
14422    function createCallback(callbackId) {
14423      var callback = function(data) {
14424        callback.data = data;
14425        callback.called = true;
14426      };
14427      callback.id = callbackId;
14428      return callback;
14429    }
14430
14431    return {
14432      /**
14433       * @ngdoc method
14434       * @name $jsonpCallbacks#createCallback
14435       * @param {string} url the url of the JSONP request
14436       * @returns {string} the callback path to send to the server as part of the JSONP request
14437       * @description
14438       * {@link $httpBackend} calls this method to create a callback and get hold of the path to the callback
14439       * to pass to the server, which will be used to call the callback with its payload in the JSONP response.
14440       */
14441      createCallback: function(url) {
14442        var callbackId = '_' + (callbacks.$$counter++).toString(36);
14443        var callbackPath = 'angular.callbacks.' + callbackId;
14444        var callback = createCallback(callbackId);
14445        callbackMap[callbackPath] = callbacks[callbackId] = callback;
14446        return callbackPath;
14447      },
14448      /**
14449       * @ngdoc method
14450       * @name $jsonpCallbacks#wasCalled
14451       * @param {string} callbackPath the path to the callback that was sent in the JSONP request
14452       * @returns {boolean} whether the callback has been called, as a result of the JSONP response
14453       * @description
14454       * {@link $httpBackend} calls this method to find out whether the JSONP response actually called the
14455       * callback that was passed in the request.
14456       */
14457      wasCalled: function(callbackPath) {
14458        return callbackMap[callbackPath].called;
14459      },
14460      /**
14461       * @ngdoc method
14462       * @name $jsonpCallbacks#getResponse
14463       * @param {string} callbackPath the path to the callback that was sent in the JSONP request
14464       * @returns {*} the data received from the response via the registered callback
14465       * @description
14466       * {@link $httpBackend} calls this method to get hold of the data that was provided to the callback
14467       * in the JSONP response.
14468       */
14469      getResponse: function(callbackPath) {
14470        return callbackMap[callbackPath].data;
14471      },
14472      /**
14473       * @ngdoc method
14474       * @name $jsonpCallbacks#removeCallback
14475       * @param {string} callbackPath the path to the callback that was sent in the JSONP request
14476       * @description
14477       * {@link $httpBackend} calls this method to remove the callback after the JSONP request has
14478       * completed or timed-out.
14479       */
14480      removeCallback: function(callbackPath) {
14481        var callback = callbackMap[callbackPath];
14482        delete callbacks[callback.id];
14483        delete callbackMap[callbackPath];
14484      }
14485    };
14486  };
14487};
14488
14489/**
14490 * @ngdoc service
14491 * @name $locale
14492 *
14493 * @description
14494 * $locale service provides localization rules for various AngularJS components. As of right now the
14495 * only public api is:
14496 *
14497 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
14498 */
14499
14500/* global stripHash: true */
14501
14502var PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/,
14503    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
14504var $locationMinErr = minErr('$location');
14505
14506
14507/**
14508 * Encode path using encodeUriSegment, ignoring forward slashes
14509 *
14510 * @param {string} path Path to encode
14511 * @returns {string}
14512 */
14513function encodePath(path) {
14514  var segments = path.split('/'),
14515      i = segments.length;
14516
14517  while (i--) {
14518    // decode forward slashes to prevent them from being double encoded
14519    segments[i] = encodeUriSegment(segments[i].replace(/%2F/g, '/'));
14520  }
14521
14522  return segments.join('/');
14523}
14524
14525function decodePath(path, html5Mode) {
14526  var segments = path.split('/'),
14527      i = segments.length;
14528
14529  while (i--) {
14530    segments[i] = decodeURIComponent(segments[i]);
14531    if (html5Mode) {
14532      // encode forward slashes to prevent them from being mistaken for path separators
14533      segments[i] = segments[i].replace(/\//g, '%2F');
14534    }
14535  }
14536
14537  return segments.join('/');
14538}
14539
14540function normalizePath(pathValue, searchValue, hashValue) {
14541  var search = toKeyValue(searchValue),
14542    hash = hashValue ? '#' + encodeUriSegment(hashValue) : '',
14543    path = encodePath(pathValue);
14544
14545  return path + (search ? '?' + search : '') + hash;
14546}
14547
14548function parseAbsoluteUrl(absoluteUrl, locationObj) {
14549  var parsedUrl = urlResolve(absoluteUrl);
14550
14551  locationObj.$$protocol = parsedUrl.protocol;
14552  locationObj.$$host = parsedUrl.hostname;
14553  locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
14554}
14555
14556var DOUBLE_SLASH_REGEX = /^\s*[\\/]{2,}/;
14557function parseAppUrl(url, locationObj, html5Mode) {
14558
14559  if (DOUBLE_SLASH_REGEX.test(url)) {
14560    throw $locationMinErr('badpath', 'Invalid url "{0}".', url);
14561  }
14562
14563  var prefixed = (url.charAt(0) !== '/');
14564  if (prefixed) {
14565    url = '/' + url;
14566  }
14567  var match = urlResolve(url);
14568  var path = prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname;
14569  locationObj.$$path = decodePath(path, html5Mode);
14570  locationObj.$$search = parseKeyValue(match.search);
14571  locationObj.$$hash = decodeURIComponent(match.hash);
14572
14573  // make sure path starts with '/';
14574  if (locationObj.$$path && locationObj.$$path.charAt(0) !== '/') {
14575    locationObj.$$path = '/' + locationObj.$$path;
14576  }
14577}
14578
14579function startsWith(str, search) {
14580  return str.slice(0, search.length) === search;
14581}
14582
14583/**
14584 *
14585 * @param {string} base
14586 * @param {string} url
14587 * @returns {string} returns text from `url` after `base` or `undefined` if it does not begin with
14588 *                   the expected string.
14589 */
14590function stripBaseUrl(base, url) {
14591  if (startsWith(url, base)) {
14592    return url.substr(base.length);
14593  }
14594}
14595
14596function stripHash(url) {
14597  var index = url.indexOf('#');
14598  return index === -1 ? url : url.substr(0, index);
14599}
14600
14601function stripFile(url) {
14602  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
14603}
14604
14605/* return the server only (scheme://host:port) */
14606function serverBase(url) {
14607  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
14608}
14609
14610
14611/**
14612 * LocationHtml5Url represents a URL
14613 * This object is exposed as $location service when HTML5 mode is enabled and supported
14614 *
14615 * @constructor
14616 * @param {string} appBase application base URL
14617 * @param {string} appBaseNoFile application base URL stripped of any filename
14618 * @param {string} basePrefix URL path prefix
14619 */
14620function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
14621  this.$$html5 = true;
14622  basePrefix = basePrefix || '';
14623  parseAbsoluteUrl(appBase, this);
14624
14625
14626  /**
14627   * Parse given HTML5 (regular) URL string into properties
14628   * @param {string} url HTML5 URL
14629   * @private
14630   */
14631  this.$$parse = function(url) {
14632    var pathUrl = stripBaseUrl(appBaseNoFile, url);
14633    if (!isString(pathUrl)) {
14634      throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
14635          appBaseNoFile);
14636    }
14637
14638    parseAppUrl(pathUrl, this, true);
14639
14640    if (!this.$$path) {
14641      this.$$path = '/';
14642    }
14643
14644    this.$$compose();
14645  };
14646
14647  this.$$normalizeUrl = function(url) {
14648    return appBaseNoFile + url.substr(1); // first char is always '/'
14649  };
14650
14651  this.$$parseLinkUrl = function(url, relHref) {
14652    if (relHref && relHref[0] === '#') {
14653      // special case for links to hash fragments:
14654      // keep the old url and only replace the hash fragment
14655      this.hash(relHref.slice(1));
14656      return true;
14657    }
14658    var appUrl, prevAppUrl;
14659    var rewrittenUrl;
14660
14661
14662    if (isDefined(appUrl = stripBaseUrl(appBase, url))) {
14663      prevAppUrl = appUrl;
14664      if (basePrefix && isDefined(appUrl = stripBaseUrl(basePrefix, appUrl))) {
14665        rewrittenUrl = appBaseNoFile + (stripBaseUrl('/', appUrl) || appUrl);
14666      } else {
14667        rewrittenUrl = appBase + prevAppUrl;
14668      }
14669    } else if (isDefined(appUrl = stripBaseUrl(appBaseNoFile, url))) {
14670      rewrittenUrl = appBaseNoFile + appUrl;
14671    } else if (appBaseNoFile === url + '/') {
14672      rewrittenUrl = appBaseNoFile;
14673    }
14674    if (rewrittenUrl) {
14675      this.$$parse(rewrittenUrl);
14676    }
14677    return !!rewrittenUrl;
14678  };
14679}
14680
14681
14682/**
14683 * LocationHashbangUrl represents URL
14684 * This object is exposed as $location service when developer doesn't opt into html5 mode.
14685 * It also serves as the base class for html5 mode fallback on legacy browsers.
14686 *
14687 * @constructor
14688 * @param {string} appBase application base URL
14689 * @param {string} appBaseNoFile application base URL stripped of any filename
14690 * @param {string} hashPrefix hashbang prefix
14691 */
14692function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
14693
14694  parseAbsoluteUrl(appBase, this);
14695
14696
14697  /**
14698   * Parse given hashbang URL into properties
14699   * @param {string} url Hashbang URL
14700   * @private
14701   */
14702  this.$$parse = function(url) {
14703    var withoutBaseUrl = stripBaseUrl(appBase, url) || stripBaseUrl(appBaseNoFile, url);
14704    var withoutHashUrl;
14705
14706    if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
14707
14708      // The rest of the URL starts with a hash so we have
14709      // got either a hashbang path or a plain hash fragment
14710      withoutHashUrl = stripBaseUrl(hashPrefix, withoutBaseUrl);
14711      if (isUndefined(withoutHashUrl)) {
14712        // There was no hashbang prefix so we just have a hash fragment
14713        withoutHashUrl = withoutBaseUrl;
14714      }
14715
14716    } else {
14717      // There was no hashbang path nor hash fragment:
14718      // If we are in HTML5 mode we use what is left as the path;
14719      // Otherwise we ignore what is left
14720      if (this.$$html5) {
14721        withoutHashUrl = withoutBaseUrl;
14722      } else {
14723        withoutHashUrl = '';
14724        if (isUndefined(withoutBaseUrl)) {
14725          appBase = url;
14726          /** @type {?} */ (this).replace();
14727        }
14728      }
14729    }
14730
14731    parseAppUrl(withoutHashUrl, this, false);
14732
14733    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
14734
14735    this.$$compose();
14736
14737    /*
14738     * In Windows, on an anchor node on documents loaded from
14739     * the filesystem, the browser will return a pathname
14740     * prefixed with the drive name ('/C:/path') when a
14741     * pathname without a drive is set:
14742     *  * a.setAttribute('href', '/foo')
14743     *   * a.pathname === '/C:/foo' //true
14744     *
14745     * Inside of AngularJS, we're always using pathnames that
14746     * do not include drive names for routing.
14747     */
14748    function removeWindowsDriveName(path, url, base) {
14749      /*
14750      Matches paths for file protocol on windows,
14751      such as /C:/foo/bar, and captures only /foo/bar.
14752      */
14753      var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
14754
14755      var firstPathSegmentMatch;
14756
14757      //Get the relative path from the input URL.
14758      if (startsWith(url, base)) {
14759        url = url.replace(base, '');
14760      }
14761
14762      // The input URL intentionally contains a first path segment that ends with a colon.
14763      if (windowsFilePathExp.exec(url)) {
14764        return path;
14765      }
14766
14767      firstPathSegmentMatch = windowsFilePathExp.exec(path);
14768      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
14769    }
14770  };
14771
14772  this.$$normalizeUrl = function(url) {
14773    return appBase + (url ? hashPrefix + url : '');
14774  };
14775
14776  this.$$parseLinkUrl = function(url, relHref) {
14777    if (stripHash(appBase) === stripHash(url)) {
14778      this.$$parse(url);
14779      return true;
14780    }
14781    return false;
14782  };
14783}
14784
14785
14786/**
14787 * LocationHashbangUrl represents URL
14788 * This object is exposed as $location service when html5 history api is enabled but the browser
14789 * does not support it.
14790 *
14791 * @constructor
14792 * @param {string} appBase application base URL
14793 * @param {string} appBaseNoFile application base URL stripped of any filename
14794 * @param {string} hashPrefix hashbang prefix
14795 */
14796function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
14797  this.$$html5 = true;
14798  LocationHashbangUrl.apply(this, arguments);
14799
14800  this.$$parseLinkUrl = function(url, relHref) {
14801    if (relHref && relHref[0] === '#') {
14802      // special case for links to hash fragments:
14803      // keep the old url and only replace the hash fragment
14804      this.hash(relHref.slice(1));
14805      return true;
14806    }
14807
14808    var rewrittenUrl;
14809    var appUrl;
14810
14811    if (appBase === stripHash(url)) {
14812      rewrittenUrl = url;
14813    } else if ((appUrl = stripBaseUrl(appBaseNoFile, url))) {
14814      rewrittenUrl = appBase + hashPrefix + appUrl;
14815    } else if (appBaseNoFile === url + '/') {
14816      rewrittenUrl = appBaseNoFile;
14817    }
14818    if (rewrittenUrl) {
14819      this.$$parse(rewrittenUrl);
14820    }
14821    return !!rewrittenUrl;
14822  };
14823
14824  this.$$normalizeUrl = function(url) {
14825    // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
14826    return appBase + hashPrefix + url;
14827  };
14828}
14829
14830
14831var locationPrototype = {
14832
14833  /**
14834   * Ensure absolute URL is initialized.
14835   * @private
14836   */
14837  $$absUrl:'',
14838
14839  /**
14840   * Are we in html5 mode?
14841   * @private
14842   */
14843  $$html5: false,
14844
14845  /**
14846   * Has any change been replacing?
14847   * @private
14848   */
14849  $$replace: false,
14850
14851  /**
14852   * Compose url and update `url` and `absUrl` property
14853   * @private
14854   */
14855  $$compose: function() {
14856    this.$$url = normalizePath(this.$$path, this.$$search, this.$$hash);
14857    this.$$absUrl = this.$$normalizeUrl(this.$$url);
14858    this.$$urlUpdatedByLocation = true;
14859  },
14860
14861  /**
14862   * @ngdoc method
14863   * @name $location#absUrl
14864   *
14865   * @description
14866   * This method is getter only.
14867   *
14868   * Return full URL representation with all segments encoded according to rules specified in
14869   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
14870   *
14871   *
14872   * ```js
14873   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
14874   * var absUrl = $location.absUrl();
14875   * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
14876   * ```
14877   *
14878   * @return {string} full URL
14879   */
14880  absUrl: locationGetter('$$absUrl'),
14881
14882  /**
14883   * @ngdoc method
14884   * @name $location#url
14885   *
14886   * @description
14887   * This method is getter / setter.
14888   *
14889   * Return URL (e.g. `/path?a=b#hash`) when called without any parameter.
14890   *
14891   * Change path, search and hash, when called with parameter and return `$location`.
14892   *
14893   *
14894   * ```js
14895   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
14896   * var url = $location.url();
14897   * // => "/some/path?foo=bar&baz=xoxo"
14898   * ```
14899   *
14900   * @param {string=} url New URL without base prefix (e.g. `/path?a=b#hash`)
14901   * @return {string} url
14902   */
14903  url: function(url) {
14904    if (isUndefined(url)) {
14905      return this.$$url;
14906    }
14907
14908    var match = PATH_MATCH.exec(url);
14909    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
14910    if (match[2] || match[1] || url === '') this.search(match[3] || '');
14911    this.hash(match[5] || '');
14912
14913    return this;
14914  },
14915
14916  /**
14917   * @ngdoc method
14918   * @name $location#protocol
14919   *
14920   * @description
14921   * This method is getter only.
14922   *
14923   * Return protocol of current URL.
14924   *
14925   *
14926   * ```js
14927   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
14928   * var protocol = $location.protocol();
14929   * // => "http"
14930   * ```
14931   *
14932   * @return {string} protocol of current URL
14933   */
14934  protocol: locationGetter('$$protocol'),
14935
14936  /**
14937   * @ngdoc method
14938   * @name $location#host
14939   *
14940   * @description
14941   * This method is getter only.
14942   *
14943   * Return host of current URL.
14944   *
14945   * Note: compared to the non-AngularJS version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
14946   *
14947   *
14948   * ```js
14949   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
14950   * var host = $location.host();
14951   * // => "example.com"
14952   *
14953   * // given URL http://user:[email protected]:8080/#/some/path?foo=bar&baz=xoxo
14954   * host = $location.host();
14955   * // => "example.com"
14956   * host = location.host;
14957   * // => "example.com:8080"
14958   * ```
14959   *
14960   * @return {string} host of current URL.
14961   */
14962  host: locationGetter('$$host'),
14963
14964  /**
14965   * @ngdoc method
14966   * @name $location#port
14967   *
14968   * @description
14969   * This method is getter only.
14970   *
14971   * Return port of current URL.
14972   *
14973   *
14974   * ```js
14975   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
14976   * var port = $location.port();
14977   * // => 80
14978   * ```
14979   *
14980   * @return {Number} port
14981   */
14982  port: locationGetter('$$port'),
14983
14984  /**
14985   * @ngdoc method
14986   * @name $location#path
14987   *
14988   * @description
14989   * This method is getter / setter.
14990   *
14991   * Return path of current URL when called without any parameter.
14992   *
14993   * Change path when called with parameter and return `$location`.
14994   *
14995   * Note: Path should always begin with forward slash (/), this method will add the forward slash
14996   * if it is missing.
14997   *
14998   *
14999   * ```js
15000   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
15001   * var path = $location.path();
15002   * // => "/some/path"
15003   * ```
15004   *
15005   * @param {(string|number)=} path New path
15006   * @return {(string|object)} path if called with no parameters, or `$location` if called with a parameter
15007   */
15008  path: locationGetterSetter('$$path', function(path) {
15009    path = path !== null ? path.toString() : '';
15010    return path.charAt(0) === '/' ? path : '/' + path;
15011  }),
15012
15013  /**
15014   * @ngdoc method
15015   * @name $location#search
15016   *
15017   * @description
15018   * This method is getter / setter.
15019   *
15020   * Return search part (as object) of current URL when called without any parameter.
15021   *
15022   * Change search part when called with parameter and return `$location`.
15023   *
15024   *
15025   * ```js
15026   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
15027   * var searchObject = $location.search();
15028   * // => {foo: 'bar', baz: 'xoxo'}
15029   *
15030   * // set foo to 'yipee'
15031   * $location.search('foo', 'yipee');
15032   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
15033   * ```
15034   *
15035   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
15036   * hash object.
15037   *
15038   * When called with a single argument the method acts as a setter, setting the `search` component
15039   * of `$location` to the specified value.
15040   *
15041   * If the argument is a hash object containing an array of values, these values will be encoded
15042   * as duplicate search parameters in the URL.
15043   *
15044   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
15045   * will override only a single search property.
15046   *
15047   * If `paramValue` is an array, it will override the property of the `search` component of
15048   * `$location` specified via the first argument.
15049   *
15050   * If `paramValue` is `null`, the property specified via the first argument will be deleted.
15051   *
15052   * If `paramValue` is `true`, the property specified via the first argument will be added with no
15053   * value nor trailing equal sign.
15054   *
15055   * @return {Object} If called with no arguments returns the parsed `search` object. If called with
15056   * one or more arguments returns `$location` object itself.
15057   */
15058  search: function(search, paramValue) {
15059    switch (arguments.length) {
15060      case 0:
15061        return this.$$search;
15062      case 1:
15063        if (isString(search) || isNumber(search)) {
15064          search = search.toString();
15065          this.$$search = parseKeyValue(search);
15066        } else if (isObject(search)) {
15067          search = copy(search, {});
15068          // remove object undefined or null properties
15069          forEach(search, function(value, key) {
15070            if (value == null) delete search[key];
15071          });
15072
15073          this.$$search = search;
15074        } else {
15075          throw $locationMinErr('isrcharg',
15076              'The first argument of the `$location#search()` call must be a string or an object.');
15077        }
15078        break;
15079      default:
15080        if (isUndefined(paramValue) || paramValue === null) {
15081          delete this.$$search[search];
15082        } else {
15083          this.$$search[search] = paramValue;
15084        }
15085    }
15086
15087    this.$$compose();
15088    return this;
15089  },
15090
15091  /**
15092   * @ngdoc method
15093   * @name $location#hash
15094   *
15095   * @description
15096   * This method is getter / setter.
15097   *
15098   * Returns the hash fragment when called without any parameters.
15099   *
15100   * Changes the hash fragment when called with a parameter and returns `$location`.
15101   *
15102   *
15103   * ```js
15104   * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
15105   * var hash = $location.hash();
15106   * // => "hashValue"
15107   * ```
15108   *
15109   * @param {(string|number)=} hash New hash fragment
15110   * @return {string} hash
15111   */
15112  hash: locationGetterSetter('$$hash', function(hash) {
15113    return hash !== null ? hash.toString() : '';
15114  }),
15115
15116  /**
15117   * @ngdoc method
15118   * @name $location#replace
15119   *
15120   * @description
15121   * If called, all changes to $location during the current `$digest` will replace the current history
15122   * record, instead of adding a new one.
15123   */
15124  replace: function() {
15125    this.$$replace = true;
15126    return this;
15127  }
15128};
15129
15130forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
15131  Location.prototype = Object.create(locationPrototype);
15132
15133  /**
15134   * @ngdoc method
15135   * @name $location#state
15136   *
15137   * @description
15138   * This method is getter / setter.
15139   *
15140   * Return the history state object when called without any parameter.
15141   *
15142   * Change the history state object when called with one parameter and return `$location`.
15143   * The state object is later passed to `pushState` or `replaceState`.
15144   *
15145   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
15146   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
15147   * older browsers (like IE9 or Android < 4.0), don't use this method.
15148   *
15149   * @param {object=} state State object for pushState or replaceState
15150   * @return {object} state
15151   */
15152  Location.prototype.state = function(state) {
15153    if (!arguments.length) {
15154      return this.$$state;
15155    }
15156
15157    if (Location !== LocationHtml5Url || !this.$$html5) {
15158      throw $locationMinErr('nostate', 'History API state support is available only ' +
15159        'in HTML5 mode and only in browsers supporting HTML5 History API');
15160    }
15161    // The user might modify `stateObject` after invoking `$location.state(stateObject)`
15162    // but we're changing the $$state reference to $browser.state() during the $digest
15163    // so the modification window is narrow.
15164    this.$$state = isUndefined(state) ? null : state;
15165    this.$$urlUpdatedByLocation = true;
15166
15167    return this;
15168  };
15169});
15170
15171
15172function locationGetter(property) {
15173  return /** @this */ function() {
15174    return this[property];
15175  };
15176}
15177
15178
15179function locationGetterSetter(property, preprocess) {
15180  return /** @this */ function(value) {
15181    if (isUndefined(value)) {
15182      return this[property];
15183    }
15184
15185    this[property] = preprocess(value);
15186    this.$$compose();
15187
15188    return this;
15189  };
15190}
15191
15192
15193/**
15194 * @ngdoc service
15195 * @name $location
15196 *
15197 * @requires $rootElement
15198 *
15199 * @description
15200 * The $location service parses the URL in the browser address bar (based on the
15201 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
15202 * available to your application. Changes to the URL in the address bar are reflected into
15203 * $location service and changes to $location are reflected into the browser address bar.
15204 *
15205 * **The $location service:**
15206 *
15207 * - Exposes the current URL in the browser address bar, so you can
15208 *   - Watch and observe the URL.
15209 *   - Change the URL.
15210 * - Synchronizes the URL with the browser when the user
15211 *   - Changes the address bar.
15212 *   - Clicks the back or forward button (or clicks a History link).
15213 *   - Clicks on a link.
15214 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
15215 *
15216 * For more information see {@link guide/$location Developer Guide: Using $location}
15217 */
15218
15219/**
15220 * @ngdoc provider
15221 * @name $locationProvider
15222 * @this
15223 *
15224 * @description
15225 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
15226 */
15227function $LocationProvider() {
15228  var hashPrefix = '!',
15229      html5Mode = {
15230        enabled: false,
15231        requireBase: true,
15232        rewriteLinks: true
15233      };
15234
15235  /**
15236   * @ngdoc method
15237   * @name $locationProvider#hashPrefix
15238   * @description
15239   * The default value for the prefix is `'!'`.
15240   * @param {string=} prefix Prefix for hash part (containing path and search)
15241   * @returns {*} current value if used as getter or itself (chaining) if used as setter
15242   */
15243  this.hashPrefix = function(prefix) {
15244    if (isDefined(prefix)) {
15245      hashPrefix = prefix;
15246      return this;
15247    } else {
15248      return hashPrefix;
15249    }
15250  };
15251
15252  /**
15253   * @ngdoc method
15254   * @name $locationProvider#html5Mode
15255   * @description
15256   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
15257   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
15258   *   properties:
15259   *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
15260   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
15261   *     support `pushState`.
15262   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
15263   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
15264   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
15265   *     See the {@link guide/$location $location guide for more information}
15266   *   - **rewriteLinks** - `{boolean|string}` - (default: `true`) When html5Mode is enabled,
15267   *     enables/disables URL rewriting for relative links. If set to a string, URL rewriting will
15268   *     only happen on links with an attribute that matches the given string. For example, if set
15269   *     to `'internal-link'`, then the URL will only be rewritten for `<a internal-link>` links.
15270   *     Note that [attribute name normalization](guide/directive#normalization) does not apply
15271   *     here, so `'internalLink'` will **not** match `'internal-link'`.
15272   *
15273   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
15274   */
15275  this.html5Mode = function(mode) {
15276    if (isBoolean(mode)) {
15277      html5Mode.enabled = mode;
15278      return this;
15279    } else if (isObject(mode)) {
15280
15281      if (isBoolean(mode.enabled)) {
15282        html5Mode.enabled = mode.enabled;
15283      }
15284
15285      if (isBoolean(mode.requireBase)) {
15286        html5Mode.requireBase = mode.requireBase;
15287      }
15288
15289      if (isBoolean(mode.rewriteLinks) || isString(mode.rewriteLinks)) {
15290        html5Mode.rewriteLinks = mode.rewriteLinks;
15291      }
15292
15293      return this;
15294    } else {
15295      return html5Mode;
15296    }
15297  };
15298
15299  /**
15300   * @ngdoc event
15301   * @name $location#$locationChangeStart
15302   * @eventType broadcast on root scope
15303   * @description
15304   * Broadcasted before a URL will change.
15305   *
15306   * This change can be prevented by calling
15307   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
15308   * details about event object. Upon successful change
15309   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
15310   *
15311   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
15312   * the browser supports the HTML5 History API.
15313   *
15314   * @param {Object} angularEvent Synthetic event object.
15315   * @param {string} newUrl New URL
15316   * @param {string=} oldUrl URL that was before it was changed.
15317   * @param {string=} newState New history state object
15318   * @param {string=} oldState History state object that was before it was changed.
15319   */
15320
15321  /**
15322   * @ngdoc event
15323   * @name $location#$locationChangeSuccess
15324   * @eventType broadcast on root scope
15325   * @description
15326   * Broadcasted after a URL was changed.
15327   *
15328   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
15329   * the browser supports the HTML5 History API.
15330   *
15331   * @param {Object} angularEvent Synthetic event object.
15332   * @param {string} newUrl New URL
15333   * @param {string=} oldUrl URL that was before it was changed.
15334   * @param {string=} newState New history state object
15335   * @param {string=} oldState History state object that was before it was changed.
15336   */
15337
15338  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
15339      function($rootScope, $browser, $sniffer, $rootElement, $window) {
15340    var $location,
15341        LocationMode,
15342        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
15343        initialUrl = $browser.url(),
15344        appBase;
15345
15346    if (html5Mode.enabled) {
15347      if (!baseHref && html5Mode.requireBase) {
15348        throw $locationMinErr('nobase',
15349          '$location in HTML5 mode requires a <base> tag to be present!');
15350      }
15351      appBase = serverBase(initialUrl) + (baseHref || '/');
15352      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
15353    } else {
15354      appBase = stripHash(initialUrl);
15355      LocationMode = LocationHashbangUrl;
15356    }
15357    var appBaseNoFile = stripFile(appBase);
15358
15359    $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
15360    $location.$$parseLinkUrl(initialUrl, initialUrl);
15361
15362    $location.$$state = $browser.state();
15363
15364    var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
15365
15366    // Determine if two URLs are equal despite potentially having different encoding/normalizing
15367    //  such as $location.absUrl() vs $browser.url()
15368    // See https://github.com/angular/angular.js/issues/16592
15369    function urlsEqual(a, b) {
15370      return a === b || urlResolve(a).href === urlResolve(b).href;
15371    }
15372
15373    function setBrowserUrlWithFallback(url, replace, state) {
15374      var oldUrl = $location.url();
15375      var oldState = $location.$$state;
15376      try {
15377        $browser.url(url, replace, state);
15378
15379        // Make sure $location.state() returns referentially identical (not just deeply equal)
15380        // state object; this makes possible quick checking if the state changed in the digest
15381        // loop. Checking deep equality would be too expensive.
15382        $location.$$state = $browser.state();
15383      } catch (e) {
15384        // Restore old values if pushState fails
15385        $location.url(oldUrl);
15386        $location.$$state = oldState;
15387
15388        throw e;
15389      }
15390    }
15391
15392    $rootElement.on('click', function(event) {
15393      var rewriteLinks = html5Mode.rewriteLinks;
15394      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
15395      // currently we open nice url link and redirect then
15396
15397      if (!rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which === 2 || event.button === 2) return;
15398
15399      var elm = jqLite(event.target);
15400
15401      // traverse the DOM up to find first A tag
15402      while (nodeName_(elm[0]) !== 'a') {
15403        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
15404        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
15405      }
15406
15407      if (isString(rewriteLinks) && isUndefined(elm.attr(rewriteLinks))) return;
15408
15409      var absHref = elm.prop('href');
15410      // get the actual href attribute - see
15411      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
15412      var relHref = elm.attr('href') || elm.attr('xlink:href');
15413
15414      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
15415        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
15416        // an animation.
15417        absHref = urlResolve(absHref.animVal).href;
15418      }
15419
15420      // Ignore when url is started with javascript: or mailto:
15421      if (IGNORE_URI_REGEXP.test(absHref)) return;
15422
15423      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
15424        if ($location.$$parseLinkUrl(absHref, relHref)) {
15425          // We do a preventDefault for all urls that are part of the AngularJS application,
15426          // in html5mode and also without, so that we are able to abort navigation without
15427          // getting double entries in the location history.
15428          event.preventDefault();
15429          // update location manually
15430          if ($location.absUrl() !== $browser.url()) {
15431            $rootScope.$apply();
15432          }
15433        }
15434      }
15435    });
15436
15437
15438    // rewrite hashbang url <> html5 url
15439    if ($location.absUrl() !== initialUrl) {
15440      $browser.url($location.absUrl(), true);
15441    }
15442
15443    var initializing = true;
15444
15445    // update $location when $browser url changes
15446    $browser.onUrlChange(function(newUrl, newState) {
15447
15448      if (!startsWith(newUrl, appBaseNoFile)) {
15449        // If we are navigating outside of the app then force a reload
15450        $window.location.href = newUrl;
15451        return;
15452      }
15453
15454      $rootScope.$evalAsync(function() {
15455        var oldUrl = $location.absUrl();
15456        var oldState = $location.$$state;
15457        var defaultPrevented;
15458        $location.$$parse(newUrl);
15459        $location.$$state = newState;
15460
15461        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
15462            newState, oldState).defaultPrevented;
15463
15464        // if the location was changed by a `$locationChangeStart` handler then stop
15465        // processing this location change
15466        if ($location.absUrl() !== newUrl) return;
15467
15468        if (defaultPrevented) {
15469          $location.$$parse(oldUrl);
15470          $location.$$state = oldState;
15471          setBrowserUrlWithFallback(oldUrl, false, oldState);
15472        } else {
15473          initializing = false;
15474          afterLocationChange(oldUrl, oldState);
15475        }
15476      });
15477      if (!$rootScope.$$phase) $rootScope.$digest();
15478    });
15479
15480    // update browser
15481    $rootScope.$watch(function $locationWatch() {
15482      if (initializing || $location.$$urlUpdatedByLocation) {
15483        $location.$$urlUpdatedByLocation = false;
15484
15485        var oldUrl = $browser.url();
15486        var newUrl = $location.absUrl();
15487        var oldState = $browser.state();
15488        var currentReplace = $location.$$replace;
15489        var urlOrStateChanged = !urlsEqual(oldUrl, newUrl) ||
15490          ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
15491
15492        if (initializing || urlOrStateChanged) {
15493          initializing = false;
15494
15495          $rootScope.$evalAsync(function() {
15496            var newUrl = $location.absUrl();
15497            var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
15498                $location.$$state, oldState).defaultPrevented;
15499
15500            // if the location was changed by a `$locationChangeStart` handler then stop
15501            // processing this location change
15502            if ($location.absUrl() !== newUrl) return;
15503
15504            if (defaultPrevented) {
15505              $location.$$parse(oldUrl);
15506              $location.$$state = oldState;
15507            } else {
15508              if (urlOrStateChanged) {
15509                setBrowserUrlWithFallback(newUrl, currentReplace,
15510                                          oldState === $location.$$state ? null : $location.$$state);
15511              }
15512              afterLocationChange(oldUrl, oldState);
15513            }
15514          });
15515        }
15516      }
15517
15518      $location.$$replace = false;
15519
15520      // we don't need to return anything because $evalAsync will make the digest loop dirty when
15521      // there is a change
15522    });
15523
15524    return $location;
15525
15526    function afterLocationChange(oldUrl, oldState) {
15527      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
15528        $location.$$state, oldState);
15529    }
15530}];
15531}
15532
15533/**
15534 * @ngdoc service
15535 * @name $log
15536 * @requires $window
15537 *
15538 * @description
15539 * Simple service for logging. Default implementation safely writes the message
15540 * into the browser's console (if present).
15541 *
15542 * The main purpose of this service is to simplify debugging and troubleshooting.
15543 *
15544 * To reveal the location of the calls to `$log` in the JavaScript console,
15545 * you can "blackbox" the AngularJS source in your browser:
15546 *
15547 * [Mozilla description of blackboxing](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Black_box_a_source).
15548 * [Chrome description of blackboxing](https://developer.chrome.com/devtools/docs/blackboxing).
15549 *
15550 * Note: Not all browsers support blackboxing.
15551 *
15552 * The default is to log `debug` messages. You can use
15553 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
15554 *
15555 * @example
15556   <example module="logExample" name="log-service">
15557     <file name="script.js">
15558       angular.module('logExample', [])
15559         .controller('LogController', ['$scope', '$log', function($scope, $log) {
15560           $scope.$log = $log;
15561           $scope.message = 'Hello World!';
15562         }]);
15563     </file>
15564     <file name="index.html">
15565       <div ng-controller="LogController">
15566         <p>Reload this page with open console, enter text and hit the log button...</p>
15567         <label>Message:
15568         <input type="text" ng-model="message" /></label>
15569         <button ng-click="$log.log(message)">log</button>
15570         <button ng-click="$log.warn(message)">warn</button>
15571         <button ng-click="$log.info(message)">info</button>
15572         <button ng-click="$log.error(message)">error</button>
15573         <button ng-click="$log.debug(message)">debug</button>
15574       </div>
15575     </file>
15576   </example>
15577 */
15578
15579/**
15580 * @ngdoc provider
15581 * @name $logProvider
15582 * @this
15583 *
15584 * @description
15585 * Use the `$logProvider` to configure how the application logs messages
15586 */
15587function $LogProvider() {
15588  var debug = true,
15589      self = this;
15590
15591  /**
15592   * @ngdoc method
15593   * @name $logProvider#debugEnabled
15594   * @description
15595   * @param {boolean=} flag enable or disable debug level messages
15596   * @returns {*} current value if used as getter or itself (chaining) if used as setter
15597   */
15598  this.debugEnabled = function(flag) {
15599    if (isDefined(flag)) {
15600      debug = flag;
15601      return this;
15602    } else {
15603      return debug;
15604    }
15605  };
15606
15607  this.$get = ['$window', function($window) {
15608    // Support: IE 9-11, Edge 12-14+
15609    // IE/Edge display errors in such a way that it requires the user to click in 4 places
15610    // to see the stack trace. There is no way to feature-detect it so there's a chance
15611    // of the user agent sniffing to go wrong but since it's only about logging, this shouldn't
15612    // break apps. Other browsers display errors in a sensible way and some of them map stack
15613    // traces along source maps if available so it makes sense to let browsers display it
15614    // as they want.
15615    var formatStackTrace = msie || /\bEdge\//.test($window.navigator && $window.navigator.userAgent);
15616
15617    return {
15618      /**
15619       * @ngdoc method
15620       * @name $log#log
15621       *
15622       * @description
15623       * Write a log message
15624       */
15625      log: consoleLog('log'),
15626
15627      /**
15628       * @ngdoc method
15629       * @name $log#info
15630       *
15631       * @description
15632       * Write an information message
15633       */
15634      info: consoleLog('info'),
15635
15636      /**
15637       * @ngdoc method
15638       * @name $log#warn
15639       *
15640       * @description
15641       * Write a warning message
15642       */
15643      warn: consoleLog('warn'),
15644
15645      /**
15646       * @ngdoc method
15647       * @name $log#error
15648       *
15649       * @description
15650       * Write an error message
15651       */
15652      error: consoleLog('error'),
15653
15654      /**
15655       * @ngdoc method
15656       * @name $log#debug
15657       *
15658       * @description
15659       * Write a debug message
15660       */
15661      debug: (function() {
15662        var fn = consoleLog('debug');
15663
15664        return function() {
15665          if (debug) {
15666            fn.apply(self, arguments);
15667          }
15668        };
15669      })()
15670    };
15671
15672    function formatError(arg) {
15673      if (isError(arg)) {
15674        if (arg.stack && formatStackTrace) {
15675          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
15676              ? 'Error: ' + arg.message + '\n' + arg.stack
15677              : arg.stack;
15678        } else if (arg.sourceURL) {
15679          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
15680        }
15681      }
15682      return arg;
15683    }
15684
15685    function consoleLog(type) {
15686      var console = $window.console || {},
15687          logFn = console[type] || console.log || noop;
15688
15689      return function() {
15690        var args = [];
15691        forEach(arguments, function(arg) {
15692          args.push(formatError(arg));
15693        });
15694        // Support: IE 9 only
15695        // console methods don't inherit from Function.prototype in IE 9 so we can't
15696        // call `logFn.apply(console, args)` directly.
15697        return Function.prototype.apply.call(logFn, console, args);
15698      };
15699    }
15700  }];
15701}
15702
15703/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15704 *     Any commits to this file should be reviewed with security in mind.  *
15705 *   Changes to this file can potentially create security vulnerabilities. *
15706 *          An approval from 2 Core members with history of modifying      *
15707 *                         this file is required.                          *
15708 *                                                                         *
15709 *  Does the change somehow allow for arbitrary javascript to be executed? *
15710 *    Or allows for someone to change the prototype of built-in objects?   *
15711 *     Or gives undesired access to variables likes document or window?    *
15712 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15713
15714var $parseMinErr = minErr('$parse');
15715
15716var objectValueOf = {}.constructor.prototype.valueOf;
15717
15718// Sandboxing AngularJS Expressions
15719// ------------------------------
15720// AngularJS expressions are no longer sandboxed. So it is now even easier to access arbitrary JS code by
15721// various means such as obtaining a reference to native JS functions like the Function constructor.
15722//
15723// As an example, consider the following AngularJS expression:
15724//
15725//   {}.toString.constructor('alert("evil JS code")')
15726//
15727// It is important to realize that if you create an expression from a string that contains user provided
15728// content then it is possible that your application contains a security vulnerability to an XSS style attack.
15729//
15730// See https://docs.angularjs.org/guide/security
15731
15732
15733function getStringValue(name) {
15734  // Property names must be strings. This means that non-string objects cannot be used
15735  // as keys in an object. Any non-string object, including a number, is typecasted
15736  // into a string via the toString method.
15737  // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
15738  //
15739  // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
15740  // to a string. It's not always possible. If `name` is an object and its `toString` method is
15741  // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
15742  //
15743  // TypeError: Cannot convert object to primitive value
15744  //
15745  // For performance reasons, we don't catch this error here and allow it to propagate up the call
15746  // stack. Note that you'll get the same error in JavaScript if you try to access a property using
15747  // such a 'broken' object as a key.
15748  return name + '';
15749}
15750
15751
15752var OPERATORS = createMap();
15753forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
15754var ESCAPE = {'n':'\n', 'f':'\f', 'r':'\r', 't':'\t', 'v':'\v', '\'':'\'', '"':'"'};
15755
15756
15757/////////////////////////////////////////
15758
15759
15760/**
15761 * @constructor
15762 */
15763var Lexer = function Lexer(options) {
15764  this.options = options;
15765};
15766
15767Lexer.prototype = {
15768  constructor: Lexer,
15769
15770  lex: function(text) {
15771    this.text = text;
15772    this.index = 0;
15773    this.tokens = [];
15774
15775    while (this.index < this.text.length) {
15776      var ch = this.text.charAt(this.index);
15777      if (ch === '"' || ch === '\'') {
15778        this.readString(ch);
15779      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
15780        this.readNumber();
15781      } else if (this.isIdentifierStart(this.peekMultichar())) {
15782        this.readIdent();
15783      } else if (this.is(ch, '(){}[].,;:?')) {
15784        this.tokens.push({index: this.index, text: ch});
15785        this.index++;
15786      } else if (this.isWhitespace(ch)) {
15787        this.index++;
15788      } else {
15789        var ch2 = ch + this.peek();
15790        var ch3 = ch2 + this.peek(2);
15791        var op1 = OPERATORS[ch];
15792        var op2 = OPERATORS[ch2];
15793        var op3 = OPERATORS[ch3];
15794        if (op1 || op2 || op3) {
15795          var token = op3 ? ch3 : (op2 ? ch2 : ch);
15796          this.tokens.push({index: this.index, text: token, operator: true});
15797          this.index += token.length;
15798        } else {
15799          this.throwError('Unexpected next character ', this.index, this.index + 1);
15800        }
15801      }
15802    }
15803    return this.tokens;
15804  },
15805
15806  is: function(ch, chars) {
15807    return chars.indexOf(ch) !== -1;
15808  },
15809
15810  peek: function(i) {
15811    var num = i || 1;
15812    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
15813  },
15814
15815  isNumber: function(ch) {
15816    return ('0' <= ch && ch <= '9') && typeof ch === 'string';
15817  },
15818
15819  isWhitespace: function(ch) {
15820    // IE treats non-breaking space as \u00A0
15821    return (ch === ' ' || ch === '\r' || ch === '\t' ||
15822            ch === '\n' || ch === '\v' || ch === '\u00A0');
15823  },
15824
15825  isIdentifierStart: function(ch) {
15826    return this.options.isIdentifierStart ?
15827        this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
15828        this.isValidIdentifierStart(ch);
15829  },
15830
15831  isValidIdentifierStart: function(ch) {
15832    return ('a' <= ch && ch <= 'z' ||
15833            'A' <= ch && ch <= 'Z' ||
15834            '_' === ch || ch === '$');
15835  },
15836
15837  isIdentifierContinue: function(ch) {
15838    return this.options.isIdentifierContinue ?
15839        this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
15840        this.isValidIdentifierContinue(ch);
15841  },
15842
15843  isValidIdentifierContinue: function(ch, cp) {
15844    return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
15845  },
15846
15847  codePointAt: function(ch) {
15848    if (ch.length === 1) return ch.charCodeAt(0);
15849    // eslint-disable-next-line no-bitwise
15850    return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
15851  },
15852
15853  peekMultichar: function() {
15854    var ch = this.text.charAt(this.index);
15855    var peek = this.peek();
15856    if (!peek) {
15857      return ch;
15858    }
15859    var cp1 = ch.charCodeAt(0);
15860    var cp2 = peek.charCodeAt(0);
15861    if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
15862      return ch + peek;
15863    }
15864    return ch;
15865  },
15866
15867  isExpOperator: function(ch) {
15868    return (ch === '-' || ch === '+' || this.isNumber(ch));
15869  },
15870
15871  throwError: function(error, start, end) {
15872    end = end || this.index;
15873    var colStr = (isDefined(start)
15874            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
15875            : ' ' + end);
15876    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
15877        error, colStr, this.text);
15878  },
15879
15880  readNumber: function() {
15881    var number = '';
15882    var start = this.index;
15883    while (this.index < this.text.length) {
15884      var ch = lowercase(this.text.charAt(this.index));
15885      if (ch === '.' || this.isNumber(ch)) {
15886        number += ch;
15887      } else {
15888        var peekCh = this.peek();
15889        if (ch === 'e' && this.isExpOperator(peekCh)) {
15890          number += ch;
15891        } else if (this.isExpOperator(ch) &&
15892            peekCh && this.isNumber(peekCh) &&
15893            number.charAt(number.length - 1) === 'e') {
15894          number += ch;
15895        } else if (this.isExpOperator(ch) &&
15896            (!peekCh || !this.isNumber(peekCh)) &&
15897            number.charAt(number.length - 1) === 'e') {
15898          this.throwError('Invalid exponent');
15899        } else {
15900          break;
15901        }
15902      }
15903      this.index++;
15904    }
15905    this.tokens.push({
15906      index: start,
15907      text: number,
15908      constant: true,
15909      value: Number(number)
15910    });
15911  },
15912
15913  readIdent: function() {
15914    var start = this.index;
15915    this.index += this.peekMultichar().length;
15916    while (this.index < this.text.length) {
15917      var ch = this.peekMultichar();
15918      if (!this.isIdentifierContinue(ch)) {
15919        break;
15920      }
15921      this.index += ch.length;
15922    }
15923    this.tokens.push({
15924      index: start,
15925      text: this.text.slice(start, this.index),
15926      identifier: true
15927    });
15928  },
15929
15930  readString: function(quote) {
15931    var start = this.index;
15932    this.index++;
15933    var string = '';
15934    var rawString = quote;
15935    var escape = false;
15936    while (this.index < this.text.length) {
15937      var ch = this.text.charAt(this.index);
15938      rawString += ch;
15939      if (escape) {
15940        if (ch === 'u') {
15941          var hex = this.text.substring(this.index + 1, this.index + 5);
15942          if (!hex.match(/[\da-f]{4}/i)) {
15943            this.throwError('Invalid unicode escape [\\u' + hex + ']');
15944          }
15945          this.index += 4;
15946          string += String.fromCharCode(parseInt(hex, 16));
15947        } else {
15948          var rep = ESCAPE[ch];
15949          string = string + (rep || ch);
15950        }
15951        escape = false;
15952      } else if (ch === '\\') {
15953        escape = true;
15954      } else if (ch === quote) {
15955        this.index++;
15956        this.tokens.push({
15957          index: start,
15958          text: rawString,
15959          constant: true,
15960          value: string
15961        });
15962        return;
15963      } else {
15964        string += ch;
15965      }
15966      this.index++;
15967    }
15968    this.throwError('Unterminated quote', start);
15969  }
15970};
15971
15972var AST = function AST(lexer, options) {
15973  this.lexer = lexer;
15974  this.options = options;
15975};
15976
15977AST.Program = 'Program';
15978AST.ExpressionStatement = 'ExpressionStatement';
15979AST.AssignmentExpression = 'AssignmentExpression';
15980AST.ConditionalExpression = 'ConditionalExpression';
15981AST.LogicalExpression = 'LogicalExpression';
15982AST.BinaryExpression = 'BinaryExpression';
15983AST.UnaryExpression = 'UnaryExpression';
15984AST.CallExpression = 'CallExpression';
15985AST.MemberExpression = 'MemberExpression';
15986AST.Identifier = 'Identifier';
15987AST.Literal = 'Literal';
15988AST.ArrayExpression = 'ArrayExpression';
15989AST.Property = 'Property';
15990AST.ObjectExpression = 'ObjectExpression';
15991AST.ThisExpression = 'ThisExpression';
15992AST.LocalsExpression = 'LocalsExpression';
15993
15994// Internal use only
15995AST.NGValueParameter = 'NGValueParameter';
15996
15997AST.prototype = {
15998  ast: function(text) {
15999    this.text = text;
16000    this.tokens = this.lexer.lex(text);
16001
16002    var value = this.program();
16003
16004    if (this.tokens.length !== 0) {
16005      this.throwError('is an unexpected token', this.tokens[0]);
16006    }
16007
16008    return value;
16009  },
16010
16011  program: function() {
16012    var body = [];
16013    while (true) {
16014      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
16015        body.push(this.expressionStatement());
16016      if (!this.expect(';')) {
16017        return { type: AST.Program, body: body};
16018      }
16019    }
16020  },
16021
16022  expressionStatement: function() {
16023    return { type: AST.ExpressionStatement, expression: this.filterChain() };
16024  },
16025
16026  filterChain: function() {
16027    var left = this.expression();
16028    while (this.expect('|')) {
16029      left = this.filter(left);
16030    }
16031    return left;
16032  },
16033
16034  expression: function() {
16035    return this.assignment();
16036  },
16037
16038  assignment: function() {
16039    var result = this.ternary();
16040    if (this.expect('=')) {
16041      if (!isAssignable(result)) {
16042        throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
16043      }
16044
16045      result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
16046    }
16047    return result;
16048  },
16049
16050  ternary: function() {
16051    var test = this.logicalOR();
16052    var alternate;
16053    var consequent;
16054    if (this.expect('?')) {
16055      alternate = this.expression();
16056      if (this.consume(':')) {
16057        consequent = this.expression();
16058        return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
16059      }
16060    }
16061    return test;
16062  },
16063
16064  logicalOR: function() {
16065    var left = this.logicalAND();
16066    while (this.expect('||')) {
16067      left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
16068    }
16069    return left;
16070  },
16071
16072  logicalAND: function() {
16073    var left = this.equality();
16074    while (this.expect('&&')) {
16075      left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
16076    }
16077    return left;
16078  },
16079
16080  equality: function() {
16081    var left = this.relational();
16082    var token;
16083    while ((token = this.expect('==','!=','===','!=='))) {
16084      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
16085    }
16086    return left;
16087  },
16088
16089  relational: function() {
16090    var left = this.additive();
16091    var token;
16092    while ((token = this.expect('<', '>', '<=', '>='))) {
16093      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
16094    }
16095    return left;
16096  },
16097
16098  additive: function() {
16099    var left = this.multiplicative();
16100    var token;
16101    while ((token = this.expect('+','-'))) {
16102      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
16103    }
16104    return left;
16105  },
16106
16107  multiplicative: function() {
16108    var left = this.unary();
16109    var token;
16110    while ((token = this.expect('*','/','%'))) {
16111      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
16112    }
16113    return left;
16114  },
16115
16116  unary: function() {
16117    var token;
16118    if ((token = this.expect('+', '-', '!'))) {
16119      return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
16120    } else {
16121      return this.primary();
16122    }
16123  },
16124
16125  primary: function() {
16126    var primary;
16127    if (this.expect('(')) {
16128      primary = this.filterChain();
16129      this.consume(')');
16130    } else if (this.expect('[')) {
16131      primary = this.arrayDeclaration();
16132    } else if (this.expect('{')) {
16133      primary = this.object();
16134    } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
16135      primary = copy(this.selfReferential[this.consume().text]);
16136    } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
16137      primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
16138    } else if (this.peek().identifier) {
16139      primary = this.identifier();
16140    } else if (this.peek().constant) {
16141      primary = this.constant();
16142    } else {
16143      this.throwError('not a primary expression', this.peek());
16144    }
16145
16146    var next;
16147    while ((next = this.expect('(', '[', '.'))) {
16148      if (next.text === '(') {
16149        primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
16150        this.consume(')');
16151      } else if (next.text === '[') {
16152        primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
16153        this.consume(']');
16154      } else if (next.text === '.') {
16155        primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
16156      } else {
16157        this.throwError('IMPOSSIBLE');
16158      }
16159    }
16160    return primary;
16161  },
16162
16163  filter: function(baseExpression) {
16164    var args = [baseExpression];
16165    var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
16166
16167    while (this.expect(':')) {
16168      args.push(this.expression());
16169    }
16170
16171    return result;
16172  },
16173
16174  parseArguments: function() {
16175    var args = [];
16176    if (this.peekToken().text !== ')') {
16177      do {
16178        args.push(this.filterChain());
16179      } while (this.expect(','));
16180    }
16181    return args;
16182  },
16183
16184  identifier: function() {
16185    var token = this.consume();
16186    if (!token.identifier) {
16187      this.throwError('is not a valid identifier', token);
16188    }
16189    return { type: AST.Identifier, name: token.text };
16190  },
16191
16192  constant: function() {
16193    // TODO check that it is a constant
16194    return { type: AST.Literal, value: this.consume().value };
16195  },
16196
16197  arrayDeclaration: function() {
16198    var elements = [];
16199    if (this.peekToken().text !== ']') {
16200      do {
16201        if (this.peek(']')) {
16202          // Support trailing commas per ES5.1.
16203          break;
16204        }
16205        elements.push(this.expression());
16206      } while (this.expect(','));
16207    }
16208    this.consume(']');
16209
16210    return { type: AST.ArrayExpression, elements: elements };
16211  },
16212
16213  object: function() {
16214    var properties = [], property;
16215    if (this.peekToken().text !== '}') {
16216      do {
16217        if (this.peek('}')) {
16218          // Support trailing commas per ES5.1.
16219          break;
16220        }
16221        property = {type: AST.Property, kind: 'init'};
16222        if (this.peek().constant) {
16223          property.key = this.constant();
16224          property.computed = false;
16225          this.consume(':');
16226          property.value = this.expression();
16227        } else if (this.peek().identifier) {
16228          property.key = this.identifier();
16229          property.computed = false;
16230          if (this.peek(':')) {
16231            this.consume(':');
16232            property.value = this.expression();
16233          } else {
16234            property.value = property.key;
16235          }
16236        } else if (this.peek('[')) {
16237          this.consume('[');
16238          property.key = this.expression();
16239          this.consume(']');
16240          property.computed = true;
16241          this.consume(':');
16242          property.value = this.expression();
16243        } else {
16244          this.throwError('invalid key', this.peek());
16245        }
16246        properties.push(property);
16247      } while (this.expect(','));
16248    }
16249    this.consume('}');
16250
16251    return {type: AST.ObjectExpression, properties: properties };
16252  },
16253
16254  throwError: function(msg, token) {
16255    throw $parseMinErr('syntax',
16256        'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
16257          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
16258  },
16259
16260  consume: function(e1) {
16261    if (this.tokens.length === 0) {
16262      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
16263    }
16264
16265    var token = this.expect(e1);
16266    if (!token) {
16267      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
16268    }
16269    return token;
16270  },
16271
16272  peekToken: function() {
16273    if (this.tokens.length === 0) {
16274      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
16275    }
16276    return this.tokens[0];
16277  },
16278
16279  peek: function(e1, e2, e3, e4) {
16280    return this.peekAhead(0, e1, e2, e3, e4);
16281  },
16282
16283  peekAhead: function(i, e1, e2, e3, e4) {
16284    if (this.tokens.length > i) {
16285      var token = this.tokens[i];
16286      var t = token.text;
16287      if (t === e1 || t === e2 || t === e3 || t === e4 ||
16288          (!e1 && !e2 && !e3 && !e4)) {
16289        return token;
16290      }
16291    }
16292    return false;
16293  },
16294
16295  expect: function(e1, e2, e3, e4) {
16296    var token = this.peek(e1, e2, e3, e4);
16297    if (token) {
16298      this.tokens.shift();
16299      return token;
16300    }
16301    return false;
16302  },
16303
16304  selfReferential: {
16305    'this': {type: AST.ThisExpression },
16306    '$locals': {type: AST.LocalsExpression }
16307  }
16308};
16309
16310function ifDefined(v, d) {
16311  return typeof v !== 'undefined' ? v : d;
16312}
16313
16314function plusFn(l, r) {
16315  if (typeof l === 'undefined') return r;
16316  if (typeof r === 'undefined') return l;
16317  return l + r;
16318}
16319
16320function isStateless($filter, filterName) {
16321  var fn = $filter(filterName);
16322  return !fn.$stateful;
16323}
16324
16325var PURITY_ABSOLUTE = 1;
16326var PURITY_RELATIVE = 2;
16327
16328// Detect nodes which could depend on non-shallow state of objects
16329function isPure(node, parentIsPure) {
16330  switch (node.type) {
16331    // Computed members might invoke a stateful toString()
16332    case AST.MemberExpression:
16333      if (node.computed) {
16334        return false;
16335      }
16336      break;
16337
16338    // Unary always convert to primative
16339    case AST.UnaryExpression:
16340      return PURITY_ABSOLUTE;
16341
16342    // The binary + operator can invoke a stateful toString().
16343    case AST.BinaryExpression:
16344      return node.operator !== '+' ? PURITY_ABSOLUTE : false;
16345
16346    // Functions / filters probably read state from within objects
16347    case AST.CallExpression:
16348      return false;
16349  }
16350
16351  return (undefined === parentIsPure) ? PURITY_RELATIVE : parentIsPure;
16352}
16353
16354function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
16355  var allConstants;
16356  var argsToWatch;
16357  var isStatelessFilter;
16358
16359  var astIsPure = ast.isPure = isPure(ast, parentIsPure);
16360
16361  switch (ast.type) {
16362  case AST.Program:
16363    allConstants = true;
16364    forEach(ast.body, function(expr) {
16365      findConstantAndWatchExpressions(expr.expression, $filter, astIsPure);
16366      allConstants = allConstants && expr.expression.constant;
16367    });
16368    ast.constant = allConstants;
16369    break;
16370  case AST.Literal:
16371    ast.constant = true;
16372    ast.toWatch = [];
16373    break;
16374  case AST.UnaryExpression:
16375    findConstantAndWatchExpressions(ast.argument, $filter, astIsPure);
16376    ast.constant = ast.argument.constant;
16377    ast.toWatch = ast.argument.toWatch;
16378    break;
16379  case AST.BinaryExpression:
16380    findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
16381    findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
16382    ast.constant = ast.left.constant && ast.right.constant;
16383    ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
16384    break;
16385  case AST.LogicalExpression:
16386    findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
16387    findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
16388    ast.constant = ast.left.constant && ast.right.constant;
16389    ast.toWatch = ast.constant ? [] : [ast];
16390    break;
16391  case AST.ConditionalExpression:
16392    findConstantAndWatchExpressions(ast.test, $filter, astIsPure);
16393    findConstantAndWatchExpressions(ast.alternate, $filter, astIsPure);
16394    findConstantAndWatchExpressions(ast.consequent, $filter, astIsPure);
16395    ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
16396    ast.toWatch = ast.constant ? [] : [ast];
16397    break;
16398  case AST.Identifier:
16399    ast.constant = false;
16400    ast.toWatch = [ast];
16401    break;
16402  case AST.MemberExpression:
16403    findConstantAndWatchExpressions(ast.object, $filter, astIsPure);
16404    if (ast.computed) {
16405      findConstantAndWatchExpressions(ast.property, $filter, astIsPure);
16406    }
16407    ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
16408    ast.toWatch = ast.constant ? [] : [ast];
16409    break;
16410  case AST.CallExpression:
16411    isStatelessFilter = ast.filter ? isStateless($filter, ast.callee.name) : false;
16412    allConstants = isStatelessFilter;
16413    argsToWatch = [];
16414    forEach(ast.arguments, function(expr) {
16415      findConstantAndWatchExpressions(expr, $filter, astIsPure);
16416      allConstants = allConstants && expr.constant;
16417      argsToWatch.push.apply(argsToWatch, expr.toWatch);
16418    });
16419    ast.constant = allConstants;
16420    ast.toWatch = isStatelessFilter ? argsToWatch : [ast];
16421    break;
16422  case AST.AssignmentExpression:
16423    findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
16424    findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
16425    ast.constant = ast.left.constant && ast.right.constant;
16426    ast.toWatch = [ast];
16427    break;
16428  case AST.ArrayExpression:
16429    allConstants = true;
16430    argsToWatch = [];
16431    forEach(ast.elements, function(expr) {
16432      findConstantAndWatchExpressions(expr, $filter, astIsPure);
16433      allConstants = allConstants && expr.constant;
16434      argsToWatch.push.apply(argsToWatch, expr.toWatch);
16435    });
16436    ast.constant = allConstants;
16437    ast.toWatch = argsToWatch;
16438    break;
16439  case AST.ObjectExpression:
16440    allConstants = true;
16441    argsToWatch = [];
16442    forEach(ast.properties, function(property) {
16443      findConstantAndWatchExpressions(property.value, $filter, astIsPure);
16444      allConstants = allConstants && property.value.constant;
16445      argsToWatch.push.apply(argsToWatch, property.value.toWatch);
16446      if (property.computed) {
16447        //`{[key]: value}` implicitly does `key.toString()` which may be non-pure
16448        findConstantAndWatchExpressions(property.key, $filter, /*parentIsPure=*/false);
16449        allConstants = allConstants && property.key.constant;
16450        argsToWatch.push.apply(argsToWatch, property.key.toWatch);
16451      }
16452    });
16453    ast.constant = allConstants;
16454    ast.toWatch = argsToWatch;
16455    break;
16456  case AST.ThisExpression:
16457    ast.constant = false;
16458    ast.toWatch = [];
16459    break;
16460  case AST.LocalsExpression:
16461    ast.constant = false;
16462    ast.toWatch = [];
16463    break;
16464  }
16465}
16466
16467function getInputs(body) {
16468  if (body.length !== 1) return;
16469  var lastExpression = body[0].expression;
16470  var candidate = lastExpression.toWatch;
16471  if (candidate.length !== 1) return candidate;
16472  return candidate[0] !== lastExpression ? candidate : undefined;
16473}
16474
16475function isAssignable(ast) {
16476  return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
16477}
16478
16479function assignableAST(ast) {
16480  if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
16481    return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
16482  }
16483}
16484
16485function isLiteral(ast) {
16486  return ast.body.length === 0 ||
16487      ast.body.length === 1 && (
16488      ast.body[0].expression.type === AST.Literal ||
16489      ast.body[0].expression.type === AST.ArrayExpression ||
16490      ast.body[0].expression.type === AST.ObjectExpression);
16491}
16492
16493function isConstant(ast) {
16494  return ast.constant;
16495}
16496
16497function ASTCompiler($filter) {
16498  this.$filter = $filter;
16499}
16500
16501ASTCompiler.prototype = {
16502  compile: function(ast) {
16503    var self = this;
16504    this.state = {
16505      nextId: 0,
16506      filters: {},
16507      fn: {vars: [], body: [], own: {}},
16508      assign: {vars: [], body: [], own: {}},
16509      inputs: []
16510    };
16511    findConstantAndWatchExpressions(ast, self.$filter);
16512    var extra = '';
16513    var assignable;
16514    this.stage = 'assign';
16515    if ((assignable = assignableAST(ast))) {
16516      this.state.computing = 'assign';
16517      var result = this.nextId();
16518      this.recurse(assignable, result);
16519      this.return_(result);
16520      extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
16521    }
16522    var toWatch = getInputs(ast.body);
16523    self.stage = 'inputs';
16524    forEach(toWatch, function(watch, key) {
16525      var fnKey = 'fn' + key;
16526      self.state[fnKey] = {vars: [], body: [], own: {}};
16527      self.state.computing = fnKey;
16528      var intoId = self.nextId();
16529      self.recurse(watch, intoId);
16530      self.return_(intoId);
16531      self.state.inputs.push({name: fnKey, isPure: watch.isPure});
16532      watch.watchId = key;
16533    });
16534    this.state.computing = 'fn';
16535    this.stage = 'main';
16536    this.recurse(ast);
16537    var fnString =
16538      // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
16539      // This is a workaround for this until we do a better job at only removing the prefix only when we should.
16540      '"' + this.USE + ' ' + this.STRICT + '";\n' +
16541      this.filterPrefix() +
16542      'var fn=' + this.generateFunction('fn', 's,l,a,i') +
16543      extra +
16544      this.watchFns() +
16545      'return fn;';
16546
16547    // eslint-disable-next-line no-new-func
16548    var fn = (new Function('$filter',
16549        'getStringValue',
16550        'ifDefined',
16551        'plus',
16552        fnString))(
16553          this.$filter,
16554          getStringValue,
16555          ifDefined,
16556          plusFn);
16557    this.state = this.stage = undefined;
16558    return fn;
16559  },
16560
16561  USE: 'use',
16562
16563  STRICT: 'strict',
16564
16565  watchFns: function() {
16566    var result = [];
16567    var inputs = this.state.inputs;
16568    var self = this;
16569    forEach(inputs, function(input) {
16570      result.push('var ' + input.name + '=' + self.generateFunction(input.name, 's'));
16571      if (input.isPure) {
16572        result.push(input.name, '.isPure=' + JSON.stringify(input.isPure) + ';');
16573      }
16574    });
16575    if (inputs.length) {
16576      result.push('fn.inputs=[' + inputs.map(function(i) { return i.name; }).join(',') + '];');
16577    }
16578    return result.join('');
16579  },
16580
16581  generateFunction: function(name, params) {
16582    return 'function(' + params + '){' +
16583        this.varsPrefix(name) +
16584        this.body(name) +
16585        '};';
16586  },
16587
16588  filterPrefix: function() {
16589    var parts = [];
16590    var self = this;
16591    forEach(this.state.filters, function(id, filter) {
16592      parts.push(id + '=$filter(' + self.escape(filter) + ')');
16593    });
16594    if (parts.length) return 'var ' + parts.join(',') + ';';
16595    return '';
16596  },
16597
16598  varsPrefix: function(section) {
16599    return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
16600  },
16601
16602  body: function(section) {
16603    return this.state[section].body.join('');
16604  },
16605
16606  recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
16607    var left, right, self = this, args, expression, computed;
16608    recursionFn = recursionFn || noop;
16609    if (!skipWatchIdCheck && isDefined(ast.watchId)) {
16610      intoId = intoId || this.nextId();
16611      this.if_('i',
16612        this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
16613        this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
16614      );
16615      return;
16616    }
16617    switch (ast.type) {
16618    case AST.Program:
16619      forEach(ast.body, function(expression, pos) {
16620        self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
16621        if (pos !== ast.body.length - 1) {
16622          self.current().body.push(right, ';');
16623        } else {
16624          self.return_(right);
16625        }
16626      });
16627      break;
16628    case AST.Literal:
16629      expression = this.escape(ast.value);
16630      this.assign(intoId, expression);
16631      recursionFn(intoId || expression);
16632      break;
16633    case AST.UnaryExpression:
16634      this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
16635      expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
16636      this.assign(intoId, expression);
16637      recursionFn(expression);
16638      break;
16639    case AST.BinaryExpression:
16640      this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
16641      this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
16642      if (ast.operator === '+') {
16643        expression = this.plus(left, right);
16644      } else if (ast.operator === '-') {
16645        expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
16646      } else {
16647        expression = '(' + left + ')' + ast.operator + '(' + right + ')';
16648      }
16649      this.assign(intoId, expression);
16650      recursionFn(expression);
16651      break;
16652    case AST.LogicalExpression:
16653      intoId = intoId || this.nextId();
16654      self.recurse(ast.left, intoId);
16655      self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
16656      recursionFn(intoId);
16657      break;
16658    case AST.ConditionalExpression:
16659      intoId = intoId || this.nextId();
16660      self.recurse(ast.test, intoId);
16661      self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
16662      recursionFn(intoId);
16663      break;
16664    case AST.Identifier:
16665      intoId = intoId || this.nextId();
16666      if (nameId) {
16667        nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
16668        nameId.computed = false;
16669        nameId.name = ast.name;
16670      }
16671      self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
16672        function() {
16673          self.if_(self.stage === 'inputs' || 's', function() {
16674            if (create && create !== 1) {
16675              self.if_(
16676                self.isNull(self.nonComputedMember('s', ast.name)),
16677                self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
16678            }
16679            self.assign(intoId, self.nonComputedMember('s', ast.name));
16680          });
16681        }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
16682        );
16683      recursionFn(intoId);
16684      break;
16685    case AST.MemberExpression:
16686      left = nameId && (nameId.context = this.nextId()) || this.nextId();
16687      intoId = intoId || this.nextId();
16688      self.recurse(ast.object, left, undefined, function() {
16689        self.if_(self.notNull(left), function() {
16690          if (ast.computed) {
16691            right = self.nextId();
16692            self.recurse(ast.property, right);
16693            self.getStringValue(right);
16694            if (create && create !== 1) {
16695              self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
16696            }
16697            expression = self.computedMember(left, right);
16698            self.assign(intoId, expression);
16699            if (nameId) {
16700              nameId.computed = true;
16701              nameId.name = right;
16702            }
16703          } else {
16704            if (create && create !== 1) {
16705              self.if_(self.isNull(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
16706            }
16707            expression = self.nonComputedMember(left, ast.property.name);
16708            self.assign(intoId, expression);
16709            if (nameId) {
16710              nameId.computed = false;
16711              nameId.name = ast.property.name;
16712            }
16713          }
16714        }, function() {
16715          self.assign(intoId, 'undefined');
16716        });
16717        recursionFn(intoId);
16718      }, !!create);
16719      break;
16720    case AST.CallExpression:
16721      intoId = intoId || this.nextId();
16722      if (ast.filter) {
16723        right = self.filter(ast.callee.name);
16724        args = [];
16725        forEach(ast.arguments, function(expr) {
16726          var argument = self.nextId();
16727          self.recurse(expr, argument);
16728          args.push(argument);
16729        });
16730        expression = right + '(' + args.join(',') + ')';
16731        self.assign(intoId, expression);
16732        recursionFn(intoId);
16733      } else {
16734        right = self.nextId();
16735        left = {};
16736        args = [];
16737        self.recurse(ast.callee, right, left, function() {
16738          self.if_(self.notNull(right), function() {
16739            forEach(ast.arguments, function(expr) {
16740              self.recurse(expr, ast.constant ? undefined : self.nextId(), undefined, function(argument) {
16741                args.push(argument);
16742              });
16743            });
16744            if (left.name) {
16745              expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
16746            } else {
16747              expression = right + '(' + args.join(',') + ')';
16748            }
16749            self.assign(intoId, expression);
16750          }, function() {
16751            self.assign(intoId, 'undefined');
16752          });
16753          recursionFn(intoId);
16754        });
16755      }
16756      break;
16757    case AST.AssignmentExpression:
16758      right = this.nextId();
16759      left = {};
16760      this.recurse(ast.left, undefined, left, function() {
16761        self.if_(self.notNull(left.context), function() {
16762          self.recurse(ast.right, right);
16763          expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
16764          self.assign(intoId, expression);
16765          recursionFn(intoId || expression);
16766        });
16767      }, 1);
16768      break;
16769    case AST.ArrayExpression:
16770      args = [];
16771      forEach(ast.elements, function(expr) {
16772        self.recurse(expr, ast.constant ? undefined : self.nextId(), undefined, function(argument) {
16773          args.push(argument);
16774        });
16775      });
16776      expression = '[' + args.join(',') + ']';
16777      this.assign(intoId, expression);
16778      recursionFn(intoId || expression);
16779      break;
16780    case AST.ObjectExpression:
16781      args = [];
16782      computed = false;
16783      forEach(ast.properties, function(property) {
16784        if (property.computed) {
16785          computed = true;
16786        }
16787      });
16788      if (computed) {
16789        intoId = intoId || this.nextId();
16790        this.assign(intoId, '{}');
16791        forEach(ast.properties, function(property) {
16792          if (property.computed) {
16793            left = self.nextId();
16794            self.recurse(property.key, left);
16795          } else {
16796            left = property.key.type === AST.Identifier ?
16797                       property.key.name :
16798                       ('' + property.key.value);
16799          }
16800          right = self.nextId();
16801          self.recurse(property.value, right);
16802          self.assign(self.member(intoId, left, property.computed), right);
16803        });
16804      } else {
16805        forEach(ast.properties, function(property) {
16806          self.recurse(property.value, ast.constant ? undefined : self.nextId(), undefined, function(expr) {
16807            args.push(self.escape(
16808                property.key.type === AST.Identifier ? property.key.name :
16809                  ('' + property.key.value)) +
16810                ':' + expr);
16811          });
16812        });
16813        expression = '{' + args.join(',') + '}';
16814        this.assign(intoId, expression);
16815      }
16816      recursionFn(intoId || expression);
16817      break;
16818    case AST.ThisExpression:
16819      this.assign(intoId, 's');
16820      recursionFn(intoId || 's');
16821      break;
16822    case AST.LocalsExpression:
16823      this.assign(intoId, 'l');
16824      recursionFn(intoId || 'l');
16825      break;
16826    case AST.NGValueParameter:
16827      this.assign(intoId, 'v');
16828      recursionFn(intoId || 'v');
16829      break;
16830    }
16831  },
16832
16833  getHasOwnProperty: function(element, property) {
16834    var key = element + '.' + property;
16835    var own = this.current().own;
16836    if (!own.hasOwnProperty(key)) {
16837      own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
16838    }
16839    return own[key];
16840  },
16841
16842  assign: function(id, value) {
16843    if (!id) return;
16844    this.current().body.push(id, '=', value, ';');
16845    return id;
16846  },
16847
16848  filter: function(filterName) {
16849    if (!this.state.filters.hasOwnProperty(filterName)) {
16850      this.state.filters[filterName] = this.nextId(true);
16851    }
16852    return this.state.filters[filterName];
16853  },
16854
16855  ifDefined: function(id, defaultValue) {
16856    return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
16857  },
16858
16859  plus: function(left, right) {
16860    return 'plus(' + left + ',' + right + ')';
16861  },
16862
16863  return_: function(id) {
16864    this.current().body.push('return ', id, ';');
16865  },
16866
16867  if_: function(test, alternate, consequent) {
16868    if (test === true) {
16869      alternate();
16870    } else {
16871      var body = this.current().body;
16872      body.push('if(', test, '){');
16873      alternate();
16874      body.push('}');
16875      if (consequent) {
16876        body.push('else{');
16877        consequent();
16878        body.push('}');
16879      }
16880    }
16881  },
16882
16883  not: function(expression) {
16884    return '!(' + expression + ')';
16885  },
16886
16887  isNull: function(expression) {
16888    return expression + '==null';
16889  },
16890
16891  notNull: function(expression) {
16892    return expression + '!=null';
16893  },
16894
16895  nonComputedMember: function(left, right) {
16896    var SAFE_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
16897    var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
16898    if (SAFE_IDENTIFIER.test(right)) {
16899      return left + '.' + right;
16900    } else {
16901      return left  + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
16902    }
16903  },
16904
16905  computedMember: function(left, right) {
16906    return left + '[' + right + ']';
16907  },
16908
16909  member: function(left, right, computed) {
16910    if (computed) return this.computedMember(left, right);
16911    return this.nonComputedMember(left, right);
16912  },
16913
16914  getStringValue: function(item) {
16915    this.assign(item, 'getStringValue(' + item + ')');
16916  },
16917
16918  lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
16919    var self = this;
16920    return function() {
16921      self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
16922    };
16923  },
16924
16925  lazyAssign: function(id, value) {
16926    var self = this;
16927    return function() {
16928      self.assign(id, value);
16929    };
16930  },
16931
16932  stringEscapeRegex: /[^ a-zA-Z0-9]/g,
16933
16934  stringEscapeFn: function(c) {
16935    return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
16936  },
16937
16938  escape: function(value) {
16939    if (isString(value)) return '\'' + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + '\'';
16940    if (isNumber(value)) return value.toString();
16941    if (value === true) return 'true';
16942    if (value === false) return 'false';
16943    if (value === null) return 'null';
16944    if (typeof value === 'undefined') return 'undefined';
16945
16946    throw $parseMinErr('esc', 'IMPOSSIBLE');
16947  },
16948
16949  nextId: function(skip, init) {
16950    var id = 'v' + (this.state.nextId++);
16951    if (!skip) {
16952      this.current().vars.push(id + (init ? '=' + init : ''));
16953    }
16954    return id;
16955  },
16956
16957  current: function() {
16958    return this.state[this.state.computing];
16959  }
16960};
16961
16962
16963function ASTInterpreter($filter) {
16964  this.$filter = $filter;
16965}
16966
16967ASTInterpreter.prototype = {
16968  compile: function(ast) {
16969    var self = this;
16970    findConstantAndWatchExpressions(ast, self.$filter);
16971    var assignable;
16972    var assign;
16973    if ((assignable = assignableAST(ast))) {
16974      assign = this.recurse(assignable);
16975    }
16976    var toWatch = getInputs(ast.body);
16977    var inputs;
16978    if (toWatch) {
16979      inputs = [];
16980      forEach(toWatch, function(watch, key) {
16981        var input = self.recurse(watch);
16982        input.isPure = watch.isPure;
16983        watch.input = input;
16984        inputs.push(input);
16985        watch.watchId = key;
16986      });
16987    }
16988    var expressions = [];
16989    forEach(ast.body, function(expression) {
16990      expressions.push(self.recurse(expression.expression));
16991    });
16992    var fn = ast.body.length === 0 ? noop :
16993             ast.body.length === 1 ? expressions[0] :
16994             function(scope, locals) {
16995               var lastValue;
16996               forEach(expressions, function(exp) {
16997                 lastValue = exp(scope, locals);
16998               });
16999               return lastValue;
17000             };
17001    if (assign) {
17002      fn.assign = function(scope, value, locals) {
17003        return assign(scope, locals, value);
17004      };
17005    }
17006    if (inputs) {
17007      fn.inputs = inputs;
17008    }
17009    return fn;
17010  },
17011
17012  recurse: function(ast, context, create) {
17013    var left, right, self = this, args;
17014    if (ast.input) {
17015      return this.inputs(ast.input, ast.watchId);
17016    }
17017    switch (ast.type) {
17018    case AST.Literal:
17019      return this.value(ast.value, context);
17020    case AST.UnaryExpression:
17021      right = this.recurse(ast.argument);
17022      return this['unary' + ast.operator](right, context);
17023    case AST.BinaryExpression:
17024      left = this.recurse(ast.left);
17025      right = this.recurse(ast.right);
17026      return this['binary' + ast.operator](left, right, context);
17027    case AST.LogicalExpression:
17028      left = this.recurse(ast.left);
17029      right = this.recurse(ast.right);
17030      return this['binary' + ast.operator](left, right, context);
17031    case AST.ConditionalExpression:
17032      return this['ternary?:'](
17033        this.recurse(ast.test),
17034        this.recurse(ast.alternate),
17035        this.recurse(ast.consequent),
17036        context
17037      );
17038    case AST.Identifier:
17039      return self.identifier(ast.name, context, create);
17040    case AST.MemberExpression:
17041      left = this.recurse(ast.object, false, !!create);
17042      if (!ast.computed) {
17043        right = ast.property.name;
17044      }
17045      if (ast.computed) right = this.recurse(ast.property);
17046      return ast.computed ?
17047        this.computedMember(left, right, context, create) :
17048        this.nonComputedMember(left, right, context, create);
17049    case AST.CallExpression:
17050      args = [];
17051      forEach(ast.arguments, function(expr) {
17052        args.push(self.recurse(expr));
17053      });
17054      if (ast.filter) right = this.$filter(ast.callee.name);
17055      if (!ast.filter) right = this.recurse(ast.callee, true);
17056      return ast.filter ?
17057        function(scope, locals, assign, inputs) {
17058          var values = [];
17059          for (var i = 0; i < args.length; ++i) {
17060            values.push(args[i](scope, locals, assign, inputs));
17061          }
17062          var value = right.apply(undefined, values, inputs);
17063          return context ? {context: undefined, name: undefined, value: value} : value;
17064        } :
17065        function(scope, locals, assign, inputs) {
17066          var rhs = right(scope, locals, assign, inputs);
17067          var value;
17068          if (rhs.value != null) {
17069            var values = [];
17070            for (var i = 0; i < args.length; ++i) {
17071              values.push(args[i](scope, locals, assign, inputs));
17072            }
17073            value = rhs.value.apply(rhs.context, values);
17074          }
17075          return context ? {value: value} : value;
17076        };
17077    case AST.AssignmentExpression:
17078      left = this.recurse(ast.left, true, 1);
17079      right = this.recurse(ast.right);
17080      return function(scope, locals, assign, inputs) {
17081        var lhs = left(scope, locals, assign, inputs);
17082        var rhs = right(scope, locals, assign, inputs);
17083        lhs.context[lhs.name] = rhs;
17084        return context ? {value: rhs} : rhs;
17085      };
17086    case AST.ArrayExpression:
17087      args = [];
17088      forEach(ast.elements, function(expr) {
17089        args.push(self.recurse(expr));
17090      });
17091      return function(scope, locals, assign, inputs) {
17092        var value = [];
17093        for (var i = 0; i < args.length; ++i) {
17094          value.push(args[i](scope, locals, assign, inputs));
17095        }
17096        return context ? {value: value} : value;
17097      };
17098    case AST.ObjectExpression:
17099      args = [];
17100      forEach(ast.properties, function(property) {
17101        if (property.computed) {
17102          args.push({key: self.recurse(property.key),
17103                     computed: true,
17104                     value: self.recurse(property.value)
17105          });
17106        } else {
17107          args.push({key: property.key.type === AST.Identifier ?
17108                          property.key.name :
17109                          ('' + property.key.value),
17110                     computed: false,
17111                     value: self.recurse(property.value)
17112          });
17113        }
17114      });
17115      return function(scope, locals, assign, inputs) {
17116        var value = {};
17117        for (var i = 0; i < args.length; ++i) {
17118          if (args[i].computed) {
17119            value[args[i].key(scope, locals, assign, inputs)] = args[i].value(scope, locals, assign, inputs);
17120          } else {
17121            value[args[i].key] = args[i].value(scope, locals, assign, inputs);
17122          }
17123        }
17124        return context ? {value: value} : value;
17125      };
17126    case AST.ThisExpression:
17127      return function(scope) {
17128        return context ? {value: scope} : scope;
17129      };
17130    case AST.LocalsExpression:
17131      return function(scope, locals) {
17132        return context ? {value: locals} : locals;
17133      };
17134    case AST.NGValueParameter:
17135      return function(scope, locals, assign) {
17136        return context ? {value: assign} : assign;
17137      };
17138    }
17139  },
17140
17141  'unary+': function(argument, context) {
17142    return function(scope, locals, assign, inputs) {
17143      var arg = argument(scope, locals, assign, inputs);
17144      if (isDefined(arg)) {
17145        arg = +arg;
17146      } else {
17147        arg = 0;
17148      }
17149      return context ? {value: arg} : arg;
17150    };
17151  },
17152  'unary-': function(argument, context) {
17153    return function(scope, locals, assign, inputs) {
17154      var arg = argument(scope, locals, assign, inputs);
17155      if (isDefined(arg)) {
17156        arg = -arg;
17157      } else {
17158        arg = -0;
17159      }
17160      return context ? {value: arg} : arg;
17161    };
17162  },
17163  'unary!': function(argument, context) {
17164    return function(scope, locals, assign, inputs) {
17165      var arg = !argument(scope, locals, assign, inputs);
17166      return context ? {value: arg} : arg;
17167    };
17168  },
17169  'binary+': function(left, right, context) {
17170    return function(scope, locals, assign, inputs) {
17171      var lhs = left(scope, locals, assign, inputs);
17172      var rhs = right(scope, locals, assign, inputs);
17173      var arg = plusFn(lhs, rhs);
17174      return context ? {value: arg} : arg;
17175    };
17176  },
17177  'binary-': function(left, right, context) {
17178    return function(scope, locals, assign, inputs) {
17179      var lhs = left(scope, locals, assign, inputs);
17180      var rhs = right(scope, locals, assign, inputs);
17181      var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
17182      return context ? {value: arg} : arg;
17183    };
17184  },
17185  'binary*': function(left, right, context) {
17186    return function(scope, locals, assign, inputs) {
17187      var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
17188      return context ? {value: arg} : arg;
17189    };
17190  },
17191  'binary/': function(left, right, context) {
17192    return function(scope, locals, assign, inputs) {
17193      var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
17194      return context ? {value: arg} : arg;
17195    };
17196  },
17197  'binary%': function(left, right, context) {
17198    return function(scope, locals, assign, inputs) {
17199      var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
17200      return context ? {value: arg} : arg;
17201    };
17202  },
17203  'binary===': function(left, right, context) {
17204    return function(scope, locals, assign, inputs) {
17205      var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
17206      return context ? {value: arg} : arg;
17207    };
17208  },
17209  'binary!==': function(left, right, context) {
17210    return function(scope, locals, assign, inputs) {
17211      var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
17212      return context ? {value: arg} : arg;
17213    };
17214  },
17215  'binary==': function(left, right, context) {
17216    return function(scope, locals, assign, inputs) {
17217      // eslint-disable-next-line eqeqeq
17218      var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
17219      return context ? {value: arg} : arg;
17220    };
17221  },
17222  'binary!=': function(left, right, context) {
17223    return function(scope, locals, assign, inputs) {
17224      // eslint-disable-next-line eqeqeq
17225      var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
17226      return context ? {value: arg} : arg;
17227    };
17228  },
17229  'binary<': function(left, right, context) {
17230    return function(scope, locals, assign, inputs) {
17231      var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
17232      return context ? {value: arg} : arg;
17233    };
17234  },
17235  'binary>': function(left, right, context) {
17236    return function(scope, locals, assign, inputs) {
17237      var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
17238      return context ? {value: arg} : arg;
17239    };
17240  },
17241  'binary<=': function(left, right, context) {
17242    return function(scope, locals, assign, inputs) {
17243      var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
17244      return context ? {value: arg} : arg;
17245    };
17246  },
17247  'binary>=': function(left, right, context) {
17248    return function(scope, locals, assign, inputs) {
17249      var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
17250      return context ? {value: arg} : arg;
17251    };
17252  },
17253  'binary&&': function(left, right, context) {
17254    return function(scope, locals, assign, inputs) {
17255      var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
17256      return context ? {value: arg} : arg;
17257    };
17258  },
17259  'binary||': function(left, right, context) {
17260    return function(scope, locals, assign, inputs) {
17261      var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
17262      return context ? {value: arg} : arg;
17263    };
17264  },
17265  'ternary?:': function(test, alternate, consequent, context) {
17266    return function(scope, locals, assign, inputs) {
17267      var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
17268      return context ? {value: arg} : arg;
17269    };
17270  },
17271  value: function(value, context) {
17272    return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
17273  },
17274  identifier: function(name, context, create) {
17275    return function(scope, locals, assign, inputs) {
17276      var base = locals && (name in locals) ? locals : scope;
17277      if (create && create !== 1 && base && base[name] == null) {
17278        base[name] = {};
17279      }
17280      var value = base ? base[name] : undefined;
17281      if (context) {
17282        return {context: base, name: name, value: value};
17283      } else {
17284        return value;
17285      }
17286    };
17287  },
17288  computedMember: function(left, right, context, create) {
17289    return function(scope, locals, assign, inputs) {
17290      var lhs = left(scope, locals, assign, inputs);
17291      var rhs;
17292      var value;
17293      if (lhs != null) {
17294        rhs = right(scope, locals, assign, inputs);
17295        rhs = getStringValue(rhs);
17296        if (create && create !== 1) {
17297          if (lhs && !(lhs[rhs])) {
17298            lhs[rhs] = {};
17299          }
17300        }
17301        value = lhs[rhs];
17302      }
17303      if (context) {
17304        return {context: lhs, name: rhs, value: value};
17305      } else {
17306        return value;
17307      }
17308    };
17309  },
17310  nonComputedMember: function(left, right, context, create) {
17311    return function(scope, locals, assign, inputs) {
17312      var lhs = left(scope, locals, assign, inputs);
17313      if (create && create !== 1) {
17314        if (lhs && lhs[right] == null) {
17315          lhs[right] = {};
17316        }
17317      }
17318      var value = lhs != null ? lhs[right] : undefined;
17319      if (context) {
17320        return {context: lhs, name: right, value: value};
17321      } else {
17322        return value;
17323      }
17324    };
17325  },
17326  inputs: function(input, watchId) {
17327    return function(scope, value, locals, inputs) {
17328      if (inputs) return inputs[watchId];
17329      return input(scope, value, locals);
17330    };
17331  }
17332};
17333
17334/**
17335 * @constructor
17336 */
17337function Parser(lexer, $filter, options) {
17338  this.ast = new AST(lexer, options);
17339  this.astCompiler = options.csp ? new ASTInterpreter($filter) :
17340                                   new ASTCompiler($filter);
17341}
17342
17343Parser.prototype = {
17344  constructor: Parser,
17345
17346  parse: function(text) {
17347    var ast = this.getAst(text);
17348    var fn = this.astCompiler.compile(ast.ast);
17349    fn.literal = isLiteral(ast.ast);
17350    fn.constant = isConstant(ast.ast);
17351    fn.oneTime = ast.oneTime;
17352    return fn;
17353  },
17354
17355  getAst: function(exp) {
17356    var oneTime = false;
17357    exp = exp.trim();
17358
17359    if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
17360      oneTime = true;
17361      exp = exp.substring(2);
17362    }
17363    return {
17364      ast: this.ast.ast(exp),
17365      oneTime: oneTime
17366    };
17367  }
17368};
17369
17370function getValueOf(value) {
17371  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
17372}
17373
17374///////////////////////////////////
17375
17376/**
17377 * @ngdoc service
17378 * @name $parse
17379 * @kind function
17380 *
17381 * @description
17382 *
17383 * Converts AngularJS {@link guide/expression expression} into a function.
17384 *
17385 * ```js
17386 *   var getter = $parse('user.name');
17387 *   var setter = getter.assign;
17388 *   var context = {user:{name:'AngularJS'}};
17389 *   var locals = {user:{name:'local'}};
17390 *
17391 *   expect(getter(context)).toEqual('AngularJS');
17392 *   setter(context, 'newValue');
17393 *   expect(context.user.name).toEqual('newValue');
17394 *   expect(getter(context, locals)).toEqual('local');
17395 * ```
17396 *
17397 *
17398 * @param {string} expression String expression to compile.
17399 * @returns {function(context, locals)} a function which represents the compiled expression:
17400 *
17401 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17402 *      are evaluated against (typically a scope object).
17403 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17404 *      `context`.
17405 *
17406 *    The returned function also has the following properties:
17407 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
17408 *        literal.
17409 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
17410 *        constant literals.
17411 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
17412 *        set to a function to change its value on the given context.
17413 *
17414 */
17415
17416
17417/**
17418 * @ngdoc provider
17419 * @name $parseProvider
17420 * @this
17421 *
17422 * @description
17423 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
17424 *  service.
17425 */
17426function $ParseProvider() {
17427  var cache = createMap();
17428  var literals = {
17429    'true': true,
17430    'false': false,
17431    'null': null,
17432    'undefined': undefined
17433  };
17434  var identStart, identContinue;
17435
17436  /**
17437   * @ngdoc method
17438   * @name $parseProvider#addLiteral
17439   * @description
17440   *
17441   * Configure $parse service to add literal values that will be present as literal at expressions.
17442   *
17443   * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
17444   * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
17445   *
17446   **/
17447  this.addLiteral = function(literalName, literalValue) {
17448    literals[literalName] = literalValue;
17449  };
17450
17451 /**
17452  * @ngdoc method
17453  * @name $parseProvider#setIdentifierFns
17454  *
17455  * @description
17456  *
17457  * Allows defining the set of characters that are allowed in AngularJS expressions. The function
17458  * `identifierStart` will get called to know if a given character is a valid character to be the
17459  * first character for an identifier. The function `identifierContinue` will get called to know if
17460  * a given character is a valid character to be a follow-up identifier character. The functions
17461  * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
17462  * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
17463  * mind that the `string` parameter can be two characters long depending on the character
17464  * representation. It is expected for the function to return `true` or `false`, whether that
17465  * character is allowed or not.
17466  *
17467  * Since this function will be called extensively, keep the implementation of these functions fast,
17468  * as the performance of these functions have a direct impact on the expressions parsing speed.
17469  *
17470  * @param {function=} identifierStart The function that will decide whether the given character is
17471  *   a valid identifier start character.
17472  * @param {function=} identifierContinue The function that will decide whether the given character is
17473  *   a valid identifier continue character.
17474  */
17475  this.setIdentifierFns = function(identifierStart, identifierContinue) {
17476    identStart = identifierStart;
17477    identContinue = identifierContinue;
17478    return this;
17479  };
17480
17481  this.$get = ['$filter', function($filter) {
17482    var noUnsafeEval = csp().noUnsafeEval;
17483    var $parseOptions = {
17484          csp: noUnsafeEval,
17485          literals: copy(literals),
17486          isIdentifierStart: isFunction(identStart) && identStart,
17487          isIdentifierContinue: isFunction(identContinue) && identContinue
17488        };
17489    $parse.$$getAst = $$getAst;
17490    return $parse;
17491
17492    function $parse(exp, interceptorFn) {
17493      var parsedExpression, cacheKey;
17494
17495      switch (typeof exp) {
17496        case 'string':
17497          exp = exp.trim();
17498          cacheKey = exp;
17499
17500          parsedExpression = cache[cacheKey];
17501
17502          if (!parsedExpression) {
17503            var lexer = new Lexer($parseOptions);
17504            var parser = new Parser(lexer, $filter, $parseOptions);
17505            parsedExpression = parser.parse(exp);
17506
17507            cache[cacheKey] = addWatchDelegate(parsedExpression);
17508          }
17509          return addInterceptor(parsedExpression, interceptorFn);
17510
17511        case 'function':
17512          return addInterceptor(exp, interceptorFn);
17513
17514        default:
17515          return addInterceptor(noop, interceptorFn);
17516      }
17517    }
17518
17519    function $$getAst(exp) {
17520      var lexer = new Lexer($parseOptions);
17521      var parser = new Parser(lexer, $filter, $parseOptions);
17522      return parser.getAst(exp).ast;
17523    }
17524
17525    function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) {
17526
17527      if (newValue == null || oldValueOfValue == null) { // null/undefined
17528        return newValue === oldValueOfValue;
17529      }
17530
17531      if (typeof newValue === 'object') {
17532
17533        // attempt to convert the value to a primitive type
17534        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
17535        //             be cheaply dirty-checked
17536        newValue = getValueOf(newValue);
17537
17538        if (typeof newValue === 'object' && !compareObjectIdentity) {
17539          // objects/arrays are not supported - deep-watching them would be too expensive
17540          return false;
17541        }
17542
17543        // fall-through to the primitive equality check
17544      }
17545
17546      //Primitive or NaN
17547      // eslint-disable-next-line no-self-compare
17548      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
17549    }
17550
17551    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
17552      var inputExpressions = parsedExpression.inputs;
17553      var lastResult;
17554
17555      if (inputExpressions.length === 1) {
17556        var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
17557        inputExpressions = inputExpressions[0];
17558        return scope.$watch(function expressionInputWatch(scope) {
17559          var newInputValue = inputExpressions(scope);
17560          if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, inputExpressions.isPure)) {
17561            lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
17562            oldInputValueOf = newInputValue && getValueOf(newInputValue);
17563          }
17564          return lastResult;
17565        }, listener, objectEquality, prettyPrintExpression);
17566      }
17567
17568      var oldInputValueOfValues = [];
17569      var oldInputValues = [];
17570      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
17571        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
17572        oldInputValues[i] = null;
17573      }
17574
17575      return scope.$watch(function expressionInputsWatch(scope) {
17576        var changed = false;
17577
17578        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
17579          var newInputValue = inputExpressions[i](scope);
17580          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], inputExpressions[i].isPure))) {
17581            oldInputValues[i] = newInputValue;
17582            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
17583          }
17584        }
17585
17586        if (changed) {
17587          lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
17588        }
17589
17590        return lastResult;
17591      }, listener, objectEquality, prettyPrintExpression);
17592    }
17593
17594    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
17595      var isDone = parsedExpression.literal ? isAllDefined : isDefined;
17596      var unwatch, lastValue;
17597
17598      var exp = parsedExpression.$$intercepted || parsedExpression;
17599      var post = parsedExpression.$$interceptor || identity;
17600
17601      var useInputs = parsedExpression.inputs && !exp.inputs;
17602
17603      // Propagate the literal/inputs/constant attributes
17604      // ... but not oneTime since we are handling it
17605      oneTimeWatch.literal = parsedExpression.literal;
17606      oneTimeWatch.constant = parsedExpression.constant;
17607      oneTimeWatch.inputs = parsedExpression.inputs;
17608
17609      // Allow other delegates to run on this wrapped expression
17610      addWatchDelegate(oneTimeWatch);
17611
17612      unwatch = scope.$watch(oneTimeWatch, listener, objectEquality, prettyPrintExpression);
17613
17614      return unwatch;
17615
17616      function unwatchIfDone() {
17617        if (isDone(lastValue)) {
17618          unwatch();
17619        }
17620      }
17621
17622      function oneTimeWatch(scope, locals, assign, inputs) {
17623        lastValue = useInputs && inputs ? inputs[0] : exp(scope, locals, assign, inputs);
17624        if (isDone(lastValue)) {
17625          scope.$$postDigest(unwatchIfDone);
17626        }
17627        return post(lastValue);
17628      }
17629    }
17630
17631    function isAllDefined(value) {
17632      var allDefined = true;
17633      forEach(value, function(val) {
17634        if (!isDefined(val)) allDefined = false;
17635      });
17636      return allDefined;
17637    }
17638
17639    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
17640      var unwatch = scope.$watch(function constantWatch(scope) {
17641        unwatch();
17642        return parsedExpression(scope);
17643      }, listener, objectEquality);
17644      return unwatch;
17645    }
17646
17647    function addWatchDelegate(parsedExpression) {
17648      if (parsedExpression.constant) {
17649        parsedExpression.$$watchDelegate = constantWatchDelegate;
17650      } else if (parsedExpression.oneTime) {
17651        parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
17652      } else if (parsedExpression.inputs) {
17653        parsedExpression.$$watchDelegate = inputsWatchDelegate;
17654      }
17655
17656      return parsedExpression;
17657    }
17658
17659    function chainInterceptors(first, second) {
17660      function chainedInterceptor(value) {
17661        return second(first(value));
17662      }
17663      chainedInterceptor.$stateful = first.$stateful || second.$stateful;
17664      chainedInterceptor.$$pure = first.$$pure && second.$$pure;
17665
17666      return chainedInterceptor;
17667    }
17668
17669    function addInterceptor(parsedExpression, interceptorFn) {
17670      if (!interceptorFn) return parsedExpression;
17671
17672      // Extract any existing interceptors out of the parsedExpression
17673      // to ensure the original parsedExpression is always the $$intercepted
17674      if (parsedExpression.$$interceptor) {
17675        interceptorFn = chainInterceptors(parsedExpression.$$interceptor, interceptorFn);
17676        parsedExpression = parsedExpression.$$intercepted;
17677      }
17678
17679      var useInputs = false;
17680
17681      var fn = function interceptedExpression(scope, locals, assign, inputs) {
17682        var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
17683        return interceptorFn(value);
17684      };
17685
17686      // Maintain references to the interceptor/intercepted
17687      fn.$$intercepted = parsedExpression;
17688      fn.$$interceptor = interceptorFn;
17689
17690      // Propagate the literal/oneTime/constant attributes
17691      fn.literal = parsedExpression.literal;
17692      fn.oneTime = parsedExpression.oneTime;
17693      fn.constant = parsedExpression.constant;
17694
17695      // Treat the interceptor like filters.
17696      // If it is not $stateful then only watch its inputs.
17697      // If the expression itself has no inputs then use the full expression as an input.
17698      if (!interceptorFn.$stateful) {
17699        useInputs = !parsedExpression.inputs;
17700        fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
17701
17702        if (!interceptorFn.$$pure) {
17703          fn.inputs = fn.inputs.map(function(e) {
17704              // Remove the isPure flag of inputs when it is not absolute because they are now wrapped in a
17705              // non-pure interceptor function.
17706              if (e.isPure === PURITY_RELATIVE) {
17707                return function depurifier(s) { return e(s); };
17708              }
17709              return e;
17710            });
17711        }
17712      }
17713
17714      return addWatchDelegate(fn);
17715    }
17716  }];
17717}
17718
17719/**
17720 * @ngdoc service
17721 * @name $q
17722 * @requires $rootScope
17723 *
17724 * @description
17725 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
17726 * when they are done processing.
17727 *
17728 * This is a [Promises/A+](https://promisesaplus.com/)-compliant implementation of promises/deferred
17729 * objects inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
17730 *
17731 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
17732 * implementations, and the other which resembles ES6 (ES2015) promises to some degree.
17733 *
17734 * ## $q constructor
17735 *
17736 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
17737 * function as the first argument. This is similar to the native Promise implementation from ES6,
17738 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
17739 *
17740 * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
17741 * available yet.
17742 *
17743 * It can be used like so:
17744 *
17745 * ```js
17746 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
17747 *   // are available in the current lexical scope (they could have been injected or passed in).
17748 *
17749 *   function asyncGreet(name) {
17750 *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
17751 *     return $q(function(resolve, reject) {
17752 *       setTimeout(function() {
17753 *         if (okToGreet(name)) {
17754 *           resolve('Hello, ' + name + '!');
17755 *         } else {
17756 *           reject('Greeting ' + name + ' is not allowed.');
17757 *         }
17758 *       }, 1000);
17759 *     });
17760 *   }
17761 *
17762 *   var promise = asyncGreet('Robin Hood');
17763 *   promise.then(function(greeting) {
17764 *     alert('Success: ' + greeting);
17765 *   }, function(reason) {
17766 *     alert('Failed: ' + reason);
17767 *   });
17768 * ```
17769 *
17770 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
17771 *
17772 * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
17773 *
17774 * However, the more traditional CommonJS-style usage is still available, and documented below.
17775 *
17776 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
17777 * interface for interacting with an object that represents the result of an action that is
17778 * performed asynchronously, and may or may not be finished at any given point in time.
17779 *
17780 * From the perspective of dealing with error handling, deferred and promise APIs are to
17781 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
17782 *
17783 * ```js
17784 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
17785 *   // are available in the current lexical scope (they could have been injected or passed in).
17786 *
17787 *   function asyncGreet(name) {
17788 *     var deferred = $q.defer();
17789 *
17790 *     setTimeout(function() {
17791 *       deferred.notify('About to greet ' + name + '.');
17792 *
17793 *       if (okToGreet(name)) {
17794 *         deferred.resolve('Hello, ' + name + '!');
17795 *       } else {
17796 *         deferred.reject('Greeting ' + name + ' is not allowed.');
17797 *       }
17798 *     }, 1000);
17799 *
17800 *     return deferred.promise;
17801 *   }
17802 *
17803 *   var promise = asyncGreet('Robin Hood');
17804 *   promise.then(function(greeting) {
17805 *     alert('Success: ' + greeting);
17806 *   }, function(reason) {
17807 *     alert('Failed: ' + reason);
17808 *   }, function(update) {
17809 *     alert('Got notification: ' + update);
17810 *   });
17811 * ```
17812 *
17813 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
17814 * comes in the way of guarantees that promise and deferred APIs make, see
17815 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
17816 *
17817 * Additionally the promise api allows for composition that is very hard to do with the
17818 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
17819 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
17820 * section on serial or parallel joining of promises.
17821 *
17822 * ## The Deferred API
17823 *
17824 * A new instance of deferred is constructed by calling `$q.defer()`.
17825 *
17826 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
17827 * that can be used for signaling the successful or unsuccessful completion, as well as the status
17828 * of the task.
17829 *
17830 * **Methods**
17831 *
17832 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
17833 *   constructed via `$q.reject`, the promise will be rejected instead.
17834 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
17835 *   resolving it with a rejection constructed via `$q.reject`.
17836 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
17837 *   multiple times before the promise is either resolved or rejected.
17838 *
17839 * **Properties**
17840 *
17841 * - promise – `{Promise}` – promise object associated with this deferred.
17842 *
17843 *
17844 * ## The Promise API
17845 *
17846 * A new promise instance is created when a deferred instance is created and can be retrieved by
17847 * calling `deferred.promise`.
17848 *
17849 * The purpose of the promise object is to allow for interested parties to get access to the result
17850 * of the deferred task when it completes.
17851 *
17852 * **Methods**
17853 *
17854 * - `then(successCallback, [errorCallback], [notifyCallback])` – regardless of when the promise was or
17855 *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
17856 *   as soon as the result is available. The callbacks are called with a single argument: the result
17857 *   or rejection reason. Additionally, the notify callback may be called zero or more times to
17858 *   provide a progress indication, before the promise is resolved or rejected.
17859 *
17860 *   This method *returns a new promise* which is resolved or rejected via the return value of the
17861 *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
17862 *   with the value which is resolved in that promise using
17863 *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
17864 *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
17865 *   resolved or rejected from the notifyCallback method. The errorCallback and notifyCallback
17866 *   arguments are optional.
17867 *
17868 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
17869 *
17870 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
17871 *   but to do so without modifying the final value. This is useful to release resources or do some
17872 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
17873 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
17874 *   more information.
17875 *
17876 * ## Chaining promises
17877 *
17878 * Because calling the `then` method of a promise returns a new derived promise, it is easily
17879 * possible to create a chain of promises:
17880 *
17881 * ```js
17882 *   promiseB = promiseA.then(function(result) {
17883 *     return result + 1;
17884 *   });
17885 *
17886 *   // promiseB will be resolved immediately after promiseA is resolved and its value
17887 *   // will be the result of promiseA incremented by 1
17888 * ```
17889 *
17890 * It is possible to create chains of any length and since a promise can be resolved with another
17891 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
17892 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
17893 * $http's response interceptors.
17894 *
17895 *
17896 * ## Differences between Kris Kowal's Q and $q
17897 *
17898 *  There are two main differences:
17899 *
17900 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
17901 *   mechanism in AngularJS, which means faster propagation of resolution or rejection into your
17902 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
17903 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
17904 *   all the important functionality needed for common async tasks.
17905 *
17906 * ## Testing
17907 *
17908 *  ```js
17909 *    it('should simulate promise', inject(function($q, $rootScope) {
17910 *      var deferred = $q.defer();
17911 *      var promise = deferred.promise;
17912 *      var resolvedValue;
17913 *
17914 *      promise.then(function(value) { resolvedValue = value; });
17915 *      expect(resolvedValue).toBeUndefined();
17916 *
17917 *      // Simulate resolving of promise
17918 *      deferred.resolve(123);
17919 *      // Note that the 'then' function does not get called synchronously.
17920 *      // This is because we want the promise API to always be async, whether or not
17921 *      // it got called synchronously or asynchronously.
17922 *      expect(resolvedValue).toBeUndefined();
17923 *
17924 *      // Propagate promise resolution to 'then' functions using $apply().
17925 *      $rootScope.$apply();
17926 *      expect(resolvedValue).toEqual(123);
17927 *    }));
17928 *  ```
17929 *
17930 * @param {function(function, function)} resolver Function which is responsible for resolving or
17931 *   rejecting the newly created promise. The first parameter is a function which resolves the
17932 *   promise, the second parameter is a function which rejects the promise.
17933 *
17934 * @returns {Promise} The newly created promise.
17935 */
17936/**
17937 * @ngdoc provider
17938 * @name $qProvider
17939 * @this
17940 *
17941 * @description
17942 */
17943function $QProvider() {
17944  var errorOnUnhandledRejections = true;
17945  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
17946    return qFactory(function(callback) {
17947      $rootScope.$evalAsync(callback);
17948    }, $exceptionHandler, errorOnUnhandledRejections);
17949  }];
17950
17951  /**
17952   * @ngdoc method
17953   * @name $qProvider#errorOnUnhandledRejections
17954   * @kind function
17955   *
17956   * @description
17957   * Retrieves or overrides whether to generate an error when a rejected promise is not handled.
17958   * This feature is enabled by default.
17959   *
17960   * @param {boolean=} value Whether to generate an error when a rejected promise is not handled.
17961   * @returns {boolean|ng.$qProvider} Current value when called without a new value or self for
17962   *    chaining otherwise.
17963   */
17964  this.errorOnUnhandledRejections = function(value) {
17965    if (isDefined(value)) {
17966      errorOnUnhandledRejections = value;
17967      return this;
17968    } else {
17969      return errorOnUnhandledRejections;
17970    }
17971  };
17972}
17973
17974/** @this */
17975function $$QProvider() {
17976  var errorOnUnhandledRejections = true;
17977  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
17978    return qFactory(function(callback) {
17979      $browser.defer(callback);
17980    }, $exceptionHandler, errorOnUnhandledRejections);
17981  }];
17982
17983  this.errorOnUnhandledRejections = function(value) {
17984    if (isDefined(value)) {
17985      errorOnUnhandledRejections = value;
17986      return this;
17987    } else {
17988      return errorOnUnhandledRejections;
17989    }
17990  };
17991}
17992
17993/**
17994 * Constructs a promise manager.
17995 *
17996 * @param {function(function)} nextTick Function for executing functions in the next turn.
17997 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
17998 *     debugging purposes.
17999 * @param {boolean=} errorOnUnhandledRejections Whether an error should be generated on unhandled
18000 *     promises rejections.
18001 * @returns {object} Promise manager.
18002 */
18003function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
18004  var $qMinErr = minErr('$q', TypeError);
18005  var queueSize = 0;
18006  var checkQueue = [];
18007
18008  /**
18009   * @ngdoc method
18010   * @name ng.$q#defer
18011   * @kind function
18012   *
18013   * @description
18014   * Creates a `Deferred` object which represents a task which will finish in the future.
18015   *
18016   * @returns {Deferred} Returns a new instance of deferred.
18017   */
18018  function defer() {
18019    return new Deferred();
18020  }
18021
18022  function Deferred() {
18023    var promise = this.promise = new Promise();
18024    //Non prototype methods necessary to support unbound execution :/
18025    this.resolve = function(val) { resolvePromise(promise, val); };
18026    this.reject = function(reason) { rejectPromise(promise, reason); };
18027    this.notify = function(progress) { notifyPromise(promise, progress); };
18028  }
18029
18030
18031  function Promise() {
18032    this.$$state = { status: 0 };
18033  }
18034
18035  extend(Promise.prototype, {
18036    then: function(onFulfilled, onRejected, progressBack) {
18037      if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
18038        return this;
18039      }
18040      var result = new Promise();
18041
18042      this.$$state.pending = this.$$state.pending || [];
18043      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
18044      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
18045
18046      return result;
18047    },
18048
18049    'catch': function(callback) {
18050      return this.then(null, callback);
18051    },
18052
18053    'finally': function(callback, progressBack) {
18054      return this.then(function(value) {
18055        return handleCallback(value, resolve, callback);
18056      }, function(error) {
18057        return handleCallback(error, reject, callback);
18058      }, progressBack);
18059    }
18060  });
18061
18062  function processQueue(state) {
18063    var fn, promise, pending;
18064
18065    pending = state.pending;
18066    state.processScheduled = false;
18067    state.pending = undefined;
18068    try {
18069      for (var i = 0, ii = pending.length; i < ii; ++i) {
18070        markQStateExceptionHandled(state);
18071        promise = pending[i][0];
18072        fn = pending[i][state.status];
18073        try {
18074          if (isFunction(fn)) {
18075            resolvePromise(promise, fn(state.value));
18076          } else if (state.status === 1) {
18077            resolvePromise(promise, state.value);
18078          } else {
18079            rejectPromise(promise, state.value);
18080          }
18081        } catch (e) {
18082          rejectPromise(promise, e);
18083          // This error is explicitly marked for being passed to the $exceptionHandler
18084          if (e && e.$$passToExceptionHandler === true) {
18085            exceptionHandler(e);
18086          }
18087        }
18088      }
18089    } finally {
18090      --queueSize;
18091      if (errorOnUnhandledRejections && queueSize === 0) {
18092        nextTick(processChecks);
18093      }
18094    }
18095  }
18096
18097  function processChecks() {
18098    // eslint-disable-next-line no-unmodified-loop-condition
18099    while (!queueSize && checkQueue.length) {
18100      var toCheck = checkQueue.shift();
18101      if (!isStateExceptionHandled(toCheck)) {
18102        markQStateExceptionHandled(toCheck);
18103        var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value);
18104        if (isError(toCheck.value)) {
18105          exceptionHandler(toCheck.value, errorMessage);
18106        } else {
18107          exceptionHandler(errorMessage);
18108        }
18109      }
18110    }
18111  }
18112
18113  function scheduleProcessQueue(state) {
18114    if (errorOnUnhandledRejections && !state.pending && state.status === 2 && !isStateExceptionHandled(state)) {
18115      if (queueSize === 0 && checkQueue.length === 0) {
18116        nextTick(processChecks);
18117      }
18118      checkQueue.push(state);
18119    }
18120    if (state.processScheduled || !state.pending) return;
18121    state.processScheduled = true;
18122    ++queueSize;
18123    nextTick(function() { processQueue(state); });
18124  }
18125
18126  function resolvePromise(promise, val) {
18127    if (promise.$$state.status) return;
18128    if (val === promise) {
18129      $$reject(promise, $qMinErr(
18130        'qcycle',
18131        'Expected promise to be resolved with value other than itself \'{0}\'',
18132        val));
18133    } else {
18134      $$resolve(promise, val);
18135    }
18136
18137  }
18138
18139  function $$resolve(promise, val) {
18140    var then;
18141    var done = false;
18142    try {
18143      if (isObject(val) || isFunction(val)) then = val.then;
18144      if (isFunction(then)) {
18145        promise.$$state.status = -1;
18146        then.call(val, doResolve, doReject, doNotify);
18147      } else {
18148        promise.$$state.value = val;
18149        promise.$$state.status = 1;
18150        scheduleProcessQueue(promise.$$state);
18151      }
18152    } catch (e) {
18153      doReject(e);
18154    }
18155
18156    function doResolve(val) {
18157      if (done) return;
18158      done = true;
18159      $$resolve(promise, val);
18160    }
18161    function doReject(val) {
18162      if (done) return;
18163      done = true;
18164      $$reject(promise, val);
18165    }
18166    function doNotify(progress) {
18167      notifyPromise(promise, progress);
18168    }
18169  }
18170
18171  function rejectPromise(promise, reason) {
18172    if (promise.$$state.status) return;
18173    $$reject(promise, reason);
18174  }
18175
18176  function $$reject(promise, reason) {
18177    promise.$$state.value = reason;
18178    promise.$$state.status = 2;
18179    scheduleProcessQueue(promise.$$state);
18180  }
18181
18182  function notifyPromise(promise, progress) {
18183    var callbacks = promise.$$state.pending;
18184
18185    if ((promise.$$state.status <= 0) && callbacks && callbacks.length) {
18186      nextTick(function() {
18187        var callback, result;
18188        for (var i = 0, ii = callbacks.length; i < ii; i++) {
18189          result = callbacks[i][0];
18190          callback = callbacks[i][3];
18191          try {
18192            notifyPromise(result, isFunction(callback) ? callback(progress) : progress);
18193          } catch (e) {
18194            exceptionHandler(e);
18195          }
18196        }
18197      });
18198    }
18199  }
18200
18201  /**
18202   * @ngdoc method
18203   * @name $q#reject
18204   * @kind function
18205   *
18206   * @description
18207   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
18208   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
18209   * a promise chain, you don't need to worry about it.
18210   *
18211   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
18212   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
18213   * a promise error callback and you want to forward the error to the promise derived from the
18214   * current promise, you have to "rethrow" the error by returning a rejection constructed via
18215   * `reject`.
18216   *
18217   * ```js
18218   *   promiseB = promiseA.then(function(result) {
18219   *     // success: do something and resolve promiseB
18220   *     //          with the old or a new result
18221   *     return result;
18222   *   }, function(reason) {
18223   *     // error: handle the error if possible and
18224   *     //        resolve promiseB with newPromiseOrValue,
18225   *     //        otherwise forward the rejection to promiseB
18226   *     if (canHandle(reason)) {
18227   *      // handle the error and recover
18228   *      return newPromiseOrValue;
18229   *     }
18230   *     return $q.reject(reason);
18231   *   });
18232   * ```
18233   *
18234   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
18235   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
18236   */
18237  function reject(reason) {
18238    var result = new Promise();
18239    rejectPromise(result, reason);
18240    return result;
18241  }
18242
18243  function handleCallback(value, resolver, callback) {
18244    var callbackOutput = null;
18245    try {
18246      if (isFunction(callback)) callbackOutput = callback();
18247    } catch (e) {
18248      return reject(e);
18249    }
18250    if (isPromiseLike(callbackOutput)) {
18251      return callbackOutput.then(function() {
18252        return resolver(value);
18253      }, reject);
18254    } else {
18255      return resolver(value);
18256    }
18257  }
18258
18259  /**
18260   * @ngdoc method
18261   * @name $q#when
18262   * @kind function
18263   *
18264   * @description
18265   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
18266   * This is useful when you are dealing with an object that might or might not be a promise, or if
18267   * the promise comes from a source that can't be trusted.
18268   *
18269   * @param {*} value Value or a promise
18270   * @param {Function=} successCallback
18271   * @param {Function=} errorCallback
18272   * @param {Function=} progressCallback
18273   * @returns {Promise} Returns a promise of the passed value or promise
18274   */
18275
18276
18277  function when(value, callback, errback, progressBack) {
18278    var result = new Promise();
18279    resolvePromise(result, value);
18280    return result.then(callback, errback, progressBack);
18281  }
18282
18283  /**
18284   * @ngdoc method
18285   * @name $q#resolve
18286   * @kind function
18287   *
18288   * @description
18289   * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
18290   *
18291   * @param {*} value Value or a promise
18292   * @param {Function=} successCallback
18293   * @param {Function=} errorCallback
18294   * @param {Function=} progressCallback
18295   * @returns {Promise} Returns a promise of the passed value or promise
18296   */
18297  var resolve = when;
18298
18299  /**
18300   * @ngdoc method
18301   * @name $q#all
18302   * @kind function
18303   *
18304   * @description
18305   * Combines multiple promises into a single promise that is resolved when all of the input
18306   * promises are resolved.
18307   *
18308   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
18309   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
18310   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
18311   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
18312   *   with the same rejection value.
18313   */
18314
18315  function all(promises) {
18316    var result = new Promise(),
18317        counter = 0,
18318        results = isArray(promises) ? [] : {};
18319
18320    forEach(promises, function(promise, key) {
18321      counter++;
18322      when(promise).then(function(value) {
18323        results[key] = value;
18324        if (!(--counter)) resolvePromise(result, results);
18325      }, function(reason) {
18326        rejectPromise(result, reason);
18327      });
18328    });
18329
18330    if (counter === 0) {
18331      resolvePromise(result, results);
18332    }
18333
18334    return result;
18335  }
18336
18337  /**
18338   * @ngdoc method
18339   * @name $q#race
18340   * @kind function
18341   *
18342   * @description
18343   * Returns a promise that resolves or rejects as soon as one of those promises
18344   * resolves or rejects, with the value or reason from that promise.
18345   *
18346   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
18347   * @returns {Promise} a promise that resolves or rejects as soon as one of the `promises`
18348   * resolves or rejects, with the value or reason from that promise.
18349   */
18350
18351  function race(promises) {
18352    var deferred = defer();
18353
18354    forEach(promises, function(promise) {
18355      when(promise).then(deferred.resolve, deferred.reject);
18356    });
18357
18358    return deferred.promise;
18359  }
18360
18361  function $Q(resolver) {
18362    if (!isFunction(resolver)) {
18363      throw $qMinErr('norslvr', 'Expected resolverFn, got \'{0}\'', resolver);
18364    }
18365
18366    var promise = new Promise();
18367
18368    function resolveFn(value) {
18369      resolvePromise(promise, value);
18370    }
18371
18372    function rejectFn(reason) {
18373      rejectPromise(promise, reason);
18374    }
18375
18376    resolver(resolveFn, rejectFn);
18377
18378    return promise;
18379  }
18380
18381  // Let's make the instanceof operator work for promises, so that
18382  // `new $q(fn) instanceof $q` would evaluate to true.
18383  $Q.prototype = Promise.prototype;
18384
18385  $Q.defer = defer;
18386  $Q.reject = reject;
18387  $Q.when = when;
18388  $Q.resolve = resolve;
18389  $Q.all = all;
18390  $Q.race = race;
18391
18392  return $Q;
18393}
18394
18395function isStateExceptionHandled(state) {
18396  return !!state.pur;
18397}
18398function markQStateExceptionHandled(state) {
18399  state.pur = true;
18400}
18401function markQExceptionHandled(q) {
18402  // Built-in `$q` promises will always have a `$$state` property. This check is to allow
18403  // overwriting `$q` with a different promise library (e.g. Bluebird + angular-bluebird-promises).
18404  // (Currently, this is the only method that might be called with a promise, even if it is not
18405  // created by the built-in `$q`.)
18406  if (q.$$state) {
18407    markQStateExceptionHandled(q.$$state);
18408  }
18409}
18410
18411/** @this */
18412function $$RAFProvider() { //rAF
18413  this.$get = ['$window', '$timeout', function($window, $timeout) {
18414    var requestAnimationFrame = $window.requestAnimationFrame ||
18415                                $window.webkitRequestAnimationFrame;
18416
18417    var cancelAnimationFrame = $window.cancelAnimationFrame ||
18418                               $window.webkitCancelAnimationFrame ||
18419                               $window.webkitCancelRequestAnimationFrame;
18420
18421    var rafSupported = !!requestAnimationFrame;
18422    var raf = rafSupported
18423      ? function(fn) {
18424          var id = requestAnimationFrame(fn);
18425          return function() {
18426            cancelAnimationFrame(id);
18427          };
18428        }
18429      : function(fn) {
18430          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
18431          return function() {
18432            $timeout.cancel(timer);
18433          };
18434        };
18435
18436    raf.supported = rafSupported;
18437
18438    return raf;
18439  }];
18440}
18441
18442/**
18443 * DESIGN NOTES
18444 *
18445 * The design decisions behind the scope are heavily favored for speed and memory consumption.
18446 *
18447 * The typical use of scope is to watch the expressions, which most of the time return the same
18448 * value as last time so we optimize the operation.
18449 *
18450 * Closures construction is expensive in terms of speed as well as memory:
18451 *   - No closures, instead use prototypical inheritance for API
18452 *   - Internal state needs to be stored on scope directly, which means that private state is
18453 *     exposed as $$____ properties
18454 *
18455 * Loop operations are optimized by using while(count--) { ... }
18456 *   - This means that in order to keep the same order of execution as addition we have to add
18457 *     items to the array at the beginning (unshift) instead of at the end (push)
18458 *
18459 * Child scopes are created and removed often
18460 *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
18461 *
18462 * There are fewer watches than observers. This is why you don't want the observer to be implemented
18463 * in the same way as watch. Watch requires return of the initialization function which is expensive
18464 * to construct.
18465 */
18466
18467
18468/**
18469 * @ngdoc provider
18470 * @name $rootScopeProvider
18471 * @description
18472 *
18473 * Provider for the $rootScope service.
18474 */
18475
18476/**
18477 * @ngdoc method
18478 * @name $rootScopeProvider#digestTtl
18479 * @description
18480 *
18481 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
18482 * assuming that the model is unstable.
18483 *
18484 * The current default is 10 iterations.
18485 *
18486 * In complex applications it's possible that the dependencies between `$watch`s will result in
18487 * several digest iterations. However if an application needs more than the default 10 digest
18488 * iterations for its model to stabilize then you should investigate what is causing the model to
18489 * continuously change during the digest.
18490 *
18491 * Increasing the TTL could have performance implications, so you should not change it without
18492 * proper justification.
18493 *
18494 * @param {number} limit The number of digest iterations.
18495 */
18496
18497
18498/**
18499 * @ngdoc service
18500 * @name $rootScope
18501 * @this
18502 *
18503 * @description
18504 *
18505 * Every application has a single root {@link ng.$rootScope.Scope scope}.
18506 * All other scopes are descendant scopes of the root scope. Scopes provide separation
18507 * between the model and the view, via a mechanism for watching the model for changes.
18508 * They also provide event emission/broadcast and subscription facility. See the
18509 * {@link guide/scope developer guide on scopes}.
18510 */
18511function $RootScopeProvider() {
18512  var TTL = 10;
18513  var $rootScopeMinErr = minErr('$rootScope');
18514  var lastDirtyWatch = null;
18515  var applyAsyncId = null;
18516
18517  this.digestTtl = function(value) {
18518    if (arguments.length) {
18519      TTL = value;
18520    }
18521    return TTL;
18522  };
18523
18524  function createChildScopeClass(parent) {
18525    function ChildScope() {
18526      this.$$watchers = this.$$nextSibling =
18527          this.$$childHead = this.$$childTail = null;
18528      this.$$listeners = {};
18529      this.$$listenerCount = {};
18530      this.$$watchersCount = 0;
18531      this.$id = nextUid();
18532      this.$$ChildScope = null;
18533      this.$$suspended = false;
18534    }
18535    ChildScope.prototype = parent;
18536    return ChildScope;
18537  }
18538
18539  this.$get = ['$exceptionHandler', '$parse', '$browser',
18540      function($exceptionHandler, $parse, $browser) {
18541
18542    function destroyChildScope($event) {
18543        $event.currentScope.$$destroyed = true;
18544    }
18545
18546    function cleanUpScope($scope) {
18547
18548      // Support: IE 9 only
18549      if (msie === 9) {
18550        // There is a memory leak in IE9 if all child scopes are not disconnected
18551        // completely when a scope is destroyed. So this code will recurse up through
18552        // all this scopes children
18553        //
18554        // See issue https://github.com/angular/angular.js/issues/10706
18555        if ($scope.$$childHead) {
18556          cleanUpScope($scope.$$childHead);
18557        }
18558        if ($scope.$$nextSibling) {
18559          cleanUpScope($scope.$$nextSibling);
18560        }
18561      }
18562
18563      // The code below works around IE9 and V8's memory leaks
18564      //
18565      // See:
18566      // - https://code.google.com/p/v8/issues/detail?id=2073#c26
18567      // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
18568      // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
18569
18570      $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
18571          $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
18572    }
18573
18574    /**
18575     * @ngdoc type
18576     * @name $rootScope.Scope
18577     *
18578     * @description
18579     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
18580     * {@link auto.$injector $injector}. Child scopes are created using the
18581     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
18582     * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
18583     * an in-depth introduction and usage examples.
18584     *
18585     *
18586     * ## Inheritance
18587     * A scope can inherit from a parent scope, as in this example:
18588     * ```js
18589         var parent = $rootScope;
18590         var child = parent.$new();
18591
18592         parent.salutation = "Hello";
18593         expect(child.salutation).toEqual('Hello');
18594
18595         child.salutation = "Welcome";
18596         expect(child.salutation).toEqual('Welcome');
18597         expect(parent.salutation).toEqual('Hello');
18598     * ```
18599     *
18600     * When interacting with `Scope` in tests, additional helper methods are available on the
18601     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
18602     * details.
18603     *
18604     *
18605     * @param {Object.<string, function()>=} providers Map of service factory which need to be
18606     *                                       provided for the current scope. Defaults to {@link ng}.
18607     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
18608     *                              append/override services provided by `providers`. This is handy
18609     *                              when unit-testing and having the need to override a default
18610     *                              service.
18611     * @returns {Object} Newly created scope.
18612     *
18613     */
18614    function Scope() {
18615      this.$id = nextUid();
18616      this.$$phase = this.$parent = this.$$watchers =
18617                     this.$$nextSibling = this.$$prevSibling =
18618                     this.$$childHead = this.$$childTail = null;
18619      this.$root = this;
18620      this.$$destroyed = false;
18621      this.$$suspended = false;
18622      this.$$listeners = {};
18623      this.$$listenerCount = {};
18624      this.$$watchersCount = 0;
18625      this.$$isolateBindings = null;
18626    }
18627
18628    /**
18629     * @ngdoc property
18630     * @name $rootScope.Scope#$id
18631     *
18632     * @description
18633     * Unique scope ID (monotonically increasing) useful for debugging.
18634     */
18635
18636     /**
18637      * @ngdoc property
18638      * @name $rootScope.Scope#$parent
18639      *
18640      * @description
18641      * Reference to the parent scope.
18642      */
18643
18644      /**
18645       * @ngdoc property
18646       * @name $rootScope.Scope#$root
18647       *
18648       * @description
18649       * Reference to the root scope.
18650       */
18651
18652    Scope.prototype = {
18653      constructor: Scope,
18654      /**
18655       * @ngdoc method
18656       * @name $rootScope.Scope#$new
18657       * @kind function
18658       *
18659       * @description
18660       * Creates a new child {@link ng.$rootScope.Scope scope}.
18661       *
18662       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
18663       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
18664       *
18665       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
18666       * desired for the scope and its child scopes to be permanently detached from the parent and
18667       * thus stop participating in model change detection and listener notification by invoking.
18668       *
18669       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
18670       *         parent scope. The scope is isolated, as it can not see parent scope properties.
18671       *         When creating widgets, it is useful for the widget to not accidentally read parent
18672       *         state.
18673       *
18674       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
18675       *                              of the newly created scope. Defaults to `this` scope if not provided.
18676       *                              This is used when creating a transclude scope to correctly place it
18677       *                              in the scope hierarchy while maintaining the correct prototypical
18678       *                              inheritance.
18679       *
18680       * @returns {Object} The newly created child scope.
18681       *
18682       */
18683      $new: function(isolate, parent) {
18684        var child;
18685
18686        parent = parent || this;
18687
18688        if (isolate) {
18689          child = new Scope();
18690          child.$root = this.$root;
18691        } else {
18692          // Only create a child scope class if somebody asks for one,
18693          // but cache it to allow the VM to optimize lookups.
18694          if (!this.$$ChildScope) {
18695            this.$$ChildScope = createChildScopeClass(this);
18696          }
18697          child = new this.$$ChildScope();
18698        }
18699        child.$parent = parent;
18700        child.$$prevSibling = parent.$$childTail;
18701        if (parent.$$childHead) {
18702          parent.$$childTail.$$nextSibling = child;
18703          parent.$$childTail = child;
18704        } else {
18705          parent.$$childHead = parent.$$childTail = child;
18706        }
18707
18708        // When the new scope is not isolated or we inherit from `this`, and
18709        // the parent scope is destroyed, the property `$$destroyed` is inherited
18710        // prototypically. In all other cases, this property needs to be set
18711        // when the parent scope is destroyed.
18712        // The listener needs to be added after the parent is set
18713        if (isolate || parent !== this) child.$on('$destroy', destroyChildScope);
18714
18715        return child;
18716      },
18717
18718      /**
18719       * @ngdoc method
18720       * @name $rootScope.Scope#$watch
18721       * @kind function
18722       *
18723       * @description
18724       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
18725       *
18726       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
18727       *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
18728       *   its value when executed multiple times with the same input because it may be executed multiple
18729       *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
18730       *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).)
18731       * - The `listener` is called only when the value from the current `watchExpression` and the
18732       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
18733       *   see below). Inequality is determined according to reference inequality,
18734       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
18735       *    via the `!==` Javascript operator, unless `objectEquality == true`
18736       *   (see next point)
18737       * - When `objectEquality == true`, inequality of the `watchExpression` is determined
18738       *   according to the {@link angular.equals} function. To save the value of the object for
18739       *   later comparison, the {@link angular.copy} function is used. This therefore means that
18740       *   watching complex objects will have adverse memory and performance implications.
18741       * - This should not be used to watch for changes in objects that are (or contain)
18742       *   [File](https://developer.mozilla.org/docs/Web/API/File) objects due to limitations with {@link angular.copy `angular.copy`}.
18743       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
18744       *   This is achieved by rerunning the watchers until no changes are detected. The rerun
18745       *   iteration limit is 10 to prevent an infinite loop deadlock.
18746       *
18747       *
18748       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
18749       * you can register a `watchExpression` function with no `listener`. (Be prepared for
18750       * multiple calls to your `watchExpression` because it will execute multiple times in a
18751       * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
18752       *
18753       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
18754       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
18755       * watcher. In rare cases, this is undesirable because the listener is called when the result
18756       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
18757       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
18758       * listener was called due to initialization.
18759       *
18760       *
18761       *
18762       * @example
18763       * ```js
18764           // let's assume that scope was dependency injected as the $rootScope
18765           var scope = $rootScope;
18766           scope.name = 'misko';
18767           scope.counter = 0;
18768
18769           expect(scope.counter).toEqual(0);
18770           scope.$watch('name', function(newValue, oldValue) {
18771             scope.counter = scope.counter + 1;
18772           });
18773           expect(scope.counter).toEqual(0);
18774
18775           scope.$digest();
18776           // the listener is always called during the first $digest loop after it was registered
18777           expect(scope.counter).toEqual(1);
18778
18779           scope.$digest();
18780           // but now it will not be called unless the value changes
18781           expect(scope.counter).toEqual(1);
18782
18783           scope.name = 'adam';
18784           scope.$digest();
18785           expect(scope.counter).toEqual(2);
18786
18787
18788
18789           // Using a function as a watchExpression
18790           var food;
18791           scope.foodCounter = 0;
18792           expect(scope.foodCounter).toEqual(0);
18793           scope.$watch(
18794             // This function returns the value being watched. It is called for each turn of the $digest loop
18795             function() { return food; },
18796             // This is the change listener, called when the value returned from the above function changes
18797             function(newValue, oldValue) {
18798               if ( newValue !== oldValue ) {
18799                 // Only increment the counter if the value changed
18800                 scope.foodCounter = scope.foodCounter + 1;
18801               }
18802             }
18803           );
18804           // No digest has been run so the counter will be zero
18805           expect(scope.foodCounter).toEqual(0);
18806
18807           // Run the digest but since food has not changed count will still be zero
18808           scope.$digest();
18809           expect(scope.foodCounter).toEqual(0);
18810
18811           // Update food and run digest.  Now the counter will increment
18812           food = 'cheeseburger';
18813           scope.$digest();
18814           expect(scope.foodCounter).toEqual(1);
18815
18816       * ```
18817       *
18818       *
18819       *
18820       * @param {(function()|string)} watchExpression Expression that is evaluated on each
18821       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
18822       *    a call to the `listener`.
18823       *
18824       *    - `string`: Evaluated as {@link guide/expression expression}
18825       *    - `function(scope)`: called with current `scope` as a parameter.
18826       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
18827       *    of `watchExpression` changes.
18828       *
18829       *    - `newVal` contains the current value of the `watchExpression`
18830       *    - `oldVal` contains the previous value of the `watchExpression`
18831       *    - `scope` refers to the current scope
18832       * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
18833       *     comparing for reference equality.
18834       * @returns {function()} Returns a deregistration function for this listener.
18835       */
18836      $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
18837        var get = $parse(watchExp);
18838        var fn = isFunction(listener) ? listener : noop;
18839
18840        if (get.$$watchDelegate) {
18841          return get.$$watchDelegate(this, fn, objectEquality, get, watchExp);
18842        }
18843        var scope = this,
18844            array = scope.$$watchers,
18845            watcher = {
18846              fn: fn,
18847              last: initWatchVal,
18848              get: get,
18849              exp: prettyPrintExpression || watchExp,
18850              eq: !!objectEquality
18851            };
18852
18853        lastDirtyWatch = null;
18854
18855        if (!array) {
18856          array = scope.$$watchers = [];
18857          array.$$digestWatchIndex = -1;
18858        }
18859        // we use unshift since we use a while loop in $digest for speed.
18860        // the while loop reads in reverse order.
18861        array.unshift(watcher);
18862        array.$$digestWatchIndex++;
18863        incrementWatchersCount(this, 1);
18864
18865        return function deregisterWatch() {
18866          var index = arrayRemove(array, watcher);
18867          if (index >= 0) {
18868            incrementWatchersCount(scope, -1);
18869            if (index < array.$$digestWatchIndex) {
18870              array.$$digestWatchIndex--;
18871            }
18872          }
18873          lastDirtyWatch = null;
18874        };
18875      },
18876
18877      /**
18878       * @ngdoc method
18879       * @name $rootScope.Scope#$watchGroup
18880       * @kind function
18881       *
18882       * @description
18883       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
18884       * If any one expression in the collection changes the `listener` is executed.
18885       *
18886       * - The items in the `watchExpressions` array are observed via the standard `$watch` operation. Their return
18887       *   values are examined for changes on every call to `$digest`.
18888       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
18889       *
18890       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
18891       * watched using {@link ng.$rootScope.Scope#$watch $watch()}
18892       *
18893       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
18894       *    expression in `watchExpressions` changes
18895       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
18896       *    those of `watchExpression`
18897       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
18898       *    those of `watchExpression`
18899       *    The `scope` refers to the current scope.
18900       * @returns {function()} Returns a de-registration function for all listeners.
18901       */
18902      $watchGroup: function(watchExpressions, listener) {
18903        var oldValues = new Array(watchExpressions.length);
18904        var newValues = new Array(watchExpressions.length);
18905        var deregisterFns = [];
18906        var self = this;
18907        var changeReactionScheduled = false;
18908        var firstRun = true;
18909
18910        if (!watchExpressions.length) {
18911          // No expressions means we call the listener ASAP
18912          var shouldCall = true;
18913          self.$evalAsync(function() {
18914            if (shouldCall) listener(newValues, newValues, self);
18915          });
18916          return function deregisterWatchGroup() {
18917            shouldCall = false;
18918          };
18919        }
18920
18921        if (watchExpressions.length === 1) {
18922          // Special case size of one
18923          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
18924            newValues[0] = value;
18925            oldValues[0] = oldValue;
18926            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
18927          });
18928        }
18929
18930        forEach(watchExpressions, function(expr, i) {
18931          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value) {
18932            newValues[i] = value;
18933            if (!changeReactionScheduled) {
18934              changeReactionScheduled = true;
18935              self.$evalAsync(watchGroupAction);
18936            }
18937          });
18938          deregisterFns.push(unwatchFn);
18939        });
18940
18941        function watchGroupAction() {
18942          changeReactionScheduled = false;
18943
18944          try {
18945            if (firstRun) {
18946              firstRun = false;
18947              listener(newValues, newValues, self);
18948            } else {
18949              listener(newValues, oldValues, self);
18950            }
18951          } finally {
18952            for (var i = 0; i < watchExpressions.length; i++) {
18953              oldValues[i] = newValues[i];
18954            }
18955          }
18956        }
18957
18958        return function deregisterWatchGroup() {
18959          while (deregisterFns.length) {
18960            deregisterFns.shift()();
18961          }
18962        };
18963      },
18964
18965
18966      /**
18967       * @ngdoc method
18968       * @name $rootScope.Scope#$watchCollection
18969       * @kind function
18970       *
18971       * @description
18972       * Shallow watches the properties of an object and fires whenever any of the properties change
18973       * (for arrays, this implies watching the array items; for object maps, this implies watching
18974       * the properties). If a change is detected, the `listener` callback is fired.
18975       *
18976       * - The `obj` collection is observed via standard $watch operation and is examined on every
18977       *   call to $digest() to see if any items have been added, removed, or moved.
18978       * - The `listener` is called whenever anything within the `obj` has changed. Examples include
18979       *   adding, removing, and moving items belonging to an object or array.
18980       *
18981       *
18982       * @example
18983       * ```js
18984          $scope.names = ['igor', 'matias', 'misko', 'james'];
18985          $scope.dataCount = 4;
18986
18987          $scope.$watchCollection('names', function(newNames, oldNames) {
18988            $scope.dataCount = newNames.length;
18989          });
18990
18991          expect($scope.dataCount).toEqual(4);
18992          $scope.$digest();
18993
18994          //still at 4 ... no changes
18995          expect($scope.dataCount).toEqual(4);
18996
18997          $scope.names.pop();
18998          $scope.$digest();
18999
19000          //now there's been a change
19001          expect($scope.dataCount).toEqual(3);
19002       * ```
19003       *
19004       *
19005       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
19006       *    expression value should evaluate to an object or an array which is observed on each
19007       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
19008       *    collection will trigger a call to the `listener`.
19009       *
19010       * @param {function(newCollection, oldCollection, scope)} listener a callback function called
19011       *    when a change is detected.
19012       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
19013       *    - The `oldCollection` object is a copy of the former collection data.
19014       *      Due to performance considerations, the`oldCollection` value is computed only if the
19015       *      `listener` function declares two or more arguments.
19016       *    - The `scope` argument refers to the current scope.
19017       *
19018       * @returns {function()} Returns a de-registration function for this listener. When the
19019       *    de-registration function is executed, the internal watch operation is terminated.
19020       */
19021      $watchCollection: function(obj, listener) {
19022        // Mark the interceptor as
19023        // ... $$pure when literal since the instance will change when any input changes
19024        $watchCollectionInterceptor.$$pure = $parse(obj).literal;
19025        // ... $stateful when non-literal since we must read the state of the collection
19026        $watchCollectionInterceptor.$stateful = !$watchCollectionInterceptor.$$pure;
19027
19028        var self = this;
19029        // the current value, updated on each dirty-check run
19030        var newValue;
19031        // a shallow copy of the newValue from the last dirty-check run,
19032        // updated to match newValue during dirty-check run
19033        var oldValue;
19034        // a shallow copy of the newValue from when the last change happened
19035        var veryOldValue;
19036        // only track veryOldValue if the listener is asking for it
19037        var trackVeryOldValue = (listener.length > 1);
19038        var changeDetected = 0;
19039        var changeDetector = $parse(obj, $watchCollectionInterceptor);
19040        var internalArray = [];
19041        var internalObject = {};
19042        var initRun = true;
19043        var oldLength = 0;
19044
19045        function $watchCollectionInterceptor(_value) {
19046          newValue = _value;
19047          var newLength, key, bothNaN, newItem, oldItem;
19048
19049          // If the new value is undefined, then return undefined as the watch may be a one-time watch
19050          if (isUndefined(newValue)) return;
19051
19052          if (!isObject(newValue)) { // if primitive
19053            if (oldValue !== newValue) {
19054              oldValue = newValue;
19055              changeDetected++;
19056            }
19057          } else if (isArrayLike(newValue)) {
19058            if (oldValue !== internalArray) {
19059              // we are transitioning from something which was not an array into array.
19060              oldValue = internalArray;
19061              oldLength = oldValue.length = 0;
19062              changeDetected++;
19063            }
19064
19065            newLength = newValue.length;
19066
19067            if (oldLength !== newLength) {
19068              // if lengths do not match we need to trigger change notification
19069              changeDetected++;
19070              oldValue.length = oldLength = newLength;
19071            }
19072            // copy the items to oldValue and look for changes.
19073            for (var i = 0; i < newLength; i++) {
19074              oldItem = oldValue[i];
19075              newItem = newValue[i];
19076
19077              // eslint-disable-next-line no-self-compare
19078              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
19079              if (!bothNaN && (oldItem !== newItem)) {
19080                changeDetected++;
19081                oldValue[i] = newItem;
19082              }
19083            }
19084          } else {
19085            if (oldValue !== internalObject) {
19086              // we are transitioning from something which was not an object into object.
19087              oldValue = internalObject = {};
19088              oldLength = 0;
19089              changeDetected++;
19090            }
19091            // copy the items to oldValue and look for changes.
19092            newLength = 0;
19093            for (key in newValue) {
19094              if (hasOwnProperty.call(newValue, key)) {
19095                newLength++;
19096                newItem = newValue[key];
19097                oldItem = oldValue[key];
19098
19099                if (key in oldValue) {
19100                  // eslint-disable-next-line no-self-compare
19101                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
19102                  if (!bothNaN && (oldItem !== newItem)) {
19103                    changeDetected++;
19104                    oldValue[key] = newItem;
19105                  }
19106                } else {
19107                  oldLength++;
19108                  oldValue[key] = newItem;
19109                  changeDetected++;
19110                }
19111              }
19112            }
19113            if (oldLength > newLength) {
19114              // we used to have more keys, need to find them and destroy them.
19115              changeDetected++;
19116              for (key in oldValue) {
19117                if (!hasOwnProperty.call(newValue, key)) {
19118                  oldLength--;
19119                  delete oldValue[key];
19120                }
19121              }
19122            }
19123          }
19124          return changeDetected;
19125        }
19126
19127        function $watchCollectionAction() {
19128          if (initRun) {
19129            initRun = false;
19130            listener(newValue, newValue, self);
19131          } else {
19132            listener(newValue, veryOldValue, self);
19133          }
19134
19135          // make a copy for the next time a collection is changed
19136          if (trackVeryOldValue) {
19137            if (!isObject(newValue)) {
19138              //primitive
19139              veryOldValue = newValue;
19140            } else if (isArrayLike(newValue)) {
19141              veryOldValue = new Array(newValue.length);
19142              for (var i = 0; i < newValue.length; i++) {
19143                veryOldValue[i] = newValue[i];
19144              }
19145            } else { // if object
19146              veryOldValue = {};
19147              for (var key in newValue) {
19148                if (hasOwnProperty.call(newValue, key)) {
19149                  veryOldValue[key] = newValue[key];
19150                }
19151              }
19152            }
19153          }
19154        }
19155
19156        return this.$watch(changeDetector, $watchCollectionAction);
19157      },
19158
19159      /**
19160       * @ngdoc method
19161       * @name $rootScope.Scope#$digest
19162       * @kind function
19163       *
19164       * @description
19165       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
19166       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
19167       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
19168       * until no more listeners are firing. This means that it is possible to get into an infinite
19169       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
19170       * iterations exceeds 10.
19171       *
19172       * Usually, you don't call `$digest()` directly in
19173       * {@link ng.directive:ngController controllers} or in
19174       * {@link ng.$compileProvider#directive directives}.
19175       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
19176       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
19177       *
19178       * If you want to be notified whenever `$digest()` is called,
19179       * you can register a `watchExpression` function with
19180       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
19181       *
19182       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
19183       *
19184       * @example
19185       * ```js
19186           var scope = ...;
19187           scope.name = 'misko';
19188           scope.counter = 0;
19189
19190           expect(scope.counter).toEqual(0);
19191           scope.$watch('name', function(newValue, oldValue) {
19192             scope.counter = scope.counter + 1;
19193           });
19194           expect(scope.counter).toEqual(0);
19195
19196           scope.$digest();
19197           // the listener is always called during the first $digest loop after it was registered
19198           expect(scope.counter).toEqual(1);
19199
19200           scope.$digest();
19201           // but now it will not be called unless the value changes
19202           expect(scope.counter).toEqual(1);
19203
19204           scope.name = 'adam';
19205           scope.$digest();
19206           expect(scope.counter).toEqual(2);
19207       * ```
19208       *
19209       */
19210      $digest: function() {
19211        var watch, value, last, fn, get,
19212            watchers,
19213            dirty, ttl = TTL,
19214            next, current, target = asyncQueue.length ? $rootScope : this,
19215            watchLog = [],
19216            logIdx, asyncTask;
19217
19218        beginPhase('$digest');
19219        // Check for changes to browser url that happened in sync before the call to $digest
19220        $browser.$$checkUrlChange();
19221
19222        if (this === $rootScope && applyAsyncId !== null) {
19223          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
19224          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
19225          $browser.defer.cancel(applyAsyncId);
19226          flushApplyAsync();
19227        }
19228
19229        lastDirtyWatch = null;
19230
19231        do { // "while dirty" loop
19232          dirty = false;
19233          current = target;
19234
19235          // It's safe for asyncQueuePosition to be a local variable here because this loop can't
19236          // be reentered recursively. Calling $digest from a function passed to $evalAsync would
19237          // lead to a '$digest already in progress' error.
19238          for (var asyncQueuePosition = 0; asyncQueuePosition < asyncQueue.length; asyncQueuePosition++) {
19239            try {
19240              asyncTask = asyncQueue[asyncQueuePosition];
19241              fn = asyncTask.fn;
19242              fn(asyncTask.scope, asyncTask.locals);
19243            } catch (e) {
19244              $exceptionHandler(e);
19245            }
19246            lastDirtyWatch = null;
19247          }
19248          asyncQueue.length = 0;
19249
19250          traverseScopesLoop:
19251          do { // "traverse the scopes" loop
19252            if ((watchers = !current.$$suspended && current.$$watchers)) {
19253              // process our watches
19254              watchers.$$digestWatchIndex = watchers.length;
19255              while (watchers.$$digestWatchIndex--) {
19256                try {
19257                  watch = watchers[watchers.$$digestWatchIndex];
19258                  // Most common watches are on primitives, in which case we can short
19259                  // circuit it with === operator, only when === fails do we use .equals
19260                  if (watch) {
19261                    get = watch.get;
19262                    if ((value = get(current)) !== (last = watch.last) &&
19263                        !(watch.eq
19264                            ? equals(value, last)
19265                            : (isNumberNaN(value) && isNumberNaN(last)))) {
19266                      dirty = true;
19267                      lastDirtyWatch = watch;
19268                      watch.last = watch.eq ? copy(value, null) : value;
19269                      fn = watch.fn;
19270                      fn(value, ((last === initWatchVal) ? value : last), current);
19271                      if (ttl < 5) {
19272                        logIdx = 4 - ttl;
19273                        if (!watchLog[logIdx]) watchLog[logIdx] = [];
19274                        watchLog[logIdx].push({
19275                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
19276                          newVal: value,
19277                          oldVal: last
19278                        });
19279                      }
19280                    } else if (watch === lastDirtyWatch) {
19281                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
19282                      // have already been tested.
19283                      dirty = false;
19284                      break traverseScopesLoop;
19285                    }
19286                  }
19287                } catch (e) {
19288                  $exceptionHandler(e);
19289                }
19290              }
19291            }
19292
19293            // Insanity Warning: scope depth-first traversal
19294            // yes, this code is a bit crazy, but it works and we have tests to prove it!
19295            // this piece should be kept in sync with the traversal in $broadcast
19296            // (though it differs due to having the extra check for $$suspended and does not
19297            // check $$listenerCount)
19298            if (!(next = ((!current.$$suspended && current.$$watchersCount && current.$$childHead) ||
19299                (current !== target && current.$$nextSibling)))) {
19300              while (current !== target && !(next = current.$$nextSibling)) {
19301                current = current.$parent;
19302              }
19303            }
19304          } while ((current = next));
19305
19306          // `break traverseScopesLoop;` takes us to here
19307
19308          if ((dirty || asyncQueue.length) && !(ttl--)) {
19309            clearPhase();
19310            throw $rootScopeMinErr('infdig',
19311                '{0} $digest() iterations reached. Aborting!\n' +
19312                'Watchers fired in the last 5 iterations: {1}',
19313                TTL, watchLog);
19314          }
19315
19316        } while (dirty || asyncQueue.length);
19317
19318        clearPhase();
19319
19320        // postDigestQueuePosition isn't local here because this loop can be reentered recursively.
19321        while (postDigestQueuePosition < postDigestQueue.length) {
19322          try {
19323            postDigestQueue[postDigestQueuePosition++]();
19324          } catch (e) {
19325            $exceptionHandler(e);
19326          }
19327        }
19328        postDigestQueue.length = postDigestQueuePosition = 0;
19329
19330        // Check for changes to browser url that happened during the $digest
19331        // (for which no event is fired; e.g. via `history.pushState()`)
19332        $browser.$$checkUrlChange();
19333      },
19334
19335      /**
19336       * @ngdoc method
19337       * @name $rootScope.Scope#$suspend
19338       * @kind function
19339       *
19340       * @description
19341       * Suspend watchers of this scope subtree so that they will not be invoked during digest.
19342       *
19343       * This can be used to optimize your application when you know that running those watchers
19344       * is redundant.
19345       *
19346       * **Warning**
19347       *
19348       * Suspending scopes from the digest cycle can have unwanted and difficult to debug results.
19349       * Only use this approach if you are confident that you know what you are doing and have
19350       * ample tests to ensure that bindings get updated as you expect.
19351       *
19352       * Some of the things to consider are:
19353       *
19354       * * Any external event on a directive/component will not trigger a digest while the hosting
19355       *   scope is suspended - even if the event handler calls `$apply()` or `$rootScope.$digest()`.
19356       * * Transcluded content exists on a scope that inherits from outside a directive but exists
19357       *   as a child of the directive's containing scope. If the containing scope is suspended the
19358       *   transcluded scope will also be suspended, even if the scope from which the transcluded
19359       *   scope inherits is not suspended.
19360       * * Multiple directives trying to manage the suspended status of a scope can confuse each other:
19361       *    * A call to `$suspend()` on an already suspended scope is a no-op.
19362       *    * A call to `$resume()` on a non-suspended scope is a no-op.
19363       *    * If two directives suspend a scope, then one of them resumes the scope, the scope will no
19364       *      longer be suspended. This could result in the other directive believing a scope to be
19365       *      suspended when it is not.
19366       * * If a parent scope is suspended then all its descendants will be also excluded from future
19367       *   digests whether or not they have been suspended themselves. Note that this also applies to
19368       *   isolate child scopes.
19369       * * Calling `$digest()` directly on a descendant of a suspended scope will still run the watchers
19370       *   for that scope and its descendants. When digesting we only check whether the current scope is
19371       *   locally suspended, rather than checking whether it has a suspended ancestor.
19372       * * Calling `$resume()` on a scope that has a suspended ancestor will not cause the scope to be
19373       *   included in future digests until all its ancestors have been resumed.
19374       * * Resolved promises, e.g. from explicit `$q` deferreds and `$http` calls, trigger `$apply()`
19375       *   against the `$rootScope` and so will still trigger a global digest even if the promise was
19376       *   initiated by a component that lives on a suspended scope.
19377       */
19378      $suspend: function() {
19379        this.$$suspended = true;
19380      },
19381
19382      /**
19383       * @ngdoc method
19384       * @name $rootScope.Scope#$isSuspended
19385       * @kind function
19386       *
19387       * @description
19388       * Call this method to determine if this scope has been explicitly suspended. It will not
19389       * tell you whether an ancestor has been suspended.
19390       * To determine if this scope will be excluded from a digest triggered at the $rootScope,
19391       * for example, you must check all its ancestors:
19392       *
19393       * ```
19394       * function isExcludedFromDigest(scope) {
19395       *   while(scope) {
19396       *     if (scope.$isSuspended()) return true;
19397       *     scope = scope.$parent;
19398       *   }
19399       *   return false;
19400       * ```
19401       *
19402       * Be aware that a scope may not be included in digests if it has a suspended ancestor,
19403       * even if `$isSuspended()` returns false.
19404       *
19405       * @returns true if the current scope has been suspended.
19406       */
19407      $isSuspended: function() {
19408        return this.$$suspended;
19409      },
19410
19411      /**
19412       * @ngdoc method
19413       * @name $rootScope.Scope#$resume
19414       * @kind function
19415       *
19416       * @description
19417       * Resume watchers of this scope subtree in case it was suspended.
19418       *
19419       * See {@link $rootScope.Scope#$suspend} for information about the dangers of using this approach.
19420       */
19421      $resume: function() {
19422        this.$$suspended = false;
19423      },
19424
19425      /**
19426       * @ngdoc event
19427       * @name $rootScope.Scope#$destroy
19428       * @eventType broadcast on scope being destroyed
19429       *
19430       * @description
19431       * Broadcasted when a scope and its children are being destroyed.
19432       *
19433       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
19434       * clean up DOM bindings before an element is removed from the DOM.
19435       */
19436
19437      /**
19438       * @ngdoc method
19439       * @name $rootScope.Scope#$destroy
19440       * @kind function
19441       *
19442       * @description
19443       * Removes the current scope (and all of its children) from the parent scope. Removal implies
19444       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
19445       * propagate to the current scope and its children. Removal also implies that the current
19446       * scope is eligible for garbage collection.
19447       *
19448       * The `$destroy()` is usually used by directives such as
19449       * {@link ng.directive:ngRepeat ngRepeat} for managing the
19450       * unrolling of the loop.
19451       *
19452       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
19453       * Application code can register a `$destroy` event handler that will give it a chance to
19454       * perform any necessary cleanup.
19455       *
19456       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
19457       * clean up DOM bindings before an element is removed from the DOM.
19458       */
19459      $destroy: function() {
19460        // We can't destroy a scope that has been already destroyed.
19461        if (this.$$destroyed) return;
19462        var parent = this.$parent;
19463
19464        this.$broadcast('$destroy');
19465        this.$$destroyed = true;
19466
19467        if (this === $rootScope) {
19468          //Remove handlers attached to window when $rootScope is removed
19469          $browser.$$applicationDestroyed();
19470        }
19471
19472        incrementWatchersCount(this, -this.$$watchersCount);
19473        for (var eventName in this.$$listenerCount) {
19474          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
19475        }
19476
19477        // sever all the references to parent scopes (after this cleanup, the current scope should
19478        // not be retained by any of our references and should be eligible for garbage collection)
19479        if (parent && parent.$$childHead === this) parent.$$childHead = this.$$nextSibling;
19480        if (parent && parent.$$childTail === this) parent.$$childTail = this.$$prevSibling;
19481        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
19482        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
19483
19484        // Disable listeners, watchers and apply/digest methods
19485        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
19486        this.$on = this.$watch = this.$watchGroup = function() { return noop; };
19487        this.$$listeners = {};
19488
19489        // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
19490        this.$$nextSibling = null;
19491        cleanUpScope(this);
19492      },
19493
19494      /**
19495       * @ngdoc method
19496       * @name $rootScope.Scope#$eval
19497       * @kind function
19498       *
19499       * @description
19500       * Executes the `expression` on the current scope and returns the result. Any exceptions in
19501       * the expression are propagated (uncaught). This is useful when evaluating AngularJS
19502       * expressions.
19503       *
19504       * @example
19505       * ```js
19506           var scope = ng.$rootScope.Scope();
19507           scope.a = 1;
19508           scope.b = 2;
19509
19510           expect(scope.$eval('a+b')).toEqual(3);
19511           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
19512       * ```
19513       *
19514       * @param {(string|function())=} expression An AngularJS expression to be executed.
19515       *
19516       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
19517       *    - `function(scope)`: execute the function with the current `scope` parameter.
19518       *
19519       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
19520       * @returns {*} The result of evaluating the expression.
19521       */
19522      $eval: function(expr, locals) {
19523        return $parse(expr)(this, locals);
19524      },
19525
19526      /**
19527       * @ngdoc method
19528       * @name $rootScope.Scope#$evalAsync
19529       * @kind function
19530       *
19531       * @description
19532       * Executes the expression on the current scope at a later point in time.
19533       *
19534       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
19535       * that:
19536       *
19537       *   - it will execute after the function that scheduled the evaluation (preferably before DOM
19538       *     rendering).
19539       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
19540       *     `expression` execution.
19541       *
19542       * Any exceptions from the execution of the expression are forwarded to the
19543       * {@link ng.$exceptionHandler $exceptionHandler} service.
19544       *
19545       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
19546       * will be scheduled. However, it is encouraged to always call code that changes the model
19547       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
19548       *
19549       * @param {(string|function())=} expression An AngularJS expression to be executed.
19550       *
19551       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
19552       *    - `function(scope)`: execute the function with the current `scope` parameter.
19553       *
19554       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
19555       */
19556      $evalAsync: function(expr, locals) {
19557        // if we are outside of an $digest loop and this is the first time we are scheduling async
19558        // task also schedule async auto-flush
19559        if (!$rootScope.$$phase && !asyncQueue.length) {
19560          $browser.defer(function() {
19561            if (asyncQueue.length) {
19562              $rootScope.$digest();
19563            }
19564          }, null, '$evalAsync');
19565        }
19566
19567        asyncQueue.push({scope: this, fn: $parse(expr), locals: locals});
19568      },
19569
19570      $$postDigest: function(fn) {
19571        postDigestQueue.push(fn);
19572      },
19573
19574      /**
19575       * @ngdoc method
19576       * @name $rootScope.Scope#$apply
19577       * @kind function
19578       *
19579       * @description
19580       * `$apply()` is used to execute an expression in AngularJS from outside of the AngularJS
19581       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
19582       * Because we are calling into the AngularJS framework we need to perform proper scope life
19583       * cycle of {@link ng.$exceptionHandler exception handling},
19584       * {@link ng.$rootScope.Scope#$digest executing watches}.
19585       *
19586       * **Life cycle: Pseudo-Code of `$apply()`**
19587       *
19588       * ```js
19589           function $apply(expr) {
19590             try {
19591               return $eval(expr);
19592             } catch (e) {
19593               $exceptionHandler(e);
19594             } finally {
19595               $root.$digest();
19596             }
19597           }
19598       * ```
19599       *
19600       *
19601       * Scope's `$apply()` method transitions through the following stages:
19602       *
19603       * 1. The {@link guide/expression expression} is executed using the
19604       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
19605       * 2. Any exceptions from the execution of the expression are forwarded to the
19606       *    {@link ng.$exceptionHandler $exceptionHandler} service.
19607       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
19608       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
19609       *
19610       *
19611       * @param {(string|function())=} exp An AngularJS expression to be executed.
19612       *
19613       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
19614       *    - `function(scope)`: execute the function with current `scope` parameter.
19615       *
19616       * @returns {*} The result of evaluating the expression.
19617       */
19618      $apply: function(expr) {
19619        try {
19620          beginPhase('$apply');
19621          try {
19622            return this.$eval(expr);
19623          } finally {
19624            clearPhase();
19625          }
19626        } catch (e) {
19627          $exceptionHandler(e);
19628        } finally {
19629          try {
19630            $rootScope.$digest();
19631          } catch (e) {
19632            $exceptionHandler(e);
19633            // eslint-disable-next-line no-unsafe-finally
19634            throw e;
19635          }
19636        }
19637      },
19638
19639      /**
19640       * @ngdoc method
19641       * @name $rootScope.Scope#$applyAsync
19642       * @kind function
19643       *
19644       * @description
19645       * Schedule the invocation of $apply to occur at a later time. The actual time difference
19646       * varies across browsers, but is typically around ~10 milliseconds.
19647       *
19648       * This can be used to queue up multiple expressions which need to be evaluated in the same
19649       * digest.
19650       *
19651       * @param {(string|function())=} exp An AngularJS expression to be executed.
19652       *
19653       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
19654       *    - `function(scope)`: execute the function with current `scope` parameter.
19655       */
19656      $applyAsync: function(expr) {
19657        var scope = this;
19658        if (expr) {
19659          applyAsyncQueue.push($applyAsyncExpression);
19660        }
19661        expr = $parse(expr);
19662        scheduleApplyAsync();
19663
19664        function $applyAsyncExpression() {
19665          scope.$eval(expr);
19666        }
19667      },
19668
19669      /**
19670       * @ngdoc method
19671       * @name $rootScope.Scope#$on
19672       * @kind function
19673       *
19674       * @description
19675       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
19676       * discussion of event life cycle.
19677       *
19678       * The event listener function format is: `function(event, args...)`. The `event` object
19679       * passed into the listener has the following attributes:
19680       *
19681       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
19682       *     `$broadcast`-ed.
19683       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
19684       *     event propagates through the scope hierarchy, this property is set to null.
19685       *   - `name` - `{string}`: name of the event.
19686       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
19687       *     further event propagation (available only for events that were `$emit`-ed).
19688       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
19689       *     to true.
19690       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
19691       *
19692       * @param {string} name Event name to listen on.
19693       * @param {function(event, ...args)} listener Function to call when the event is emitted.
19694       * @returns {function()} Returns a deregistration function for this listener.
19695       */
19696      $on: function(name, listener) {
19697        var namedListeners = this.$$listeners[name];
19698        if (!namedListeners) {
19699          this.$$listeners[name] = namedListeners = [];
19700        }
19701        namedListeners.push(listener);
19702
19703        var current = this;
19704        do {
19705          if (!current.$$listenerCount[name]) {
19706            current.$$listenerCount[name] = 0;
19707          }
19708          current.$$listenerCount[name]++;
19709        } while ((current = current.$parent));
19710
19711        var self = this;
19712        return function() {
19713          var indexOfListener = namedListeners.indexOf(listener);
19714          if (indexOfListener !== -1) {
19715            // Use delete in the hope of the browser deallocating the memory for the array entry,
19716            // while not shifting the array indexes of other listeners.
19717            // See issue https://github.com/angular/angular.js/issues/16135
19718            delete namedListeners[indexOfListener];
19719            decrementListenerCount(self, 1, name);
19720          }
19721        };
19722      },
19723
19724
19725      /**
19726       * @ngdoc method
19727       * @name $rootScope.Scope#$emit
19728       * @kind function
19729       *
19730       * @description
19731       * Dispatches an event `name` upwards through the scope hierarchy notifying the
19732       * registered {@link ng.$rootScope.Scope#$on} listeners.
19733       *
19734       * The event life cycle starts at the scope on which `$emit` was called. All
19735       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
19736       * notified. Afterwards, the event traverses upwards toward the root scope and calls all
19737       * registered listeners along the way. The event will stop propagating if one of the listeners
19738       * cancels it.
19739       *
19740       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
19741       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
19742       *
19743       * @param {string} name Event name to emit.
19744       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
19745       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
19746       */
19747      $emit: function(name, args) {
19748        var empty = [],
19749            namedListeners,
19750            scope = this,
19751            stopPropagation = false,
19752            event = {
19753              name: name,
19754              targetScope: scope,
19755              stopPropagation: function() {stopPropagation = true;},
19756              preventDefault: function() {
19757                event.defaultPrevented = true;
19758              },
19759              defaultPrevented: false
19760            },
19761            listenerArgs = concat([event], arguments, 1),
19762            i, length;
19763
19764        do {
19765          namedListeners = scope.$$listeners[name] || empty;
19766          event.currentScope = scope;
19767          for (i = 0, length = namedListeners.length; i < length; i++) {
19768
19769            // if listeners were deregistered, defragment the array
19770            if (!namedListeners[i]) {
19771              namedListeners.splice(i, 1);
19772              i--;
19773              length--;
19774              continue;
19775            }
19776            try {
19777              //allow all listeners attached to the current scope to run
19778              namedListeners[i].apply(null, listenerArgs);
19779            } catch (e) {
19780              $exceptionHandler(e);
19781            }
19782          }
19783          //if any listener on the current scope stops propagation, prevent bubbling
19784          if (stopPropagation) {
19785            break;
19786          }
19787          //traverse upwards
19788          scope = scope.$parent;
19789        } while (scope);
19790
19791        event.currentScope = null;
19792
19793        return event;
19794      },
19795
19796
19797      /**
19798       * @ngdoc method
19799       * @name $rootScope.Scope#$broadcast
19800       * @kind function
19801       *
19802       * @description
19803       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
19804       * registered {@link ng.$rootScope.Scope#$on} listeners.
19805       *
19806       * The event life cycle starts at the scope on which `$broadcast` was called. All
19807       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
19808       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
19809       * scope and calls all registered listeners along the way. The event cannot be canceled.
19810       *
19811       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
19812       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
19813       *
19814       * @param {string} name Event name to broadcast.
19815       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
19816       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
19817       */
19818      $broadcast: function(name, args) {
19819        var target = this,
19820            current = target,
19821            next = target,
19822            event = {
19823              name: name,
19824              targetScope: target,
19825              preventDefault: function() {
19826                event.defaultPrevented = true;
19827              },
19828              defaultPrevented: false
19829            };
19830
19831        if (!target.$$listenerCount[name]) return event;
19832
19833        var listenerArgs = concat([event], arguments, 1),
19834            listeners, i, length;
19835
19836        //down while you can, then up and next sibling or up and next sibling until back at root
19837        while ((current = next)) {
19838          event.currentScope = current;
19839          listeners = current.$$listeners[name] || [];
19840          for (i = 0, length = listeners.length; i < length; i++) {
19841            // if listeners were deregistered, defragment the array
19842            if (!listeners[i]) {
19843              listeners.splice(i, 1);
19844              i--;
19845              length--;
19846              continue;
19847            }
19848
19849            try {
19850              listeners[i].apply(null, listenerArgs);
19851            } catch (e) {
19852              $exceptionHandler(e);
19853            }
19854          }
19855
19856          // Insanity Warning: scope depth-first traversal
19857          // yes, this code is a bit crazy, but it works and we have tests to prove it!
19858          // this piece should be kept in sync with the traversal in $digest
19859          // (though it differs due to having the extra check for $$listenerCount and
19860          // does not check $$suspended)
19861          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
19862              (current !== target && current.$$nextSibling)))) {
19863            while (current !== target && !(next = current.$$nextSibling)) {
19864              current = current.$parent;
19865            }
19866          }
19867        }
19868
19869        event.currentScope = null;
19870        return event;
19871      }
19872    };
19873
19874    var $rootScope = new Scope();
19875
19876    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
19877    var asyncQueue = $rootScope.$$asyncQueue = [];
19878    var postDigestQueue = $rootScope.$$postDigestQueue = [];
19879    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
19880
19881    var postDigestQueuePosition = 0;
19882
19883    return $rootScope;
19884
19885
19886    function beginPhase(phase) {
19887      if ($rootScope.$$phase) {
19888        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
19889      }
19890
19891      $rootScope.$$phase = phase;
19892    }
19893
19894    function clearPhase() {
19895      $rootScope.$$phase = null;
19896    }
19897
19898    function incrementWatchersCount(current, count) {
19899      do {
19900        current.$$watchersCount += count;
19901      } while ((current = current.$parent));
19902    }
19903
19904    function decrementListenerCount(current, count, name) {
19905      do {
19906        current.$$listenerCount[name] -= count;
19907
19908        if (current.$$listenerCount[name] === 0) {
19909          delete current.$$listenerCount[name];
19910        }
19911      } while ((current = current.$parent));
19912    }
19913
19914    /**
19915     * function used as an initial value for watchers.
19916     * because it's unique we can easily tell it apart from other values
19917     */
19918    function initWatchVal() {}
19919
19920    function flushApplyAsync() {
19921      while (applyAsyncQueue.length) {
19922        try {
19923          applyAsyncQueue.shift()();
19924        } catch (e) {
19925          $exceptionHandler(e);
19926        }
19927      }
19928      applyAsyncId = null;
19929    }
19930
19931    function scheduleApplyAsync() {
19932      if (applyAsyncId === null) {
19933        applyAsyncId = $browser.defer(function() {
19934          $rootScope.$apply(flushApplyAsync);
19935        }, null, '$applyAsync');
19936      }
19937    }
19938  }];
19939}
19940
19941/**
19942 * @ngdoc service
19943 * @name $rootElement
19944 *
19945 * @description
19946 * The root element of AngularJS application. This is either the element where {@link
19947 * ng.directive:ngApp ngApp} was declared or the element passed into
19948 * {@link angular.bootstrap}. The element represents the root element of application. It is also the
19949 * location where the application's {@link auto.$injector $injector} service gets
19950 * published, and can be retrieved using `$rootElement.injector()`.
19951 */
19952
19953
19954// the implementation is in angular.bootstrap
19955
19956/**
19957 * @this
19958 * @description
19959 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
19960 */
19961function $$SanitizeUriProvider() {
19962
19963  var aHrefSanitizationTrustedUrlList = /^\s*(https?|s?ftp|mailto|tel|file):/,
19964    imgSrcSanitizationTrustedUrlList = /^\s*((https?|ftp|file|blob):|data:image\/)/;
19965
19966  /**
19967   * @description
19968   * Retrieves or overrides the default regular expression that is used for determining trusted safe
19969   * urls during a[href] sanitization.
19970   *
19971   * The sanitization is a security measure aimed at prevent XSS attacks via HTML anchor links.
19972   *
19973   * Any url due to be assigned to an `a[href]` attribute via interpolation is marked as requiring
19974   * the $sce.URL security context. When interpolation occurs a call is made to `$sce.trustAsUrl(url)`
19975   * which in turn may call `$$sanitizeUri(url, isMedia)` to sanitize the potentially malicious URL.
19976   *
19977   * If the URL matches the `aHrefSanitizationTrustedUrlList` regular expression, it is returned unchanged.
19978   *
19979   * If there is no match the URL is returned prefixed with `'unsafe:'` to ensure that when it is written
19980   * to the DOM it is inactive and potentially malicious code will not be executed.
19981   *
19982   * @param {RegExp=} regexp New regexp to trust urls with.
19983   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
19984   *    chaining otherwise.
19985   */
19986  this.aHrefSanitizationTrustedUrlList = function(regexp) {
19987    if (isDefined(regexp)) {
19988      aHrefSanitizationTrustedUrlList = regexp;
19989      return this;
19990    }
19991    return aHrefSanitizationTrustedUrlList;
19992  };
19993
19994
19995  /**
19996   * @description
19997   * Retrieves or overrides the default regular expression that is used for determining trusted safe
19998   * urls during img[src] sanitization.
19999   *
20000   * The sanitization is a security measure aimed at prevent XSS attacks via HTML image src links.
20001   *
20002   * Any URL due to be assigned to an `img[src]` attribute via interpolation is marked as requiring
20003   * the $sce.MEDIA_URL security context. When interpolation occurs a call is made to
20004   * `$sce.trustAsMediaUrl(url)` which in turn may call `$$sanitizeUri(url, isMedia)` to sanitize
20005   * the potentially malicious URL.
20006   *
20007   * If the URL matches the `imgSrcSanitizationTrustedUrlList` regular expression, it is returned
20008   * unchanged.
20009   *
20010   * If there is no match the URL is returned prefixed with `'unsafe:'` to ensure that when it is written
20011   * to the DOM it is inactive and potentially malicious code will not be executed.
20012   *
20013   * @param {RegExp=} regexp New regexp to trust urls with.
20014   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
20015   *    chaining otherwise.
20016   */
20017  this.imgSrcSanitizationTrustedUrlList = function(regexp) {
20018    if (isDefined(regexp)) {
20019      imgSrcSanitizationTrustedUrlList = regexp;
20020      return this;
20021    }
20022    return imgSrcSanitizationTrustedUrlList;
20023  };
20024
20025  this.$get = function() {
20026    return function sanitizeUri(uri, isMediaUrl) {
20027      // if (!uri) return uri;
20028      var regex = isMediaUrl ? imgSrcSanitizationTrustedUrlList : aHrefSanitizationTrustedUrlList;
20029      var normalizedVal = urlResolve(uri && uri.trim()).href;
20030      if (normalizedVal !== '' && !normalizedVal.match(regex)) {
20031        return 'unsafe:' + normalizedVal;
20032      }
20033      return uri;
20034    };
20035  };
20036}
20037
20038/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20039 *     Any commits to this file should be reviewed with security in mind.  *
20040 *   Changes to this file can potentially create security vulnerabilities. *
20041 *          An approval from 2 Core members with history of modifying      *
20042 *                         this file is required.                          *
20043 *                                                                         *
20044 *  Does the change somehow allow for arbitrary javascript to be executed? *
20045 *    Or allows for someone to change the prototype of built-in objects?   *
20046 *     Or gives undesired access to variables likes document or window?    *
20047 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
20048
20049/* exported $SceProvider, $SceDelegateProvider */
20050
20051var $sceMinErr = minErr('$sce');
20052
20053var SCE_CONTEXTS = {
20054  // HTML is used when there's HTML rendered (e.g. ng-bind-html, iframe srcdoc binding).
20055  HTML: 'html',
20056
20057  // Style statements or stylesheets. Currently unused in AngularJS.
20058  CSS: 'css',
20059
20060  // An URL used in a context where it refers to the source of media, which are not expected to be run
20061  // as scripts, such as an image, audio, video, etc.
20062  MEDIA_URL: 'mediaUrl',
20063
20064  // An URL used in a context where it does not refer to a resource that loads code.
20065  // A value that can be trusted as a URL can also trusted as a MEDIA_URL.
20066  URL: 'url',
20067
20068  // RESOURCE_URL is a subtype of URL used where the referred-to resource could be interpreted as
20069  // code. (e.g. ng-include, script src binding, templateUrl)
20070  // A value that can be trusted as a RESOURCE_URL, can also trusted as a URL and a MEDIA_URL.
20071  RESOURCE_URL: 'resourceUrl',
20072
20073  // Script. Currently unused in AngularJS.
20074  JS: 'js'
20075};
20076
20077// Helper functions follow.
20078
20079var UNDERSCORE_LOWERCASE_REGEXP = /_([a-z])/g;
20080
20081function snakeToCamel(name) {
20082  return name
20083    .replace(UNDERSCORE_LOWERCASE_REGEXP, fnCamelCaseReplace);
20084}
20085
20086function adjustMatcher(matcher) {
20087  if (matcher === 'self') {
20088    return matcher;
20089  } else if (isString(matcher)) {
20090    // Strings match exactly except for 2 wildcards - '*' and '**'.
20091    // '*' matches any character except those from the set ':/.?&'.
20092    // '**' matches any character (like .* in a RegExp).
20093    // More than 2 *'s raises an error as it's ill defined.
20094    if (matcher.indexOf('***') > -1) {
20095      throw $sceMinErr('iwcard',
20096          'Illegal sequence *** in string matcher.  String: {0}', matcher);
20097    }
20098    matcher = escapeForRegexp(matcher).
20099                  replace(/\\\*\\\*/g, '.*').
20100                  replace(/\\\*/g, '[^:/.?&;]*');
20101    return new RegExp('^' + matcher + '$');
20102  } else if (isRegExp(matcher)) {
20103    // The only other type of matcher allowed is a Regexp.
20104    // Match entire URL / disallow partial matches.
20105    // Flags are reset (i.e. no global, ignoreCase or multiline)
20106    return new RegExp('^' + matcher.source + '$');
20107  } else {
20108    throw $sceMinErr('imatcher',
20109        'Matchers may only be "self", string patterns or RegExp objects');
20110  }
20111}
20112
20113
20114function adjustMatchers(matchers) {
20115  var adjustedMatchers = [];
20116  if (isDefined(matchers)) {
20117    forEach(matchers, function(matcher) {
20118      adjustedMatchers.push(adjustMatcher(matcher));
20119    });
20120  }
20121  return adjustedMatchers;
20122}
20123
20124
20125/**
20126 * @ngdoc service
20127 * @name $sceDelegate
20128 * @kind function
20129 *
20130 * @description
20131 *
20132 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
20133 * Contextual Escaping (SCE)} services to AngularJS.
20134 *
20135 * For an overview of this service and the functionnality it provides in AngularJS, see the main
20136 * page for {@link ng.$sce SCE}. The current page is targeted for developers who need to alter how
20137 * SCE works in their application, which shouldn't be needed in most cases.
20138 *
20139 * <div class="alert alert-danger">
20140 * AngularJS strongly relies on contextual escaping for the security of bindings: disabling or
20141 * modifying this might cause cross site scripting (XSS) vulnerabilities. For libraries owners,
20142 * changes to this service will also influence users, so be extra careful and document your changes.
20143 * </div>
20144 *
20145 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
20146 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
20147 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
20148 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
20149 * work because `$sce` delegates to `$sceDelegate` for these operations.
20150 *
20151 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
20152 *
20153 * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
20154 * can override it completely to change the behavior of `$sce`, the common case would
20155 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
20156 * your own trusted and banned resource lists for trusting URLs used for loading AngularJS resources
20157 * such as templates.  Refer {@link ng.$sceDelegateProvider#trustedResourceUrlList
20158 * $sceDelegateProvider.trustedResourceUrlList} and {@link
20159 * ng.$sceDelegateProvider#bannedResourceUrlList $sceDelegateProvider.bannedResourceUrlList}
20160 */
20161
20162/**
20163 * @ngdoc provider
20164 * @name $sceDelegateProvider
20165 * @this
20166 *
20167 * @description
20168 *
20169 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
20170 * $sceDelegate service}, used as a delegate for {@link ng.$sce Strict Contextual Escaping (SCE)}.
20171 *
20172 * The `$sceDelegateProvider` allows one to get/set the `trustedResourceUrlList` and
20173 * `bannedResourceUrlList` used to ensure that the URLs used for sourcing AngularJS templates and
20174 * other script-running URLs are safe (all places that use the `$sce.RESOURCE_URL` context). See
20175 * {@link ng.$sceDelegateProvider#trustedResourceUrlList
20176 * $sceDelegateProvider.trustedResourceUrlList} and
20177 * {@link ng.$sceDelegateProvider#bannedResourceUrlList $sceDelegateProvider.bannedResourceUrlList},
20178 *
20179 * For the general details about this service in AngularJS, read the main page for {@link ng.$sce
20180 * Strict Contextual Escaping (SCE)}.
20181 *
20182 * **Example**:  Consider the following case. <a name="example"></a>
20183 *
20184 * - your app is hosted at url `http://myapp.example.com/`
20185 * - but some of your templates are hosted on other domains you control such as
20186 *   `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
20187 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
20188 *
20189 * Here is what a secure configuration for this scenario might look like:
20190 *
20191 * ```
20192 *  angular.module('myApp', []).config(function($sceDelegateProvider) {
20193 *    $sceDelegateProvider.trustedResourceUrlList([
20194 *      // Allow same origin resource loads.
20195 *      'self',
20196 *      // Allow loading from our assets domain.  Notice the difference between * and **.
20197 *      'http://srv*.assets.example.com/**'
20198 *    ]);
20199 *
20200 *    // The banned resource URL list overrides the trusted resource URL list so the open redirect
20201 *    // here is blocked.
20202 *    $sceDelegateProvider.bannedResourceUrlList([
20203 *      'http://myapp.example.com/clickThru**'
20204 *    ]);
20205 *  });
20206 * ```
20207 * Note that an empty trusted resource URL list will block every resource URL from being loaded, and will require
20208 * you to manually mark each one as trusted with `$sce.trustAsResourceUrl`. However, templates
20209 * requested by {@link ng.$templateRequest $templateRequest} that are present in
20210 * {@link ng.$templateCache $templateCache} will not go through this check. If you have a mechanism
20211 * to populate your templates in that cache at config time, then it is a good idea to remove 'self'
20212 * from the trusted resource URL lsit. This helps to mitigate the security impact of certain types
20213 * of issues, like for instance attacker-controlled `ng-includes`.
20214 */
20215
20216function $SceDelegateProvider() {
20217  this.SCE_CONTEXTS = SCE_CONTEXTS;
20218
20219  // Resource URLs can also be trusted by policy.
20220  var trustedResourceUrlList = ['self'],
20221      bannedResourceUrlList = [];
20222
20223  /**
20224   * @ngdoc method
20225   * @name $sceDelegateProvider#trustedResourceUrlList
20226   * @kind function
20227   *
20228   * @param {Array=} trustedResourceUrlList When provided, replaces the trustedResourceUrlList with
20229   *     the value provided.  This must be an array or null.  A snapshot of this array is used so
20230   *     further changes to the array are ignored.
20231   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
20232   *     allowed in this array.
20233   *
20234   * @return {Array} The currently set trusted resource URL array.
20235   *
20236   * @description
20237   * Sets/Gets the list trusted of resource URLs.
20238   *
20239   * The **default value** when no `trustedResourceUrlList` has been explicitly set is `['self']`
20240   * allowing only same origin resource requests.
20241   *
20242   * <div class="alert alert-warning">
20243   * **Note:** the default `trustedResourceUrlList` of 'self' is not recommended if your app shares
20244   * its origin with other apps! It is a good idea to limit it to only your application's directory.
20245   * </div>
20246   */
20247  this.trustedResourceUrlList = function(value) {
20248    if (arguments.length) {
20249      trustedResourceUrlList = adjustMatchers(value);
20250    }
20251    return trustedResourceUrlList;
20252  };
20253
20254  /**
20255   * @ngdoc method
20256   * @name $sceDelegateProvider#resourceUrlWhitelist
20257   * @kind function
20258   *
20259   * @deprecated
20260   * sinceVersion="1.8.1"
20261   *
20262   * This method is deprecated. Use {@link $sceDelegateProvider#trustedResourceUrlList
20263   * trustedResourceUrlList} instead.
20264   */
20265  Object.defineProperty(this, 'resourceUrlWhitelist', {
20266    get: function() {
20267      return this.trustedResourceUrlList;
20268    },
20269    set: function(value) {
20270      this.trustedResourceUrlList = value;
20271    }
20272  });
20273
20274  /**
20275   * @ngdoc method
20276   * @name $sceDelegateProvider#bannedResourceUrlList
20277   * @kind function
20278   *
20279   * @param {Array=} bannedResourceUrlList When provided, replaces the `bannedResourceUrlList` with
20280   *     the value provided. This must be an array or null. A snapshot of this array is used so
20281   *     further changes to the array are ignored.</p><p>
20282   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
20283   *     allowed in this array.</p><p>
20284   *     The typical usage for the `bannedResourceUrlList` is to **block
20285   *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
20286   *     these would otherwise be trusted but actually return content from the redirected domain.
20287   *     </p><p>
20288   *     Finally, **the banned resource URL list overrides the trusted resource URL list** and has
20289   *     the final say.
20290   *
20291   * @return {Array} The currently set `bannedResourceUrlList` array.
20292   *
20293   * @description
20294   * Sets/Gets the `bannedResourceUrlList` of trusted resource URLs.
20295   *
20296   * The **default value** when no trusted resource URL list has been explicitly set is the empty
20297   * array (i.e. there is no `bannedResourceUrlList`.)
20298   */
20299  this.bannedResourceUrlList = function(value) {
20300    if (arguments.length) {
20301      bannedResourceUrlList = adjustMatchers(value);
20302    }
20303    return bannedResourceUrlList;
20304  };
20305
20306  /**
20307   * @ngdoc method
20308   * @name $sceDelegateProvider#resourceUrlBlacklist
20309   * @kind function
20310   *
20311   * @deprecated
20312   * sinceVersion="1.8.1"
20313   *
20314   * This method is deprecated. Use {@link $sceDelegateProvider#bannedResourceUrlList
20315   * bannedResourceUrlList} instead.
20316   */
20317  Object.defineProperty(this, 'resourceUrlBlacklist', {
20318    get: function() {
20319      return this.bannedResourceUrlList;
20320    },
20321    set: function(value) {
20322      this.bannedResourceUrlList = value;
20323    }
20324  });
20325
20326  this.$get = ['$injector', '$$sanitizeUri', function($injector, $$sanitizeUri) {
20327
20328    var htmlSanitizer = function htmlSanitizer(html) {
20329      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
20330    };
20331
20332    if ($injector.has('$sanitize')) {
20333      htmlSanitizer = $injector.get('$sanitize');
20334    }
20335
20336
20337    function matchUrl(matcher, parsedUrl) {
20338      if (matcher === 'self') {
20339        return urlIsSameOrigin(parsedUrl) || urlIsSameOriginAsBaseUrl(parsedUrl);
20340      } else {
20341        // definitely a regex.  See adjustMatchers()
20342        return !!matcher.exec(parsedUrl.href);
20343      }
20344    }
20345
20346    function isResourceUrlAllowedByPolicy(url) {
20347      var parsedUrl = urlResolve(url.toString());
20348      var i, n, allowed = false;
20349      // Ensure that at least one item from the trusted resource URL list allows this url.
20350      for (i = 0, n = trustedResourceUrlList.length; i < n; i++) {
20351        if (matchUrl(trustedResourceUrlList[i], parsedUrl)) {
20352          allowed = true;
20353          break;
20354        }
20355      }
20356      if (allowed) {
20357        // Ensure that no item from the banned resource URL list has blocked this url.
20358        for (i = 0, n = bannedResourceUrlList.length; i < n; i++) {
20359          if (matchUrl(bannedResourceUrlList[i], parsedUrl)) {
20360            allowed = false;
20361            break;
20362          }
20363        }
20364      }
20365      return allowed;
20366    }
20367
20368    function generateHolderType(Base) {
20369      var holderType = function TrustedValueHolderType(trustedValue) {
20370        this.$$unwrapTrustedValue = function() {
20371          return trustedValue;
20372        };
20373      };
20374      if (Base) {
20375        holderType.prototype = new Base();
20376      }
20377      holderType.prototype.valueOf = function sceValueOf() {
20378        return this.$$unwrapTrustedValue();
20379      };
20380      holderType.prototype.toString = function sceToString() {
20381        return this.$$unwrapTrustedValue().toString();
20382      };
20383      return holderType;
20384    }
20385
20386    var trustedValueHolderBase = generateHolderType(),
20387        byType = {};
20388
20389    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
20390    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
20391    byType[SCE_CONTEXTS.MEDIA_URL] = generateHolderType(trustedValueHolderBase);
20392    byType[SCE_CONTEXTS.URL] = generateHolderType(byType[SCE_CONTEXTS.MEDIA_URL]);
20393    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
20394    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
20395
20396    /**
20397     * @ngdoc method
20398     * @name $sceDelegate#trustAs
20399     *
20400     * @description
20401     * Returns a trusted representation of the parameter for the specified context. This trusted
20402     * object will later on be used as-is, without any security check, by bindings or directives
20403     * that require this security context.
20404     * For instance, marking a string as trusted for the `$sce.HTML` context will entirely bypass
20405     * the potential `$sanitize` call in corresponding `$sce.HTML` bindings or directives, such as
20406     * `ng-bind-html`. Note that in most cases you won't need to call this function: if you have the
20407     * sanitizer loaded, passing the value itself will render all the HTML that does not pose a
20408     * security risk.
20409     *
20410     * See {@link ng.$sceDelegate#getTrusted getTrusted} for the function that will consume those
20411     * trusted values, and {@link ng.$sce $sce} for general documentation about strict contextual
20412     * escaping.
20413     *
20414     * @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`,
20415     *     `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`.
20416     *
20417     * @param {*} value The value that should be considered trusted.
20418     * @return {*} A trusted representation of value, that can be used in the given context.
20419     */
20420    function trustAs(type, trustedValue) {
20421      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
20422      if (!Constructor) {
20423        throw $sceMinErr('icontext',
20424            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
20425            type, trustedValue);
20426      }
20427      if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
20428        return trustedValue;
20429      }
20430      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
20431      // mutable objects, we ensure here that the value passed in is actually a string.
20432      if (typeof trustedValue !== 'string') {
20433        throw $sceMinErr('itype',
20434            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
20435            type);
20436      }
20437      return new Constructor(trustedValue);
20438    }
20439
20440    /**
20441     * @ngdoc method
20442     * @name $sceDelegate#valueOf
20443     *
20444     * @description
20445     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
20446     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
20447     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
20448     *
20449     * If the passed parameter is not a value that had been returned by {@link
20450     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, it must be returned as-is.
20451     *
20452     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
20453     *     call or anything else.
20454     * @return {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
20455     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
20456     *     `value` unchanged.
20457     */
20458    function valueOf(maybeTrusted) {
20459      if (maybeTrusted instanceof trustedValueHolderBase) {
20460        return maybeTrusted.$$unwrapTrustedValue();
20461      } else {
20462        return maybeTrusted;
20463      }
20464    }
20465
20466    /**
20467     * @ngdoc method
20468     * @name $sceDelegate#getTrusted
20469     *
20470     * @description
20471     * Given an object and a security context in which to assign it, returns a value that's safe to
20472     * use in this context, which was represented by the parameter. To do so, this function either
20473     * unwraps the safe type it has been given (for instance, a {@link ng.$sceDelegate#trustAs
20474     * `$sceDelegate.trustAs`} result), or it might try to sanitize the value given, depending on
20475     * the context and sanitizer availablility.
20476     *
20477     * The contexts that can be sanitized are $sce.MEDIA_URL, $sce.URL and $sce.HTML. The first two are available
20478     * by default, and the third one relies on the `$sanitize` service (which may be loaded through
20479     * the `ngSanitize` module). Furthermore, for $sce.RESOURCE_URL context, a plain string may be
20480     * accepted if the resource url policy defined by {@link ng.$sceDelegateProvider#trustedResourceUrlList
20481     * `$sceDelegateProvider.trustedResourceUrlList`} and {@link ng.$sceDelegateProvider#bannedResourceUrlList
20482     * `$sceDelegateProvider.bannedResourceUrlList`} accepts that resource.
20483     *
20484     * This function will throw if the safe type isn't appropriate for this context, or if the
20485     * value given cannot be accepted in the context (which might be caused by sanitization not
20486     * being available, or the value not being recognized as safe).
20487     *
20488     * <div class="alert alert-danger">
20489     * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
20490     * (XSS) vulnerability in your application.
20491     * </div>
20492     *
20493     * @param {string} type The context in which this value is to be used (such as `$sce.HTML`).
20494     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
20495     *     `$sceDelegate.trustAs`} call, or anything else (which will not be considered trusted.)
20496     * @return {*} A version of the value that's safe to use in the given context, or throws an
20497     *     exception if this is impossible.
20498     */
20499    function getTrusted(type, maybeTrusted) {
20500      if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
20501        return maybeTrusted;
20502      }
20503      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
20504      // If maybeTrusted is a trusted class instance or subclass instance, then unwrap and return
20505      // as-is.
20506      if (constructor && maybeTrusted instanceof constructor) {
20507        return maybeTrusted.$$unwrapTrustedValue();
20508      }
20509
20510      // If maybeTrusted is a trusted class instance but not of the correct trusted type
20511      // then unwrap it and allow it to pass through to the rest of the checks
20512      if (isFunction(maybeTrusted.$$unwrapTrustedValue)) {
20513        maybeTrusted = maybeTrusted.$$unwrapTrustedValue();
20514      }
20515
20516      // If we get here, then we will either sanitize the value or throw an exception.
20517      if (type === SCE_CONTEXTS.MEDIA_URL || type === SCE_CONTEXTS.URL) {
20518        // we attempt to sanitize non-resource URLs
20519        return $$sanitizeUri(maybeTrusted.toString(), type === SCE_CONTEXTS.MEDIA_URL);
20520      } else if (type === SCE_CONTEXTS.RESOURCE_URL) {
20521        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
20522          return maybeTrusted;
20523        } else {
20524          throw $sceMinErr('insecurl',
20525              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
20526              maybeTrusted.toString());
20527        }
20528      } else if (type === SCE_CONTEXTS.HTML) {
20529        // htmlSanitizer throws its own error when no sanitizer is available.
20530        return htmlSanitizer(maybeTrusted);
20531      }
20532      // Default error when the $sce service has no way to make the input safe.
20533      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
20534    }
20535
20536    return { trustAs: trustAs,
20537             getTrusted: getTrusted,
20538             valueOf: valueOf };
20539  }];
20540}
20541
20542
20543/**
20544 * @ngdoc provider
20545 * @name $sceProvider
20546 * @this
20547 *
20548 * @description
20549 *
20550 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
20551 * -   enable/disable Strict Contextual Escaping (SCE) in a module
20552 * -   override the default implementation with a custom delegate
20553 *
20554 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
20555 */
20556
20557/**
20558 * @ngdoc service
20559 * @name $sce
20560 * @kind function
20561 *
20562 * @description
20563 *
20564 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
20565 *
20566 * ## Strict Contextual Escaping
20567 *
20568 * Strict Contextual Escaping (SCE) is a mode in which AngularJS constrains bindings to only render
20569 * trusted values. Its goal is to assist in writing code in a way that (a) is secure by default, and
20570 * (b) makes auditing for security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
20571 *
20572 * ### Overview
20573 *
20574 * To systematically block XSS security bugs, AngularJS treats all values as untrusted by default in
20575 * HTML or sensitive URL bindings. When binding untrusted values, AngularJS will automatically
20576 * run security checks on them (sanitizations, trusted URL resource, depending on context), or throw
20577 * when it cannot guarantee the security of the result. That behavior depends strongly on contexts:
20578 * HTML can be sanitized, but template URLs cannot, for instance.
20579 *
20580 * To illustrate this, consider the `ng-bind-html` directive. It renders its value directly as HTML:
20581 * we call that the *context*. When given an untrusted input, AngularJS will attempt to sanitize it
20582 * before rendering if a sanitizer is available, and throw otherwise. To bypass sanitization and
20583 * render the input as-is, you will need to mark it as trusted for that context before attempting
20584 * to bind it.
20585 *
20586 * As of version 1.2, AngularJS ships with SCE enabled by default.
20587 *
20588 * ### In practice
20589 *
20590 * Here's an example of a binding in a privileged context:
20591 *
20592 * ```
20593 * <input ng-model="userHtml" aria-label="User input">
20594 * <div ng-bind-html="userHtml"></div>
20595 * ```
20596 *
20597 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
20598 * disabled, this application allows the user to render arbitrary HTML into the DIV, which would
20599 * be an XSS security bug. In a more realistic example, one may be rendering user comments, blog
20600 * articles, etc. via bindings. (HTML is just one example of a context where rendering user
20601 * controlled input creates security vulnerabilities.)
20602 *
20603 * For the case of HTML, you might use a library, either on the client side, or on the server side,
20604 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
20605 *
20606 * How would you ensure that every place that used these types of bindings was bound to a value that
20607 * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
20608 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
20609 * properties/fields and forgot to update the binding to the sanitized value?
20610 *
20611 * To be secure by default, AngularJS makes sure bindings go through that sanitization, or
20612 * any similar validation process, unless there's a good reason to trust the given value in this
20613 * context.  That trust is formalized with a function call. This means that as a developer, you
20614 * can assume all untrusted bindings are safe. Then, to audit your code for binding security issues,
20615 * you just need to ensure the values you mark as trusted indeed are safe - because they were
20616 * received from your server, sanitized by your library, etc. You can organize your codebase to
20617 * help with this - perhaps allowing only the files in a specific directory to do this.
20618 * Ensuring that the internal API exposed by that code doesn't markup arbitrary values as safe then
20619 * becomes a more manageable task.
20620 *
20621 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
20622 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
20623 * build the trusted versions of your values.
20624 *
20625 * ### How does it work?
20626 *
20627 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
20628 * $sce.getTrusted(context, value)} rather than to the value directly.  Think of this function as
20629 * a way to enforce the required security context in your data sink. Directives use {@link
20630 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs
20631 * the {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. Also,
20632 * when binding without directives, AngularJS will understand the context of your bindings
20633 * automatically.
20634 *
20635 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
20636 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
20637 * simplified):
20638 *
20639 * ```
20640 * var ngBindHtmlDirective = ['$sce', function($sce) {
20641 *   return function(scope, element, attr) {
20642 *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
20643 *       element.html(value || '');
20644 *     });
20645 *   };
20646 * }];
20647 * ```
20648 *
20649 * ### Impact on loading templates
20650 *
20651 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
20652 * `templateUrl`'s specified by {@link guide/directive directives}.
20653 *
20654 * By default, AngularJS only loads templates from the same domain and protocol as the application
20655 * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
20656 * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
20657 * protocols, you may either add them to the {@link ng.$sceDelegateProvider#trustedResourceUrlList
20658 * trustedResourceUrlList} or {@link ng.$sce#trustAsResourceUrl wrap them} into trusted values.
20659 *
20660 * *Please note*:
20661 * The browser's
20662 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
20663 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
20664 * policy apply in addition to this and may further restrict whether the template is successfully
20665 * loaded.  This means that without the right CORS policy, loading templates from a different domain
20666 * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
20667 * browsers.
20668 *
20669 * ### This feels like too much overhead
20670 *
20671 * It's important to remember that SCE only applies to interpolation expressions.
20672 *
20673 * If your expressions are constant literals, they're automatically trusted and you don't need to
20674 * call `$sce.trustAs` on them (e.g.
20675 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works (remember to include the
20676 * `ngSanitize` module). The `$sceDelegate` will also use the `$sanitize` service if it is available
20677 * when binding untrusted values to `$sce.HTML` context.
20678 * AngularJS provides an implementation in `angular-sanitize.js`, and if you
20679 * wish to use it, you will also need to depend on the {@link ngSanitize `ngSanitize`} module in
20680 * your application.
20681 *
20682 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
20683 * templates in `ng-include` from your application's domain without having to even know about SCE.
20684 * It blocks loading templates from other domains or loading templates over http from an https
20685 * served document.  You can change these by setting your own custom {@link
20686 * ng.$sceDelegateProvider#trustedResourceUrlList trusted resource URL list} and {@link
20687 * ng.$sceDelegateProvider#bannedResourceUrlList banned resource URL list} for matching such URLs.
20688 *
20689 * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
20690 * application that's secure and can be audited to verify that with much more ease than bolting
20691 * security onto an application later.
20692 *
20693 * <a name="contexts"></a>
20694 * ### What trusted context types are supported?
20695 *
20696 * | Context             | Notes          |
20697 * |---------------------|----------------|
20698 * | `$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. |
20699 * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
20700 * | `$sce.MEDIA_URL`    | For URLs that are safe to render as media. Is automatically converted from string by sanitizing when needed. |
20701 * | `$sce.URL`          | For URLs that are safe to follow as links. Is automatically converted from string by sanitizing when needed. Note that `$sce.URL` makes a stronger statement about the URL than `$sce.MEDIA_URL` does and therefore contexts requiring values trusted for `$sce.URL` can be used anywhere that values trusted for `$sce.MEDIA_URL` are required.|
20702 * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application.  Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.)  <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` or `$sce.MEDIA_URL` do and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` or `$sce.MEDIA_URL` are required. <br><br> The {@link $sceDelegateProvider#trustedResourceUrlList $sceDelegateProvider#trustedResourceUrlList()} and {@link $sceDelegateProvider#bannedResourceUrlList $sceDelegateProvider#bannedResourceUrlList()} can be used to restrict trusted origins for `RESOURCE_URL` |
20703 * | `$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. |
20704 *
20705 *
20706 * <div class="alert alert-warning">
20707 * Be aware that, before AngularJS 1.7.0, `a[href]` and `img[src]` used to sanitize their
20708 * interpolated values directly rather than rely upon {@link ng.$sce#getTrusted `$sce.getTrusted`}.
20709 *
20710 * **As of 1.7.0, this is no longer the case.**
20711 *
20712 * Now such interpolations are marked as requiring `$sce.URL` (for `a[href]`) or `$sce.MEDIA_URL`
20713 * (for `img[src]`), so that the sanitization happens (via `$sce.getTrusted...`) when the `$interpolate`
20714 * service evaluates the expressions.
20715 * </div>
20716 *
20717 * There are no CSS or JS context bindings in AngularJS currently, so their corresponding `$sce.trustAs`
20718 * functions aren't useful yet. This might evolve.
20719 *
20720 * ### Format of items in {@link ng.$sceDelegateProvider#trustedResourceUrlList trustedResourceUrlList}/{@link ng.$sceDelegateProvider#bannedResourceUrlList bannedResourceUrlList} <a name="resourceUrlPatternItem"></a>
20721 *
20722 *  Each element in these arrays must be one of the following:
20723 *
20724 *  - **'self'**
20725 *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
20726 *      domain** as the application document using the **same protocol**.
20727 *  - **String** (except the special value `'self'`)
20728 *    - The string is matched against the full *normalized / absolute URL* of the resource
20729 *      being tested (substring matches are not good enough.)
20730 *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
20731 *      match themselves.
20732 *    - `*`: matches zero or more occurrences of any character other than one of the following 6
20733 *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
20734 *      for matching resource URL lists.
20735 *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
20736 *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
20737 *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
20738 *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
20739 *      http://foo.example.com/templates/**).
20740 *  - **RegExp** (*see caveat below*)
20741 *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
20742 *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
20743 *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
20744 *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
20745 *      small number of cases.  A `.` character in the regex used when matching the scheme or a
20746 *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
20747 *      is highly recommended to use the string patterns and only fall back to regular expressions
20748 *      as a last resort.
20749 *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
20750 *      matched against the **entire** *normalized / absolute URL* of the resource being tested
20751 *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
20752 *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
20753 *    - If you are generating your JavaScript from some other templating engine (not
20754 *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
20755 *      remember to escape your regular expression (and be aware that you might need more than
20756 *      one level of escaping depending on your templating engine and the way you interpolated
20757 *      the value.)  Do make use of your platform's escaping mechanism as it might be good
20758 *      enough before coding your own.  E.g. Ruby has
20759 *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
20760 *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
20761 *      Javascript lacks a similar built in function for escaping.  Take a look at Google
20762 *      Closure library's [goog.string.regExpEscape(s)](
20763 *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
20764 *
20765 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
20766 *
20767 * ### Show me an example using SCE.
20768 *
20769 * <example module="mySceApp" deps="angular-sanitize.js" name="sce-service">
20770 * <file name="index.html">
20771 *   <div ng-controller="AppController as myCtrl">
20772 *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
20773 *     <b>User comments</b><br>
20774 *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
20775 *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
20776 *     exploit.
20777 *     <div class="well">
20778 *       <div ng-repeat="userComment in myCtrl.userComments">
20779 *         <b>{{userComment.name}}</b>:
20780 *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
20781 *         <br>
20782 *       </div>
20783 *     </div>
20784 *   </div>
20785 * </file>
20786 *
20787 * <file name="script.js">
20788 *   angular.module('mySceApp', ['ngSanitize'])
20789 *     .controller('AppController', ['$http', '$templateCache', '$sce',
20790 *       function AppController($http, $templateCache, $sce) {
20791 *         var self = this;
20792 *         $http.get('test_data.json', {cache: $templateCache}).then(function(response) {
20793 *           self.userComments = response.data;
20794 *         });
20795 *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
20796 *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
20797 *             'sanitization.&quot;">Hover over this text.</span>');
20798 *       }]);
20799 * </file>
20800 *
20801 * <file name="test_data.json">
20802 * [
20803 *   { "name": "Alice",
20804 *     "htmlComment":
20805 *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
20806 *   },
20807 *   { "name": "Bob",
20808 *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
20809 *   }
20810 * ]
20811 * </file>
20812 *
20813 * <file name="protractor.js" type="protractor">
20814 *   describe('SCE doc demo', function() {
20815 *     it('should sanitize untrusted values', function() {
20816 *       expect(element.all(by.css('.htmlComment')).first().getAttribute('innerHTML'))
20817 *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
20818 *     });
20819 *
20820 *     it('should NOT sanitize explicitly trusted values', function() {
20821 *       expect(element(by.id('explicitlyTrustedHtml')).getAttribute('innerHTML')).toBe(
20822 *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
20823 *           'sanitization.&quot;">Hover over this text.</span>');
20824 *     });
20825 *   });
20826 * </file>
20827 * </example>
20828 *
20829 *
20830 *
20831 * ## Can I disable SCE completely?
20832 *
20833 * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
20834 * for little coding overhead.  It will be much harder to take an SCE disabled application and
20835 * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
20836 * for cases where you have a lot of existing code that was written before SCE was introduced and
20837 * you're migrating them a module at a time. Also do note that this is an app-wide setting, so if
20838 * you are writing a library, you will cause security bugs applications using it.
20839 *
20840 * That said, here's how you can completely disable SCE:
20841 *
20842 * ```
20843 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
20844 *   // Completely disable SCE.  For demonstration purposes only!
20845 *   // Do not use in new projects or libraries.
20846 *   $sceProvider.enabled(false);
20847 * });
20848 * ```
20849 *
20850 */
20851
20852function $SceProvider() {
20853  var enabled = true;
20854
20855  /**
20856   * @ngdoc method
20857   * @name $sceProvider#enabled
20858   * @kind function
20859   *
20860   * @param {boolean=} value If provided, then enables/disables SCE application-wide.
20861   * @return {boolean} True if SCE is enabled, false otherwise.
20862   *
20863   * @description
20864   * Enables/disables SCE and returns the current value.
20865   */
20866  this.enabled = function(value) {
20867    if (arguments.length) {
20868      enabled = !!value;
20869    }
20870    return enabled;
20871  };
20872
20873
20874  /* Design notes on the default implementation for SCE.
20875   *
20876   * The API contract for the SCE delegate
20877   * -------------------------------------
20878   * The SCE delegate object must provide the following 3 methods:
20879   *
20880   * - trustAs(contextEnum, value)
20881   *     This method is used to tell the SCE service that the provided value is OK to use in the
20882   *     contexts specified by contextEnum.  It must return an object that will be accepted by
20883   *     getTrusted() for a compatible contextEnum and return this value.
20884   *
20885   * - valueOf(value)
20886   *     For values that were not produced by trustAs(), return them as is.  For values that were
20887   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
20888   *     trustAs is wrapping the given values into some type, this operation unwraps it when given
20889   *     such a value.
20890   *
20891   * - getTrusted(contextEnum, value)
20892   *     This function should return the value that is safe to use in the context specified by
20893   *     contextEnum or throw and exception otherwise.
20894   *
20895   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
20896   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
20897   * instance, an implementation could maintain a registry of all trusted objects by context.  In
20898   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
20899   * return the same object passed in if it was found in the registry under a compatible context or
20900   * throw an exception otherwise.  An implementation might only wrap values some of the time based
20901   * on some criteria.  getTrusted() might return a value and not throw an exception for special
20902   * constants or objects even if not wrapped.  All such implementations fulfill this contract.
20903   *
20904   *
20905   * A note on the inheritance model for SCE contexts
20906   * ------------------------------------------------
20907   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
20908   * is purely an implementation details.
20909   *
20910   * The contract is simply this:
20911   *
20912   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
20913   *     will also succeed.
20914   *
20915   * Inheritance happens to capture this in a natural way. In some future, we may not use
20916   * inheritance anymore. That is OK because no code outside of sce.js and sceSpecs.js would need to
20917   * be aware of this detail.
20918   */
20919
20920  this.$get = ['$parse', '$sceDelegate', function(
20921                $parse,   $sceDelegate) {
20922    // Support: IE 9-11 only
20923    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
20924    // the "expression(javascript expression)" syntax which is insecure.
20925    if (enabled && msie < 8) {
20926      throw $sceMinErr('iequirks',
20927        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
20928        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
20929        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
20930    }
20931
20932    var sce = shallowCopy(SCE_CONTEXTS);
20933
20934    /**
20935     * @ngdoc method
20936     * @name $sce#isEnabled
20937     * @kind function
20938     *
20939     * @return {Boolean} True if SCE is enabled, false otherwise.  If you want to set the value, you
20940     *     have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
20941     *
20942     * @description
20943     * Returns a boolean indicating if SCE is enabled.
20944     */
20945    sce.isEnabled = function() {
20946      return enabled;
20947    };
20948    sce.trustAs = $sceDelegate.trustAs;
20949    sce.getTrusted = $sceDelegate.getTrusted;
20950    sce.valueOf = $sceDelegate.valueOf;
20951
20952    if (!enabled) {
20953      sce.trustAs = sce.getTrusted = function(type, value) { return value; };
20954      sce.valueOf = identity;
20955    }
20956
20957    /**
20958     * @ngdoc method
20959     * @name $sce#parseAs
20960     *
20961     * @description
20962     * Converts AngularJS {@link guide/expression expression} into a function.  This is like {@link
20963     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
20964     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
20965     * *result*)}
20966     *
20967     * @param {string} type The SCE context in which this result will be used.
20968     * @param {string} expression String expression to compile.
20969     * @return {function(context, locals)} A function which represents the compiled expression:
20970     *
20971     *    * `context` – `{object}` – an object against which any expressions embedded in the
20972     *      strings are evaluated against (typically a scope object).
20973     *    * `locals` – `{object=}` – local variables context object, useful for overriding values
20974     *      in `context`.
20975     */
20976    sce.parseAs = function sceParseAs(type, expr) {
20977      var parsed = $parse(expr);
20978      if (parsed.literal && parsed.constant) {
20979        return parsed;
20980      } else {
20981        return $parse(expr, function(value) {
20982          return sce.getTrusted(type, value);
20983        });
20984      }
20985    };
20986
20987    /**
20988     * @ngdoc method
20989     * @name $sce#trustAs
20990     *
20991     * @description
20992     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, returns a
20993     * wrapped object that represents your value, and the trust you have in its safety for the given
20994     * context. AngularJS can then use that value as-is in bindings of the specified secure context.
20995     * This is used in bindings for `ng-bind-html`, `ng-include`, and most `src` attribute
20996     * interpolations. See {@link ng.$sce $sce} for strict contextual escaping.
20997     *
20998     * @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`,
20999     *     `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`.
21000     *
21001     * @param {*} value The value that that should be considered trusted.
21002     * @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
21003     *     in the context you specified.
21004     */
21005
21006    /**
21007     * @ngdoc method
21008     * @name $sce#trustAsHtml
21009     *
21010     * @description
21011     * Shorthand method.  `$sce.trustAsHtml(value)` →
21012     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
21013     *
21014     * @param {*} value The value to mark as trusted for `$sce.HTML` context.
21015     * @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
21016     *     in `$sce.HTML` context (like `ng-bind-html`).
21017     */
21018
21019    /**
21020     * @ngdoc method
21021     * @name $sce#trustAsCss
21022     *
21023     * @description
21024     * Shorthand method.  `$sce.trustAsCss(value)` →
21025     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.CSS, value)`}
21026     *
21027     * @param {*} value The value to mark as trusted for `$sce.CSS` context.
21028     * @return {*} A wrapped version of value that can be used as a trusted variant
21029     *     of your `value` in `$sce.CSS` context. This context is currently unused, so there are
21030     *     almost no reasons to use this function so far.
21031     */
21032
21033    /**
21034     * @ngdoc method
21035     * @name $sce#trustAsUrl
21036     *
21037     * @description
21038     * Shorthand method.  `$sce.trustAsUrl(value)` →
21039     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
21040     *
21041     * @param {*} value The value to mark as trusted for `$sce.URL` context.
21042     * @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
21043     *     in `$sce.URL` context. That context is currently unused, so there are almost no reasons
21044     *     to use this function so far.
21045     */
21046
21047    /**
21048     * @ngdoc method
21049     * @name $sce#trustAsResourceUrl
21050     *
21051     * @description
21052     * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
21053     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
21054     *
21055     * @param {*} value The value to mark as trusted for `$sce.RESOURCE_URL` context.
21056     * @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
21057     *     in `$sce.RESOURCE_URL` context (template URLs in `ng-include`, most `src` attribute
21058     *     bindings, ...)
21059     */
21060
21061    /**
21062     * @ngdoc method
21063     * @name $sce#trustAsJs
21064     *
21065     * @description
21066     * Shorthand method.  `$sce.trustAsJs(value)` →
21067     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
21068     *
21069     * @param {*} value The value to mark as trusted for `$sce.JS` context.
21070     * @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
21071     *     in `$sce.JS` context. That context is currently unused, so there are almost no reasons to
21072     *     use this function so far.
21073     */
21074
21075    /**
21076     * @ngdoc method
21077     * @name $sce#getTrusted
21078     *
21079     * @description
21080     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
21081     * takes any input, and either returns a value that's safe to use in the specified context,
21082     * or throws an exception. This function is aware of trusted values created by the `trustAs`
21083     * function and its shorthands, and when contexts are appropriate, returns the unwrapped value
21084     * as-is. Finally, this function can also throw when there is no way to turn `maybeTrusted` in a
21085     * safe value (e.g., no sanitization is available or possible.)
21086     *
21087     * @param {string} type The context in which this value is to be used.
21088     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs
21089     *     `$sce.trustAs`} call, or anything else (which will not be considered trusted.)
21090     * @return {*} A version of the value that's safe to use in the given context, or throws an
21091     *     exception if this is impossible.
21092     */
21093
21094    /**
21095     * @ngdoc method
21096     * @name $sce#getTrustedHtml
21097     *
21098     * @description
21099     * Shorthand method.  `$sce.getTrustedHtml(value)` →
21100     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
21101     *
21102     * @param {*} value The value to pass to `$sce.getTrusted`.
21103     * @return {*} The return value of `$sce.getTrusted($sce.HTML, value)`
21104     */
21105
21106    /**
21107     * @ngdoc method
21108     * @name $sce#getTrustedCss
21109     *
21110     * @description
21111     * Shorthand method.  `$sce.getTrustedCss(value)` →
21112     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
21113     *
21114     * @param {*} value The value to pass to `$sce.getTrusted`.
21115     * @return {*} The return value of `$sce.getTrusted($sce.CSS, value)`
21116     */
21117
21118    /**
21119     * @ngdoc method
21120     * @name $sce#getTrustedUrl
21121     *
21122     * @description
21123     * Shorthand method.  `$sce.getTrustedUrl(value)` →
21124     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
21125     *
21126     * @param {*} value The value to pass to `$sce.getTrusted`.
21127     * @return {*} The return value of `$sce.getTrusted($sce.URL, value)`
21128     */
21129
21130    /**
21131     * @ngdoc method
21132     * @name $sce#getTrustedResourceUrl
21133     *
21134     * @description
21135     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
21136     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
21137     *
21138     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
21139     * @return {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
21140     */
21141
21142    /**
21143     * @ngdoc method
21144     * @name $sce#getTrustedJs
21145     *
21146     * @description
21147     * Shorthand method.  `$sce.getTrustedJs(value)` →
21148     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
21149     *
21150     * @param {*} value The value to pass to `$sce.getTrusted`.
21151     * @return {*} The return value of `$sce.getTrusted($sce.JS, value)`
21152     */
21153
21154    /**
21155     * @ngdoc method
21156     * @name $sce#parseAsHtml
21157     *
21158     * @description
21159     * Shorthand method.  `$sce.parseAsHtml(expression string)` →
21160     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
21161     *
21162     * @param {string} expression String expression to compile.
21163     * @return {function(context, locals)} A function which represents the compiled expression:
21164     *
21165     *    * `context` – `{object}` – an object against which any expressions embedded in the
21166     *      strings are evaluated against (typically a scope object).
21167     *    * `locals` – `{object=}` – local variables context object, useful for overriding values
21168     *      in `context`.
21169     */
21170
21171    /**
21172     * @ngdoc method
21173     * @name $sce#parseAsCss
21174     *
21175     * @description
21176     * Shorthand method.  `$sce.parseAsCss(value)` →
21177     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
21178     *
21179     * @param {string} expression String expression to compile.
21180     * @return {function(context, locals)} A function which represents the compiled expression:
21181     *
21182     *    * `context` – `{object}` – an object against which any expressions embedded in the
21183     *      strings are evaluated against (typically a scope object).
21184     *    * `locals` – `{object=}` – local variables context object, useful for overriding values
21185     *      in `context`.
21186     */
21187
21188    /**
21189     * @ngdoc method
21190     * @name $sce#parseAsUrl
21191     *
21192     * @description
21193     * Shorthand method.  `$sce.parseAsUrl(value)` →
21194     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
21195     *
21196     * @param {string} expression String expression to compile.
21197     * @return {function(context, locals)} A function which represents the compiled expression:
21198     *
21199     *    * `context` – `{object}` – an object against which any expressions embedded in the
21200     *      strings are evaluated against (typically a scope object).
21201     *    * `locals` – `{object=}` – local variables context object, useful for overriding values
21202     *      in `context`.
21203     */
21204
21205    /**
21206     * @ngdoc method
21207     * @name $sce#parseAsResourceUrl
21208     *
21209     * @description
21210     * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
21211     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
21212     *
21213     * @param {string} expression String expression to compile.
21214     * @return {function(context, locals)} A function which represents the compiled expression:
21215     *
21216     *    * `context` – `{object}` – an object against which any expressions embedded in the
21217     *      strings are evaluated against (typically a scope object).
21218     *    * `locals` – `{object=}` – local variables context object, useful for overriding values
21219     *      in `context`.
21220     */
21221
21222    /**
21223     * @ngdoc method
21224     * @name $sce#parseAsJs
21225     *
21226     * @description
21227     * Shorthand method.  `$sce.parseAsJs(value)` →
21228     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
21229     *
21230     * @param {string} expression String expression to compile.
21231     * @return {function(context, locals)} A function which represents the compiled expression:
21232     *
21233     *    * `context` – `{object}` – an object against which any expressions embedded in the
21234     *      strings are evaluated against (typically a scope object).
21235     *    * `locals` – `{object=}` – local variables context object, useful for overriding values
21236     *      in `context`.
21237     */
21238
21239    // Shorthand delegations.
21240    var parse = sce.parseAs,
21241        getTrusted = sce.getTrusted,
21242        trustAs = sce.trustAs;
21243
21244    forEach(SCE_CONTEXTS, function(enumValue, name) {
21245      var lName = lowercase(name);
21246      sce[snakeToCamel('parse_as_' + lName)] = function(expr) {
21247        return parse(enumValue, expr);
21248      };
21249      sce[snakeToCamel('get_trusted_' + lName)] = function(value) {
21250        return getTrusted(enumValue, value);
21251      };
21252      sce[snakeToCamel('trust_as_' + lName)] = function(value) {
21253        return trustAs(enumValue, value);
21254      };
21255    });
21256
21257    return sce;
21258  }];
21259}
21260
21261/* exported $SnifferProvider */
21262
21263/**
21264 * !!! This is an undocumented "private" service !!!
21265 *
21266 * @name $sniffer
21267 * @requires $window
21268 * @requires $document
21269 * @this
21270 *
21271 * @property {boolean} history Does the browser support html5 history api ?
21272 * @property {boolean} transitions Does the browser support CSS transition events ?
21273 * @property {boolean} animations Does the browser support CSS animation events ?
21274 *
21275 * @description
21276 * This is very simple implementation of testing browser's features.
21277 */
21278function $SnifferProvider() {
21279  this.$get = ['$window', '$document', function($window, $document) {
21280    var eventSupport = {},
21281        // Chrome Packaged Apps are not allowed to access `history.pushState`.
21282        // If not sandboxed, they can be detected by the presence of `chrome.app.runtime`
21283        // (see https://developer.chrome.com/apps/api_index). If sandboxed, they can be detected by
21284        // the presence of an extension runtime ID and the absence of other Chrome runtime APIs
21285        // (see https://developer.chrome.com/apps/manifest/sandbox).
21286        // (NW.js apps have access to Chrome APIs, but do support `history`.)
21287        isNw = $window.nw && $window.nw.process,
21288        isChromePackagedApp =
21289            !isNw &&
21290            $window.chrome &&
21291            ($window.chrome.app && $window.chrome.app.runtime ||
21292                !$window.chrome.app && $window.chrome.runtime && $window.chrome.runtime.id),
21293        hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
21294        android =
21295          toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
21296        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
21297        document = $document[0] || {},
21298        bodyStyle = document.body && document.body.style,
21299        transitions = false,
21300        animations = false;
21301
21302    if (bodyStyle) {
21303      // Support: Android <5, Blackberry Browser 10, default Chrome in Android 4.4.x
21304      // Mentioned browsers need a -webkit- prefix for transitions & animations.
21305      transitions = !!('transition' in bodyStyle || 'webkitTransition' in bodyStyle);
21306      animations = !!('animation' in bodyStyle || 'webkitAnimation' in bodyStyle);
21307    }
21308
21309
21310    return {
21311      // Android has history.pushState, but it does not update location correctly
21312      // so let's not use the history API at all.
21313      // http://code.google.com/p/android/issues/detail?id=17471
21314      // https://github.com/angular/angular.js/issues/904
21315
21316      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
21317      // so let's not use the history API also
21318      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
21319      history: !!(hasHistoryPushState && !(android < 4) && !boxee),
21320      hasEvent: function(event) {
21321        // Support: IE 9-11 only
21322        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
21323        // it. In particular the event is not fired when backspace or delete key are pressed or
21324        // when cut operation is performed.
21325        // IE10+ implements 'input' event but it erroneously fires under various situations,
21326        // e.g. when placeholder changes, or a form is focused.
21327        if (event === 'input' && msie) return false;
21328
21329        if (isUndefined(eventSupport[event])) {
21330          var divElm = document.createElement('div');
21331          eventSupport[event] = 'on' + event in divElm;
21332        }
21333
21334        return eventSupport[event];
21335      },
21336      csp: csp(),
21337      transitions: transitions,
21338      animations: animations,
21339      android: android
21340    };
21341  }];
21342}
21343
21344/**
21345 * ! This is a private undocumented service !
21346 *
21347 * @name $$taskTrackerFactory
21348 * @description
21349 * A function to create `TaskTracker` instances.
21350 *
21351 * A `TaskTracker` can keep track of pending tasks (grouped by type) and can notify interested
21352 * parties when all pending tasks (or tasks of a specific type) have been completed.
21353 *
21354 * @param {$log} log - A logger instance (such as `$log`). Used to log error during callback
21355 *     execution.
21356 *
21357 * @this
21358 */
21359function $$TaskTrackerFactoryProvider() {
21360  this.$get = valueFn(function(log) { return new TaskTracker(log); });
21361}
21362
21363function TaskTracker(log) {
21364  var self = this;
21365  var taskCounts = {};
21366  var taskCallbacks = [];
21367
21368  var ALL_TASKS_TYPE = self.ALL_TASKS_TYPE = '$$all$$';
21369  var DEFAULT_TASK_TYPE = self.DEFAULT_TASK_TYPE = '$$default$$';
21370
21371  /**
21372   * Execute the specified function and decrement the appropriate `taskCounts` counter.
21373   * If the counter reaches 0, all corresponding `taskCallbacks` are executed.
21374   *
21375   * @param {Function} fn - The function to execute.
21376   * @param {string=} [taskType=DEFAULT_TASK_TYPE] - The type of task that is being completed.
21377   */
21378  self.completeTask = completeTask;
21379
21380  /**
21381   * Increase the task count for the specified task type (or the default task type if non is
21382   * specified).
21383   *
21384   * @param {string=} [taskType=DEFAULT_TASK_TYPE] - The type of task whose count will be increased.
21385   */
21386  self.incTaskCount = incTaskCount;
21387
21388  /**
21389   * Execute the specified callback when all pending tasks have been completed.
21390   *
21391   * If there are no pending tasks, the callback is executed immediately. You can optionally limit
21392   * the tasks that will be waited for to a specific type, by passing a `taskType`.
21393   *
21394   * @param {function} callback - The function to call when there are no pending tasks.
21395   * @param {string=} [taskType=ALL_TASKS_TYPE] - The type of tasks that will be waited for.
21396   */
21397  self.notifyWhenNoPendingTasks = notifyWhenNoPendingTasks;
21398
21399  function completeTask(fn, taskType) {
21400    taskType = taskType || DEFAULT_TASK_TYPE;
21401
21402    try {
21403      fn();
21404    } finally {
21405      decTaskCount(taskType);
21406
21407      var countForType = taskCounts[taskType];
21408      var countForAll = taskCounts[ALL_TASKS_TYPE];
21409
21410      // If at least one of the queues (`ALL_TASKS_TYPE` or `taskType`) is empty, run callbacks.
21411      if (!countForAll || !countForType) {
21412        var getNextCallback = !countForAll ? getLastCallback : getLastCallbackForType;
21413        var nextCb;
21414
21415        while ((nextCb = getNextCallback(taskType))) {
21416          try {
21417            nextCb();
21418          } catch (e) {
21419            log.error(e);
21420          }
21421        }
21422      }
21423    }
21424  }
21425
21426  function decTaskCount(taskType) {
21427    taskType = taskType || DEFAULT_TASK_TYPE;
21428    if (taskCounts[taskType]) {
21429      taskCounts[taskType]--;
21430      taskCounts[ALL_TASKS_TYPE]--;
21431    }
21432  }
21433
21434  function getLastCallback() {
21435    var cbInfo = taskCallbacks.pop();
21436    return cbInfo && cbInfo.cb;
21437  }
21438
21439  function getLastCallbackForType(taskType) {
21440    for (var i = taskCallbacks.length - 1; i >= 0; --i) {
21441      var cbInfo = taskCallbacks[i];
21442      if (cbInfo.type === taskType) {
21443        taskCallbacks.splice(i, 1);
21444        return cbInfo.cb;
21445      }
21446    }
21447  }
21448
21449  function incTaskCount(taskType) {
21450    taskType = taskType || DEFAULT_TASK_TYPE;
21451    taskCounts[taskType] = (taskCounts[taskType] || 0) + 1;
21452    taskCounts[ALL_TASKS_TYPE] = (taskCounts[ALL_TASKS_TYPE] || 0) + 1;
21453  }
21454
21455  function notifyWhenNoPendingTasks(callback, taskType) {
21456    taskType = taskType || ALL_TASKS_TYPE;
21457    if (!taskCounts[taskType]) {
21458      callback();
21459    } else {
21460      taskCallbacks.push({type: taskType, cb: callback});
21461    }
21462  }
21463}
21464
21465var $templateRequestMinErr = minErr('$templateRequest');
21466
21467/**
21468 * @ngdoc provider
21469 * @name $templateRequestProvider
21470 * @this
21471 *
21472 * @description
21473 * Used to configure the options passed to the {@link $http} service when making a template request.
21474 *
21475 * For example, it can be used for specifying the "Accept" header that is sent to the server, when
21476 * requesting a template.
21477 */
21478function $TemplateRequestProvider() {
21479
21480  var httpOptions;
21481
21482  /**
21483   * @ngdoc method
21484   * @name $templateRequestProvider#httpOptions
21485   * @description
21486   * The options to be passed to the {@link $http} service when making the request.
21487   * You can use this to override options such as the "Accept" header for template requests.
21488   *
21489   * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
21490   * options if not overridden here.
21491   *
21492   * @param {string=} value new value for the {@link $http} options.
21493   * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
21494   */
21495  this.httpOptions = function(val) {
21496    if (val) {
21497      httpOptions = val;
21498      return this;
21499    }
21500    return httpOptions;
21501  };
21502
21503  /**
21504   * @ngdoc service
21505   * @name $templateRequest
21506   *
21507   * @description
21508   * The `$templateRequest` service runs security checks then downloads the provided template using
21509   * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
21510   * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
21511   * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
21512   * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
21513   * when `tpl` is of type string and `$templateCache` has the matching entry.
21514   *
21515   * If you want to pass custom options to the `$http` service, such as setting the Accept header you
21516   * can configure this via {@link $templateRequestProvider#httpOptions}.
21517   *
21518   * `$templateRequest` is used internally by {@link $compile}, {@link ngRoute.$route}, and directives such
21519   * as {@link ngInclude} to download and cache templates.
21520   *
21521   * 3rd party modules should use `$templateRequest` if their services or directives are loading
21522   * templates.
21523   *
21524   * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
21525   * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
21526   *
21527   * @return {Promise} a promise for the HTTP response data of the given URL.
21528   *
21529   * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
21530   */
21531  this.$get = ['$exceptionHandler', '$templateCache', '$http', '$q', '$sce',
21532    function($exceptionHandler, $templateCache, $http, $q, $sce) {
21533
21534      function handleRequestFn(tpl, ignoreRequestError) {
21535        handleRequestFn.totalPendingRequests++;
21536
21537        // We consider the template cache holds only trusted templates, so
21538        // there's no need to go through adding the template again to the trusted
21539        // resources for keys that already are included in there. This also makes
21540        // AngularJS accept any script directive, no matter its name. However, we
21541        // still need to unwrap trusted types.
21542        if (!isString(tpl) || isUndefined($templateCache.get(tpl))) {
21543          tpl = $sce.getTrustedResourceUrl(tpl);
21544        }
21545
21546        var transformResponse = $http.defaults && $http.defaults.transformResponse;
21547
21548        if (isArray(transformResponse)) {
21549          transformResponse = transformResponse.filter(function(transformer) {
21550            return transformer !== defaultHttpResponseTransform;
21551          });
21552        } else if (transformResponse === defaultHttpResponseTransform) {
21553          transformResponse = null;
21554        }
21555
21556        return $http.get(tpl, extend({
21557            cache: $templateCache,
21558            transformResponse: transformResponse
21559          }, httpOptions))
21560          .finally(function() {
21561            handleRequestFn.totalPendingRequests--;
21562          })
21563          .then(function(response) {
21564            return $templateCache.put(tpl, response.data);
21565          }, handleError);
21566
21567        function handleError(resp) {
21568          if (!ignoreRequestError) {
21569            resp = $templateRequestMinErr('tpload',
21570                'Failed to load template: {0} (HTTP status: {1} {2})',
21571                tpl, resp.status, resp.statusText);
21572
21573            $exceptionHandler(resp);
21574          }
21575
21576          return $q.reject(resp);
21577        }
21578      }
21579
21580      handleRequestFn.totalPendingRequests = 0;
21581
21582      return handleRequestFn;
21583    }
21584  ];
21585}
21586
21587/** @this */
21588function $$TestabilityProvider() {
21589  this.$get = ['$rootScope', '$browser', '$location',
21590       function($rootScope,   $browser,   $location) {
21591
21592    /**
21593     * @name $testability
21594     *
21595     * @description
21596     * The private $$testability service provides a collection of methods for use when debugging
21597     * or by automated test and debugging tools.
21598     */
21599    var testability = {};
21600
21601    /**
21602     * @name $$testability#findBindings
21603     *
21604     * @description
21605     * Returns an array of elements that are bound (via ng-bind or {{}})
21606     * to expressions matching the input.
21607     *
21608     * @param {Element} element The element root to search from.
21609     * @param {string} expression The binding expression to match.
21610     * @param {boolean} opt_exactMatch If true, only returns exact matches
21611     *     for the expression. Filters and whitespace are ignored.
21612     */
21613    testability.findBindings = function(element, expression, opt_exactMatch) {
21614      var bindings = element.getElementsByClassName('ng-binding');
21615      var matches = [];
21616      forEach(bindings, function(binding) {
21617        var dataBinding = angular.element(binding).data('$binding');
21618        if (dataBinding) {
21619          forEach(dataBinding, function(bindingName) {
21620            if (opt_exactMatch) {
21621              var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
21622              if (matcher.test(bindingName)) {
21623                matches.push(binding);
21624              }
21625            } else {
21626              if (bindingName.indexOf(expression) !== -1) {
21627                matches.push(binding);
21628              }
21629            }
21630          });
21631        }
21632      });
21633      return matches;
21634    };
21635
21636    /**
21637     * @name $$testability#findModels
21638     *
21639     * @description
21640     * Returns an array of elements that are two-way found via ng-model to
21641     * expressions matching the input.
21642     *
21643     * @param {Element} element The element root to search from.
21644     * @param {string} expression The model expression to match.
21645     * @param {boolean} opt_exactMatch If true, only returns exact matches
21646     *     for the expression.
21647     */
21648    testability.findModels = function(element, expression, opt_exactMatch) {
21649      var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
21650      for (var p = 0; p < prefixes.length; ++p) {
21651        var attributeEquals = opt_exactMatch ? '=' : '*=';
21652        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
21653        var elements = element.querySelectorAll(selector);
21654        if (elements.length) {
21655          return elements;
21656        }
21657      }
21658    };
21659
21660    /**
21661     * @name $$testability#getLocation
21662     *
21663     * @description
21664     * Shortcut for getting the location in a browser agnostic way. Returns
21665     *     the path, search, and hash. (e.g. /path?a=b#hash)
21666     */
21667    testability.getLocation = function() {
21668      return $location.url();
21669    };
21670
21671    /**
21672     * @name $$testability#setLocation
21673     *
21674     * @description
21675     * Shortcut for navigating to a location without doing a full page reload.
21676     *
21677     * @param {string} url The location url (path, search and hash,
21678     *     e.g. /path?a=b#hash) to go to.
21679     */
21680    testability.setLocation = function(url) {
21681      if (url !== $location.url()) {
21682        $location.url(url);
21683        $rootScope.$digest();
21684      }
21685    };
21686
21687    /**
21688     * @name $$testability#whenStable
21689     *
21690     * @description
21691     * Calls the callback when all pending tasks are completed.
21692     *
21693     * Types of tasks waited for include:
21694     * - Pending timeouts (via {@link $timeout}).
21695     * - Pending HTTP requests (via {@link $http}).
21696     * - In-progress route transitions (via {@link $route}).
21697     * - Pending tasks scheduled via {@link $rootScope#$applyAsync}.
21698     * - Pending tasks scheduled via {@link $rootScope#$evalAsync}.
21699     *   These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises).
21700     *
21701     * @param {function} callback
21702     */
21703    testability.whenStable = function(callback) {
21704      $browser.notifyWhenNoOutstandingRequests(callback);
21705    };
21706
21707    return testability;
21708  }];
21709}
21710
21711var $timeoutMinErr = minErr('$timeout');
21712
21713/** @this */
21714function $TimeoutProvider() {
21715  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
21716       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
21717
21718    var deferreds = {};
21719
21720
21721    /**
21722     * @ngdoc service
21723     * @name $timeout
21724     *
21725     * @description
21726     * AngularJS's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
21727     * block and delegates any exceptions to
21728     * {@link ng.$exceptionHandler $exceptionHandler} service.
21729     *
21730     * The return value of calling `$timeout` is a promise, which will be resolved when
21731     * the delay has passed and the timeout function, if provided, is executed.
21732     *
21733     * To cancel a timeout request, call `$timeout.cancel(promise)`.
21734     *
21735     * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
21736     * synchronously flush the queue of deferred functions.
21737     *
21738     * If you only want a promise that will be resolved after some specified delay
21739     * then you can call `$timeout` without the `fn` function.
21740     *
21741     * @param {function()=} fn A function, whose execution should be delayed.
21742     * @param {number=} [delay=0] Delay in milliseconds.
21743     * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
21744     *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
21745     * @param {...*=} Pass additional parameters to the executed function.
21746     * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
21747     *   will be resolved with the return value of the `fn` function.
21748     *
21749     */
21750    function timeout(fn, delay, invokeApply) {
21751      if (!isFunction(fn)) {
21752        invokeApply = delay;
21753        delay = fn;
21754        fn = noop;
21755      }
21756
21757      var args = sliceArgs(arguments, 3),
21758          skipApply = (isDefined(invokeApply) && !invokeApply),
21759          deferred = (skipApply ? $$q : $q).defer(),
21760          promise = deferred.promise,
21761          timeoutId;
21762
21763      timeoutId = $browser.defer(function() {
21764        try {
21765          deferred.resolve(fn.apply(null, args));
21766        } catch (e) {
21767          deferred.reject(e);
21768          $exceptionHandler(e);
21769        } finally {
21770          delete deferreds[promise.$$timeoutId];
21771        }
21772
21773        if (!skipApply) $rootScope.$apply();
21774      }, delay, '$timeout');
21775
21776      promise.$$timeoutId = timeoutId;
21777      deferreds[timeoutId] = deferred;
21778
21779      return promise;
21780    }
21781
21782
21783    /**
21784     * @ngdoc method
21785     * @name $timeout#cancel
21786     *
21787     * @description
21788     * Cancels a task associated with the `promise`. As a result of this, the promise will be
21789     * resolved with a rejection.
21790     *
21791     * @param {Promise=} promise Promise returned by the `$timeout` function.
21792     * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
21793     *   canceled.
21794     */
21795    timeout.cancel = function(promise) {
21796      if (!promise) return false;
21797
21798      if (!promise.hasOwnProperty('$$timeoutId')) {
21799        throw $timeoutMinErr('badprom',
21800            '`$timeout.cancel()` called with a promise that was not generated by `$timeout()`.');
21801      }
21802
21803      if (!deferreds.hasOwnProperty(promise.$$timeoutId)) return false;
21804
21805      var id = promise.$$timeoutId;
21806      var deferred = deferreds[id];
21807
21808      // Timeout cancels should not report an unhandled promise.
21809      markQExceptionHandled(deferred.promise);
21810      deferred.reject('canceled');
21811      delete deferreds[id];
21812
21813      return $browser.defer.cancel(id);
21814    };
21815
21816    return timeout;
21817  }];
21818}
21819
21820// NOTE:  The usage of window and document instead of $window and $document here is
21821// deliberate.  This service depends on the specific behavior of anchor nodes created by the
21822// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
21823// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
21824// doesn't know about mocked locations and resolves URLs to the real document - which is
21825// exactly the behavior needed here.  There is little value is mocking these out for this
21826// service.
21827var urlParsingNode = window.document.createElement('a');
21828var originUrl = urlResolve(window.location.href);
21829var baseUrlParsingNode;
21830
21831urlParsingNode.href = 'http://[::1]';
21832
21833// Support: IE 9-11 only, Edge 16-17 only (fixed in 18 Preview)
21834// IE/Edge don't wrap IPv6 addresses' hostnames in square brackets
21835// when parsed out of an anchor element.
21836var ipv6InBrackets = urlParsingNode.hostname === '[::1]';
21837
21838/**
21839 *
21840 * Implementation Notes for non-IE browsers
21841 * ----------------------------------------
21842 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
21843 * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
21844 * URL will be resolved into an absolute URL in the context of the application document.
21845 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
21846 * properties are all populated to reflect the normalized URL.  This approach has wide
21847 * compatibility - Safari 1+, Mozilla 1+ etc.  See
21848 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
21849 *
21850 * Implementation Notes for IE
21851 * ---------------------------
21852 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
21853 * browsers.  However, the parsed components will not be set if the URL assigned did not specify
21854 * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
21855 * work around that by performing the parsing in a 2nd step by taking a previously normalized
21856 * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
21857 * properties such as protocol, hostname, port, etc.
21858 *
21859 * References:
21860 *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
21861 *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
21862 *   http://url.spec.whatwg.org/#urlutils
21863 *   https://github.com/angular/angular.js/pull/2902
21864 *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
21865 *
21866 * @kind function
21867 * @param {string|object} url The URL to be parsed. If `url` is not a string, it will be returned
21868 *     unchanged.
21869 * @description Normalizes and parses a URL.
21870 * @returns {object} Returns the normalized URL as a dictionary.
21871 *
21872 *   | member name   | Description                                                            |
21873 *   |---------------|------------------------------------------------------------------------|
21874 *   | href          | A normalized version of the provided URL if it was not an absolute URL |
21875 *   | protocol      | The protocol without the trailing colon                                |
21876 *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
21877 *   | search        | The search params, minus the question mark                             |
21878 *   | hash          | The hash string, minus the hash symbol                                 |
21879 *   | hostname      | The hostname                                                           |
21880 *   | port          | The port, without ":"                                                  |
21881 *   | pathname      | The pathname, beginning with "/"                                       |
21882 *
21883 */
21884function urlResolve(url) {
21885  if (!isString(url)) return url;
21886
21887  var href = url;
21888
21889  // Support: IE 9-11 only
21890  if (msie) {
21891    // Normalize before parse.  Refer Implementation Notes on why this is
21892    // done in two steps on IE.
21893    urlParsingNode.setAttribute('href', href);
21894    href = urlParsingNode.href;
21895  }
21896
21897  urlParsingNode.setAttribute('href', href);
21898
21899  var hostname = urlParsingNode.hostname;
21900
21901  if (!ipv6InBrackets && hostname.indexOf(':') > -1) {
21902    hostname = '[' + hostname + ']';
21903  }
21904
21905  return {
21906    href: urlParsingNode.href,
21907    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
21908    host: urlParsingNode.host,
21909    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
21910    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
21911    hostname: hostname,
21912    port: urlParsingNode.port,
21913    pathname: (urlParsingNode.pathname.charAt(0) === '/')
21914      ? urlParsingNode.pathname
21915      : '/' + urlParsingNode.pathname
21916  };
21917}
21918
21919/**
21920 * Parse a request URL and determine whether this is a same-origin request as the application
21921 * document.
21922 *
21923 * @param {string|object} requestUrl The url of the request as a string that will be resolved
21924 * or a parsed URL object.
21925 * @returns {boolean} Whether the request is for the same origin as the application document.
21926 */
21927function urlIsSameOrigin(requestUrl) {
21928  return urlsAreSameOrigin(requestUrl, originUrl);
21929}
21930
21931/**
21932 * Parse a request URL and determine whether it is same-origin as the current document base URL.
21933 *
21934 * Note: The base URL is usually the same as the document location (`location.href`) but can
21935 * be overriden by using the `<base>` tag.
21936 *
21937 * @param {string|object} requestUrl The url of the request as a string that will be resolved
21938 * or a parsed URL object.
21939 * @returns {boolean} Whether the URL is same-origin as the document base URL.
21940 */
21941function urlIsSameOriginAsBaseUrl(requestUrl) {
21942  return urlsAreSameOrigin(requestUrl, getBaseUrl());
21943}
21944
21945/**
21946 * Create a function that can check a URL's origin against a list of allowed/trusted origins.
21947 * The current location's origin is implicitly trusted.
21948 *
21949 * @param {string[]} trustedOriginUrls - A list of URLs (strings), whose origins are trusted.
21950 *
21951 * @returns {Function} - A function that receives a URL (string or parsed URL object) and returns
21952 *     whether it is of an allowed origin.
21953 */
21954function urlIsAllowedOriginFactory(trustedOriginUrls) {
21955  var parsedAllowedOriginUrls = [originUrl].concat(trustedOriginUrls.map(urlResolve));
21956
21957  /**
21958   * Check whether the specified URL (string or parsed URL object) has an origin that is allowed
21959   * based on a list of trusted-origin URLs. The current location's origin is implicitly
21960   * trusted.
21961   *
21962   * @param {string|Object} requestUrl - The URL to be checked (provided as a string that will be
21963   *     resolved or a parsed URL object).
21964   *
21965   * @returns {boolean} - Whether the specified URL is of an allowed origin.
21966   */
21967  return function urlIsAllowedOrigin(requestUrl) {
21968    var parsedUrl = urlResolve(requestUrl);
21969    return parsedAllowedOriginUrls.some(urlsAreSameOrigin.bind(null, parsedUrl));
21970  };
21971}
21972
21973/**
21974 * Determine if two URLs share the same origin.
21975 *
21976 * @param {string|Object} url1 - First URL to compare as a string or a normalized URL in the form of
21977 *     a dictionary object returned by `urlResolve()`.
21978 * @param {string|object} url2 - Second URL to compare as a string or a normalized URL in the form
21979 *     of a dictionary object returned by `urlResolve()`.
21980 *
21981 * @returns {boolean} - True if both URLs have the same origin, and false otherwise.
21982 */
21983function urlsAreSameOrigin(url1, url2) {
21984  url1 = urlResolve(url1);
21985  url2 = urlResolve(url2);
21986
21987  return (url1.protocol === url2.protocol &&
21988          url1.host === url2.host);
21989}
21990
21991/**
21992 * Returns the current document base URL.
21993 * @returns {string}
21994 */
21995function getBaseUrl() {
21996  if (window.document.baseURI) {
21997    return window.document.baseURI;
21998  }
21999
22000  // `document.baseURI` is available everywhere except IE
22001  if (!baseUrlParsingNode) {
22002    baseUrlParsingNode = window.document.createElement('a');
22003    baseUrlParsingNode.href = '.';
22004
22005    // Work-around for IE bug described in Implementation Notes. The fix in `urlResolve()` is not
22006    // suitable here because we need to track changes to the base URL.
22007    baseUrlParsingNode = baseUrlParsingNode.cloneNode(false);
22008  }
22009  return baseUrlParsingNode.href;
22010}
22011
22012/**
22013 * @ngdoc service
22014 * @name $window
22015 * @this
22016 *
22017 * @description
22018 * A reference to the browser's `window` object. While `window`
22019 * is globally available in JavaScript, it causes testability problems, because
22020 * it is a global variable. In AngularJS we always refer to it through the
22021 * `$window` service, so it may be overridden, removed or mocked for testing.
22022 *
22023 * Expressions, like the one defined for the `ngClick` directive in the example
22024 * below, are evaluated with respect to the current scope.  Therefore, there is
22025 * no risk of inadvertently coding in a dependency on a global value in such an
22026 * expression.
22027 *
22028 * @example
22029   <example module="windowExample" name="window-service">
22030     <file name="index.html">
22031       <script>
22032         angular.module('windowExample', [])
22033           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
22034             $scope.greeting = 'Hello, World!';
22035             $scope.doGreeting = function(greeting) {
22036               $window.alert(greeting);
22037             };
22038           }]);
22039       </script>
22040       <div ng-controller="ExampleController">
22041         <input type="text" ng-model="greeting" aria-label="greeting" />
22042         <button ng-click="doGreeting(greeting)">ALERT</button>
22043       </div>
22044     </file>
22045     <file name="protractor.js" type="protractor">
22046      it('should display the greeting in the input box', function() {
22047       element(by.model('greeting')).sendKeys('Hello, E2E Tests');
22048       // If we click the button it will block the test runner
22049       // element(':button').click();
22050      });
22051     </file>
22052   </example>
22053 */
22054function $WindowProvider() {
22055  this.$get = valueFn(window);
22056}
22057
22058/**
22059 * @name $$cookieReader
22060 * @requires $document
22061 *
22062 * @description
22063 * This is a private service for reading cookies used by $http and ngCookies
22064 *
22065 * @return {Object} a key/value map of the current cookies
22066 */
22067function $$CookieReader($document) {
22068  var rawDocument = $document[0] || {};
22069  var lastCookies = {};
22070  var lastCookieString = '';
22071
22072  function safeGetCookie(rawDocument) {
22073    try {
22074      return rawDocument.cookie || '';
22075    } catch (e) {
22076      return '';
22077    }
22078  }
22079
22080  function safeDecodeURIComponent(str) {
22081    try {
22082      return decodeURIComponent(str);
22083    } catch (e) {
22084      return str;
22085    }
22086  }
22087
22088  return function() {
22089    var cookieArray, cookie, i, index, name;
22090    var currentCookieString = safeGetCookie(rawDocument);
22091
22092    if (currentCookieString !== lastCookieString) {
22093      lastCookieString = currentCookieString;
22094      cookieArray = lastCookieString.split('; ');
22095      lastCookies = {};
22096
22097      for (i = 0; i < cookieArray.length; i++) {
22098        cookie = cookieArray[i];
22099        index = cookie.indexOf('=');
22100        if (index > 0) { //ignore nameless cookies
22101          name = safeDecodeURIComponent(cookie.substring(0, index));
22102          // the first value that is seen for a cookie is the most
22103          // specific one.  values for the same cookie name that
22104          // follow are for less specific paths.
22105          if (isUndefined(lastCookies[name])) {
22106            lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
22107          }
22108        }
22109      }
22110    }
22111    return lastCookies;
22112  };
22113}
22114
22115$$CookieReader.$inject = ['$document'];
22116
22117/** @this */
22118function $$CookieReaderProvider() {
22119  this.$get = $$CookieReader;
22120}
22121
22122/* global currencyFilter: true,
22123 dateFilter: true,
22124 filterFilter: true,
22125 jsonFilter: true,
22126 limitToFilter: true,
22127 lowercaseFilter: true,
22128 numberFilter: true,
22129 orderByFilter: true,
22130 uppercaseFilter: true,
22131 */
22132
22133/**
22134 * @ngdoc provider
22135 * @name $filterProvider
22136 * @description
22137 *
22138 * Filters are just functions which transform input to an output. However filters need to be
22139 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
22140 * annotated with dependencies and is responsible for creating a filter function.
22141 *
22142 * <div class="alert alert-warning">
22143 * **Note:** Filter names must be valid AngularJS {@link expression} identifiers, such as `uppercase` or `orderBy`.
22144 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
22145 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
22146 * (`myapp_subsection_filterx`).
22147 * </div>
22148 *
22149 * ```js
22150 *   // Filter registration
22151 *   function MyModule($provide, $filterProvider) {
22152 *     // create a service to demonstrate injection (not always needed)
22153 *     $provide.value('greet', function(name){
22154 *       return 'Hello ' + name + '!';
22155 *     });
22156 *
22157 *     // register a filter factory which uses the
22158 *     // greet service to demonstrate DI.
22159 *     $filterProvider.register('greet', function(greet){
22160 *       // return the filter function which uses the greet service
22161 *       // to generate salutation
22162 *       return function(text) {
22163 *         // filters need to be forgiving so check input validity
22164 *         return text && greet(text) || text;
22165 *       };
22166 *     });
22167 *   }
22168 * ```
22169 *
22170 * The filter function is registered with the `$injector` under the filter name suffix with
22171 * `Filter`.
22172 *
22173 * ```js
22174 *   it('should be the same instance', inject(
22175 *     function($filterProvider) {
22176 *       $filterProvider.register('reverse', function(){
22177 *         return ...;
22178 *       });
22179 *     },
22180 *     function($filter, reverseFilter) {
22181 *       expect($filter('reverse')).toBe(reverseFilter);
22182 *     });
22183 * ```
22184 *
22185 *
22186 * For more information about how AngularJS filters work, and how to create your own filters, see
22187 * {@link guide/filter Filters} in the AngularJS Developer Guide.
22188 */
22189
22190/**
22191 * @ngdoc service
22192 * @name $filter
22193 * @kind function
22194 * @description
22195 * Filters are used for formatting data displayed to the user.
22196 *
22197 * They can be used in view templates, controllers or services. AngularJS comes
22198 * with a collection of [built-in filters](api/ng/filter), but it is easy to
22199 * define your own as well.
22200 *
22201 * The general syntax in templates is as follows:
22202 *
22203 * ```html
22204 * {{ expression [| filter_name[:parameter_value] ... ] }}
22205 * ```
22206 *
22207 * @param {String} name Name of the filter function to retrieve
22208 * @return {Function} the filter function
22209 * @example
22210   <example name="$filter" module="filterExample">
22211     <file name="index.html">
22212       <div ng-controller="MainCtrl">
22213        <h3>{{ originalText }}</h3>
22214        <h3>{{ filteredText }}</h3>
22215       </div>
22216     </file>
22217
22218     <file name="script.js">
22219      angular.module('filterExample', [])
22220      .controller('MainCtrl', function($scope, $filter) {
22221        $scope.originalText = 'hello';
22222        $scope.filteredText = $filter('uppercase')($scope.originalText);
22223      });
22224     </file>
22225   </example>
22226  */
22227$FilterProvider.$inject = ['$provide'];
22228/** @this */
22229function $FilterProvider($provide) {
22230  var suffix = 'Filter';
22231
22232  /**
22233   * @ngdoc method
22234   * @name $filterProvider#register
22235   * @param {string|Object} name Name of the filter function, or an object map of filters where
22236   *    the keys are the filter names and the values are the filter factories.
22237   *
22238   *    <div class="alert alert-warning">
22239   *    **Note:** Filter names must be valid AngularJS {@link expression} identifiers, such as `uppercase` or `orderBy`.
22240   *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
22241   *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
22242   *    (`myapp_subsection_filterx`).
22243   *    </div>
22244    * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
22245   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
22246   *    of the registered filter instances.
22247   */
22248  function register(name, factory) {
22249    if (isObject(name)) {
22250      var filters = {};
22251      forEach(name, function(filter, key) {
22252        filters[key] = register(key, filter);
22253      });
22254      return filters;
22255    } else {
22256      return $provide.factory(name + suffix, factory);
22257    }
22258  }
22259  this.register = register;
22260
22261  this.$get = ['$injector', function($injector) {
22262    return function(name) {
22263      return $injector.get(name + suffix);
22264    };
22265  }];
22266
22267  ////////////////////////////////////////
22268
22269  /* global
22270    currencyFilter: false,
22271    dateFilter: false,
22272    filterFilter: false,
22273    jsonFilter: false,
22274    limitToFilter: false,
22275    lowercaseFilter: false,
22276    numberFilter: false,
22277    orderByFilter: false,
22278    uppercaseFilter: false
22279  */
22280
22281  register('currency', currencyFilter);
22282  register('date', dateFilter);
22283  register('filter', filterFilter);
22284  register('json', jsonFilter);
22285  register('limitTo', limitToFilter);
22286  register('lowercase', lowercaseFilter);
22287  register('number', numberFilter);
22288  register('orderBy', orderByFilter);
22289  register('uppercase', uppercaseFilter);
22290}
22291
22292/**
22293 * @ngdoc filter
22294 * @name filter
22295 * @kind function
22296 *
22297 * @description
22298 * Selects a subset of items from `array` and returns it as a new array.
22299 *
22300 * @param {Array} array The source array.
22301 * <div class="alert alert-info">
22302 *   **Note**: If the array contains objects that reference themselves, filtering is not possible.
22303 * </div>
22304 * @param {string|Object|function()} expression The predicate to be used for selecting items from
22305 *   `array`.
22306 *
22307 *   Can be one of:
22308 *
22309 *   - `string`: The string is used for matching against the contents of the `array`. All strings or
22310 *     objects with string properties in `array` that match this string will be returned. This also
22311 *     applies to nested object properties.
22312 *     The predicate can be negated by prefixing the string with `!`.
22313 *
22314 *   - `Object`: A pattern object can be used to filter specific properties on objects contained
22315 *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
22316 *     which have property `name` containing "M" and property `phone` containing "1". A special
22317 *     property name (`$` by default) can be used (e.g. as in `{$: "text"}`) to accept a match
22318 *     against any property of the object or its nested object properties. That's equivalent to the
22319 *     simple substring match with a `string` as described above. The special property name can be
22320 *     overwritten, using the `anyPropertyKey` parameter.
22321 *     The predicate can be negated by prefixing the string with `!`.
22322 *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
22323 *     not containing "M".
22324 *
22325 *     Note that a named property will match properties on the same level only, while the special
22326 *     `$` property will match properties on the same level or deeper. E.g. an array item like
22327 *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
22328 *     **will** be matched by `{$: 'John'}`.
22329 *
22330 *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
22331 *     The function is called for each element of the array, with the element, its index, and
22332 *     the entire array itself as arguments.
22333 *
22334 *     The final result is an array of those elements that the predicate returned true for.
22335 *
22336 * @param {function(actual, expected)|true|false} [comparator] Comparator which is used in
22337 *     determining if values retrieved using `expression` (when it is not a function) should be
22338 *     considered a match based on the expected value (from the filter expression) and actual
22339 *     value (from the object in the array).
22340 *
22341 *   Can be one of:
22342 *
22343 *   - `function(actual, expected)`:
22344 *     The function will be given the object value and the predicate value to compare and
22345 *     should return true if both values should be considered equal.
22346 *
22347 *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
22348 *     This is essentially strict comparison of expected and actual.
22349 *
22350 *   - `false`: A short hand for a function which will look for a substring match in a case
22351 *     insensitive way. Primitive values are converted to strings. Objects are not compared against
22352 *     primitives, unless they have a custom `toString` method (e.g. `Date` objects).
22353 *
22354 *
22355 *   Defaults to `false`.
22356 *
22357 * @param {string} [anyPropertyKey] The special property name that matches against any property.
22358 *     By default `$`.
22359 *
22360 * @example
22361   <example name="filter-filter">
22362     <file name="index.html">
22363       <div ng-init="friends = [{name:'John', phone:'555-1276'},
22364                                {name:'Mary', phone:'800-BIG-MARY'},
22365                                {name:'Mike', phone:'555-4321'},
22366                                {name:'Adam', phone:'555-5678'},
22367                                {name:'Julie', phone:'555-8765'},
22368                                {name:'Juliette', phone:'555-5678'}]"></div>
22369
22370       <label>Search: <input ng-model="searchText"></label>
22371       <table id="searchTextResults">
22372         <tr><th>Name</th><th>Phone</th></tr>
22373         <tr ng-repeat="friend in friends | filter:searchText">
22374           <td>{{friend.name}}</td>
22375           <td>{{friend.phone}}</td>
22376         </tr>
22377       </table>
22378       <hr>
22379       <label>Any: <input ng-model="search.$"></label> <br>
22380       <label>Name only <input ng-model="search.name"></label><br>
22381       <label>Phone only <input ng-model="search.phone"></label><br>
22382       <label>Equality <input type="checkbox" ng-model="strict"></label><br>
22383       <table id="searchObjResults">
22384         <tr><th>Name</th><th>Phone</th></tr>
22385         <tr ng-repeat="friendObj in friends | filter:search:strict">
22386           <td>{{friendObj.name}}</td>
22387           <td>{{friendObj.phone}}</td>
22388         </tr>
22389       </table>
22390     </file>
22391     <file name="protractor.js" type="protractor">
22392       var expectFriendNames = function(expectedNames, key) {
22393         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
22394           arr.forEach(function(wd, i) {
22395             expect(wd.getText()).toMatch(expectedNames[i]);
22396           });
22397         });
22398       };
22399
22400       it('should search across all fields when filtering with a string', function() {
22401         var searchText = element(by.model('searchText'));
22402         searchText.clear();
22403         searchText.sendKeys('m');
22404         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
22405
22406         searchText.clear();
22407         searchText.sendKeys('76');
22408         expectFriendNames(['John', 'Julie'], 'friend');
22409       });
22410
22411       it('should search in specific fields when filtering with a predicate object', function() {
22412         var searchAny = element(by.model('search.$'));
22413         searchAny.clear();
22414         searchAny.sendKeys('i');
22415         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
22416       });
22417       it('should use a equal comparison when comparator is true', function() {
22418         var searchName = element(by.model('search.name'));
22419         var strict = element(by.model('strict'));
22420         searchName.clear();
22421         searchName.sendKeys('Julie');
22422         strict.click();
22423         expectFriendNames(['Julie'], 'friendObj');
22424       });
22425     </file>
22426   </example>
22427 */
22428
22429function filterFilter() {
22430  return function(array, expression, comparator, anyPropertyKey) {
22431    if (!isArrayLike(array)) {
22432      if (array == null) {
22433        return array;
22434      } else {
22435        throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
22436      }
22437    }
22438
22439    anyPropertyKey = anyPropertyKey || '$';
22440    var expressionType = getTypeForFilter(expression);
22441    var predicateFn;
22442    var matchAgainstAnyProp;
22443
22444    switch (expressionType) {
22445      case 'function':
22446        predicateFn = expression;
22447        break;
22448      case 'boolean':
22449      case 'null':
22450      case 'number':
22451      case 'string':
22452        matchAgainstAnyProp = true;
22453        // falls through
22454      case 'object':
22455        predicateFn = createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp);
22456        break;
22457      default:
22458        return array;
22459    }
22460
22461    return Array.prototype.filter.call(array, predicateFn);
22462  };
22463}
22464
22465// Helper functions for `filterFilter`
22466function createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp) {
22467  var shouldMatchPrimitives = isObject(expression) && (anyPropertyKey in expression);
22468  var predicateFn;
22469
22470  if (comparator === true) {
22471    comparator = equals;
22472  } else if (!isFunction(comparator)) {
22473    comparator = function(actual, expected) {
22474      if (isUndefined(actual)) {
22475        // No substring matching against `undefined`
22476        return false;
22477      }
22478      if ((actual === null) || (expected === null)) {
22479        // No substring matching against `null`; only match against `null`
22480        return actual === expected;
22481      }
22482      if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
22483        // Should not compare primitives against objects, unless they have custom `toString` method
22484        return false;
22485      }
22486
22487      actual = lowercase('' + actual);
22488      expected = lowercase('' + expected);
22489      return actual.indexOf(expected) !== -1;
22490    };
22491  }
22492
22493  predicateFn = function(item) {
22494    if (shouldMatchPrimitives && !isObject(item)) {
22495      return deepCompare(item, expression[anyPropertyKey], comparator, anyPropertyKey, false);
22496    }
22497    return deepCompare(item, expression, comparator, anyPropertyKey, matchAgainstAnyProp);
22498  };
22499
22500  return predicateFn;
22501}
22502
22503function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstAnyProp, dontMatchWholeObject) {
22504  var actualType = getTypeForFilter(actual);
22505  var expectedType = getTypeForFilter(expected);
22506
22507  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
22508    return !deepCompare(actual, expected.substring(1), comparator, anyPropertyKey, matchAgainstAnyProp);
22509  } else if (isArray(actual)) {
22510    // In case `actual` is an array, consider it a match
22511    // if ANY of it's items matches `expected`
22512    return actual.some(function(item) {
22513      return deepCompare(item, expected, comparator, anyPropertyKey, matchAgainstAnyProp);
22514    });
22515  }
22516
22517  switch (actualType) {
22518    case 'object':
22519      var key;
22520      if (matchAgainstAnyProp) {
22521        for (key in actual) {
22522          // Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined
22523          // See: https://github.com/angular/angular.js/issues/15644
22524          if (key.charAt && (key.charAt(0) !== '$') &&
22525              deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
22526            return true;
22527          }
22528        }
22529        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, anyPropertyKey, false);
22530      } else if (expectedType === 'object') {
22531        for (key in expected) {
22532          var expectedVal = expected[key];
22533          if (isFunction(expectedVal) || isUndefined(expectedVal)) {
22534            continue;
22535          }
22536
22537          var matchAnyProperty = key === anyPropertyKey;
22538          var actualVal = matchAnyProperty ? actual : actual[key];
22539          if (!deepCompare(actualVal, expectedVal, comparator, anyPropertyKey, matchAnyProperty, matchAnyProperty)) {
22540            return false;
22541          }
22542        }
22543        return true;
22544      } else {
22545        return comparator(actual, expected);
22546      }
22547    case 'function':
22548      return false;
22549    default:
22550      return comparator(actual, expected);
22551  }
22552}
22553
22554// Used for easily differentiating between `null` and actual `object`
22555function getTypeForFilter(val) {
22556  return (val === null) ? 'null' : typeof val;
22557}
22558
22559var MAX_DIGITS = 22;
22560var DECIMAL_SEP = '.';
22561var ZERO_CHAR = '0';
22562
22563/**
22564 * @ngdoc filter
22565 * @name currency
22566 * @kind function
22567 *
22568 * @description
22569 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
22570 * symbol for current locale is used.
22571 *
22572 * @param {number} amount Input to filter.
22573 * @param {string=} symbol Currency symbol or identifier to be displayed.
22574 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
22575 * @returns {string} Formatted number.
22576 *
22577 *
22578 * @example
22579   <example module="currencyExample" name="currency-filter">
22580     <file name="index.html">
22581       <script>
22582         angular.module('currencyExample', [])
22583           .controller('ExampleController', ['$scope', function($scope) {
22584             $scope.amount = 1234.56;
22585           }]);
22586       </script>
22587       <div ng-controller="ExampleController">
22588         <input type="number" ng-model="amount" aria-label="amount"> <br>
22589         default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
22590         custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span><br>
22591         no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
22592       </div>
22593     </file>
22594     <file name="protractor.js" type="protractor">
22595       it('should init with 1234.56', function() {
22596         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
22597         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
22598         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
22599       });
22600       it('should update', function() {
22601         if (browser.params.browser === 'safari') {
22602           // Safari does not understand the minus key. See
22603           // https://github.com/angular/protractor/issues/481
22604           return;
22605         }
22606         element(by.model('amount')).clear();
22607         element(by.model('amount')).sendKeys('-1234');
22608         expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
22609         expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
22610         expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
22611       });
22612     </file>
22613   </example>
22614 */
22615currencyFilter.$inject = ['$locale'];
22616function currencyFilter($locale) {
22617  var formats = $locale.NUMBER_FORMATS;
22618  return function(amount, currencySymbol, fractionSize) {
22619    if (isUndefined(currencySymbol)) {
22620      currencySymbol = formats.CURRENCY_SYM;
22621    }
22622
22623    if (isUndefined(fractionSize)) {
22624      fractionSize = formats.PATTERNS[1].maxFrac;
22625    }
22626
22627    // If the currency symbol is empty, trim whitespace around the symbol
22628    var currencySymbolRe = !currencySymbol ? /\s*\u00A4\s*/g : /\u00A4/g;
22629
22630    // if null or undefined pass it through
22631    return (amount == null)
22632        ? amount
22633        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
22634            replace(currencySymbolRe, currencySymbol);
22635  };
22636}
22637
22638/**
22639 * @ngdoc filter
22640 * @name number
22641 * @kind function
22642 *
22643 * @description
22644 * Formats a number as text.
22645 *
22646 * If the input is null or undefined, it will just be returned.
22647 * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
22648 * If the input is not a number an empty string is returned.
22649 *
22650 *
22651 * @param {number|string} number Number to format.
22652 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
22653 * If this is not provided then the fraction size is computed from the current locale's number
22654 * formatting pattern. In the case of the default locale, it will be 3.
22655 * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
22656 *                   locale (e.g., in the en_US locale it will have "." as the decimal separator and
22657 *                   include "," group separators after each third digit).
22658 *
22659 * @example
22660   <example module="numberFilterExample" name="number-filter">
22661     <file name="index.html">
22662       <script>
22663         angular.module('numberFilterExample', [])
22664           .controller('ExampleController', ['$scope', function($scope) {
22665             $scope.val = 1234.56789;
22666           }]);
22667       </script>
22668       <div ng-controller="ExampleController">
22669         <label>Enter number: <input ng-model='val'></label><br>
22670         Default formatting: <span id='number-default'>{{val | number}}</span><br>
22671         No fractions: <span>{{val | number:0}}</span><br>
22672         Negative number: <span>{{-val | number:4}}</span>
22673       </div>
22674     </file>
22675     <file name="protractor.js" type="protractor">
22676       it('should format numbers', function() {
22677         expect(element(by.id('number-default')).getText()).toBe('1,234.568');
22678         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
22679         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
22680       });
22681
22682       it('should update', function() {
22683         element(by.model('val')).clear();
22684         element(by.model('val')).sendKeys('3374.333');
22685         expect(element(by.id('number-default')).getText()).toBe('3,374.333');
22686         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
22687         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
22688      });
22689     </file>
22690   </example>
22691 */
22692numberFilter.$inject = ['$locale'];
22693function numberFilter($locale) {
22694  var formats = $locale.NUMBER_FORMATS;
22695  return function(number, fractionSize) {
22696
22697    // if null or undefined pass it through
22698    return (number == null)
22699        ? number
22700        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
22701                       fractionSize);
22702  };
22703}
22704
22705/**
22706 * Parse a number (as a string) into three components that can be used
22707 * for formatting the number.
22708 *
22709 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
22710 *
22711 * @param  {string} numStr The number to parse
22712 * @return {object} An object describing this number, containing the following keys:
22713 *  - d : an array of digits containing leading zeros as necessary
22714 *  - i : the number of the digits in `d` that are to the left of the decimal point
22715 *  - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
22716 *
22717 */
22718function parse(numStr) {
22719  var exponent = 0, digits, numberOfIntegerDigits;
22720  var i, j, zeros;
22721
22722  // Decimal point?
22723  if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
22724    numStr = numStr.replace(DECIMAL_SEP, '');
22725  }
22726
22727  // Exponential form?
22728  if ((i = numStr.search(/e/i)) > 0) {
22729    // Work out the exponent.
22730    if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
22731    numberOfIntegerDigits += +numStr.slice(i + 1);
22732    numStr = numStr.substring(0, i);
22733  } else if (numberOfIntegerDigits < 0) {
22734    // There was no decimal point or exponent so it is an integer.
22735    numberOfIntegerDigits = numStr.length;
22736  }
22737
22738  // Count the number of leading zeros.
22739  for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) { /* empty */ }
22740
22741  if (i === (zeros = numStr.length)) {
22742    // The digits are all zero.
22743    digits = [0];
22744    numberOfIntegerDigits = 1;
22745  } else {
22746    // Count the number of trailing zeros
22747    zeros--;
22748    while (numStr.charAt(zeros) === ZERO_CHAR) zeros--;
22749
22750    // Trailing zeros are insignificant so ignore them
22751    numberOfIntegerDigits -= i;
22752    digits = [];
22753    // Convert string to array of digits without leading/trailing zeros.
22754    for (j = 0; i <= zeros; i++, j++) {
22755      digits[j] = +numStr.charAt(i);
22756    }
22757  }
22758
22759  // If the number overflows the maximum allowed digits then use an exponent.
22760  if (numberOfIntegerDigits > MAX_DIGITS) {
22761    digits = digits.splice(0, MAX_DIGITS - 1);
22762    exponent = numberOfIntegerDigits - 1;
22763    numberOfIntegerDigits = 1;
22764  }
22765
22766  return { d: digits, e: exponent, i: numberOfIntegerDigits };
22767}
22768
22769/**
22770 * Round the parsed number to the specified number of decimal places
22771 * This function changed the parsedNumber in-place
22772 */
22773function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
22774    var digits = parsedNumber.d;
22775    var fractionLen = digits.length - parsedNumber.i;
22776
22777    // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
22778    fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
22779
22780    // The index of the digit to where rounding is to occur
22781    var roundAt = fractionSize + parsedNumber.i;
22782    var digit = digits[roundAt];
22783
22784    if (roundAt > 0) {
22785      // Drop fractional digits beyond `roundAt`
22786      digits.splice(Math.max(parsedNumber.i, roundAt));
22787
22788      // Set non-fractional digits beyond `roundAt` to 0
22789      for (var j = roundAt; j < digits.length; j++) {
22790        digits[j] = 0;
22791      }
22792    } else {
22793      // We rounded to zero so reset the parsedNumber
22794      fractionLen = Math.max(0, fractionLen);
22795      parsedNumber.i = 1;
22796      digits.length = Math.max(1, roundAt = fractionSize + 1);
22797      digits[0] = 0;
22798      for (var i = 1; i < roundAt; i++) digits[i] = 0;
22799    }
22800
22801    if (digit >= 5) {
22802      if (roundAt - 1 < 0) {
22803        for (var k = 0; k > roundAt; k--) {
22804          digits.unshift(0);
22805          parsedNumber.i++;
22806        }
22807        digits.unshift(1);
22808        parsedNumber.i++;
22809      } else {
22810        digits[roundAt - 1]++;
22811      }
22812    }
22813
22814    // Pad out with zeros to get the required fraction length
22815    for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
22816
22817
22818    // Do any carrying, e.g. a digit was rounded up to 10
22819    var carry = digits.reduceRight(function(carry, d, i, digits) {
22820      d = d + carry;
22821      digits[i] = d % 10;
22822      return Math.floor(d / 10);
22823    }, 0);
22824    if (carry) {
22825      digits.unshift(carry);
22826      parsedNumber.i++;
22827    }
22828}
22829
22830/**
22831 * Format a number into a string
22832 * @param  {number} number       The number to format
22833 * @param  {{
22834 *           minFrac, // the minimum number of digits required in the fraction part of the number
22835 *           maxFrac, // the maximum number of digits required in the fraction part of the number
22836 *           gSize,   // number of digits in each group of separated digits
22837 *           lgSize,  // number of digits in the last group of digits before the decimal separator
22838 *           negPre,  // the string to go in front of a negative number (e.g. `-` or `(`))
22839 *           posPre,  // the string to go in front of a positive number
22840 *           negSuf,  // the string to go after a negative number (e.g. `)`)
22841 *           posSuf   // the string to go after a positive number
22842 *         }} pattern
22843 * @param  {string} groupSep     The string to separate groups of number (e.g. `,`)
22844 * @param  {string} decimalSep   The string to act as the decimal separator (e.g. `.`)
22845 * @param  {[type]} fractionSize The size of the fractional part of the number
22846 * @return {string}              The number formatted as a string
22847 */
22848function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
22849
22850  if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
22851
22852  var isInfinity = !isFinite(number);
22853  var isZero = false;
22854  var numStr = Math.abs(number) + '',
22855      formattedText = '',
22856      parsedNumber;
22857
22858  if (isInfinity) {
22859    formattedText = '\u221e';
22860  } else {
22861    parsedNumber = parse(numStr);
22862
22863    roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
22864
22865    var digits = parsedNumber.d;
22866    var integerLen = parsedNumber.i;
22867    var exponent = parsedNumber.e;
22868    var decimals = [];
22869    isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
22870
22871    // pad zeros for small numbers
22872    while (integerLen < 0) {
22873      digits.unshift(0);
22874      integerLen++;
22875    }
22876
22877    // extract decimals digits
22878    if (integerLen > 0) {
22879      decimals = digits.splice(integerLen, digits.length);
22880    } else {
22881      decimals = digits;
22882      digits = [0];
22883    }
22884
22885    // format the integer digits with grouping separators
22886    var groups = [];
22887    if (digits.length >= pattern.lgSize) {
22888      groups.unshift(digits.splice(-pattern.lgSize, digits.length).join(''));
22889    }
22890    while (digits.length > pattern.gSize) {
22891      groups.unshift(digits.splice(-pattern.gSize, digits.length).join(''));
22892    }
22893    if (digits.length) {
22894      groups.unshift(digits.join(''));
22895    }
22896    formattedText = groups.join(groupSep);
22897
22898    // append the decimal digits
22899    if (decimals.length) {
22900      formattedText += decimalSep + decimals.join('');
22901    }
22902
22903    if (exponent) {
22904      formattedText += 'e+' + exponent;
22905    }
22906  }
22907  if (number < 0 && !isZero) {
22908    return pattern.negPre + formattedText + pattern.negSuf;
22909  } else {
22910    return pattern.posPre + formattedText + pattern.posSuf;
22911  }
22912}
22913
22914function padNumber(num, digits, trim, negWrap) {
22915  var neg = '';
22916  if (num < 0 || (negWrap && num <= 0)) {
22917    if (negWrap) {
22918      num = -num + 1;
22919    } else {
22920      num = -num;
22921      neg = '-';
22922    }
22923  }
22924  num = '' + num;
22925  while (num.length < digits) num = ZERO_CHAR + num;
22926  if (trim) {
22927    num = num.substr(num.length - digits);
22928  }
22929  return neg + num;
22930}
22931
22932
22933function dateGetter(name, size, offset, trim, negWrap) {
22934  offset = offset || 0;
22935  return function(date) {
22936    var value = date['get' + name]();
22937    if (offset > 0 || value > -offset) {
22938      value += offset;
22939    }
22940    if (value === 0 && offset === -12) value = 12;
22941    return padNumber(value, size, trim, negWrap);
22942  };
22943}
22944
22945function dateStrGetter(name, shortForm, standAlone) {
22946  return function(date, formats) {
22947    var value = date['get' + name]();
22948    var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
22949    var get = uppercase(propPrefix + name);
22950
22951    return formats[get][value];
22952  };
22953}
22954
22955function timeZoneGetter(date, formats, offset) {
22956  var zone = -1 * offset;
22957  var paddedZone = (zone >= 0) ? '+' : '';
22958
22959  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
22960                padNumber(Math.abs(zone % 60), 2);
22961
22962  return paddedZone;
22963}
22964
22965function getFirstThursdayOfYear(year) {
22966    // 0 = index of January
22967    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
22968    // 4 = index of Thursday (+1 to account for 1st = 5)
22969    // 11 = index of *next* Thursday (+1 account for 1st = 12)
22970    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
22971}
22972
22973function getThursdayThisWeek(datetime) {
22974    return new Date(datetime.getFullYear(), datetime.getMonth(),
22975      // 4 = index of Thursday
22976      datetime.getDate() + (4 - datetime.getDay()));
22977}
22978
22979function weekGetter(size) {
22980   return function(date) {
22981      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
22982         thisThurs = getThursdayThisWeek(date);
22983
22984      var diff = +thisThurs - +firstThurs,
22985         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
22986
22987      return padNumber(result, size);
22988   };
22989}
22990
22991function ampmGetter(date, formats) {
22992  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
22993}
22994
22995function eraGetter(date, formats) {
22996  return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
22997}
22998
22999function longEraGetter(date, formats) {
23000  return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
23001}
23002
23003var DATE_FORMATS = {
23004  yyyy: dateGetter('FullYear', 4, 0, false, true),
23005    yy: dateGetter('FullYear', 2, 0, true, true),
23006     y: dateGetter('FullYear', 1, 0, false, true),
23007  MMMM: dateStrGetter('Month'),
23008   MMM: dateStrGetter('Month', true),
23009    MM: dateGetter('Month', 2, 1),
23010     M: dateGetter('Month', 1, 1),
23011  LLLL: dateStrGetter('Month', false, true),
23012    dd: dateGetter('Date', 2),
23013     d: dateGetter('Date', 1),
23014    HH: dateGetter('Hours', 2),
23015     H: dateGetter('Hours', 1),
23016    hh: dateGetter('Hours', 2, -12),
23017     h: dateGetter('Hours', 1, -12),
23018    mm: dateGetter('Minutes', 2),
23019     m: dateGetter('Minutes', 1),
23020    ss: dateGetter('Seconds', 2),
23021     s: dateGetter('Seconds', 1),
23022     // while ISO 8601 requires fractions to be prefixed with `.` or `,`
23023     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
23024   sss: dateGetter('Milliseconds', 3),
23025  EEEE: dateStrGetter('Day'),
23026   EEE: dateStrGetter('Day', true),
23027     a: ampmGetter,
23028     Z: timeZoneGetter,
23029    ww: weekGetter(2),
23030     w: weekGetter(1),
23031     G: eraGetter,
23032     GG: eraGetter,
23033     GGG: eraGetter,
23034     GGGG: longEraGetter
23035};
23036
23037var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))([\s\S]*)/,
23038    NUMBER_STRING = /^-?\d+$/;
23039
23040/**
23041 * @ngdoc filter
23042 * @name date
23043 * @kind function
23044 *
23045 * @description
23046 *   Formats `date` to a string based on the requested `format`.
23047 *
23048 *   `format` string can be composed of the following elements:
23049 *
23050 *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
23051 *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
23052 *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
23053 *   * `'MMMM'`: Month in year (January-December)
23054 *   * `'MMM'`: Month in year (Jan-Dec)
23055 *   * `'MM'`: Month in year, padded (01-12)
23056 *   * `'M'`: Month in year (1-12)
23057 *   * `'LLLL'`: Stand-alone month in year (January-December)
23058 *   * `'dd'`: Day in month, padded (01-31)
23059 *   * `'d'`: Day in month (1-31)
23060 *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
23061 *   * `'EEE'`: Day in Week, (Sun-Sat)
23062 *   * `'HH'`: Hour in day, padded (00-23)
23063 *   * `'H'`: Hour in day (0-23)
23064 *   * `'hh'`: Hour in AM/PM, padded (01-12)
23065 *   * `'h'`: Hour in AM/PM, (1-12)
23066 *   * `'mm'`: Minute in hour, padded (00-59)
23067 *   * `'m'`: Minute in hour (0-59)
23068 *   * `'ss'`: Second in minute, padded (00-59)
23069 *   * `'s'`: Second in minute (0-59)
23070 *   * `'sss'`: Millisecond in second, padded (000-999)
23071 *   * `'a'`: AM/PM marker
23072 *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
23073 *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
23074 *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
23075 *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
23076 *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
23077 *
23078 *   `format` string can also be one of the following predefined
23079 *   {@link guide/i18n localizable formats}:
23080 *
23081 *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
23082 *     (e.g. Sep 3, 2010 12:05:08 PM)
23083 *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
23084 *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
23085 *     (e.g. Friday, September 3, 2010)
23086 *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
23087 *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
23088 *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
23089 *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
23090 *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
23091 *
23092 *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
23093 *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
23094 *   (e.g. `"h 'o''clock'"`).
23095 *
23096 *   Any other characters in the `format` string will be output as-is.
23097 *
23098 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
23099 *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
23100 *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
23101 *    specified in the string input, the time is considered to be in the local timezone.
23102 * @param {string=} format Formatting rules (see Description). If not specified,
23103 *    `mediumDate` is used.
23104 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
23105 *    continental US time zone abbreviations, but for general use, use a time zone offset, for
23106 *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
23107 *    If not specified, the timezone of the browser will be used.
23108 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
23109 *
23110 * @example
23111   <example name="filter-date">
23112     <file name="index.html">
23113       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
23114           <span>{{1288323623006 | date:'medium'}}</span><br>
23115       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
23116          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
23117       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
23118          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
23119       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
23120          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
23121     </file>
23122     <file name="protractor.js" type="protractor">
23123       it('should format date', function() {
23124         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
23125            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
23126         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
23127            toMatch(/2010-10-2\d \d{2}:\d{2}:\d{2} (-|\+)?\d{4}/);
23128         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
23129            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
23130         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
23131            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
23132       });
23133     </file>
23134   </example>
23135 */
23136dateFilter.$inject = ['$locale'];
23137function dateFilter($locale) {
23138
23139
23140  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
23141                     // 1        2       3         4          5          6          7          8  9     10      11
23142  function jsonStringToDate(string) {
23143    var match;
23144    if ((match = string.match(R_ISO8601_STR))) {
23145      var date = new Date(0),
23146          tzHour = 0,
23147          tzMin  = 0,
23148          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
23149          timeSetter = match[8] ? date.setUTCHours : date.setHours;
23150
23151      if (match[9]) {
23152        tzHour = toInt(match[9] + match[10]);
23153        tzMin = toInt(match[9] + match[11]);
23154      }
23155      dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
23156      var h = toInt(match[4] || 0) - tzHour;
23157      var m = toInt(match[5] || 0) - tzMin;
23158      var s = toInt(match[6] || 0);
23159      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
23160      timeSetter.call(date, h, m, s, ms);
23161      return date;
23162    }
23163    return string;
23164  }
23165
23166
23167  return function(date, format, timezone) {
23168    var text = '',
23169        parts = [],
23170        fn, match;
23171
23172    format = format || 'mediumDate';
23173    format = $locale.DATETIME_FORMATS[format] || format;
23174    if (isString(date)) {
23175      date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
23176    }
23177
23178    if (isNumber(date)) {
23179      date = new Date(date);
23180    }
23181
23182    if (!isDate(date) || !isFinite(date.getTime())) {
23183      return date;
23184    }
23185
23186    while (format) {
23187      match = DATE_FORMATS_SPLIT.exec(format);
23188      if (match) {
23189        parts = concat(parts, match, 1);
23190        format = parts.pop();
23191      } else {
23192        parts.push(format);
23193        format = null;
23194      }
23195    }
23196
23197    var dateTimezoneOffset = date.getTimezoneOffset();
23198    if (timezone) {
23199      dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
23200      date = convertTimezoneToLocal(date, timezone, true);
23201    }
23202    forEach(parts, function(value) {
23203      fn = DATE_FORMATS[value];
23204      text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
23205                 : value === '\'\'' ? '\'' : value.replace(/(^'|'$)/g, '').replace(/''/g, '\'');
23206    });
23207
23208    return text;
23209  };
23210}
23211
23212
23213/**
23214 * @ngdoc filter
23215 * @name json
23216 * @kind function
23217 *
23218 * @description
23219 *   Allows you to convert a JavaScript object into JSON string.
23220 *
23221 *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
23222 *   the binding is automatically converted to JSON.
23223 *
23224 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
23225 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
23226 * @returns {string} JSON string.
23227 *
23228 *
23229 * @example
23230   <example name="filter-json">
23231     <file name="index.html">
23232       <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
23233       <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
23234     </file>
23235     <file name="protractor.js" type="protractor">
23236       it('should jsonify filtered objects', function() {
23237         expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n {2}"name": ?"value"\n}/);
23238         expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n {4}"name": ?"value"\n}/);
23239       });
23240     </file>
23241   </example>
23242 *
23243 */
23244function jsonFilter() {
23245  return function(object, spacing) {
23246    if (isUndefined(spacing)) {
23247        spacing = 2;
23248    }
23249    return toJson(object, spacing);
23250  };
23251}
23252
23253
23254/**
23255 * @ngdoc filter
23256 * @name lowercase
23257 * @kind function
23258 * @description
23259 * Converts string to lowercase.
23260 *
23261 * See the {@link ng.uppercase uppercase filter documentation} for a functionally identical example.
23262 *
23263 * @see angular.lowercase
23264 */
23265var lowercaseFilter = valueFn(lowercase);
23266
23267
23268/**
23269 * @ngdoc filter
23270 * @name uppercase
23271 * @kind function
23272 * @description
23273 * Converts string to uppercase.
23274 * @example
23275   <example module="uppercaseFilterExample" name="filter-uppercase">
23276     <file name="index.html">
23277       <script>
23278         angular.module('uppercaseFilterExample', [])
23279           .controller('ExampleController', ['$scope', function($scope) {
23280             $scope.title = 'This is a title';
23281           }]);
23282       </script>
23283       <div ng-controller="ExampleController">
23284         <!-- This title should be formatted normally -->
23285         <h1>{{title}}</h1>
23286         <!-- This title should be capitalized -->
23287         <h1>{{title | uppercase}}</h1>
23288       </div>
23289     </file>
23290   </example>
23291 */
23292var uppercaseFilter = valueFn(uppercase);
23293
23294/**
23295 * @ngdoc filter
23296 * @name limitTo
23297 * @kind function
23298 *
23299 * @description
23300 * Creates a new array or string containing only a specified number of elements. The elements are
23301 * taken from either the beginning or the end of the source array, string or number, as specified by
23302 * the value and sign (positive or negative) of `limit`. Other array-like objects are also supported
23303 * (e.g. array subclasses, NodeLists, jqLite/jQuery collections etc). If a number is used as input,
23304 * it is converted to a string.
23305 *
23306 * @param {Array|ArrayLike|string|number} input - Array/array-like, string or number to be limited.
23307 * @param {string|number} limit - The length of the returned array or string. If the `limit` number
23308 *     is positive, `limit` number of items from the beginning of the source array/string are copied.
23309 *     If the number is negative, `limit` number  of items from the end of the source array/string
23310 *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
23311 *     the input will be returned unchanged.
23312 * @param {(string|number)=} begin - Index at which to begin limitation. As a negative index,
23313 *     `begin` indicates an offset from the end of `input`. Defaults to `0`.
23314 * @returns {Array|string} A new sub-array or substring of length `limit` or less if the input had
23315 *     less than `limit` elements.
23316 *
23317 * @example
23318   <example module="limitToExample" name="limit-to-filter">
23319     <file name="index.html">
23320       <script>
23321         angular.module('limitToExample', [])
23322           .controller('ExampleController', ['$scope', function($scope) {
23323             $scope.numbers = [1,2,3,4,5,6,7,8,9];
23324             $scope.letters = "abcdefghi";
23325             $scope.longNumber = 2345432342;
23326             $scope.numLimit = 3;
23327             $scope.letterLimit = 3;
23328             $scope.longNumberLimit = 3;
23329           }]);
23330       </script>
23331       <div ng-controller="ExampleController">
23332         <label>
23333            Limit {{numbers}} to:
23334            <input type="number" step="1" ng-model="numLimit">
23335         </label>
23336         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
23337         <label>
23338            Limit {{letters}} to:
23339            <input type="number" step="1" ng-model="letterLimit">
23340         </label>
23341         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
23342         <label>
23343            Limit {{longNumber}} to:
23344            <input type="number" step="1" ng-model="longNumberLimit">
23345         </label>
23346         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
23347       </div>
23348     </file>
23349     <file name="protractor.js" type="protractor">
23350       var numLimitInput = element(by.model('numLimit'));
23351       var letterLimitInput = element(by.model('letterLimit'));
23352       var longNumberLimitInput = element(by.model('longNumberLimit'));
23353       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
23354       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
23355       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
23356
23357       it('should limit the number array to first three items', function() {
23358         expect(numLimitInput.getAttribute('value')).toBe('3');
23359         expect(letterLimitInput.getAttribute('value')).toBe('3');
23360         expect(longNumberLimitInput.getAttribute('value')).toBe('3');
23361         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
23362         expect(limitedLetters.getText()).toEqual('Output letters: abc');
23363         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
23364       });
23365
23366       // There is a bug in safari and protractor that doesn't like the minus key
23367       // it('should update the output when -3 is entered', function() {
23368       //   numLimitInput.clear();
23369       //   numLimitInput.sendKeys('-3');
23370       //   letterLimitInput.clear();
23371       //   letterLimitInput.sendKeys('-3');
23372       //   longNumberLimitInput.clear();
23373       //   longNumberLimitInput.sendKeys('-3');
23374       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
23375       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
23376       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
23377       // });
23378
23379       it('should not exceed the maximum size of input array', function() {
23380         numLimitInput.clear();
23381         numLimitInput.sendKeys('100');
23382         letterLimitInput.clear();
23383         letterLimitInput.sendKeys('100');
23384         longNumberLimitInput.clear();
23385         longNumberLimitInput.sendKeys('100');
23386         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
23387         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
23388         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
23389       });
23390     </file>
23391   </example>
23392*/
23393function limitToFilter() {
23394  return function(input, limit, begin) {
23395    if (Math.abs(Number(limit)) === Infinity) {
23396      limit = Number(limit);
23397    } else {
23398      limit = toInt(limit);
23399    }
23400    if (isNumberNaN(limit)) return input;
23401
23402    if (isNumber(input)) input = input.toString();
23403    if (!isArrayLike(input)) return input;
23404
23405    begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
23406    begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
23407
23408    if (limit >= 0) {
23409      return sliceFn(input, begin, begin + limit);
23410    } else {
23411      if (begin === 0) {
23412        return sliceFn(input, limit, input.length);
23413      } else {
23414        return sliceFn(input, Math.max(0, begin + limit), begin);
23415      }
23416    }
23417  };
23418}
23419
23420function sliceFn(input, begin, end) {
23421  if (isString(input)) return input.slice(begin, end);
23422
23423  return slice.call(input, begin, end);
23424}
23425
23426/**
23427 * @ngdoc filter
23428 * @name orderBy
23429 * @kind function
23430 *
23431 * @description
23432 * Returns an array containing the items from the specified `collection`, ordered by a `comparator`
23433 * function based on the values computed using the `expression` predicate.
23434 *
23435 * For example, `[{id: 'foo'}, {id: 'bar'}] | orderBy:'id'` would result in
23436 * `[{id: 'bar'}, {id: 'foo'}]`.
23437 *
23438 * The `collection` can be an Array or array-like object (e.g. NodeList, jQuery object, TypedArray,
23439 * String, etc).
23440 *
23441 * The `expression` can be a single predicate, or a list of predicates each serving as a tie-breaker
23442 * for the preceding one. The `expression` is evaluated against each item and the output is used
23443 * for comparing with other items.
23444 *
23445 * You can change the sorting order by setting `reverse` to `true`. By default, items are sorted in
23446 * ascending order.
23447 *
23448 * The comparison is done using the `comparator` function. If none is specified, a default, built-in
23449 * comparator is used (see below for details - in a nutshell, it compares numbers numerically and
23450 * strings alphabetically).
23451 *
23452 * ### Under the hood
23453 *
23454 * Ordering the specified `collection` happens in two phases:
23455 *
23456 * 1. All items are passed through the predicate (or predicates), and the returned values are saved
23457 *    along with their type (`string`, `number` etc). For example, an item `{label: 'foo'}`, passed
23458 *    through a predicate that extracts the value of the `label` property, would be transformed to:
23459 *    ```
23460 *    {
23461 *      value: 'foo',
23462 *      type: 'string',
23463 *      index: ...
23464 *    }
23465 *    ```
23466 *    **Note:** `null` values use `'null'` as their type.
23467 * 2. The comparator function is used to sort the items, based on the derived values, types and
23468 *    indices.
23469 *
23470 * If you use a custom comparator, it will be called with pairs of objects of the form
23471 * `{value: ..., type: '...', index: ...}` and is expected to return `0` if the objects are equal
23472 * (as far as the comparator is concerned), `-1` if the 1st one should be ranked higher than the
23473 * second, or `1` otherwise.
23474 *
23475 * In order to ensure that the sorting will be deterministic across platforms, if none of the
23476 * specified predicates can distinguish between two items, `orderBy` will automatically introduce a
23477 * dummy predicate that returns the item's index as `value`.
23478 * (If you are using a custom comparator, make sure it can handle this predicate as well.)
23479 *
23480 * If a custom comparator still can't distinguish between two items, then they will be sorted based
23481 * on their index using the built-in comparator.
23482 *
23483 * Finally, in an attempt to simplify things, if a predicate returns an object as the extracted
23484 * value for an item, `orderBy` will try to convert that object to a primitive value, before passing
23485 * it to the comparator. The following rules govern the conversion:
23486 *
23487 * 1. If the object has a `valueOf()` method that returns a primitive, its return value will be
23488 *    used instead.<br />
23489 *    (If the object has a `valueOf()` method that returns another object, then the returned object
23490 *    will be used in subsequent steps.)
23491 * 2. If the object has a custom `toString()` method (i.e. not the one inherited from `Object`) that
23492 *    returns a primitive, its return value will be used instead.<br />
23493 *    (If the object has a `toString()` method that returns another object, then the returned object
23494 *    will be used in subsequent steps.)
23495 * 3. No conversion; the object itself is used.
23496 *
23497 * ### The default comparator
23498 *
23499 * The default, built-in comparator should be sufficient for most usecases. In short, it compares
23500 * numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to
23501 * using their index in the original collection, sorts values of different types by type and puts
23502 * `undefined` and `null` values at the end of the sorted list.
23503 *
23504 * More specifically, it follows these steps to determine the relative order of items:
23505 *
23506 * 1. If the compared values are of different types:
23507 *    - If one of the values is undefined, consider it "greater than" the other.
23508 *    - Else if one of the values is null, consider it "greater than" the other.
23509 *    - Else compare the types themselves alphabetically.
23510 * 2. If both values are of type `string`, compare them alphabetically in a case- and
23511 *    locale-insensitive way.
23512 * 3. If both values are objects, compare their indices instead.
23513 * 4. Otherwise, return:
23514 *    -  `0`, if the values are equal (by strict equality comparison, i.e. using `===`).
23515 *    - `-1`, if the 1st value is "less than" the 2nd value (compared using the `<` operator).
23516 *    -  `1`, otherwise.
23517 *
23518 * **Note:** If you notice numbers not being sorted as expected, make sure they are actually being
23519 *           saved as numbers and not strings.
23520 * **Note:** For the purpose of sorting, `null` and `undefined` are considered "greater than"
23521 *           any other value (with undefined "greater than" null). This effectively means that `null`
23522 *           and `undefined` values end up at the end of a list sorted in ascending order.
23523 * **Note:** `null` values use `'null'` as their type to be able to distinguish them from objects.
23524 *
23525 * @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort.
23526 * @param {(Function|string|Array.<Function|string>)=} expression - A predicate (or list of
23527 *    predicates) to be used by the comparator to determine the order of elements.
23528 *
23529 *    Can be one of:
23530 *
23531 *    - `Function`: A getter function. This function will be called with each item as argument and
23532 *      the return value will be used for sorting.
23533 *    - `string`: An AngularJS expression. This expression will be evaluated against each item and the
23534 *      result will be used for sorting. For example, use `'label'` to sort by a property called
23535 *      `label` or `'label.substring(0, 3)'` to sort by the first 3 characters of the `label`
23536 *      property.<br />
23537 *      (The result of a constant expression is interpreted as a property name to be used for
23538 *      comparison. For example, use `'"special name"'` (note the extra pair of quotes) to sort by a
23539 *      property called `special name`.)<br />
23540 *      An expression can be optionally prefixed with `+` or `-` to control the sorting direction,
23541 *      ascending or descending. For example, `'+label'` or `'-label'`. If no property is provided,
23542 *      (e.g. `'+'` or `'-'`), the collection element itself is used in comparisons.
23543 *    - `Array`: An array of function and/or string predicates. If a predicate cannot determine the
23544 *      relative order of two items, the next predicate is used as a tie-breaker.
23545 *
23546 * **Note:** If the predicate is missing or empty then it defaults to `'+'`.
23547 *
23548 * @param {boolean=} reverse - If `true`, reverse the sorting order.
23549 * @param {(Function)=} comparator - The comparator function used to determine the relative order of
23550 *    value pairs. If omitted, the built-in comparator will be used.
23551 *
23552 * @returns {Array} - The sorted array.
23553 *
23554 *
23555 * @example
23556 * ### Ordering a table with `ngRepeat`
23557 *
23558 * The example below demonstrates a simple {@link ngRepeat ngRepeat}, where the data is sorted by
23559 * age in descending order (expression is set to `'-age'`). The `comparator` is not set, which means
23560 * it defaults to the built-in comparator.
23561 *
23562   <example name="orderBy-static" module="orderByExample1">
23563     <file name="index.html">
23564       <div ng-controller="ExampleController">
23565         <table class="friends">
23566           <tr>
23567             <th>Name</th>
23568             <th>Phone Number</th>
23569             <th>Age</th>
23570           </tr>
23571           <tr ng-repeat="friend in friends | orderBy:'-age'">
23572             <td>{{friend.name}}</td>
23573             <td>{{friend.phone}}</td>
23574             <td>{{friend.age}}</td>
23575           </tr>
23576         </table>
23577       </div>
23578     </file>
23579     <file name="script.js">
23580       angular.module('orderByExample1', [])
23581         .controller('ExampleController', ['$scope', function($scope) {
23582           $scope.friends = [
23583             {name: 'John',   phone: '555-1212',  age: 10},
23584             {name: 'Mary',   phone: '555-9876',  age: 19},
23585             {name: 'Mike',   phone: '555-4321',  age: 21},
23586             {name: 'Adam',   phone: '555-5678',  age: 35},
23587             {name: 'Julie',  phone: '555-8765',  age: 29}
23588           ];
23589         }]);
23590     </file>
23591     <file name="style.css">
23592       .friends {
23593         border-collapse: collapse;
23594       }
23595
23596       .friends th {
23597         border-bottom: 1px solid;
23598       }
23599       .friends td, .friends th {
23600         border-left: 1px solid;
23601         padding: 5px 10px;
23602       }
23603       .friends td:first-child, .friends th:first-child {
23604         border-left: none;
23605       }
23606     </file>
23607     <file name="protractor.js" type="protractor">
23608       // Element locators
23609       var names = element.all(by.repeater('friends').column('friend.name'));
23610
23611       it('should sort friends by age in reverse order', function() {
23612         expect(names.get(0).getText()).toBe('Adam');
23613         expect(names.get(1).getText()).toBe('Julie');
23614         expect(names.get(2).getText()).toBe('Mike');
23615         expect(names.get(3).getText()).toBe('Mary');
23616         expect(names.get(4).getText()).toBe('John');
23617       });
23618     </file>
23619   </example>
23620 * <hr />
23621 *
23622 * @example
23623 * ### Changing parameters dynamically
23624 *
23625 * All parameters can be changed dynamically. The next example shows how you can make the columns of
23626 * a table sortable, by binding the `expression` and `reverse` parameters to scope properties.
23627 *
23628   <example name="orderBy-dynamic" module="orderByExample2">
23629     <file name="index.html">
23630       <div ng-controller="ExampleController">
23631         <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
23632         <hr/>
23633         <button ng-click="propertyName = null; reverse = false">Set to unsorted</button>
23634         <hr/>
23635         <table class="friends">
23636           <tr>
23637             <th>
23638               <button ng-click="sortBy('name')">Name</button>
23639               <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
23640             </th>
23641             <th>
23642               <button ng-click="sortBy('phone')">Phone Number</button>
23643               <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
23644             </th>
23645             <th>
23646               <button ng-click="sortBy('age')">Age</button>
23647               <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
23648             </th>
23649           </tr>
23650           <tr ng-repeat="friend in friends | orderBy:propertyName:reverse">
23651             <td>{{friend.name}}</td>
23652             <td>{{friend.phone}}</td>
23653             <td>{{friend.age}}</td>
23654           </tr>
23655         </table>
23656       </div>
23657     </file>
23658     <file name="script.js">
23659       angular.module('orderByExample2', [])
23660         .controller('ExampleController', ['$scope', function($scope) {
23661           var friends = [
23662             {name: 'John',   phone: '555-1212',  age: 10},
23663             {name: 'Mary',   phone: '555-9876',  age: 19},
23664             {name: 'Mike',   phone: '555-4321',  age: 21},
23665             {name: 'Adam',   phone: '555-5678',  age: 35},
23666             {name: 'Julie',  phone: '555-8765',  age: 29}
23667           ];
23668
23669           $scope.propertyName = 'age';
23670           $scope.reverse = true;
23671           $scope.friends = friends;
23672
23673           $scope.sortBy = function(propertyName) {
23674             $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;
23675             $scope.propertyName = propertyName;
23676           };
23677         }]);
23678     </file>
23679     <file name="style.css">
23680       .friends {
23681         border-collapse: collapse;
23682       }
23683
23684       .friends th {
23685         border-bottom: 1px solid;
23686       }
23687       .friends td, .friends th {
23688         border-left: 1px solid;
23689         padding: 5px 10px;
23690       }
23691       .friends td:first-child, .friends th:first-child {
23692         border-left: none;
23693       }
23694
23695       .sortorder:after {
23696         content: '\25b2';   // BLACK UP-POINTING TRIANGLE
23697       }
23698       .sortorder.reverse:after {
23699         content: '\25bc';   // BLACK DOWN-POINTING TRIANGLE
23700       }
23701     </file>
23702     <file name="protractor.js" type="protractor">
23703       // Element locators
23704       var unsortButton = element(by.partialButtonText('unsorted'));
23705       var nameHeader = element(by.partialButtonText('Name'));
23706       var phoneHeader = element(by.partialButtonText('Phone'));
23707       var ageHeader = element(by.partialButtonText('Age'));
23708       var firstName = element(by.repeater('friends').column('friend.name').row(0));
23709       var lastName = element(by.repeater('friends').column('friend.name').row(4));
23710
23711       it('should sort friends by some property, when clicking on the column header', function() {
23712         expect(firstName.getText()).toBe('Adam');
23713         expect(lastName.getText()).toBe('John');
23714
23715         phoneHeader.click();
23716         expect(firstName.getText()).toBe('John');
23717         expect(lastName.getText()).toBe('Mary');
23718
23719         nameHeader.click();
23720         expect(firstName.getText()).toBe('Adam');
23721         expect(lastName.getText()).toBe('Mike');
23722
23723         ageHeader.click();
23724         expect(firstName.getText()).toBe('John');
23725         expect(lastName.getText()).toBe('Adam');
23726       });
23727
23728       it('should sort friends in reverse order, when clicking on the same column', function() {
23729         expect(firstName.getText()).toBe('Adam');
23730         expect(lastName.getText()).toBe('John');
23731
23732         ageHeader.click();
23733         expect(firstName.getText()).toBe('John');
23734         expect(lastName.getText()).toBe('Adam');
23735
23736         ageHeader.click();
23737         expect(firstName.getText()).toBe('Adam');
23738         expect(lastName.getText()).toBe('John');
23739       });
23740
23741       it('should restore the original order, when clicking "Set to unsorted"', function() {
23742         expect(firstName.getText()).toBe('Adam');
23743         expect(lastName.getText()).toBe('John');
23744
23745         unsortButton.click();
23746         expect(firstName.getText()).toBe('John');
23747         expect(lastName.getText()).toBe('Julie');
23748       });
23749     </file>
23750   </example>
23751 * <hr />
23752 *
23753 * @example
23754 * ### Using `orderBy` inside a controller
23755 *
23756 * It is also possible to call the `orderBy` filter manually, by injecting `orderByFilter`, and
23757 * calling it with the desired parameters. (Alternatively, you could inject the `$filter` factory
23758 * and retrieve the `orderBy` filter with `$filter('orderBy')`.)
23759 *
23760   <example name="orderBy-call-manually" module="orderByExample3">
23761     <file name="index.html">
23762       <div ng-controller="ExampleController">
23763         <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
23764         <hr/>
23765         <button ng-click="sortBy(null)">Set to unsorted</button>
23766         <hr/>
23767         <table class="friends">
23768           <tr>
23769             <th>
23770               <button ng-click="sortBy('name')">Name</button>
23771               <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
23772             </th>
23773             <th>
23774               <button ng-click="sortBy('phone')">Phone Number</button>
23775               <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
23776             </th>
23777             <th>
23778               <button ng-click="sortBy('age')">Age</button>
23779               <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
23780             </th>
23781           </tr>
23782           <tr ng-repeat="friend in friends">
23783             <td>{{friend.name}}</td>
23784             <td>{{friend.phone}}</td>
23785             <td>{{friend.age}}</td>
23786           </tr>
23787         </table>
23788       </div>
23789     </file>
23790     <file name="script.js">
23791       angular.module('orderByExample3', [])
23792         .controller('ExampleController', ['$scope', 'orderByFilter', function($scope, orderBy) {
23793           var friends = [
23794             {name: 'John',   phone: '555-1212',  age: 10},
23795             {name: 'Mary',   phone: '555-9876',  age: 19},
23796             {name: 'Mike',   phone: '555-4321',  age: 21},
23797             {name: 'Adam',   phone: '555-5678',  age: 35},
23798             {name: 'Julie',  phone: '555-8765',  age: 29}
23799           ];
23800
23801           $scope.propertyName = 'age';
23802           $scope.reverse = true;
23803           $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
23804
23805           $scope.sortBy = function(propertyName) {
23806             $scope.reverse = (propertyName !== null && $scope.propertyName === propertyName)
23807                 ? !$scope.reverse : false;
23808             $scope.propertyName = propertyName;
23809             $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
23810           };
23811         }]);
23812     </file>
23813     <file name="style.css">
23814       .friends {
23815         border-collapse: collapse;
23816       }
23817
23818       .friends th {
23819         border-bottom: 1px solid;
23820       }
23821       .friends td, .friends th {
23822         border-left: 1px solid;
23823         padding: 5px 10px;
23824       }
23825       .friends td:first-child, .friends th:first-child {
23826         border-left: none;
23827       }
23828
23829       .sortorder:after {
23830         content: '\25b2';   // BLACK UP-POINTING TRIANGLE
23831       }
23832       .sortorder.reverse:after {
23833         content: '\25bc';   // BLACK DOWN-POINTING TRIANGLE
23834       }
23835     </file>
23836     <file name="protractor.js" type="protractor">
23837       // Element locators
23838       var unsortButton = element(by.partialButtonText('unsorted'));
23839       var nameHeader = element(by.partialButtonText('Name'));
23840       var phoneHeader = element(by.partialButtonText('Phone'));
23841       var ageHeader = element(by.partialButtonText('Age'));
23842       var firstName = element(by.repeater('friends').column('friend.name').row(0));
23843       var lastName = element(by.repeater('friends').column('friend.name').row(4));
23844
23845       it('should sort friends by some property, when clicking on the column header', function() {
23846         expect(firstName.getText()).toBe('Adam');
23847         expect(lastName.getText()).toBe('John');
23848
23849         phoneHeader.click();
23850         expect(firstName.getText()).toBe('John');
23851         expect(lastName.getText()).toBe('Mary');
23852
23853         nameHeader.click();
23854         expect(firstName.getText()).toBe('Adam');
23855         expect(lastName.getText()).toBe('Mike');
23856
23857         ageHeader.click();
23858         expect(firstName.getText()).toBe('John');
23859         expect(lastName.getText()).toBe('Adam');
23860       });
23861
23862       it('should sort friends in reverse order, when clicking on the same column', function() {
23863         expect(firstName.getText()).toBe('Adam');
23864         expect(lastName.getText()).toBe('John');
23865
23866         ageHeader.click();
23867         expect(firstName.getText()).toBe('John');
23868         expect(lastName.getText()).toBe('Adam');
23869
23870         ageHeader.click();
23871         expect(firstName.getText()).toBe('Adam');
23872         expect(lastName.getText()).toBe('John');
23873       });
23874
23875       it('should restore the original order, when clicking "Set to unsorted"', function() {
23876         expect(firstName.getText()).toBe('Adam');
23877         expect(lastName.getText()).toBe('John');
23878
23879         unsortButton.click();
23880         expect(firstName.getText()).toBe('John');
23881         expect(lastName.getText()).toBe('Julie');
23882       });
23883     </file>
23884   </example>
23885 * <hr />
23886 *
23887 * @example
23888 * ### Using a custom comparator
23889 *
23890 * If you have very specific requirements about the way items are sorted, you can pass your own
23891 * comparator function. For example, you might need to compare some strings in a locale-sensitive
23892 * way. (When specifying a custom comparator, you also need to pass a value for the `reverse`
23893 * argument - passing `false` retains the default sorting order, i.e. ascending.)
23894 *
23895   <example name="orderBy-custom-comparator" module="orderByExample4">
23896     <file name="index.html">
23897       <div ng-controller="ExampleController">
23898         <div class="friends-container custom-comparator">
23899           <h3>Locale-sensitive Comparator</h3>
23900           <table class="friends">
23901             <tr>
23902               <th>Name</th>
23903               <th>Favorite Letter</th>
23904             </tr>
23905             <tr ng-repeat="friend in friends | orderBy:'favoriteLetter':false:localeSensitiveComparator">
23906               <td>{{friend.name}}</td>
23907               <td>{{friend.favoriteLetter}}</td>
23908             </tr>
23909           </table>
23910         </div>
23911         <div class="friends-container default-comparator">
23912           <h3>Default Comparator</h3>
23913           <table class="friends">
23914             <tr>
23915               <th>Name</th>
23916               <th>Favorite Letter</th>
23917             </tr>
23918             <tr ng-repeat="friend in friends | orderBy:'favoriteLetter'">
23919               <td>{{friend.name}}</td>
23920               <td>{{friend.favoriteLetter}}</td>
23921             </tr>
23922           </table>
23923         </div>
23924       </div>
23925     </file>
23926     <file name="script.js">
23927       angular.module('orderByExample4', [])
23928         .controller('ExampleController', ['$scope', function($scope) {
23929           $scope.friends = [
23930             {name: 'John',   favoriteLetter: 'Ä'},
23931             {name: 'Mary',   favoriteLetter: 'Ü'},
23932             {name: 'Mike',   favoriteLetter: 'Ö'},
23933             {name: 'Adam',   favoriteLetter: 'H'},
23934             {name: 'Julie',  favoriteLetter: 'Z'}
23935           ];
23936
23937           $scope.localeSensitiveComparator = function(v1, v2) {
23938             // If we don't get strings, just compare by index
23939             if (v1.type !== 'string' || v2.type !== 'string') {
23940               return (v1.index < v2.index) ? -1 : 1;
23941             }
23942
23943             // Compare strings alphabetically, taking locale into account
23944             return v1.value.localeCompare(v2.value);
23945           };
23946         }]);
23947     </file>
23948     <file name="style.css">
23949       .friends-container {
23950         display: inline-block;
23951         margin: 0 30px;
23952       }
23953
23954       .friends {
23955         border-collapse: collapse;
23956       }
23957
23958       .friends th {
23959         border-bottom: 1px solid;
23960       }
23961       .friends td, .friends th {
23962         border-left: 1px solid;
23963         padding: 5px 10px;
23964       }
23965       .friends td:first-child, .friends th:first-child {
23966         border-left: none;
23967       }
23968     </file>
23969     <file name="protractor.js" type="protractor">
23970       // Element locators
23971       var container = element(by.css('.custom-comparator'));
23972       var names = container.all(by.repeater('friends').column('friend.name'));
23973
23974       it('should sort friends by favorite letter (in correct alphabetical order)', function() {
23975         expect(names.get(0).getText()).toBe('John');
23976         expect(names.get(1).getText()).toBe('Adam');
23977         expect(names.get(2).getText()).toBe('Mike');
23978         expect(names.get(3).getText()).toBe('Mary');
23979         expect(names.get(4).getText()).toBe('Julie');
23980       });
23981     </file>
23982   </example>
23983 *
23984 */
23985orderByFilter.$inject = ['$parse'];
23986function orderByFilter($parse) {
23987  return function(array, sortPredicate, reverseOrder, compareFn) {
23988
23989    if (array == null) return array;
23990    if (!isArrayLike(array)) {
23991      throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
23992    }
23993
23994    if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
23995    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
23996
23997    var predicates = processPredicates(sortPredicate);
23998
23999    var descending = reverseOrder ? -1 : 1;
24000
24001    // Define the `compare()` function. Use a default comparator if none is specified.
24002    var compare = isFunction(compareFn) ? compareFn : defaultCompare;
24003
24004    // The next three lines are a version of a Swartzian Transform idiom from Perl
24005    // (sometimes called the Decorate-Sort-Undecorate idiom)
24006    // See https://en.wikipedia.org/wiki/Schwartzian_transform
24007    var compareValues = Array.prototype.map.call(array, getComparisonObject);
24008    compareValues.sort(doComparison);
24009    array = compareValues.map(function(item) { return item.value; });
24010
24011    return array;
24012
24013    function getComparisonObject(value, index) {
24014      // NOTE: We are adding an extra `tieBreaker` value based on the element's index.
24015      // This will be used to keep the sort stable when none of the input predicates can
24016      // distinguish between two elements.
24017      return {
24018        value: value,
24019        tieBreaker: {value: index, type: 'number', index: index},
24020        predicateValues: predicates.map(function(predicate) {
24021          return getPredicateValue(predicate.get(value), index);
24022        })
24023      };
24024    }
24025
24026    function doComparison(v1, v2) {
24027      for (var i = 0, ii = predicates.length; i < ii; i++) {
24028        var result = compare(v1.predicateValues[i], v2.predicateValues[i]);
24029        if (result) {
24030          return result * predicates[i].descending * descending;
24031        }
24032      }
24033
24034      return (compare(v1.tieBreaker, v2.tieBreaker) || defaultCompare(v1.tieBreaker, v2.tieBreaker)) * descending;
24035    }
24036  };
24037
24038  function processPredicates(sortPredicates) {
24039    return sortPredicates.map(function(predicate) {
24040      var descending = 1, get = identity;
24041
24042      if (isFunction(predicate)) {
24043        get = predicate;
24044      } else if (isString(predicate)) {
24045        if ((predicate.charAt(0) === '+' || predicate.charAt(0) === '-')) {
24046          descending = predicate.charAt(0) === '-' ? -1 : 1;
24047          predicate = predicate.substring(1);
24048        }
24049        if (predicate !== '') {
24050          get = $parse(predicate);
24051          if (get.constant) {
24052            var key = get();
24053            get = function(value) { return value[key]; };
24054          }
24055        }
24056      }
24057      return {get: get, descending: descending};
24058    });
24059  }
24060
24061  function isPrimitive(value) {
24062    switch (typeof value) {
24063      case 'number': /* falls through */
24064      case 'boolean': /* falls through */
24065      case 'string':
24066        return true;
24067      default:
24068        return false;
24069    }
24070  }
24071
24072  function objectValue(value) {
24073    // If `valueOf` is a valid function use that
24074    if (isFunction(value.valueOf)) {
24075      value = value.valueOf();
24076      if (isPrimitive(value)) return value;
24077    }
24078    // If `toString` is a valid function and not the one from `Object.prototype` use that
24079    if (hasCustomToString(value)) {
24080      value = value.toString();
24081      if (isPrimitive(value)) return value;
24082    }
24083
24084    return value;
24085  }
24086
24087  function getPredicateValue(value, index) {
24088    var type = typeof value;
24089    if (value === null) {
24090      type = 'null';
24091    } else if (type === 'object') {
24092      value = objectValue(value);
24093    }
24094    return {value: value, type: type, index: index};
24095  }
24096
24097  function defaultCompare(v1, v2) {
24098    var result = 0;
24099    var type1 = v1.type;
24100    var type2 = v2.type;
24101
24102    if (type1 === type2) {
24103      var value1 = v1.value;
24104      var value2 = v2.value;
24105
24106      if (type1 === 'string') {
24107        // Compare strings case-insensitively
24108        value1 = value1.toLowerCase();
24109        value2 = value2.toLowerCase();
24110      } else if (type1 === 'object') {
24111        // For basic objects, use the position of the object
24112        // in the collection instead of the value
24113        if (isObject(value1)) value1 = v1.index;
24114        if (isObject(value2)) value2 = v2.index;
24115      }
24116
24117      if (value1 !== value2) {
24118        result = value1 < value2 ? -1 : 1;
24119      }
24120    } else {
24121      result = (type1 === 'undefined') ? 1 :
24122        (type2 === 'undefined') ? -1 :
24123        (type1 === 'null') ? 1 :
24124        (type2 === 'null') ? -1 :
24125        (type1 < type2) ? -1 : 1;
24126    }
24127
24128    return result;
24129  }
24130}
24131
24132function ngDirective(directive) {
24133  if (isFunction(directive)) {
24134    directive = {
24135      link: directive
24136    };
24137  }
24138  directive.restrict = directive.restrict || 'AC';
24139  return valueFn(directive);
24140}
24141
24142/**
24143 * @ngdoc directive
24144 * @name a
24145 * @restrict E
24146 *
24147 * @description
24148 * Modifies the default behavior of the html a tag so that the default action is prevented when
24149 * the href attribute is empty.
24150 *
24151 * For dynamically creating `href` attributes for a tags, see the {@link ng.ngHref `ngHref`} directive.
24152 */
24153var htmlAnchorDirective = valueFn({
24154  restrict: 'E',
24155  compile: function(element, attr) {
24156    if (!attr.href && !attr.xlinkHref) {
24157      return function(scope, element) {
24158        // If the linked element is not an anchor tag anymore, do nothing
24159        if (element[0].nodeName.toLowerCase() !== 'a') return;
24160
24161        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
24162        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
24163                   'xlink:href' : 'href';
24164        element.on('click', function(event) {
24165          // if we have no href url, then don't navigate anywhere.
24166          if (!element.attr(href)) {
24167            event.preventDefault();
24168          }
24169        });
24170      };
24171    }
24172  }
24173});
24174
24175/**
24176 * @ngdoc directive
24177 * @name ngHref
24178 * @restrict A
24179 * @priority 99
24180 *
24181 * @description
24182 * Using AngularJS markup like `{{hash}}` in an href attribute will
24183 * make the link go to the wrong URL if the user clicks it before
24184 * AngularJS has a chance to replace the `{{hash}}` markup with its
24185 * value. Until AngularJS replaces the markup the link will be broken
24186 * and will most likely return a 404 error. The `ngHref` directive
24187 * solves this problem.
24188 *
24189 * The wrong way to write it:
24190 * ```html
24191 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
24192 * ```
24193 *
24194 * The correct way to write it:
24195 * ```html
24196 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
24197 * ```
24198 *
24199 * @element A
24200 * @param {template} ngHref any string which can contain `{{}}` markup.
24201 *
24202 * @example
24203 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
24204 * in links and their different behaviors:
24205    <example name="ng-href">
24206      <file name="index.html">
24207        <input ng-model="value" /><br />
24208        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
24209        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
24210        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
24211        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
24212        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
24213        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
24214      </file>
24215      <file name="protractor.js" type="protractor">
24216        it('should execute ng-click but not reload when href without value', function() {
24217          element(by.id('link-1')).click();
24218          expect(element(by.model('value')).getAttribute('value')).toEqual('1');
24219          expect(element(by.id('link-1')).getAttribute('href')).toBe('');
24220        });
24221
24222        it('should execute ng-click but not reload when href empty string', function() {
24223          element(by.id('link-2')).click();
24224          expect(element(by.model('value')).getAttribute('value')).toEqual('2');
24225          expect(element(by.id('link-2')).getAttribute('href')).toBe('');
24226        });
24227
24228        it('should execute ng-click and change url when ng-href specified', function() {
24229          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
24230
24231          element(by.id('link-3')).click();
24232
24233          // At this point, we navigate away from an AngularJS page, so we need
24234          // to use browser.driver to get the base webdriver.
24235
24236          browser.wait(function() {
24237            return browser.driver.getCurrentUrl().then(function(url) {
24238              return url.match(/\/123$/);
24239            });
24240          }, 5000, 'page should navigate to /123');
24241        });
24242
24243        it('should execute ng-click but not reload when href empty string and name specified', function() {
24244          element(by.id('link-4')).click();
24245          expect(element(by.model('value')).getAttribute('value')).toEqual('4');
24246          expect(element(by.id('link-4')).getAttribute('href')).toBe('');
24247        });
24248
24249        it('should execute ng-click but not reload when no href but name specified', function() {
24250          element(by.id('link-5')).click();
24251          expect(element(by.model('value')).getAttribute('value')).toEqual('5');
24252          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
24253        });
24254
24255        it('should only change url when only ng-href', function() {
24256          element(by.model('value')).clear();
24257          element(by.model('value')).sendKeys('6');
24258          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
24259
24260          element(by.id('link-6')).click();
24261
24262          // At this point, we navigate away from an AngularJS page, so we need
24263          // to use browser.driver to get the base webdriver.
24264          browser.wait(function() {
24265            return browser.driver.getCurrentUrl().then(function(url) {
24266              return url.match(/\/6$/);
24267            });
24268          }, 5000, 'page should navigate to /6');
24269        });
24270      </file>
24271    </example>
24272 */
24273
24274/**
24275 * @ngdoc directive
24276 * @name ngSrc
24277 * @restrict A
24278 * @priority 99
24279 *
24280 * @description
24281 * Using AngularJS markup like `{{hash}}` in a `src` attribute doesn't
24282 * work right: The browser will fetch from the URL with the literal
24283 * text `{{hash}}` until AngularJS replaces the expression inside
24284 * `{{hash}}`. The `ngSrc` directive solves this problem.
24285 *
24286 * The buggy way to write it:
24287 * ```html
24288 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
24289 * ```
24290 *
24291 * The correct way to write it:
24292 * ```html
24293 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
24294 * ```
24295 *
24296 * @element IMG
24297 * @param {template} ngSrc any string which can contain `{{}}` markup.
24298 */
24299
24300/**
24301 * @ngdoc directive
24302 * @name ngSrcset
24303 * @restrict A
24304 * @priority 99
24305 *
24306 * @description
24307 * Using AngularJS markup like `{{hash}}` in a `srcset` attribute doesn't
24308 * work right: The browser will fetch from the URL with the literal
24309 * text `{{hash}}` until AngularJS replaces the expression inside
24310 * `{{hash}}`. The `ngSrcset` directive solves this problem.
24311 *
24312 * The buggy way to write it:
24313 * ```html
24314 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
24315 * ```
24316 *
24317 * The correct way to write it:
24318 * ```html
24319 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
24320 * ```
24321 *
24322 * @element IMG
24323 * @param {template} ngSrcset any string which can contain `{{}}` markup.
24324 */
24325
24326/**
24327 * @ngdoc directive
24328 * @name ngDisabled
24329 * @restrict A
24330 * @priority 100
24331 *
24332 * @description
24333 *
24334 * This directive sets the `disabled` attribute on the element (typically a form control,
24335 * e.g. `input`, `button`, `select` etc.) if the
24336 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
24337 *
24338 * A special directive is necessary because we cannot use interpolation inside the `disabled`
24339 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
24340 *
24341 * @example
24342    <example name="ng-disabled">
24343      <file name="index.html">
24344        <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
24345        <button ng-model="button" ng-disabled="checked">Button</button>
24346      </file>
24347      <file name="protractor.js" type="protractor">
24348        it('should toggle button', function() {
24349          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
24350          element(by.model('checked')).click();
24351          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
24352        });
24353      </file>
24354    </example>
24355 *
24356 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
24357 *     then the `disabled` attribute will be set on the element
24358 */
24359
24360
24361/**
24362 * @ngdoc directive
24363 * @name ngChecked
24364 * @restrict A
24365 * @priority 100
24366 *
24367 * @description
24368 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
24369 *
24370 * Note that this directive should not be used together with {@link ngModel `ngModel`},
24371 * as this can lead to unexpected behavior.
24372 *
24373 * A special directive is necessary because we cannot use interpolation inside the `checked`
24374 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
24375 *
24376 * @example
24377    <example name="ng-checked">
24378      <file name="index.html">
24379        <label>Check me to check both: <input type="checkbox" ng-model="leader"></label><br/>
24380        <input id="checkFollower" type="checkbox" ng-checked="leader" aria-label="Follower input">
24381      </file>
24382      <file name="protractor.js" type="protractor">
24383        it('should check both checkBoxes', function() {
24384          expect(element(by.id('checkFollower')).getAttribute('checked')).toBeFalsy();
24385          element(by.model('leader')).click();
24386          expect(element(by.id('checkFollower')).getAttribute('checked')).toBeTruthy();
24387        });
24388      </file>
24389    </example>
24390 *
24391 * @element INPUT
24392 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
24393 *     then the `checked` attribute will be set on the element
24394 */
24395
24396
24397/**
24398 * @ngdoc directive
24399 * @name ngReadonly
24400 * @restrict A
24401 * @priority 100
24402 *
24403 * @description
24404 *
24405 * Sets the `readonly` attribute on the element, if the expression inside `ngReadonly` is truthy.
24406 * Note that `readonly` applies only to `input` elements with specific types. [See the input docs on
24407 * MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) for more information.
24408 *
24409 * A special directive is necessary because we cannot use interpolation inside the `readonly`
24410 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
24411 *
24412 * @example
24413    <example name="ng-readonly">
24414      <file name="index.html">
24415        <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
24416        <input type="text" ng-readonly="checked" value="I'm AngularJS" aria-label="Readonly field" />
24417      </file>
24418      <file name="protractor.js" type="protractor">
24419        it('should toggle readonly attr', function() {
24420          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
24421          element(by.model('checked')).click();
24422          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
24423        });
24424      </file>
24425    </example>
24426 *
24427 * @element INPUT
24428 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
24429 *     then special attribute "readonly" will be set on the element
24430 */
24431
24432
24433/**
24434 * @ngdoc directive
24435 * @name ngSelected
24436 * @restrict A
24437 * @priority 100
24438 *
24439 * @description
24440 *
24441 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
24442 *
24443 * A special directive is necessary because we cannot use interpolation inside the `selected`
24444 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
24445 *
24446 * <div class="alert alert-warning">
24447 *   **Note:** `ngSelected` does not interact with the `select` and `ngModel` directives, it only
24448 *   sets the `selected` attribute on the element. If you are using `ngModel` on the select, you
24449 *   should not use `ngSelected` on the options, as `ngModel` will set the select value and
24450 *   selected options.
24451 * </div>
24452 *
24453 * @example
24454    <example name="ng-selected">
24455      <file name="index.html">
24456        <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
24457        <select aria-label="ngSelected demo">
24458          <option>Hello!</option>
24459          <option id="greet" ng-selected="selected">Greetings!</option>
24460        </select>
24461      </file>
24462      <file name="protractor.js" type="protractor">
24463        it('should select Greetings!', function() {
24464          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
24465          element(by.model('selected')).click();
24466          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
24467        });
24468      </file>
24469    </example>
24470 *
24471 * @element OPTION
24472 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
24473 *     then special attribute "selected" will be set on the element
24474 */
24475
24476/**
24477 * @ngdoc directive
24478 * @name ngOpen
24479 * @restrict A
24480 * @priority 100
24481 *
24482 * @description
24483 *
24484 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
24485 *
24486 * A special directive is necessary because we cannot use interpolation inside the `open`
24487 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
24488 *
24489 * ## A note about browser compatibility
24490 *
24491 * Internet Explorer and Edge do not support the `details` element, it is
24492 * recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead.
24493 *
24494 * @example
24495     <example name="ng-open">
24496       <file name="index.html">
24497         <label>Toggle details: <input type="checkbox" ng-model="open"></label><br/>
24498         <details id="details" ng-open="open">
24499            <summary>List</summary>
24500            <ul>
24501              <li>Apple</li>
24502              <li>Orange</li>
24503              <li>Durian</li>
24504            </ul>
24505         </details>
24506       </file>
24507       <file name="protractor.js" type="protractor">
24508         it('should toggle open', function() {
24509           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
24510           element(by.model('open')).click();
24511           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
24512         });
24513       </file>
24514     </example>
24515 *
24516 * @element DETAILS
24517 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
24518 *     then special attribute "open" will be set on the element
24519 */
24520
24521var ngAttributeAliasDirectives = {};
24522
24523// boolean attrs are evaluated
24524forEach(BOOLEAN_ATTR, function(propName, attrName) {
24525  // binding to multiple is not supported
24526  if (propName === 'multiple') return;
24527
24528  function defaultLinkFn(scope, element, attr) {
24529    scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
24530      attr.$set(attrName, !!value);
24531    });
24532  }
24533
24534  var normalized = directiveNormalize('ng-' + attrName);
24535  var linkFn = defaultLinkFn;
24536
24537  if (propName === 'checked') {
24538    linkFn = function(scope, element, attr) {
24539      // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
24540      if (attr.ngModel !== attr[normalized]) {
24541        defaultLinkFn(scope, element, attr);
24542      }
24543    };
24544  }
24545
24546  ngAttributeAliasDirectives[normalized] = function() {
24547    return {
24548      restrict: 'A',
24549      priority: 100,
24550      link: linkFn
24551    };
24552  };
24553});
24554
24555// aliased input attrs are evaluated
24556forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
24557  ngAttributeAliasDirectives[ngAttr] = function() {
24558    return {
24559      priority: 100,
24560      link: function(scope, element, attr) {
24561        //special case ngPattern when a literal regular expression value
24562        //is used as the expression (this way we don't have to watch anything).
24563        if (ngAttr === 'ngPattern' && attr.ngPattern.charAt(0) === '/') {
24564          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
24565          if (match) {
24566            attr.$set('ngPattern', new RegExp(match[1], match[2]));
24567            return;
24568          }
24569        }
24570
24571        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
24572          attr.$set(ngAttr, value);
24573        });
24574      }
24575    };
24576  };
24577});
24578
24579// ng-src, ng-srcset, ng-href are interpolated
24580forEach(['src', 'srcset', 'href'], function(attrName) {
24581  var normalized = directiveNormalize('ng-' + attrName);
24582  ngAttributeAliasDirectives[normalized] = ['$sce', function($sce) {
24583    return {
24584      priority: 99, // it needs to run after the attributes are interpolated
24585      link: function(scope, element, attr) {
24586        var propName = attrName,
24587            name = attrName;
24588
24589        if (attrName === 'href' &&
24590            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
24591          name = 'xlinkHref';
24592          attr.$attr[name] = 'xlink:href';
24593          propName = null;
24594        }
24595
24596        // We need to sanitize the url at least once, in case it is a constant
24597        // non-interpolated attribute.
24598        attr.$set(normalized, $sce.getTrustedMediaUrl(attr[normalized]));
24599
24600        attr.$observe(normalized, function(value) {
24601          if (!value) {
24602            if (attrName === 'href') {
24603              attr.$set(name, null);
24604            }
24605            return;
24606          }
24607
24608          attr.$set(name, value);
24609
24610          // Support: IE 9-11 only
24611          // On IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
24612          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
24613          // to set the property as well to achieve the desired effect.
24614          // We use attr[attrName] value since $set might have sanitized the url.
24615          if (msie && propName) element.prop(propName, attr[name]);
24616        });
24617      }
24618    };
24619  }];
24620});
24621
24622/* global -nullFormCtrl, -PENDING_CLASS, -SUBMITTED_CLASS
24623 */
24624var nullFormCtrl = {
24625  $addControl: noop,
24626  $getControls: valueFn([]),
24627  $$renameControl: nullFormRenameControl,
24628  $removeControl: noop,
24629  $setValidity: noop,
24630  $setDirty: noop,
24631  $setPristine: noop,
24632  $setSubmitted: noop,
24633  $$setSubmitted: noop
24634},
24635PENDING_CLASS = 'ng-pending',
24636SUBMITTED_CLASS = 'ng-submitted';
24637
24638function nullFormRenameControl(control, name) {
24639  control.$name = name;
24640}
24641
24642/**
24643 * @ngdoc type
24644 * @name form.FormController
24645 *
24646 * @property {boolean} $pristine True if user has not interacted with the form yet.
24647 * @property {boolean} $dirty True if user has already interacted with the form.
24648 * @property {boolean} $valid True if all of the containing forms and controls are valid.
24649 * @property {boolean} $invalid True if at least one containing control or form is invalid.
24650 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
24651 *
24652 * @property {Object} $pending An object hash, containing references to controls or forms with
24653 *  pending validators, where:
24654 *
24655 *  - keys are validations tokens (error names).
24656 *  - values are arrays of controls or forms that have a pending validator for the given error name.
24657 *
24658 * See {@link form.FormController#$error $error} for a list of built-in validation tokens.
24659 *
24660 * @property {Object} $error An object hash, containing references to controls or forms with failing
24661 *  validators, where:
24662 *
24663 *  - keys are validation tokens (error names),
24664 *  - values are arrays of controls or forms that have a failing validator for the given error name.
24665 *
24666 *  Built-in validation tokens:
24667 *  - `email`
24668 *  - `max`
24669 *  - `maxlength`
24670 *  - `min`
24671 *  - `minlength`
24672 *  - `number`
24673 *  - `pattern`
24674 *  - `required`
24675 *  - `url`
24676 *  - `date`
24677 *  - `datetimelocal`
24678 *  - `time`
24679 *  - `week`
24680 *  - `month`
24681 *
24682 * @description
24683 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
24684 * such as being valid/invalid or dirty/pristine.
24685 *
24686 * Each {@link ng.directive:form form} directive creates an instance
24687 * of `FormController`.
24688 *
24689 */
24690//asks for $scope to fool the BC controller module
24691FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
24692function FormController($element, $attrs, $scope, $animate, $interpolate) {
24693  this.$$controls = [];
24694
24695  // init state
24696  this.$error = {};
24697  this.$$success = {};
24698  this.$pending = undefined;
24699  this.$name = $interpolate($attrs.name || $attrs.ngForm || '')($scope);
24700  this.$dirty = false;
24701  this.$pristine = true;
24702  this.$valid = true;
24703  this.$invalid = false;
24704  this.$submitted = false;
24705  this.$$parentForm = nullFormCtrl;
24706
24707  this.$$element = $element;
24708  this.$$animate = $animate;
24709
24710  setupValidity(this);
24711}
24712
24713FormController.prototype = {
24714  /**
24715   * @ngdoc method
24716   * @name form.FormController#$rollbackViewValue
24717   *
24718   * @description
24719   * Rollback all form controls pending updates to the `$modelValue`.
24720   *
24721   * Updates may be pending by a debounced event or because the input is waiting for a some future
24722   * event defined in `ng-model-options`. This method is typically needed by the reset button of
24723   * a form that uses `ng-model-options` to pend updates.
24724   */
24725  $rollbackViewValue: function() {
24726    forEach(this.$$controls, function(control) {
24727      control.$rollbackViewValue();
24728    });
24729  },
24730
24731  /**
24732   * @ngdoc method
24733   * @name form.FormController#$commitViewValue
24734   *
24735   * @description
24736   * Commit all form controls pending updates to the `$modelValue`.
24737   *
24738   * Updates may be pending by a debounced event or because the input is waiting for a some future
24739   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
24740   * usually handles calling this in response to input events.
24741   */
24742  $commitViewValue: function() {
24743    forEach(this.$$controls, function(control) {
24744      control.$commitViewValue();
24745    });
24746  },
24747
24748  /**
24749   * @ngdoc method
24750   * @name form.FormController#$addControl
24751   * @param {object} control control object, either a {@link form.FormController} or an
24752   * {@link ngModel.NgModelController}
24753   *
24754   * @description
24755   * Register a control with the form. Input elements using ngModelController do this automatically
24756   * when they are linked.
24757   *
24758   * Note that the current state of the control will not be reflected on the new parent form. This
24759   * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
24760   * state.
24761   *
24762   * However, if the method is used programmatically, for example by adding dynamically created controls,
24763   * or controls that have been previously removed without destroying their corresponding DOM element,
24764   * it's the developers responsibility to make sure the current state propagates to the parent form.
24765   *
24766   * For example, if an input control is added that is already `$dirty` and has `$error` properties,
24767   * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
24768   */
24769  $addControl: function(control) {
24770    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
24771    // and not added to the scope.  Now we throw an error.
24772    assertNotHasOwnProperty(control.$name, 'input');
24773    this.$$controls.push(control);
24774
24775    if (control.$name) {
24776      this[control.$name] = control;
24777    }
24778
24779    control.$$parentForm = this;
24780  },
24781
24782  /**
24783   * @ngdoc method
24784   * @name form.FormController#$getControls
24785   * @returns {Array} the controls that are currently part of this form
24786   *
24787   * @description
24788   * This method returns a **shallow copy** of the controls that are currently part of this form.
24789   * The controls can be instances of {@link form.FormController `FormController`}
24790   * ({@link ngForm "child-forms"}) and of {@link ngModel.NgModelController `NgModelController`}.
24791   * If you need access to the controls of child-forms, you have to call `$getControls()`
24792   * recursively on them.
24793   * This can be used for example to iterate over all controls to validate them.
24794   *
24795   * The controls can be accessed normally, but adding to, or removing controls from the array has
24796   * no effect on the form. Instead, use {@link form.FormController#$addControl `$addControl()`} and
24797   * {@link form.FormController#$removeControl `$removeControl()`} for this use-case.
24798   * Likewise, adding a control to, or removing a control from the form is not reflected
24799   * in the shallow copy. That means you should get a fresh copy from `$getControls()` every time
24800   * you need access to the controls.
24801   */
24802  $getControls: function() {
24803    return shallowCopy(this.$$controls);
24804  },
24805
24806  // Private API: rename a form control
24807  $$renameControl: function(control, newName) {
24808    var oldName = control.$name;
24809
24810    if (this[oldName] === control) {
24811      delete this[oldName];
24812    }
24813    this[newName] = control;
24814    control.$name = newName;
24815  },
24816
24817  /**
24818   * @ngdoc method
24819   * @name form.FormController#$removeControl
24820   * @param {object} control control object, either a {@link form.FormController} or an
24821   * {@link ngModel.NgModelController}
24822   *
24823   * @description
24824   * Deregister a control from the form.
24825   *
24826   * Input elements using ngModelController do this automatically when they are destroyed.
24827   *
24828   * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
24829   * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
24830   * different from case to case. For example, removing the only `$dirty` control from a form may or
24831   * may not mean that the form is still `$dirty`.
24832   */
24833  $removeControl: function(control) {
24834    if (control.$name && this[control.$name] === control) {
24835      delete this[control.$name];
24836    }
24837    forEach(this.$pending, function(value, name) {
24838      // eslint-disable-next-line no-invalid-this
24839      this.$setValidity(name, null, control);
24840    }, this);
24841    forEach(this.$error, function(value, name) {
24842      // eslint-disable-next-line no-invalid-this
24843      this.$setValidity(name, null, control);
24844    }, this);
24845    forEach(this.$$success, function(value, name) {
24846      // eslint-disable-next-line no-invalid-this
24847      this.$setValidity(name, null, control);
24848    }, this);
24849
24850    arrayRemove(this.$$controls, control);
24851    control.$$parentForm = nullFormCtrl;
24852  },
24853
24854  /**
24855   * @ngdoc method
24856   * @name form.FormController#$setDirty
24857   *
24858   * @description
24859   * Sets the form to a dirty state.
24860   *
24861   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
24862   * state (ng-dirty class). This method will also propagate to parent forms.
24863   */
24864  $setDirty: function() {
24865    this.$$animate.removeClass(this.$$element, PRISTINE_CLASS);
24866    this.$$animate.addClass(this.$$element, DIRTY_CLASS);
24867    this.$dirty = true;
24868    this.$pristine = false;
24869    this.$$parentForm.$setDirty();
24870  },
24871
24872  /**
24873   * @ngdoc method
24874   * @name form.FormController#$setPristine
24875   *
24876   * @description
24877   * Sets the form to its pristine state.
24878   *
24879   * This method sets the form's `$pristine` state to true, the `$dirty` state to false, removes
24880   * the `ng-dirty` class and adds the `ng-pristine` class. Additionally, it sets the `$submitted`
24881   * state to false.
24882   *
24883   * This method will also propagate to all the controls contained in this form.
24884   *
24885   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
24886   * saving or resetting it.
24887   */
24888  $setPristine: function() {
24889    this.$$animate.setClass(this.$$element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
24890    this.$dirty = false;
24891    this.$pristine = true;
24892    this.$submitted = false;
24893    forEach(this.$$controls, function(control) {
24894      control.$setPristine();
24895    });
24896  },
24897
24898  /**
24899   * @ngdoc method
24900   * @name form.FormController#$setUntouched
24901   *
24902   * @description
24903   * Sets the form to its untouched state.
24904   *
24905   * This method can be called to remove the 'ng-touched' class and set the form controls to their
24906   * untouched state (ng-untouched class).
24907   *
24908   * Setting a form controls back to their untouched state is often useful when setting the form
24909   * back to its pristine state.
24910   */
24911  $setUntouched: function() {
24912    forEach(this.$$controls, function(control) {
24913      control.$setUntouched();
24914    });
24915  },
24916
24917  /**
24918   * @ngdoc method
24919   * @name form.FormController#$setSubmitted
24920   *
24921   * @description
24922   * Sets the form to its `$submitted` state. This will also set `$submitted` on all child and
24923   * parent forms of the form.
24924   */
24925  $setSubmitted: function() {
24926    var rootForm = this;
24927    while (rootForm.$$parentForm && (rootForm.$$parentForm !== nullFormCtrl)) {
24928      rootForm = rootForm.$$parentForm;
24929    }
24930    rootForm.$$setSubmitted();
24931  },
24932
24933  $$setSubmitted: function() {
24934    this.$$animate.addClass(this.$$element, SUBMITTED_CLASS);
24935    this.$submitted = true;
24936    forEach(this.$$controls, function(control) {
24937      if (control.$$setSubmitted) {
24938        control.$$setSubmitted();
24939      }
24940    });
24941  }
24942};
24943
24944/**
24945 * @ngdoc method
24946 * @name form.FormController#$setValidity
24947 *
24948 * @description
24949 * Change the validity state of the form, and notify the parent form (if any).
24950 *
24951 * Application developers will rarely need to call this method directly. It is used internally, by
24952 * {@link ngModel.NgModelController#$setValidity NgModelController.$setValidity()}, to propagate a
24953 * control's validity state to the parent `FormController`.
24954 *
24955 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be
24956 *        assigned to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` (for
24957 *        unfulfilled `$asyncValidators`), so that it is available for data-binding. The
24958 *        `validationErrorKey` should be in camelCase and will get converted into dash-case for
24959 *        class name. Example: `myError` will result in `ng-valid-my-error` and
24960 *        `ng-invalid-my-error` classes and can be bound to as `{{ someForm.$error.myError }}`.
24961 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending
24962 *        (undefined),  or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
24963 *        Skipped is used by AngularJS when validators do not run because of parse errors and when
24964 *        `$asyncValidators` do not run because any of the `$validators` failed.
24965 * @param {NgModelController | FormController} controller - The controller whose validity state is
24966 *        triggering the change.
24967 */
24968addSetValidityMethod({
24969  clazz: FormController,
24970  set: function(object, property, controller) {
24971    var list = object[property];
24972    if (!list) {
24973      object[property] = [controller];
24974    } else {
24975      var index = list.indexOf(controller);
24976      if (index === -1) {
24977        list.push(controller);
24978      }
24979    }
24980  },
24981  unset: function(object, property, controller) {
24982    var list = object[property];
24983    if (!list) {
24984      return;
24985    }
24986    arrayRemove(list, controller);
24987    if (list.length === 0) {
24988      delete object[property];
24989    }
24990  }
24991});
24992
24993/**
24994 * @ngdoc directive
24995 * @name ngForm
24996 * @restrict EAC
24997 *
24998 * @description
24999 * Helper directive that makes it possible to create control groups inside a
25000 * {@link ng.directive:form `form`} directive.
25001 * These "child forms" can be used, for example, to determine the validity of a sub-group of
25002 * controls.
25003 *
25004 * <div class="alert alert-danger">
25005 * **Note**: `ngForm` cannot be used as a replacement for `<form>`, because it lacks its
25006 * [built-in HTML functionality](https://html.spec.whatwg.org/#the-form-element).
25007 * Specifically, you cannot submit `ngForm` like a `<form>` tag. That means,
25008 * you cannot send data to the server with `ngForm`, or integrate it with
25009 * {@link ng.directive:ngSubmit `ngSubmit`}.
25010 * </div>
25011 *
25012 * @param {string=} ngForm|name Name of the form. If specified, the form controller will
25013 *                              be published into the related scope, under this name.
25014 *
25015 */
25016
25017 /**
25018 * @ngdoc directive
25019 * @name form
25020 * @restrict E
25021 *
25022 * @description
25023 * Directive that instantiates
25024 * {@link form.FormController FormController}.
25025 *
25026 * If the `name` attribute is specified, the form controller is published onto the current scope under
25027 * this name.
25028 *
25029 * ## Alias: {@link ng.directive:ngForm `ngForm`}
25030 *
25031 * In AngularJS, forms can be nested. This means that the outer form is valid when all of the child
25032 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
25033 * AngularJS provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
25034 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
25035 * of controls needs to be determined.
25036 *
25037 * ## CSS classes
25038 *  - `ng-valid` is set if the form is valid.
25039 *  - `ng-invalid` is set if the form is invalid.
25040 *  - `ng-pending` is set if the form is pending.
25041 *  - `ng-pristine` is set if the form is pristine.
25042 *  - `ng-dirty` is set if the form is dirty.
25043 *  - `ng-submitted` is set if the form was submitted.
25044 *
25045 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
25046 *
25047 *
25048 * ## Submitting a form and preventing the default action
25049 *
25050 * Since the role of forms in client-side AngularJS applications is different than in classical
25051 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
25052 * page reload that sends the data to the server. Instead some javascript logic should be triggered
25053 * to handle the form submission in an application-specific way.
25054 *
25055 * For this reason, AngularJS prevents the default action (form submission to the server) unless the
25056 * `<form>` element has an `action` attribute specified.
25057 *
25058 * You can use one of the following two ways to specify what javascript method should be called when
25059 * a form is submitted:
25060 *
25061 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
25062 * - {@link ng.directive:ngClick ngClick} directive on the first
25063  *  button or input field of type submit (input[type=submit])
25064 *
25065 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
25066 * or {@link ng.directive:ngClick ngClick} directives.
25067 * This is because of the following form submission rules in the HTML specification:
25068 *
25069 * - If a form has only one input field then hitting enter in this field triggers form submit
25070 * (`ngSubmit`)
25071 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
25072 * doesn't trigger submit
25073 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
25074 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
25075 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
25076 *
25077 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
25078 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
25079 * to have access to the updated model.
25080 *
25081 * @animations
25082 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
25083 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
25084 * other validations that are performed within the form. Animations in ngForm are similar to how
25085 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
25086 * as JS animations.
25087 *
25088 * The following example shows a simple way to utilize CSS transitions to style a form element
25089 * that has been rendered as invalid after it has been validated:
25090 *
25091 * <pre>
25092 * //be sure to include ngAnimate as a module to hook into more
25093 * //advanced animations
25094 * .my-form {
25095 *   transition:0.5s linear all;
25096 *   background: white;
25097 * }
25098 * .my-form.ng-invalid {
25099 *   background: red;
25100 *   color:white;
25101 * }
25102 * </pre>
25103 *
25104 * @example
25105    <example name="ng-form" deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
25106      <file name="index.html">
25107       <script>
25108         angular.module('formExample', [])
25109           .controller('FormController', ['$scope', function($scope) {
25110             $scope.userType = 'guest';
25111           }]);
25112       </script>
25113       <style>
25114        .my-form {
25115          transition:all linear 0.5s;
25116          background: transparent;
25117        }
25118        .my-form.ng-invalid {
25119          background: red;
25120        }
25121       </style>
25122       <form name="myForm" ng-controller="FormController" class="my-form">
25123         userType: <input name="input" ng-model="userType" required>
25124         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
25125         <code>userType = {{userType}}</code><br>
25126         <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
25127         <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
25128         <code>myForm.$valid = {{myForm.$valid}}</code><br>
25129         <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
25130        </form>
25131      </file>
25132      <file name="protractor.js" type="protractor">
25133        it('should initialize to model', function() {
25134          var userType = element(by.binding('userType'));
25135          var valid = element(by.binding('myForm.input.$valid'));
25136
25137          expect(userType.getText()).toContain('guest');
25138          expect(valid.getText()).toContain('true');
25139        });
25140
25141        it('should be invalid if empty', function() {
25142          var userType = element(by.binding('userType'));
25143          var valid = element(by.binding('myForm.input.$valid'));
25144          var userInput = element(by.model('userType'));
25145
25146          userInput.clear();
25147          userInput.sendKeys('');
25148
25149          expect(userType.getText()).toEqual('userType =');
25150          expect(valid.getText()).toContain('false');
25151        });
25152      </file>
25153    </example>
25154 *
25155 * @param {string=} name Name of the form. If specified, the form controller will be published into
25156 *                       related scope, under this name.
25157 */
25158var formDirectiveFactory = function(isNgForm) {
25159  return ['$timeout', '$parse', function($timeout, $parse) {
25160    var formDirective = {
25161      name: 'form',
25162      restrict: isNgForm ? 'EAC' : 'E',
25163      require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
25164      controller: FormController,
25165      compile: function ngFormCompile(formElement, attr) {
25166        // Setup initial state of the control
25167        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
25168
25169        var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
25170
25171        return {
25172          pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
25173            var controller = ctrls[0];
25174
25175            // if `action` attr is not present on the form, prevent the default action (submission)
25176            if (!('action' in attr)) {
25177              // we can't use jq events because if a form is destroyed during submission the default
25178              // action is not prevented. see #1238
25179              //
25180              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
25181              // page reload if the form was destroyed by submission of the form via a click handler
25182              // on a button in the form. Looks like an IE9 specific bug.
25183              var handleFormSubmission = function(event) {
25184                scope.$apply(function() {
25185                  controller.$commitViewValue();
25186                  controller.$setSubmitted();
25187                });
25188
25189                event.preventDefault();
25190              };
25191
25192              formElement[0].addEventListener('submit', handleFormSubmission);
25193
25194              // unregister the preventDefault listener so that we don't not leak memory but in a
25195              // way that will achieve the prevention of the default action.
25196              formElement.on('$destroy', function() {
25197                $timeout(function() {
25198                  formElement[0].removeEventListener('submit', handleFormSubmission);
25199                }, 0, false);
25200              });
25201            }
25202
25203            var parentFormCtrl = ctrls[1] || controller.$$parentForm;
25204            parentFormCtrl.$addControl(controller);
25205
25206            var setter = nameAttr ? getSetter(controller.$name) : noop;
25207
25208            if (nameAttr) {
25209              setter(scope, controller);
25210              attr.$observe(nameAttr, function(newValue) {
25211                if (controller.$name === newValue) return;
25212                setter(scope, undefined);
25213                controller.$$parentForm.$$renameControl(controller, newValue);
25214                setter = getSetter(controller.$name);
25215                setter(scope, controller);
25216              });
25217            }
25218            formElement.on('$destroy', function() {
25219              controller.$$parentForm.$removeControl(controller);
25220              setter(scope, undefined);
25221              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
25222            });
25223          }
25224        };
25225      }
25226    };
25227
25228    return formDirective;
25229
25230    function getSetter(expression) {
25231      if (expression === '') {
25232        //create an assignable expression, so forms with an empty name can be renamed later
25233        return $parse('this[""]').assign;
25234      }
25235      return $parse(expression).assign || noop;
25236    }
25237  }];
25238};
25239
25240var formDirective = formDirectiveFactory();
25241var ngFormDirective = formDirectiveFactory(true);
25242
25243
25244
25245// helper methods
25246function setupValidity(instance) {
25247  instance.$$classCache = {};
25248  instance.$$classCache[INVALID_CLASS] = !(instance.$$classCache[VALID_CLASS] = instance.$$element.hasClass(VALID_CLASS));
25249}
25250function addSetValidityMethod(context) {
25251  var clazz = context.clazz,
25252      set = context.set,
25253      unset = context.unset;
25254
25255  clazz.prototype.$setValidity = function(validationErrorKey, state, controller) {
25256    if (isUndefined(state)) {
25257      createAndSet(this, '$pending', validationErrorKey, controller);
25258    } else {
25259      unsetAndCleanup(this, '$pending', validationErrorKey, controller);
25260    }
25261    if (!isBoolean(state)) {
25262      unset(this.$error, validationErrorKey, controller);
25263      unset(this.$$success, validationErrorKey, controller);
25264    } else {
25265      if (state) {
25266        unset(this.$error, validationErrorKey, controller);
25267        set(this.$$success, validationErrorKey, controller);
25268      } else {
25269        set(this.$error, validationErrorKey, controller);
25270        unset(this.$$success, validationErrorKey, controller);
25271      }
25272    }
25273    if (this.$pending) {
25274      cachedToggleClass(this, PENDING_CLASS, true);
25275      this.$valid = this.$invalid = undefined;
25276      toggleValidationCss(this, '', null);
25277    } else {
25278      cachedToggleClass(this, PENDING_CLASS, false);
25279      this.$valid = isObjectEmpty(this.$error);
25280      this.$invalid = !this.$valid;
25281      toggleValidationCss(this, '', this.$valid);
25282    }
25283
25284    // re-read the state as the set/unset methods could have
25285    // combined state in this.$error[validationError] (used for forms),
25286    // where setting/unsetting only increments/decrements the value,
25287    // and does not replace it.
25288    var combinedState;
25289    if (this.$pending && this.$pending[validationErrorKey]) {
25290      combinedState = undefined;
25291    } else if (this.$error[validationErrorKey]) {
25292      combinedState = false;
25293    } else if (this.$$success[validationErrorKey]) {
25294      combinedState = true;
25295    } else {
25296      combinedState = null;
25297    }
25298
25299    toggleValidationCss(this, validationErrorKey, combinedState);
25300    this.$$parentForm.$setValidity(validationErrorKey, combinedState, this);
25301  };
25302
25303  function createAndSet(ctrl, name, value, controller) {
25304    if (!ctrl[name]) {
25305      ctrl[name] = {};
25306    }
25307    set(ctrl[name], value, controller);
25308  }
25309
25310  function unsetAndCleanup(ctrl, name, value, controller) {
25311    if (ctrl[name]) {
25312      unset(ctrl[name], value, controller);
25313    }
25314    if (isObjectEmpty(ctrl[name])) {
25315      ctrl[name] = undefined;
25316    }
25317  }
25318
25319  function cachedToggleClass(ctrl, className, switchValue) {
25320    if (switchValue && !ctrl.$$classCache[className]) {
25321      ctrl.$$animate.addClass(ctrl.$$element, className);
25322      ctrl.$$classCache[className] = true;
25323    } else if (!switchValue && ctrl.$$classCache[className]) {
25324      ctrl.$$animate.removeClass(ctrl.$$element, className);
25325      ctrl.$$classCache[className] = false;
25326    }
25327  }
25328
25329  function toggleValidationCss(ctrl, validationErrorKey, isValid) {
25330    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
25331
25332    cachedToggleClass(ctrl, VALID_CLASS + validationErrorKey, isValid === true);
25333    cachedToggleClass(ctrl, INVALID_CLASS + validationErrorKey, isValid === false);
25334  }
25335}
25336
25337function isObjectEmpty(obj) {
25338  if (obj) {
25339    for (var prop in obj) {
25340      if (obj.hasOwnProperty(prop)) {
25341        return false;
25342      }
25343    }
25344  }
25345  return true;
25346}
25347
25348/* global
25349  VALID_CLASS: false,
25350  INVALID_CLASS: false,
25351  PRISTINE_CLASS: false,
25352  DIRTY_CLASS: false,
25353  ngModelMinErr: false
25354*/
25355
25356// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
25357var 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)$/;
25358// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
25359// Note: We are being more lenient, because browsers are too.
25360//   1. Scheme
25361//   2. Slashes
25362//   3. Username
25363//   4. Password
25364//   5. Hostname
25365//   6. Port
25366//   7. Path
25367//   8. Query
25368//   9. Fragment
25369//                 1111111111111111 222   333333    44444        55555555555555555555555     666     77777777     8888888     999
25370var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
25371// eslint-disable-next-line max-len
25372var 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])?)*$/;
25373var NUMBER_REGEXP = /^\s*(-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
25374var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
25375var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
25376var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
25377var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
25378var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
25379
25380var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
25381var PARTIAL_VALIDATION_TYPES = createMap();
25382forEach('date,datetime-local,month,time,week'.split(','), function(type) {
25383  PARTIAL_VALIDATION_TYPES[type] = true;
25384});
25385
25386var inputType = {
25387
25388  /**
25389   * @ngdoc input
25390   * @name input[text]
25391   *
25392   * @description
25393   * Standard HTML text input with AngularJS data binding, inherited by most of the `input` elements.
25394   *
25395   *
25396   * @param {string} ngModel Assignable AngularJS expression to data-bind to.
25397   * @param {string=} name Property name of the form under which the control is published.
25398   * @param {string=} required Adds `required` validation error key if the value is not entered.
25399   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25400   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25401   *    `required` when you want to data-bind to the `required` attribute.
25402   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
25403   *    minlength.
25404   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
25405   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
25406   *    any length.
25407   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
25408   *    that contains the regular expression body that will be converted to a regular expression
25409   *    as in the ngPattern directive.
25410   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
25411   *    does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
25412   *    If the expression evaluates to a RegExp object, then this is used directly.
25413   *    If the expression evaluates to a string, then it will be converted to a RegExp
25414   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
25415   *    `new RegExp('^abc$')`.<br />
25416   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
25417   *    start at the index of the last search's match, thus not taking the whole input value into
25418   *    account.
25419   * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
25420   *    interaction with the input element.
25421   * @param {boolean=} [ngTrim=true] If set to false AngularJS will not automatically trim the input.
25422   *    This parameter is ignored for input[type=password] controls, which will never trim the
25423   *    input.
25424   *
25425   * @example
25426      <example name="text-input-directive" module="textInputExample">
25427        <file name="index.html">
25428         <script>
25429           angular.module('textInputExample', [])
25430             .controller('ExampleController', ['$scope', function($scope) {
25431               $scope.example = {
25432                 text: 'guest',
25433                 word: /^\s*\w*\s*$/
25434               };
25435             }]);
25436         </script>
25437         <form name="myForm" ng-controller="ExampleController">
25438           <label>Single word:
25439             <input type="text" name="input" ng-model="example.text"
25440                    ng-pattern="example.word" required ng-trim="false">
25441           </label>
25442           <div role="alert">
25443             <span class="error" ng-show="myForm.input.$error.required">
25444               Required!</span>
25445             <span class="error" ng-show="myForm.input.$error.pattern">
25446               Single word only!</span>
25447           </div>
25448           <code>text = {{example.text}}</code><br/>
25449           <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br/>
25450           <code>myForm.input.$error = {{myForm.input.$error}}</code><br/>
25451           <code>myForm.$valid = {{myForm.$valid}}</code><br/>
25452           <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br/>
25453          </form>
25454        </file>
25455        <file name="protractor.js" type="protractor">
25456          var text = element(by.binding('example.text'));
25457          var valid = element(by.binding('myForm.input.$valid'));
25458          var input = element(by.model('example.text'));
25459
25460          it('should initialize to model', function() {
25461            expect(text.getText()).toContain('guest');
25462            expect(valid.getText()).toContain('true');
25463          });
25464
25465          it('should be invalid if empty', function() {
25466            input.clear();
25467            input.sendKeys('');
25468
25469            expect(text.getText()).toEqual('text =');
25470            expect(valid.getText()).toContain('false');
25471          });
25472
25473          it('should be invalid if multi word', function() {
25474            input.clear();
25475            input.sendKeys('hello world');
25476
25477            expect(valid.getText()).toContain('false');
25478          });
25479        </file>
25480      </example>
25481   */
25482  'text': textInputType,
25483
25484    /**
25485     * @ngdoc input
25486     * @name input[date]
25487     *
25488     * @description
25489     * Input with date validation and transformation. In browsers that do not yet support
25490     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
25491     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
25492     * modern browsers do not yet support this input type, it is important to provide cues to users on the
25493     * expected input format via a placeholder or label.
25494     *
25495     * The model must always be a Date object, otherwise AngularJS will throw an error.
25496     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
25497     *
25498     * The timezone to be used to read/write the `Date` instance in the model can be defined using
25499     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
25500     *
25501     * @param {string} ngModel Assignable AngularJS expression to data-bind to.
25502     * @param {string=} name Property name of the form under which the control is published.
25503     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
25504     *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
25505     *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
25506     *   constraint validation.
25507     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
25508     *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
25509     *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
25510     *   constraint validation.
25511     * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
25512     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
25513     * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
25514     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
25515     * @param {string=} required Sets `required` validation error key if the value is not entered.
25516     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25517     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25518     *    `required` when you want to data-bind to the `required` attribute.
25519     * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
25520     *    interaction with the input element.
25521     *
25522     * @example
25523     <example name="date-input-directive" module="dateInputExample">
25524     <file name="index.html">
25525       <script>
25526          angular.module('dateInputExample', [])
25527            .controller('DateController', ['$scope', function($scope) {
25528              $scope.example = {
25529                value: new Date(2013, 9, 22)
25530              };
25531            }]);
25532       </script>
25533       <form name="myForm" ng-controller="DateController as dateCtrl">
25534          <label for="exampleInput">Pick a date in 2013:</label>
25535          <input type="date" id="exampleInput" name="input" ng-model="example.value"
25536              placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
25537          <div role="alert">
25538            <span class="error" ng-show="myForm.input.$error.required">
25539                Required!</span>
25540            <span class="error" ng-show="myForm.input.$error.date">
25541                Not a valid date!</span>
25542           </div>
25543           <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
25544           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
25545           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
25546           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25547           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25548       </form>
25549     </file>
25550     <file name="protractor.js" type="protractor">
25551        var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
25552        var valid = element(by.binding('myForm.input.$valid'));
25553
25554        // currently protractor/webdriver does not support
25555        // sending keys to all known HTML5 input controls
25556        // for various browsers (see https://github.com/angular/protractor/issues/562).
25557        function setInput(val) {
25558          // set the value of the element and force validation.
25559          var scr = "var ipt = document.getElementById('exampleInput'); " +
25560          "ipt.value = '" + val + "';" +
25561          "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
25562          browser.executeScript(scr);
25563        }
25564
25565        it('should initialize to model', function() {
25566          expect(value.getText()).toContain('2013-10-22');
25567          expect(valid.getText()).toContain('myForm.input.$valid = true');
25568        });
25569
25570        it('should be invalid if empty', function() {
25571          setInput('');
25572          expect(value.getText()).toEqual('value =');
25573          expect(valid.getText()).toContain('myForm.input.$valid = false');
25574        });
25575
25576        it('should be invalid if over max', function() {
25577          setInput('2015-01-01');
25578          expect(value.getText()).toContain('');
25579          expect(valid.getText()).toContain('myForm.input.$valid = false');
25580        });
25581     </file>
25582     </example>
25583     */
25584  'date': createDateInputType('date', DATE_REGEXP,
25585         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
25586         'yyyy-MM-dd'),
25587
25588   /**
25589    * @ngdoc input
25590    * @name input[datetime-local]
25591    *
25592    * @description
25593    * Input with datetime validation and transformation. In browsers that do not yet support
25594    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
25595    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
25596    *
25597    * The model must always be a Date object, otherwise AngularJS will throw an error.
25598    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
25599    *
25600    * The timezone to be used to read/write the `Date` instance in the model can be defined using
25601    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
25602    *
25603    * The format of the displayed time can be adjusted with the
25604    * {@link ng.directive:ngModelOptions#ngModelOptions-arguments ngModelOptions} `timeSecondsFormat`
25605    * and `timeStripZeroSeconds`.
25606    *
25607    * @param {string} ngModel Assignable AngularJS expression to data-bind to.
25608    * @param {string=} name Property name of the form under which the control is published.
25609    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
25610    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
25611    *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
25612    *   Note that `min` will also add native HTML5 constraint validation.
25613    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
25614    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
25615    *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
25616    *   Note that `max` will also add native HTML5 constraint validation.
25617    * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
25618    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
25619    * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
25620    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
25621    * @param {string=} required Sets `required` validation error key if the value is not entered.
25622    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25623    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25624    *    `required` when you want to data-bind to the `required` attribute.
25625    * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
25626    *    interaction with the input element.
25627    *
25628    * @example
25629    <example name="datetimelocal-input-directive" module="dateExample">
25630    <file name="index.html">
25631      <script>
25632        angular.module('dateExample', [])
25633          .controller('DateController', ['$scope', function($scope) {
25634            $scope.example = {
25635              value: new Date(2010, 11, 28, 14, 57)
25636            };
25637          }]);
25638      </script>
25639      <form name="myForm" ng-controller="DateController as dateCtrl">
25640        <label for="exampleInput">Pick a date between in 2013:</label>
25641        <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
25642            placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
25643        <div role="alert">
25644          <span class="error" ng-show="myForm.input.$error.required">
25645              Required!</span>
25646          <span class="error" ng-show="myForm.input.$error.datetimelocal">
25647              Not a valid date!</span>
25648        </div>
25649        <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
25650        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
25651        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
25652        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25653        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25654      </form>
25655    </file>
25656    <file name="protractor.js" type="protractor">
25657      var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
25658      var valid = element(by.binding('myForm.input.$valid'));
25659
25660      // currently protractor/webdriver does not support
25661      // sending keys to all known HTML5 input controls
25662      // for various browsers (https://github.com/angular/protractor/issues/562).
25663      function setInput(val) {
25664        // set the value of the element and force validation.
25665        var scr = "var ipt = document.getElementById('exampleInput'); " +
25666        "ipt.value = '" + val + "';" +
25667        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
25668        browser.executeScript(scr);
25669      }
25670
25671      it('should initialize to model', function() {
25672        expect(value.getText()).toContain('2010-12-28T14:57:00');
25673        expect(valid.getText()).toContain('myForm.input.$valid = true');
25674      });
25675
25676      it('should be invalid if empty', function() {
25677        setInput('');
25678        expect(value.getText()).toEqual('value =');
25679        expect(valid.getText()).toContain('myForm.input.$valid = false');
25680      });
25681
25682      it('should be invalid if over max', function() {
25683        setInput('2015-01-01T23:59:00');
25684        expect(value.getText()).toContain('');
25685        expect(valid.getText()).toContain('myForm.input.$valid = false');
25686      });
25687    </file>
25688    </example>
25689    */
25690  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
25691      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
25692      'yyyy-MM-ddTHH:mm:ss.sss'),
25693
25694  /**
25695   * @ngdoc input
25696   * @name input[time]
25697   *
25698   * @description
25699   * Input with time validation and transformation. In browsers that do not yet support
25700   * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
25701   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
25702   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
25703   *
25704   * The model must always be a Date object, otherwise AngularJS will throw an error.
25705   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
25706   *
25707   * The timezone to be used to read/write the `Date` instance in the model can be defined using
25708   * {@link ng.directive:ngModelOptions#ngModelOptions-arguments ngModelOptions}. By default,
25709   * this is the timezone of the browser.
25710   *
25711   * The format of the displayed time can be adjusted with the
25712   * {@link ng.directive:ngModelOptions#ngModelOptions-arguments ngModelOptions} `timeSecondsFormat`
25713   * and `timeStripZeroSeconds`.
25714   *
25715   * @param {string} ngModel Assignable AngularJS expression to data-bind to.
25716   * @param {string=} name Property name of the form under which the control is published.
25717   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
25718   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
25719   *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
25720   *   native HTML5 constraint validation.
25721   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
25722   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
25723   *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
25724   *   native HTML5 constraint validation.
25725   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
25726   *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
25727   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
25728   *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
25729   * @param {string=} required Sets `required` validation error key if the value is not entered.
25730   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25731   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25732   *    `required` when you want to data-bind to the `required` attribute.
25733   * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
25734   *    interaction with the input element.
25735   *
25736   * @example
25737   <example name="time-input-directive" module="timeExample">
25738   <file name="index.html">
25739     <script>
25740      angular.module('timeExample', [])
25741        .controller('DateController', ['$scope', function($scope) {
25742          $scope.example = {
25743            value: new Date(1970, 0, 1, 14, 57, 0)
25744          };
25745        }]);
25746     </script>
25747     <form name="myForm" ng-controller="DateController as dateCtrl">
25748        <label for="exampleInput">Pick a time between 8am and 5pm:</label>
25749        <input type="time" id="exampleInput" name="input" ng-model="example.value"
25750            placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
25751        <div role="alert">
25752          <span class="error" ng-show="myForm.input.$error.required">
25753              Required!</span>
25754          <span class="error" ng-show="myForm.input.$error.time">
25755              Not a valid date!</span>
25756        </div>
25757        <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
25758        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
25759        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
25760        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25761        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25762     </form>
25763   </file>
25764   <file name="protractor.js" type="protractor">
25765      var value = element(by.binding('example.value | date: "HH:mm:ss"'));
25766      var valid = element(by.binding('myForm.input.$valid'));
25767
25768      // currently protractor/webdriver does not support
25769      // sending keys to all known HTML5 input controls
25770      // for various browsers (https://github.com/angular/protractor/issues/562).
25771      function setInput(val) {
25772        // set the value of the element and force validation.
25773        var scr = "var ipt = document.getElementById('exampleInput'); " +
25774        "ipt.value = '" + val + "';" +
25775        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
25776        browser.executeScript(scr);
25777      }
25778
25779      it('should initialize to model', function() {
25780        expect(value.getText()).toContain('14:57:00');
25781        expect(valid.getText()).toContain('myForm.input.$valid = true');
25782      });
25783
25784      it('should be invalid if empty', function() {
25785        setInput('');
25786        expect(value.getText()).toEqual('value =');
25787        expect(valid.getText()).toContain('myForm.input.$valid = false');
25788      });
25789
25790      it('should be invalid if over max', function() {
25791        setInput('23:59:00');
25792        expect(value.getText()).toContain('');
25793        expect(valid.getText()).toContain('myForm.input.$valid = false');
25794      });
25795   </file>
25796   </example>
25797   */
25798  'time': createDateInputType('time', TIME_REGEXP,
25799      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
25800     'HH:mm:ss.sss'),
25801
25802   /**
25803    * @ngdoc input
25804    * @name input[week]
25805    *
25806    * @description
25807    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
25808    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
25809    * week format (yyyy-W##), for example: `2013-W02`.
25810    *
25811    * The model must always be a Date object, otherwise AngularJS will throw an error.
25812    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
25813    *
25814    * The value of the resulting Date object will be set to Thursday at 00:00:00 of the requested week,
25815    * due to ISO-8601 week numbering standards. Information on ISO's system for numbering the weeks of the
25816    * year can be found at: https://en.wikipedia.org/wiki/ISO_8601#Week_dates
25817    *
25818    * The timezone to be used to read/write the `Date` instance in the model can be defined using
25819    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
25820    *
25821    * @param {string} ngModel Assignable AngularJS expression to data-bind to.
25822    * @param {string=} name Property name of the form under which the control is published.
25823    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
25824    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
25825    *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
25826    *   native HTML5 constraint validation.
25827    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
25828    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
25829    *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
25830    *   native HTML5 constraint validation.
25831    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
25832    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
25833    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
25834    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
25835    * @param {string=} required Sets `required` validation error key if the value is not entered.
25836    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25837    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25838    *    `required` when you want to data-bind to the `required` attribute.
25839    * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
25840    *    interaction with the input element.
25841    *
25842    * @example
25843    <example name="week-input-directive" module="weekExample">
25844    <file name="index.html">
25845      <script>
25846      angular.module('weekExample', [])
25847        .controller('DateController', ['$scope', function($scope) {
25848          $scope.example = {
25849            value: new Date(2013, 0, 3)
25850          };
25851        }]);
25852      </script>
25853      <form name="myForm" ng-controller="DateController as dateCtrl">
25854        <label>Pick a date between in 2013:
25855          <input id="exampleInput" type="week" name="input" ng-model="example.value"
25856                 placeholder="YYYY-W##" min="2012-W32"
25857                 max="2013-W52" required />
25858        </label>
25859        <div role="alert">
25860          <span class="error" ng-show="myForm.input.$error.required">
25861              Required!</span>
25862          <span class="error" ng-show="myForm.input.$error.week">
25863              Not a valid date!</span>
25864        </div>
25865        <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
25866        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
25867        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
25868        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25869        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25870      </form>
25871    </file>
25872    <file name="protractor.js" type="protractor">
25873      var value = element(by.binding('example.value | date: "yyyy-Www"'));
25874      var valid = element(by.binding('myForm.input.$valid'));
25875
25876      // currently protractor/webdriver does not support
25877      // sending keys to all known HTML5 input controls
25878      // for various browsers (https://github.com/angular/protractor/issues/562).
25879      function setInput(val) {
25880        // set the value of the element and force validation.
25881        var scr = "var ipt = document.getElementById('exampleInput'); " +
25882        "ipt.value = '" + val + "';" +
25883        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
25884        browser.executeScript(scr);
25885      }
25886
25887      it('should initialize to model', function() {
25888        expect(value.getText()).toContain('2013-W01');
25889        expect(valid.getText()).toContain('myForm.input.$valid = true');
25890      });
25891
25892      it('should be invalid if empty', function() {
25893        setInput('');
25894        expect(value.getText()).toEqual('value =');
25895        expect(valid.getText()).toContain('myForm.input.$valid = false');
25896      });
25897
25898      it('should be invalid if over max', function() {
25899        setInput('2015-W01');
25900        expect(value.getText()).toContain('');
25901        expect(valid.getText()).toContain('myForm.input.$valid = false');
25902      });
25903    </file>
25904    </example>
25905    */
25906  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
25907
25908  /**
25909   * @ngdoc input
25910   * @name input[month]
25911   *
25912   * @description
25913   * Input with month validation and transformation. In browsers that do not yet support
25914   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
25915   * month format (yyyy-MM), for example: `2009-01`.
25916   *
25917   * The model must always be a Date object, otherwise AngularJS will throw an error.
25918   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
25919   * If the model is not set to the first of the month, the next view to model update will set it
25920   * to the first of the month.
25921   *
25922   * The timezone to be used to read/write the `Date` instance in the model can be defined using
25923   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
25924   *
25925   * @param {string} ngModel Assignable AngularJS expression to data-bind to.
25926   * @param {string=} name Property name of the form under which the control is published.
25927   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
25928   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
25929   *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
25930   *   native HTML5 constraint validation.
25931   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
25932   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
25933   *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
25934   *   native HTML5 constraint validation.
25935   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
25936   *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
25937   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
25938   *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
25939
25940   * @param {string=} required Sets `required` validation error key if the value is not entered.
25941   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25942   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25943   *    `required` when you want to data-bind to the `required` attribute.
25944   * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
25945   *    interaction with the input element.
25946   *
25947   * @example
25948   <example name="month-input-directive" module="monthExample">
25949   <file name="index.html">
25950     <script>
25951      angular.module('monthExample', [])
25952        .controller('DateController', ['$scope', function($scope) {
25953          $scope.example = {
25954            value: new Date(2013, 9, 1)
25955          };
25956        }]);
25957     </script>
25958     <form name="myForm" ng-controller="DateController as dateCtrl">
25959       <label for="exampleInput">Pick a month in 2013:</label>
25960       <input id="exampleInput" type="month" name="input" ng-model="example.value"
25961          placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
25962       <div role="alert">
25963         <span class="error" ng-show="myForm.input.$error.required">
25964            Required!</span>
25965         <span class="error" ng-show="myForm.input.$error.month">
25966            Not a valid month!</span>
25967       </div>
25968       <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
25969       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
25970       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
25971       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25972       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25973     </form>
25974   </file>
25975   <file name="protractor.js" type="protractor">
25976      var value = element(by.binding('example.value | date: "yyyy-MM"'));
25977      var valid = element(by.binding('myForm.input.$valid'));
25978
25979      // currently protractor/webdriver does not support
25980      // sending keys to all known HTML5 input controls
25981      // for various browsers (https://github.com/angular/protractor/issues/562).
25982      function setInput(val) {
25983        // set the value of the element and force validation.
25984        var scr = "var ipt = document.getElementById('exampleInput'); " +
25985        "ipt.value = '" + val + "';" +
25986        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
25987        browser.executeScript(scr);
25988      }
25989
25990      it('should initialize to model', function() {
25991        expect(value.getText()).toContain('2013-10');
25992        expect(valid.getText()).toContain('myForm.input.$valid = true');
25993      });
25994
25995      it('should be invalid if empty', function() {
25996        setInput('');
25997        expect(value.getText()).toEqual('value =');
25998        expect(valid.getText()).toContain('myForm.input.$valid = false');
25999      });
26000
26001      it('should be invalid if over max', function() {
26002        setInput('2015-01');
26003        expect(value.getText()).toContain('');
26004        expect(valid.getText()).toContain('myForm.input.$valid = false');
26005      });
26006   </file>
26007   </example>
26008   */
26009  'month': createDateInputType('month', MONTH_REGEXP,
26010     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
26011     'yyyy-MM'),
26012
26013  /**
26014   * @ngdoc input
26015   * @name input[number]
26016   *
26017   * @description
26018   * Text input with number validation and transformation. Sets the `number` validation
26019   * error if not a valid number.
26020   *
26021   * <div class="alert alert-warning">
26022   * The model must always be of type `number` otherwise AngularJS will throw an error.
26023   * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
26024   * error docs for more information and an example of how to convert your model if necessary.
26025   * </div>
26026   *
26027   *
26028   *
26029   * @knownIssue
26030   *
26031   * ### HTML5 constraint validation and `allowInvalid`
26032   *
26033   * In browsers that follow the
26034   * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
26035   * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
26036   * If a non-number is entered in the input, the browser will report the value as an empty string,
26037   * which means the view / model values in `ngModel` and subsequently the scope value
26038   * will also be an empty string.
26039   *
26040   * @knownIssue
26041   *
26042   * ### Large numbers and `step` validation
26043   *
26044   * The `step` validation will not work correctly for very large numbers (e.g. 9999999999) due to
26045   * Javascript's arithmetic limitations. If you need to handle large numbers, purpose-built
26046   * libraries (e.g. https://github.com/MikeMcl/big.js/), can be included into AngularJS by
26047   * {@link guide/forms#modifying-built-in-validators overwriting the validators}
26048   * for `number` and / or `step`, or by {@link guide/forms#custom-validation applying custom validators}
26049   * to an `input[text]` element. The source for `input[number]` type can be used as a starting
26050   * point for both implementations.
26051   *
26052   * @param {string} ngModel Assignable AngularJS expression to data-bind to.
26053   * @param {string=} name Property name of the form under which the control is published.
26054   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
26055   *    Can be interpolated.
26056   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
26057   *    Can be interpolated.
26058   * @param {string=} ngMin Like `min`, sets the `min` validation error key if the value entered is less than `ngMin`,
26059   *    but does not trigger HTML5 native validation. Takes an expression.
26060   * @param {string=} ngMax Like `max`, sets the `max` validation error key if the value entered is greater than `ngMax`,
26061   *    but does not trigger HTML5 native validation. Takes an expression.
26062   * @param {string=} step Sets the `step` validation error key if the value entered does not fit the `step` constraint.
26063   *    Can be interpolated.
26064   * @param {string=} ngStep Like `step`, sets the `step` validation error key if the value entered does not fit the `ngStep` constraint,
26065   *    but does not trigger HTML5 native validation. Takes an expression.
26066   * @param {string=} required Sets `required` validation error key if the value is not entered.
26067   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26068   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26069   *    `required` when you want to data-bind to the `required` attribute.
26070   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
26071   *    minlength.
26072   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
26073   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
26074   *    any length.
26075   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
26076   *    that contains the regular expression body that will be converted to a regular expression
26077   *    as in the ngPattern directive.
26078   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
26079   *    does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
26080   *    If the expression evaluates to a RegExp object, then this is used directly.
26081   *    If the expression evaluates to a string, then it will be converted to a RegExp
26082   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
26083   *    `new RegExp('^abc$')`.<br />
26084   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
26085   *    start at the index of the last search's match, thus not taking the whole input value into
26086   *    account.
26087   * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
26088   *    interaction with the input element.
26089   *
26090   * @example
26091      <example name="number-input-directive" module="numberExample">
26092        <file name="index.html">
26093         <script>
26094           angular.module('numberExample', [])
26095             .controller('ExampleController', ['$scope', function($scope) {
26096               $scope.example = {
26097                 value: 12
26098               };
26099             }]);
26100         </script>
26101         <form name="myForm" ng-controller="ExampleController">
26102           <label>Number:
26103             <input type="number" name="input" ng-model="example.value"
26104                    min="0" max="99" required>
26105          </label>
26106           <div role="alert">
26107             <span class="error" ng-show="myForm.input.$error.required">
26108               Required!</span>
26109             <span class="error" ng-show="myForm.input.$error.number">
26110               Not valid number!</span>
26111           </div>
26112           <tt>value = {{example.value}}</tt><br/>
26113           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
26114           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
26115           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
26116           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
26117          </form>
26118        </file>
26119        <file name="protractor.js" type="protractor">
26120          var value = element(by.binding('example.value'));
26121          var valid = element(by.binding('myForm.input.$valid'));
26122          var input = element(by.model('example.value'));
26123
26124          it('should initialize to model', function() {
26125            expect(value.getText()).toContain('12');
26126            expect(valid.getText()).toContain('true');
26127          });
26128
26129          it('should be invalid if empty', function() {
26130            input.clear();
26131            input.sendKeys('');
26132            expect(value.getText()).toEqual('value =');
26133            expect(valid.getText()).toContain('false');
26134          });
26135
26136          it('should be invalid if over max', function() {
26137            input.clear();
26138            input.sendKeys('123');
26139            expect(value.getText()).toEqual('value =');
26140            expect(valid.getText()).toContain('false');
26141          });
26142        </file>
26143      </example>
26144   */
26145  'number': numberInputType,
26146
26147
26148  /**
26149   * @ngdoc input
26150   * @name input[url]
26151   *
26152   * @description
26153   * Text input with URL validation. Sets the `url` validation error key if the content is not a
26154   * valid URL.
26155   *
26156   * <div class="alert alert-warning">
26157   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
26158   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
26159   * the built-in validators (see the {@link guide/forms Forms guide})
26160   * </div>
26161   *
26162   * @param {string} ngModel Assignable AngularJS expression to data-bind to.
26163   * @param {string=} name Property name of the form under which the control is published.
26164   * @param {string=} required Sets `required` validation error key if the value is not entered.
26165   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26166   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26167   *    `required` when you want to data-bind to the `required` attribute.
26168   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
26169   *    minlength.
26170   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
26171   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
26172   *    any length.
26173   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
26174   *    that contains the regular expression body that will be converted to a regular expression
26175   *    as in the ngPattern directive.
26176   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
26177   *    does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
26178   *    If the expression evaluates to a RegExp object, then this is used directly.
26179   *    If the expression evaluates to a string, then it will be converted to a RegExp
26180   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
26181   *    `new RegExp('^abc$')`.<br />
26182   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
26183   *    start at the index of the last search's match, thus not taking the whole input value into
26184   *    account.
26185   * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
26186   *    interaction with the input element.
26187   *
26188   * @example
26189      <example name="url-input-directive" module="urlExample">
26190        <file name="index.html">
26191         <script>
26192           angular.module('urlExample', [])
26193             .controller('ExampleController', ['$scope', function($scope) {
26194               $scope.url = {
26195                 text: 'http://google.com'
26196               };
26197             }]);
26198         </script>
26199         <form name="myForm" ng-controller="ExampleController">
26200           <label>URL:
26201             <input type="url" name="input" ng-model="url.text" required>
26202           <label>
26203           <div role="alert">
26204             <span class="error" ng-show="myForm.input.$error.required">
26205               Required!</span>
26206             <span class="error" ng-show="myForm.input.$error.url">
26207               Not valid url!</span>
26208           </div>
26209           <tt>text = {{url.text}}</tt><br/>
26210           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
26211           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
26212           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
26213           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
26214           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
26215          </form>
26216        </file>
26217        <file name="protractor.js" type="protractor">
26218          var text = element(by.binding('url.text'));
26219          var valid = element(by.binding('myForm.input.$valid'));
26220          var input = element(by.model('url.text'));
26221
26222          it('should initialize to model', function() {
26223            expect(text.getText()).toContain('http://google.com');
26224            expect(valid.getText()).toContain('true');
26225          });
26226
26227          it('should be invalid if empty', function() {
26228            input.clear();
26229            input.sendKeys('');
26230
26231            expect(text.getText()).toEqual('text =');
26232            expect(valid.getText()).toContain('false');
26233          });
26234
26235          it('should be invalid if not url', function() {
26236            input.clear();
26237            input.sendKeys('box');
26238
26239            expect(valid.getText()).toContain('false');
26240          });
26241        </file>
26242      </example>
26243   */
26244  'url': urlInputType,
26245
26246
26247  /**
26248   * @ngdoc input
26249   * @name input[email]
26250   *
26251   * @description
26252   * Text input with email validation. Sets the `email` validation error key if not a valid email
26253   * address.
26254   *
26255   * <div class="alert alert-warning">
26256   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
26257   * used in Chromium, which may not fulfill your app's requirements.
26258   * If you need stricter (e.g. requiring a top-level domain), or more relaxed validation
26259   * (e.g. allowing IPv6 address literals) you can use `ng-pattern` or
26260   * modify the built-in validators (see the {@link guide/forms Forms guide}).
26261   * </div>
26262   *
26263   * @param {string} ngModel Assignable AngularJS expression to data-bind to.
26264   * @param {string=} name Property name of the form under which the control is published.
26265   * @param {string=} required Sets `required` validation error key if the value is not entered.
26266   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26267   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26268   *    `required` when you want to data-bind to the `required` attribute.
26269   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
26270   *    minlength.
26271   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
26272   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
26273   *    any length.
26274   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
26275   *    that contains the regular expression body that will be converted to a regular expression
26276   *    as in the ngPattern directive.
26277   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
26278   *    does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
26279   *    If the expression evaluates to a RegExp object, then this is used directly.
26280   *    If the expression evaluates to a string, then it will be converted to a RegExp
26281   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
26282   *    `new RegExp('^abc$')`.<br />
26283   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
26284   *    start at the index of the last search's match, thus not taking the whole input value into
26285   *    account.
26286   * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
26287   *    interaction with the input element.
26288   *
26289   * @example
26290      <example name="email-input-directive" module="emailExample">
26291        <file name="index.html">
26292         <script>
26293           angular.module('emailExample', [])
26294             .controller('ExampleController', ['$scope', function($scope) {
26295               $scope.email = {
26296                 text: '[email protected]'
26297               };
26298             }]);
26299         </script>
26300           <form name="myForm" ng-controller="ExampleController">
26301             <label>Email:
26302               <input type="email" name="input" ng-model="email.text" required>
26303             </label>
26304             <div role="alert">
26305               <span class="error" ng-show="myForm.input.$error.required">
26306                 Required!</span>
26307               <span class="error" ng-show="myForm.input.$error.email">
26308                 Not valid email!</span>
26309             </div>
26310             <tt>text = {{email.text}}</tt><br/>
26311             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
26312             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
26313             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
26314             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
26315             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
26316           </form>
26317         </file>
26318        <file name="protractor.js" type="protractor">
26319          var text = element(by.binding('email.text'));
26320          var valid = element(by.binding('myForm.input.$valid'));
26321          var input = element(by.model('email.text'));
26322
26323          it('should initialize to model', function() {
26324            expect(text.getText()).toContain('[email protected]');
26325            expect(valid.getText()).toContain('true');
26326          });
26327
26328          it('should be invalid if empty', function() {
26329            input.clear();
26330            input.sendKeys('');
26331            expect(text.getText()).toEqual('text =');
26332            expect(valid.getText()).toContain('false');
26333          });
26334
26335          it('should be invalid if not email', function() {
26336            input.clear();
26337            input.sendKeys('xxx');
26338
26339            expect(valid.getText()).toContain('false');
26340          });
26341        </file>
26342      </example>
26343   */
26344  'email': emailInputType,
26345
26346
26347  /**
26348   * @ngdoc input
26349   * @name input[radio]
26350   *
26351   * @description
26352   * HTML radio button.
26353   *
26354   * **Note:**<br>
26355   * All inputs controlled by {@link ngModel ngModel} (including those of type `radio`) will use the
26356   * value of their `name` attribute to determine the property under which their
26357   * {@link ngModel.NgModelController NgModelController} will be published on the parent
26358   * {@link form.FormController FormController}. Thus, if you use the same `name` for multiple
26359   * inputs of a form (e.g. a group of radio inputs), only _one_ `NgModelController` will be
26360   * published on the parent `FormController` under that name. The rest of the controllers will
26361   * continue to work as expected, but you won't be able to access them as properties on the parent
26362   * `FormController`.
26363   *
26364   * <div class="alert alert-info">
26365   *   <p>
26366   *     In plain HTML forms, the `name` attribute is used to identify groups of radio inputs, so
26367   *     that the browser can manage their state (checked/unchecked) based on the state of other
26368   *     inputs in the same group.
26369   *   </p>
26370   *   <p>
26371   *     In AngularJS forms, this is not necessary. The input's state will be updated based on the
26372   *     value of the underlying model data.
26373   *   </p>
26374   * </div>
26375   *
26376   * <div class="alert alert-success">
26377   *   If you omit the `name` attribute on a radio input, `ngModel` will automatically assign it a
26378   *   unique name.
26379   * </div>
26380   *
26381   * @param {string} ngModel Assignable AngularJS expression to data-bind to.
26382   * @param {string} value The value to which the `ngModel` expression should be set when selected.
26383   *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
26384   *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
26385   * @param {string=} name Property name of the form under which the control is published.
26386   * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
26387   *    interaction with the input element.
26388   * @param {string} ngValue AngularJS expression to which `ngModel` will be be set when the radio
26389   *    is selected. Should be used instead of the `value` attribute if you need
26390   *    a non-string `ngModel` (`boolean`, `array`, ...).
26391   *
26392   * @example
26393      <example name="radio-input-directive" module="radioExample">
26394        <file name="index.html">
26395         <script>
26396           angular.module('radioExample', [])
26397             .controller('ExampleController', ['$scope', function($scope) {
26398               $scope.color = {
26399                 name: 'blue'
26400               };
26401               $scope.specialValue = {
26402                 "id": "12345",
26403                 "value": "green"
26404               };
26405             }]);
26406         </script>
26407         <form name="myForm" ng-controller="ExampleController">
26408           <label>
26409             <input type="radio" ng-model="color.name" value="red">
26410             Red
26411           </label><br/>
26412           <label>
26413             <input type="radio" ng-model="color.name" ng-value="specialValue">
26414             Green
26415           </label><br/>
26416           <label>
26417             <input type="radio" ng-model="color.name" value="blue">
26418             Blue
26419           </label><br/>
26420           <tt>color = {{color.name | json}}</tt><br/>
26421          </form>
26422          Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
26423        </file>
26424        <file name="protractor.js" type="protractor">
26425          it('should change state', function() {
26426            var inputs = element.all(by.model('color.name'));
26427            var color = element(by.binding('color.name'));
26428
26429            expect(color.getText()).toContain('blue');
26430
26431            inputs.get(0).click();
26432            expect(color.getText()).toContain('red');
26433
26434            inputs.get(1).click();
26435            expect(color.getText()).toContain('green');
26436          });
26437        </file>
26438      </example>
26439   */
26440  'radio': radioInputType,
26441
26442  /**
26443   * @ngdoc input
26444   * @name input[range]
26445   *
26446   * @description
26447   * Native range input with validation and transformation.
26448   *
26449   * The model for the range input must always be a `Number`.
26450   *
26451   * IE9 and other browsers that do not support the `range` type fall back
26452   * to a text input without any default values for `min`, `max` and `step`. Model binding,
26453   * validation and number parsing are nevertheless supported.
26454   *
26455   * Browsers that support range (latest Chrome, Safari, Firefox, Edge) treat `input[range]`
26456   * in a way that never allows the input to hold an invalid value. That means:
26457   * - any non-numerical value is set to `(max + min) / 2`.
26458   * - any numerical value that is less than the current min val, or greater than the current max val
26459   * is set to the min / max val respectively.
26460   * - additionally, the current `step` is respected, so the nearest value that satisfies a step
26461   * is used.
26462   *
26463   * See the [HTML Spec on input[type=range]](https://www.w3.org/TR/html5/forms.html#range-state-(type=range))
26464   * for more info.
26465   *
26466   * This has the following consequences for AngularJS:
26467   *
26468   * Since the element value should always reflect the current model value, a range input
26469   * will set the bound ngModel expression to the value that the browser has set for the
26470   * input element. For example, in the following input `<input type="range" ng-model="model.value">`,
26471   * if the application sets `model.value = null`, the browser will set the input to `'50'`.
26472   * AngularJS will then set the model to `50`, to prevent input and model value being out of sync.
26473   *
26474   * That means the model for range will immediately be set to `50` after `ngModel` has been
26475   * initialized. It also means a range input can never have the required error.
26476   *
26477   * This does not only affect changes to the model value, but also to the values of the `min`,
26478   * `max`, and `step` attributes. When these change in a way that will cause the browser to modify
26479   * the input value, AngularJS will also update the model value.
26480   *
26481   * Automatic value adjustment also means that a range input element can never have the `required`,
26482   * `min`, or `max` errors.
26483   *
26484   * However, `step` is currently only fully implemented by Firefox. Other browsers have problems
26485   * when the step value changes dynamically - they do not adjust the element value correctly, but
26486   * instead may set the `stepMismatch` error. If that's the case, the AngularJS will set the `step`
26487   * error on the input, and set the model to `undefined`.
26488   *
26489   * Note that `input[range]` is not compatible with`ngMax`, `ngMin`, and `ngStep`, because they do
26490   * not set the `min` and `max` attributes, which means that the browser won't automatically adjust
26491   * the input value based on their values, and will always assume min = 0, max = 100, and step = 1.
26492   *
26493   * @param {string}  ngModel Assignable AngularJS expression to data-bind to.
26494   * @param {string=} name Property name of the form under which the control is published.
26495   * @param {string=} min Sets the `min` validation to ensure that the value entered is greater
26496   *                  than `min`. Can be interpolated.
26497   * @param {string=} max Sets the `max` validation to ensure that the value entered is less than `max`.
26498   *                  Can be interpolated.
26499   * @param {string=} step Sets the `step` validation to ensure that the value entered matches the `step`
26500   *                  Can be interpolated.
26501   * @param {expression=} ngChange AngularJS expression to be executed when the ngModel value changes due
26502   *                      to user interaction with the input element.
26503   * @param {expression=} ngChecked If the expression is truthy, then the `checked` attribute will be set on the
26504   *                      element. **Note** : `ngChecked` should not be used alongside `ngModel`.
26505   *                      Checkout {@link ng.directive:ngChecked ngChecked} for usage.
26506   *
26507   * @example
26508      <example name="range-input-directive" module="rangeExample">
26509        <file name="index.html">
26510          <script>
26511            angular.module('rangeExample', [])
26512              .controller('ExampleController', ['$scope', function($scope) {
26513                $scope.value = 75;
26514                $scope.min = 10;
26515                $scope.max = 90;
26516              }]);
26517          </script>
26518          <form name="myForm" ng-controller="ExampleController">
26519
26520            Model as range: <input type="range" name="range" ng-model="value" min="{{min}}"  max="{{max}}">
26521            <hr>
26522            Model as number: <input type="number" ng-model="value"><br>
26523            Min: <input type="number" ng-model="min"><br>
26524            Max: <input type="number" ng-model="max"><br>
26525            value = <code>{{value}}</code><br/>
26526            myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/>
26527            myForm.range.$error = <code>{{myForm.range.$error}}</code>
26528          </form>
26529        </file>
26530      </example>
26531
26532   * ## Range Input with ngMin & ngMax attributes
26533
26534   * @example
26535      <example name="range-input-directive-ng" module="rangeExample">
26536        <file name="index.html">
26537          <script>
26538            angular.module('rangeExample', [])
26539              .controller('ExampleController', ['$scope', function($scope) {
26540                $scope.value = 75;
26541                $scope.min = 10;
26542                $scope.max = 90;
26543              }]);
26544          </script>
26545          <form name="myForm" ng-controller="ExampleController">
26546            Model as range: <input type="range" name="range" ng-model="value" ng-min="min" ng-max="max">
26547            <hr>
26548            Model as number: <input type="number" ng-model="value"><br>
26549            Min: <input type="number" ng-model="min"><br>
26550            Max: <input type="number" ng-model="max"><br>
26551            value = <code>{{value}}</code><br/>
26552            myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/>
26553            myForm.range.$error = <code>{{myForm.range.$error}}</code>
26554          </form>
26555        </file>
26556      </example>
26557
26558   */
26559  'range': rangeInputType,
26560
26561  /**
26562   * @ngdoc input
26563   * @name input[checkbox]
26564   *
26565   * @description
26566   * HTML checkbox.
26567   *
26568   * @param {string} ngModel Assignable AngularJS expression to data-bind to.
26569   * @param {string=} name Property name of the form under which the control is published.
26570   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
26571   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
26572   * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
26573   *    interaction with the input element.
26574   *
26575   * @example
26576      <example name="checkbox-input-directive" module="checkboxExample">
26577        <file name="index.html">
26578         <script>
26579           angular.module('checkboxExample', [])
26580             .controller('ExampleController', ['$scope', function($scope) {
26581               $scope.checkboxModel = {
26582                value1 : true,
26583                value2 : 'YES'
26584              };
26585             }]);
26586         </script>
26587         <form name="myForm" ng-controller="ExampleController">
26588           <label>Value1:
26589             <input type="checkbox" ng-model="checkboxModel.value1">
26590           </label><br/>
26591           <label>Value2:
26592             <input type="checkbox" ng-model="checkboxModel.value2"
26593                    ng-true-value="'YES'" ng-false-value="'NO'">
26594            </label><br/>
26595           <tt>value1 = {{checkboxModel.value1}}</tt><br/>
26596           <tt>value2 = {{checkboxModel.value2}}</tt><br/>
26597          </form>
26598        </file>
26599        <file name="protractor.js" type="protractor">
26600          it('should change state', function() {
26601            var value1 = element(by.binding('checkboxModel.value1'));
26602            var value2 = element(by.binding('checkboxModel.value2'));
26603
26604            expect(value1.getText()).toContain('true');
26605            expect(value2.getText()).toContain('YES');
26606
26607            element(by.model('checkboxModel.value1')).click();
26608            element(by.model('checkboxModel.value2')).click();
26609
26610            expect(value1.getText()).toContain('false');
26611            expect(value2.getText()).toContain('NO');
26612          });
26613        </file>
26614      </example>
26615   */
26616  'checkbox': checkboxInputType,
26617
26618  'hidden': noop,
26619  'button': noop,
26620  'submit': noop,
26621  'reset': noop,
26622  'file': noop
26623};
26624
26625function stringBasedInputType(ctrl) {
26626  ctrl.$formatters.push(function(value) {
26627    return ctrl.$isEmpty(value) ? value : value.toString();
26628  });
26629}
26630
26631function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
26632  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
26633  stringBasedInputType(ctrl);
26634}
26635
26636function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
26637  var type = lowercase(element[0].type);
26638
26639  // In composition mode, users are still inputting intermediate text buffer,
26640  // hold the listener until composition is done.
26641  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
26642  if (!$sniffer.android) {
26643    var composing = false;
26644
26645    element.on('compositionstart', function() {
26646      composing = true;
26647    });
26648
26649    // Support: IE9+
26650    element.on('compositionupdate', function(ev) {
26651      // End composition when ev.data is empty string on 'compositionupdate' event.
26652      // When the input de-focusses (e.g. by clicking away), IE triggers 'compositionupdate'
26653      // instead of 'compositionend'.
26654      if (isUndefined(ev.data) || ev.data === '') {
26655        composing = false;
26656      }
26657    });
26658
26659    element.on('compositionend', function() {
26660      composing = false;
26661      listener();
26662    });
26663  }
26664
26665  var timeout;
26666
26667  var listener = function(ev) {
26668    if (timeout) {
26669      $browser.defer.cancel(timeout);
26670      timeout = null;
26671    }
26672    if (composing) return;
26673    var value = element.val(),
26674        event = ev && ev.type;
26675
26676    // By default we will trim the value
26677    // If the attribute ng-trim exists we will avoid trimming
26678    // If input type is 'password', the value is never trimmed
26679    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
26680      value = trim(value);
26681    }
26682
26683    // If a control is suffering from bad input (due to native validators), browsers discard its
26684    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
26685    // control's value is the same empty value twice in a row.
26686    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
26687      ctrl.$setViewValue(value, event);
26688    }
26689  };
26690
26691  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
26692  // input event on backspace, delete or cut
26693  if ($sniffer.hasEvent('input')) {
26694    element.on('input', listener);
26695  } else {
26696    var deferListener = function(ev, input, origValue) {
26697      if (!timeout) {
26698        timeout = $browser.defer(function() {
26699          timeout = null;
26700          if (!input || input.value !== origValue) {
26701            listener(ev);
26702          }
26703        });
26704      }
26705    };
26706
26707    element.on('keydown', /** @this */ function(event) {
26708      var key = event.keyCode;
26709
26710      // ignore
26711      //    command            modifiers                   arrows
26712      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
26713
26714      deferListener(event, this, this.value);
26715    });
26716
26717    // if user modifies input value using context menu in IE, we need "paste", "cut" and "drop" events to catch it
26718    if ($sniffer.hasEvent('paste')) {
26719      element.on('paste cut drop', deferListener);
26720    }
26721  }
26722
26723  // if user paste into input using mouse on older browser
26724  // or form autocomplete on newer browser, we need "change" event to catch it
26725  element.on('change', listener);
26726
26727  // Some native input types (date-family) have the ability to change validity without
26728  // firing any input/change events.
26729  // For these event types, when native validators are present and the browser supports the type,
26730  // check for validity changes on various DOM events.
26731  if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
26732    element.on(PARTIAL_VALIDATION_EVENTS, /** @this */ function(ev) {
26733      if (!timeout) {
26734        var validity = this[VALIDITY_STATE_PROPERTY];
26735        var origBadInput = validity.badInput;
26736        var origTypeMismatch = validity.typeMismatch;
26737        timeout = $browser.defer(function() {
26738          timeout = null;
26739          if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
26740            listener(ev);
26741          }
26742        });
26743      }
26744    });
26745  }
26746
26747  ctrl.$render = function() {
26748    // Workaround for Firefox validation #12102.
26749    var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
26750    if (element.val() !== value) {
26751      element.val(value);
26752    }
26753  };
26754}
26755
26756function weekParser(isoWeek, existingDate) {
26757  if (isDate(isoWeek)) {
26758    return isoWeek;
26759  }
26760
26761  if (isString(isoWeek)) {
26762    WEEK_REGEXP.lastIndex = 0;
26763    var parts = WEEK_REGEXP.exec(isoWeek);
26764    if (parts) {
26765      var year = +parts[1],
26766          week = +parts[2],
26767          hours = 0,
26768          minutes = 0,
26769          seconds = 0,
26770          milliseconds = 0,
26771          firstThurs = getFirstThursdayOfYear(year),
26772          addDays = (week - 1) * 7;
26773
26774      if (existingDate) {
26775        hours = existingDate.getHours();
26776        minutes = existingDate.getMinutes();
26777        seconds = existingDate.getSeconds();
26778        milliseconds = existingDate.getMilliseconds();
26779      }
26780
26781      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
26782    }
26783  }
26784
26785  return NaN;
26786}
26787
26788function createDateParser(regexp, mapping) {
26789  return function(iso, previousDate) {
26790    var parts, map;
26791
26792    if (isDate(iso)) {
26793      return iso;
26794    }
26795
26796    if (isString(iso)) {
26797      // When a date is JSON'ified to wraps itself inside of an extra
26798      // set of double quotes. This makes the date parsing code unable
26799      // to match the date string and parse it as a date.
26800      if (iso.charAt(0) === '"' && iso.charAt(iso.length - 1) === '"') {
26801        iso = iso.substring(1, iso.length - 1);
26802      }
26803      if (ISO_DATE_REGEXP.test(iso)) {
26804        return new Date(iso);
26805      }
26806      regexp.lastIndex = 0;
26807      parts = regexp.exec(iso);
26808
26809      if (parts) {
26810        parts.shift();
26811        if (previousDate) {
26812          map = {
26813            yyyy: previousDate.getFullYear(),
26814            MM: previousDate.getMonth() + 1,
26815            dd: previousDate.getDate(),
26816            HH: previousDate.getHours(),
26817            mm: previousDate.getMinutes(),
26818            ss: previousDate.getSeconds(),
26819            sss: previousDate.getMilliseconds() / 1000
26820          };
26821        } else {
26822          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
26823        }
26824
26825        forEach(parts, function(part, index) {
26826          if (index < mapping.length) {
26827            map[mapping[index]] = +part;
26828          }
26829        });
26830
26831        var date = new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
26832        if (map.yyyy < 100) {
26833          // In the constructor, 2-digit years map to 1900-1999.
26834          // Use `setFullYear()` to set the correct year.
26835          date.setFullYear(map.yyyy);
26836        }
26837
26838        return date;
26839      }
26840    }
26841
26842    return NaN;
26843  };
26844}
26845
26846function createDateInputType(type, regexp, parseDate, format) {
26847  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
26848    badInputChecker(scope, element, attr, ctrl, type);
26849    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
26850
26851    var isTimeType = type === 'time' || type === 'datetimelocal';
26852    var previousDate;
26853    var previousTimezone;
26854
26855    ctrl.$parsers.push(function(value) {
26856      if (ctrl.$isEmpty(value)) return null;
26857
26858      if (regexp.test(value)) {
26859        // Note: We cannot read ctrl.$modelValue, as there might be a different
26860        // parser/formatter in the processing chain so that the model
26861        // contains some different data format!
26862        return parseDateAndConvertTimeZoneToLocal(value, previousDate);
26863      }
26864      ctrl.$$parserName = type;
26865      return undefined;
26866    });
26867
26868    ctrl.$formatters.push(function(value) {
26869      if (value && !isDate(value)) {
26870        throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
26871      }
26872      if (isValidDate(value)) {
26873        previousDate = value;
26874        var timezone = ctrl.$options.getOption('timezone');
26875
26876        if (timezone) {
26877          previousTimezone = timezone;
26878          previousDate = convertTimezoneToLocal(previousDate, timezone, true);
26879        }
26880
26881        return formatter(value, timezone);
26882      } else {
26883        previousDate = null;
26884        previousTimezone = null;
26885        return '';
26886      }
26887    });
26888
26889    if (isDefined(attr.min) || attr.ngMin) {
26890      var minVal = attr.min || $parse(attr.ngMin)(scope);
26891      var parsedMinVal = parseObservedDateValue(minVal);
26892
26893      ctrl.$validators.min = function(value) {
26894        return !isValidDate(value) || isUndefined(parsedMinVal) || parseDate(value) >= parsedMinVal;
26895      };
26896      attr.$observe('min', function(val) {
26897        if (val !== minVal) {
26898          parsedMinVal = parseObservedDateValue(val);
26899          minVal = val;
26900          ctrl.$validate();
26901        }
26902      });
26903    }
26904
26905    if (isDefined(attr.max) || attr.ngMax) {
26906      var maxVal = attr.max || $parse(attr.ngMax)(scope);
26907      var parsedMaxVal = parseObservedDateValue(maxVal);
26908
26909      ctrl.$validators.max = function(value) {
26910        return !isValidDate(value) || isUndefined(parsedMaxVal) || parseDate(value) <= parsedMaxVal;
26911      };
26912      attr.$observe('max', function(val) {
26913        if (val !== maxVal) {
26914          parsedMaxVal = parseObservedDateValue(val);
26915          maxVal = val;
26916          ctrl.$validate();
26917        }
26918      });
26919    }
26920
26921    function isValidDate(value) {
26922      // Invalid Date: getTime() returns NaN
26923      return value && !(value.getTime && value.getTime() !== value.getTime());
26924    }
26925
26926    function parseObservedDateValue(val) {
26927      return isDefined(val) && !isDate(val) ? parseDateAndConvertTimeZoneToLocal(val) || undefined : val;
26928    }
26929
26930    function parseDateAndConvertTimeZoneToLocal(value, previousDate) {
26931      var timezone = ctrl.$options.getOption('timezone');
26932
26933      if (previousTimezone && previousTimezone !== timezone) {
26934        // If the timezone has changed, adjust the previousDate to the default timezone
26935        // so that the new date is converted with the correct timezone offset
26936        previousDate = addDateMinutes(previousDate, timezoneToOffset(previousTimezone));
26937      }
26938
26939      var parsedDate = parseDate(value, previousDate);
26940
26941      if (!isNaN(parsedDate) && timezone) {
26942        parsedDate = convertTimezoneToLocal(parsedDate, timezone);
26943      }
26944      return parsedDate;
26945    }
26946
26947    function formatter(value, timezone) {
26948      var targetFormat = format;
26949
26950      if (isTimeType && isString(ctrl.$options.getOption('timeSecondsFormat'))) {
26951        targetFormat = format
26952          .replace('ss.sss', ctrl.$options.getOption('timeSecondsFormat'))
26953          .replace(/:$/, '');
26954      }
26955
26956      var formatted =  $filter('date')(value, targetFormat, timezone);
26957
26958      if (isTimeType && ctrl.$options.getOption('timeStripZeroSeconds')) {
26959        formatted = formatted.replace(/(?::00)?(?:\.000)?$/, '');
26960      }
26961
26962      return formatted;
26963    }
26964  };
26965}
26966
26967function badInputChecker(scope, element, attr, ctrl, parserName) {
26968  var node = element[0];
26969  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
26970  if (nativeValidation) {
26971    ctrl.$parsers.push(function(value) {
26972      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
26973      if (validity.badInput || validity.typeMismatch) {
26974        ctrl.$$parserName = parserName;
26975        return undefined;
26976      }
26977
26978      return value;
26979    });
26980  }
26981}
26982
26983function numberFormatterParser(ctrl) {
26984  ctrl.$parsers.push(function(value) {
26985    if (ctrl.$isEmpty(value))      return null;
26986    if (NUMBER_REGEXP.test(value)) return parseFloat(value);
26987
26988    ctrl.$$parserName = 'number';
26989    return undefined;
26990  });
26991
26992  ctrl.$formatters.push(function(value) {
26993    if (!ctrl.$isEmpty(value)) {
26994      if (!isNumber(value)) {
26995        throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
26996      }
26997      value = value.toString();
26998    }
26999    return value;
27000  });
27001}
27002
27003function parseNumberAttrVal(val) {
27004  if (isDefined(val) && !isNumber(val)) {
27005    val = parseFloat(val);
27006  }
27007  return !isNumberNaN(val) ? val : undefined;
27008}
27009
27010function isNumberInteger(num) {
27011  // See http://stackoverflow.com/questions/14636536/how-to-check-if-a-variable-is-an-integer-in-javascript#14794066
27012  // (minus the assumption that `num` is a number)
27013
27014  // eslint-disable-next-line no-bitwise
27015  return (num | 0) === num;
27016}
27017
27018function countDecimals(num) {
27019  var numString = num.toString();
27020  var decimalSymbolIndex = numString.indexOf('.');
27021
27022  if (decimalSymbolIndex === -1) {
27023    if (-1 < num && num < 1) {
27024      // It may be in the exponential notation format (`1e-X`)
27025      var match = /e-(\d+)$/.exec(numString);
27026
27027      if (match) {
27028        return Number(match[1]);
27029      }
27030    }
27031
27032    return 0;
27033  }
27034
27035  return numString.length - decimalSymbolIndex - 1;
27036}
27037
27038function isValidForStep(viewValue, stepBase, step) {
27039  // At this point `stepBase` and `step` are expected to be non-NaN values
27040  // and `viewValue` is expected to be a valid stringified number.
27041  var value = Number(viewValue);
27042
27043  var isNonIntegerValue = !isNumberInteger(value);
27044  var isNonIntegerStepBase = !isNumberInteger(stepBase);
27045  var isNonIntegerStep = !isNumberInteger(step);
27046
27047  // Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or
27048  // `0.5 % 0.1 !== 0`), we need to convert all numbers to integers.
27049  if (isNonIntegerValue || isNonIntegerStepBase || isNonIntegerStep) {
27050    var valueDecimals = isNonIntegerValue ? countDecimals(value) : 0;
27051    var stepBaseDecimals = isNonIntegerStepBase ? countDecimals(stepBase) : 0;
27052    var stepDecimals = isNonIntegerStep ? countDecimals(step) : 0;
27053
27054    var decimalCount = Math.max(valueDecimals, stepBaseDecimals, stepDecimals);
27055    var multiplier = Math.pow(10, decimalCount);
27056
27057    value = value * multiplier;
27058    stepBase = stepBase * multiplier;
27059    step = step * multiplier;
27060
27061    if (isNonIntegerValue) value = Math.round(value);
27062    if (isNonIntegerStepBase) stepBase = Math.round(stepBase);
27063    if (isNonIntegerStep) step = Math.round(step);
27064  }
27065
27066  return (value - stepBase) % step === 0;
27067}
27068
27069function numberInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
27070  badInputChecker(scope, element, attr, ctrl, 'number');
27071  numberFormatterParser(ctrl);
27072  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
27073
27074  var parsedMinVal;
27075
27076  if (isDefined(attr.min) || attr.ngMin) {
27077    var minVal = attr.min || $parse(attr.ngMin)(scope);
27078    parsedMinVal = parseNumberAttrVal(minVal);
27079
27080    ctrl.$validators.min = function(modelValue, viewValue) {
27081      return ctrl.$isEmpty(viewValue) || isUndefined(parsedMinVal) || viewValue >= parsedMinVal;
27082    };
27083
27084    attr.$observe('min', function(val) {
27085      if (val !== minVal) {
27086        parsedMinVal = parseNumberAttrVal(val);
27087        minVal = val;
27088        // TODO(matsko): implement validateLater to reduce number of validations
27089        ctrl.$validate();
27090      }
27091    });
27092  }
27093
27094  if (isDefined(attr.max) || attr.ngMax) {
27095    var maxVal = attr.max || $parse(attr.ngMax)(scope);
27096    var parsedMaxVal = parseNumberAttrVal(maxVal);
27097
27098    ctrl.$validators.max = function(modelValue, viewValue) {
27099      return ctrl.$isEmpty(viewValue) || isUndefined(parsedMaxVal) || viewValue <= parsedMaxVal;
27100    };
27101
27102    attr.$observe('max', function(val) {
27103      if (val !== maxVal) {
27104        parsedMaxVal = parseNumberAttrVal(val);
27105        maxVal = val;
27106        // TODO(matsko): implement validateLater to reduce number of validations
27107        ctrl.$validate();
27108      }
27109    });
27110  }
27111
27112  if (isDefined(attr.step) || attr.ngStep) {
27113    var stepVal = attr.step || $parse(attr.ngStep)(scope);
27114    var parsedStepVal = parseNumberAttrVal(stepVal);
27115
27116    ctrl.$validators.step = function(modelValue, viewValue) {
27117      return ctrl.$isEmpty(viewValue) || isUndefined(parsedStepVal) ||
27118        isValidForStep(viewValue, parsedMinVal || 0, parsedStepVal);
27119    };
27120
27121    attr.$observe('step', function(val) {
27122      // TODO(matsko): implement validateLater to reduce number of validations
27123      if (val !== stepVal) {
27124        parsedStepVal = parseNumberAttrVal(val);
27125        stepVal = val;
27126        ctrl.$validate();
27127      }
27128
27129    });
27130
27131  }
27132}
27133
27134function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
27135  badInputChecker(scope, element, attr, ctrl, 'range');
27136  numberFormatterParser(ctrl);
27137  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
27138
27139  var supportsRange = ctrl.$$hasNativeValidators && element[0].type === 'range',
27140      minVal = supportsRange ? 0 : undefined,
27141      maxVal = supportsRange ? 100 : undefined,
27142      stepVal = supportsRange ? 1 : undefined,
27143      validity = element[0].validity,
27144      hasMinAttr = isDefined(attr.min),
27145      hasMaxAttr = isDefined(attr.max),
27146      hasStepAttr = isDefined(attr.step);
27147
27148  var originalRender = ctrl.$render;
27149
27150  ctrl.$render = supportsRange && isDefined(validity.rangeUnderflow) && isDefined(validity.rangeOverflow) ?
27151    //Browsers that implement range will set these values automatically, but reading the adjusted values after
27152    //$render would cause the min / max validators to be applied with the wrong value
27153    function rangeRender() {
27154      originalRender();
27155      ctrl.$setViewValue(element.val());
27156    } :
27157    originalRender;
27158
27159  if (hasMinAttr) {
27160    minVal = parseNumberAttrVal(attr.min);
27161
27162    ctrl.$validators.min = supportsRange ?
27163      // Since all browsers set the input to a valid value, we don't need to check validity
27164      function noopMinValidator() { return true; } :
27165      // non-support browsers validate the min val
27166      function minValidator(modelValue, viewValue) {
27167        return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal;
27168      };
27169
27170    setInitialValueAndObserver('min', minChange);
27171  }
27172
27173  if (hasMaxAttr) {
27174    maxVal = parseNumberAttrVal(attr.max);
27175
27176    ctrl.$validators.max = supportsRange ?
27177      // Since all browsers set the input to a valid value, we don't need to check validity
27178      function noopMaxValidator() { return true; } :
27179      // non-support browsers validate the max val
27180      function maxValidator(modelValue, viewValue) {
27181        return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal;
27182      };
27183
27184    setInitialValueAndObserver('max', maxChange);
27185  }
27186
27187  if (hasStepAttr) {
27188    stepVal = parseNumberAttrVal(attr.step);
27189
27190    ctrl.$validators.step = supportsRange ?
27191      function nativeStepValidator() {
27192        // Currently, only FF implements the spec on step change correctly (i.e. adjusting the
27193        // input element value to a valid value). It's possible that other browsers set the stepMismatch
27194        // validity error instead, so we can at least report an error in that case.
27195        return !validity.stepMismatch;
27196      } :
27197      // ngStep doesn't set the setp attr, so the browser doesn't adjust the input value as setting step would
27198      function stepValidator(modelValue, viewValue) {
27199        return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) ||
27200               isValidForStep(viewValue, minVal || 0, stepVal);
27201      };
27202
27203    setInitialValueAndObserver('step', stepChange);
27204  }
27205
27206  function setInitialValueAndObserver(htmlAttrName, changeFn) {
27207    // interpolated attributes set the attribute value only after a digest, but we need the
27208    // attribute value when the input is first rendered, so that the browser can adjust the
27209    // input value based on the min/max value
27210    element.attr(htmlAttrName, attr[htmlAttrName]);
27211    var oldVal = attr[htmlAttrName];
27212    attr.$observe(htmlAttrName, function wrappedObserver(val) {
27213      if (val !== oldVal) {
27214        oldVal = val;
27215        changeFn(val);
27216      }
27217    });
27218  }
27219
27220  function minChange(val) {
27221    minVal = parseNumberAttrVal(val);
27222    // ignore changes before model is initialized
27223    if (isNumberNaN(ctrl.$modelValue)) {
27224      return;
27225    }
27226
27227    if (supportsRange) {
27228      var elVal = element.val();
27229      // IE11 doesn't set the el val correctly if the minVal is greater than the element value
27230      if (minVal > elVal) {
27231        elVal = minVal;
27232        element.val(elVal);
27233      }
27234      ctrl.$setViewValue(elVal);
27235    } else {
27236      // TODO(matsko): implement validateLater to reduce number of validations
27237      ctrl.$validate();
27238    }
27239  }
27240
27241  function maxChange(val) {
27242    maxVal = parseNumberAttrVal(val);
27243    // ignore changes before model is initialized
27244    if (isNumberNaN(ctrl.$modelValue)) {
27245      return;
27246    }
27247
27248    if (supportsRange) {
27249      var elVal = element.val();
27250      // IE11 doesn't set the el val correctly if the maxVal is less than the element value
27251      if (maxVal < elVal) {
27252        element.val(maxVal);
27253        // IE11 and Chrome don't set the value to the minVal when max < min
27254        elVal = maxVal < minVal ? minVal : maxVal;
27255      }
27256      ctrl.$setViewValue(elVal);
27257    } else {
27258      // TODO(matsko): implement validateLater to reduce number of validations
27259      ctrl.$validate();
27260    }
27261  }
27262
27263  function stepChange(val) {
27264    stepVal = parseNumberAttrVal(val);
27265    // ignore changes before model is initialized
27266    if (isNumberNaN(ctrl.$modelValue)) {
27267      return;
27268    }
27269
27270    // Some browsers don't adjust the input value correctly, but set the stepMismatch error
27271    if (!supportsRange) {
27272      // TODO(matsko): implement validateLater to reduce number of validations
27273      ctrl.$validate();
27274    } else if (ctrl.$viewValue !== element.val()) {
27275      ctrl.$setViewValue(element.val());
27276    }
27277  }
27278}
27279
27280function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
27281  // Note: no badInputChecker here by purpose as `url` is only a validation
27282  // in browsers, i.e. we can always read out input.value even if it is not valid!
27283  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
27284  stringBasedInputType(ctrl);
27285
27286  ctrl.$validators.url = function(modelValue, viewValue) {
27287    var value = modelValue || viewValue;
27288    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
27289  };
27290}
27291
27292function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
27293  // Note: no badInputChecker here by purpose as `url` is only a validation
27294  // in browsers, i.e. we can always read out input.value even if it is not valid!
27295  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
27296  stringBasedInputType(ctrl);
27297
27298  ctrl.$validators.email = function(modelValue, viewValue) {
27299    var value = modelValue || viewValue;
27300    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
27301  };
27302}
27303
27304function radioInputType(scope, element, attr, ctrl) {
27305  var doTrim = !attr.ngTrim || trim(attr.ngTrim) !== 'false';
27306  // make the name unique, if not defined
27307  if (isUndefined(attr.name)) {
27308    element.attr('name', nextUid());
27309  }
27310
27311  var listener = function(ev) {
27312    var value;
27313    if (element[0].checked) {
27314      value = attr.value;
27315      if (doTrim) {
27316        value = trim(value);
27317      }
27318      ctrl.$setViewValue(value, ev && ev.type);
27319    }
27320  };
27321
27322  element.on('change', listener);
27323
27324  ctrl.$render = function() {
27325    var value = attr.value;
27326    if (doTrim) {
27327      value = trim(value);
27328    }
27329    element[0].checked = (value === ctrl.$viewValue);
27330  };
27331
27332  attr.$observe('value', ctrl.$render);
27333}
27334
27335function parseConstantExpr($parse, context, name, expression, fallback) {
27336  var parseFn;
27337  if (isDefined(expression)) {
27338    parseFn = $parse(expression);
27339    if (!parseFn.constant) {
27340      throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
27341                                   '`{1}`.', name, expression);
27342    }
27343    return parseFn(context);
27344  }
27345  return fallback;
27346}
27347
27348function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
27349  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
27350  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
27351
27352  var listener = function(ev) {
27353    ctrl.$setViewValue(element[0].checked, ev && ev.type);
27354  };
27355
27356  element.on('change', listener);
27357
27358  ctrl.$render = function() {
27359    element[0].checked = ctrl.$viewValue;
27360  };
27361
27362  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
27363  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
27364  // it to a boolean.
27365  ctrl.$isEmpty = function(value) {
27366    return value === false;
27367  };
27368
27369  ctrl.$formatters.push(function(value) {
27370    return equals(value, trueValue);
27371  });
27372
27373  ctrl.$parsers.push(function(value) {
27374    return value ? trueValue : falseValue;
27375  });
27376}
27377
27378
27379/**
27380 * @ngdoc directive
27381 * @name textarea
27382 * @restrict E
27383 *
27384 * @description
27385 * HTML textarea element control with AngularJS data-binding. The data-binding and validation
27386 * properties of this element are exactly the same as those of the
27387 * {@link ng.directive:input input element}.
27388 *
27389 * @param {string} ngModel Assignable AngularJS expression to data-bind to.
27390 * @param {string=} name Property name of the form under which the control is published.
27391 * @param {string=} required Sets `required` validation error key if the value is not entered.
27392 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
27393 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
27394 *    `required` when you want to data-bind to the `required` attribute.
27395 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
27396 *    minlength.
27397 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
27398 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
27399 *    length.
27400 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
27401 *    does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
27402 *    If the expression evaluates to a RegExp object, then this is used directly.
27403 *    If the expression evaluates to a string, then it will be converted to a RegExp
27404 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
27405 *    `new RegExp('^abc$')`.<br />
27406 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
27407 *    start at the index of the last search's match, thus not taking the whole input value into
27408 *    account.
27409 * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
27410 *    interaction with the input element.
27411 * @param {boolean=} [ngTrim=true] If set to false AngularJS will not automatically trim the input.
27412 *
27413 * @knownIssue
27414 *
27415 * When specifying the `placeholder` attribute of `<textarea>`, Internet Explorer will temporarily
27416 * insert the placeholder value as the textarea's content. If the placeholder value contains
27417 * interpolation (`{{ ... }}`), an error will be logged in the console when AngularJS tries to update
27418 * the value of the by-then-removed text node. This doesn't affect the functionality of the
27419 * textarea, but can be undesirable.
27420 *
27421 * You can work around this Internet Explorer issue by using `ng-attr-placeholder` instead of
27422 * `placeholder` on textareas, whenever you need interpolation in the placeholder value. You can
27423 * find more details on `ngAttr` in the
27424 * [Interpolation](guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes) section of the
27425 * Developer Guide.
27426 */
27427
27428
27429/**
27430 * @ngdoc directive
27431 * @name input
27432 * @restrict E
27433 *
27434 * @description
27435 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
27436 * input state control, and validation.
27437 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
27438 *
27439 * <div class="alert alert-warning">
27440 * **Note:** Not every feature offered is available for all input types.
27441 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
27442 * </div>
27443 *
27444 * @param {string} ngModel Assignable AngularJS expression to data-bind to.
27445 * @param {string=} name Property name of the form under which the control is published.
27446 * @param {string=} required Sets `required` validation error key if the value is not entered.
27447 * @param {boolean=} ngRequired Sets `required` attribute if set to true
27448 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
27449 *    minlength.
27450 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
27451 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
27452 *    length.
27453 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
27454 *    value does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
27455 *    If the expression evaluates to a RegExp object, then this is used directly.
27456 *    If the expression evaluates to a string, then it will be converted to a RegExp
27457 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
27458 *    `new RegExp('^abc$')`.<br />
27459 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
27460 *    start at the index of the last search's match, thus not taking the whole input value into
27461 *    account.
27462 * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
27463 *    interaction with the input element.
27464 * @param {boolean=} [ngTrim=true] If set to false AngularJS will not automatically trim the input.
27465 *    This parameter is ignored for input[type=password] controls, which will never trim the
27466 *    input.
27467 *
27468 * @example
27469    <example name="input-directive" module="inputExample">
27470      <file name="index.html">
27471       <script>
27472          angular.module('inputExample', [])
27473            .controller('ExampleController', ['$scope', function($scope) {
27474              $scope.user = {name: 'guest', last: 'visitor'};
27475            }]);
27476       </script>
27477       <div ng-controller="ExampleController">
27478         <form name="myForm">
27479           <label>
27480              User name:
27481              <input type="text" name="userName" ng-model="user.name" required>
27482           </label>
27483           <div role="alert">
27484             <span class="error" ng-show="myForm.userName.$error.required">
27485              Required!</span>
27486           </div>
27487           <label>
27488              Last name:
27489              <input type="text" name="lastName" ng-model="user.last"
27490              ng-minlength="3" ng-maxlength="10">
27491           </label>
27492           <div role="alert">
27493             <span class="error" ng-show="myForm.lastName.$error.minlength">
27494               Too short!</span>
27495             <span class="error" ng-show="myForm.lastName.$error.maxlength">
27496               Too long!</span>
27497           </div>
27498         </form>
27499         <hr>
27500         <tt>user = {{user}}</tt><br/>
27501         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
27502         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
27503         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
27504         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
27505         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
27506         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
27507         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
27508         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
27509       </div>
27510      </file>
27511      <file name="protractor.js" type="protractor">
27512        var user = element(by.exactBinding('user'));
27513        var userNameValid = element(by.binding('myForm.userName.$valid'));
27514        var lastNameValid = element(by.binding('myForm.lastName.$valid'));
27515        var lastNameError = element(by.binding('myForm.lastName.$error'));
27516        var formValid = element(by.binding('myForm.$valid'));
27517        var userNameInput = element(by.model('user.name'));
27518        var userLastInput = element(by.model('user.last'));
27519
27520        it('should initialize to model', function() {
27521          expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
27522          expect(userNameValid.getText()).toContain('true');
27523          expect(formValid.getText()).toContain('true');
27524        });
27525
27526        it('should be invalid if empty when required', function() {
27527          userNameInput.clear();
27528          userNameInput.sendKeys('');
27529
27530          expect(user.getText()).toContain('{"last":"visitor"}');
27531          expect(userNameValid.getText()).toContain('false');
27532          expect(formValid.getText()).toContain('false');
27533        });
27534
27535        it('should be valid if empty when min length is set', function() {
27536          userLastInput.clear();
27537          userLastInput.sendKeys('');
27538
27539          expect(user.getText()).toContain('{"name":"guest","last":""}');
27540          expect(lastNameValid.getText()).toContain('true');
27541          expect(formValid.getText()).toContain('true');
27542        });
27543
27544        it('should be invalid if less than required min length', function() {
27545          userLastInput.clear();
27546          userLastInput.sendKeys('xx');
27547
27548          expect(user.getText()).toContain('{"name":"guest"}');
27549          expect(lastNameValid.getText()).toContain('false');
27550          expect(lastNameError.getText()).toContain('minlength');
27551          expect(formValid.getText()).toContain('false');
27552        });
27553
27554        it('should be invalid if longer than max length', function() {
27555          userLastInput.clear();
27556          userLastInput.sendKeys('some ridiculously long name');
27557
27558          expect(user.getText()).toContain('{"name":"guest"}');
27559          expect(lastNameValid.getText()).toContain('false');
27560          expect(lastNameError.getText()).toContain('maxlength');
27561          expect(formValid.getText()).toContain('false');
27562        });
27563      </file>
27564    </example>
27565 */
27566var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
27567    function($browser, $sniffer, $filter, $parse) {
27568  return {
27569    restrict: 'E',
27570    require: ['?ngModel'],
27571    link: {
27572      pre: function(scope, element, attr, ctrls) {
27573        if (ctrls[0]) {
27574          (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
27575                                                              $browser, $filter, $parse);
27576        }
27577      }
27578    }
27579  };
27580}];
27581
27582
27583var hiddenInputBrowserCacheDirective = function() {
27584  var valueProperty = {
27585    configurable: true,
27586    enumerable: false,
27587    get: function() {
27588      return this.getAttribute('value') || '';
27589    },
27590    set: function(val) {
27591      this.setAttribute('value', val);
27592    }
27593  };
27594
27595  return {
27596    restrict: 'E',
27597    priority: 200,
27598    compile: function(_, attr) {
27599      if (lowercase(attr.type) !== 'hidden') {
27600        return;
27601      }
27602
27603      return {
27604        pre: function(scope, element, attr, ctrls) {
27605          var node = element[0];
27606
27607          // Support: Edge
27608          // Moving the DOM around prevents autofillling
27609          if (node.parentNode) {
27610            node.parentNode.insertBefore(node, node.nextSibling);
27611          }
27612
27613          // Support: FF, IE
27614          // Avoiding direct assignment to .value prevents autofillling
27615          if (Object.defineProperty) {
27616            Object.defineProperty(node, 'value', valueProperty);
27617          }
27618        }
27619      };
27620    }
27621  };
27622};
27623
27624
27625
27626var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
27627/**
27628 * @ngdoc directive
27629 * @name ngValue
27630 * @restrict A
27631 * @priority 100
27632 *
27633 * @description
27634 * Binds the given expression to the value of the element.
27635 *
27636 * It is mainly used on {@link input[radio] `input[radio]`} and option elements,
27637 * so that when the element is selected, the {@link ngModel `ngModel`} of that element (or its
27638 * {@link select `select`} parent element) is set to the bound value. It is especially useful
27639 * for dynamically generated lists using {@link ngRepeat `ngRepeat`}, as shown below.
27640 *
27641 * It can also be used to achieve one-way binding of a given expression to an input element
27642 * such as an `input[text]` or a `textarea`, when that element does not use ngModel.
27643 *
27644 * @element ANY
27645 * @param {string=} ngValue AngularJS expression, whose value will be bound to the `value` attribute
27646 * and `value` property of the element.
27647 *
27648 * @example
27649    <example name="ngValue-directive" module="valueExample">
27650      <file name="index.html">
27651       <script>
27652          angular.module('valueExample', [])
27653            .controller('ExampleController', ['$scope', function($scope) {
27654              $scope.names = ['pizza', 'unicorns', 'robots'];
27655              $scope.my = { favorite: 'unicorns' };
27656            }]);
27657       </script>
27658        <form ng-controller="ExampleController">
27659          <h2>Which is your favorite?</h2>
27660            <label ng-repeat="name in names" for="{{name}}">
27661              {{name}}
27662              <input type="radio"
27663                     ng-model="my.favorite"
27664                     ng-value="name"
27665                     id="{{name}}"
27666                     name="favorite">
27667            </label>
27668          <div>You chose {{my.favorite}}</div>
27669        </form>
27670      </file>
27671      <file name="protractor.js" type="protractor">
27672        var favorite = element(by.binding('my.favorite'));
27673
27674        it('should initialize to model', function() {
27675          expect(favorite.getText()).toContain('unicorns');
27676        });
27677        it('should bind the values to the inputs', function() {
27678          element.all(by.model('my.favorite')).get(0).click();
27679          expect(favorite.getText()).toContain('pizza');
27680        });
27681      </file>
27682    </example>
27683 */
27684var ngValueDirective = function() {
27685  /**
27686   *  inputs use the value attribute as their default value if the value property is not set.
27687   *  Once the value property has been set (by adding input), it will not react to changes to
27688   *  the value attribute anymore. Setting both attribute and property fixes this behavior, and
27689   *  makes it possible to use ngValue as a sort of one-way bind.
27690   */
27691  function updateElementValue(element, attr, value) {
27692    // Support: IE9 only
27693    // In IE9 values are converted to string (e.g. `input.value = null` results in `input.value === 'null'`).
27694    var propValue = isDefined(value) ? value : (msie === 9) ? '' : null;
27695    element.prop('value', propValue);
27696    attr.$set('value', value);
27697  }
27698
27699  return {
27700    restrict: 'A',
27701    priority: 100,
27702    compile: function(tpl, tplAttr) {
27703      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
27704        return function ngValueConstantLink(scope, elm, attr) {
27705          var value = scope.$eval(attr.ngValue);
27706          updateElementValue(elm, attr, value);
27707        };
27708      } else {
27709        return function ngValueLink(scope, elm, attr) {
27710          scope.$watch(attr.ngValue, function valueWatchAction(value) {
27711            updateElementValue(elm, attr, value);
27712          });
27713        };
27714      }
27715    }
27716  };
27717};
27718
27719/**
27720 * @ngdoc directive
27721 * @name ngBind
27722 * @restrict AC
27723 *
27724 * @description
27725 * The `ngBind` attribute tells AngularJS to replace the text content of the specified HTML element
27726 * with the value of a given expression, and to update the text content when the value of that
27727 * expression changes.
27728 *
27729 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
27730 * `{{ expression }}` which is similar but less verbose.
27731 *
27732 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
27733 * displayed by the browser in its raw state before AngularJS compiles it. Since `ngBind` is an
27734 * element attribute, it makes the bindings invisible to the user while the page is loading.
27735 *
27736 * An alternative solution to this problem would be using the
27737 * {@link ng.directive:ngCloak ngCloak} directive.
27738 *
27739 *
27740 * @element ANY
27741 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
27742 *
27743 * @example
27744 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
27745   <example module="bindExample" name="ng-bind">
27746     <file name="index.html">
27747       <script>
27748         angular.module('bindExample', [])
27749           .controller('ExampleController', ['$scope', function($scope) {
27750             $scope.name = 'Whirled';
27751           }]);
27752       </script>
27753       <div ng-controller="ExampleController">
27754         <label>Enter name: <input type="text" ng-model="name"></label><br>
27755         Hello <span ng-bind="name"></span>!
27756       </div>
27757     </file>
27758     <file name="protractor.js" type="protractor">
27759       it('should check ng-bind', function() {
27760         var nameInput = element(by.model('name'));
27761
27762         expect(element(by.binding('name')).getText()).toBe('Whirled');
27763         nameInput.clear();
27764         nameInput.sendKeys('world');
27765         expect(element(by.binding('name')).getText()).toBe('world');
27766       });
27767     </file>
27768   </example>
27769 */
27770var ngBindDirective = ['$compile', function($compile) {
27771  return {
27772    restrict: 'AC',
27773    compile: function ngBindCompile(templateElement) {
27774      $compile.$$addBindingClass(templateElement);
27775      return function ngBindLink(scope, element, attr) {
27776        $compile.$$addBindingInfo(element, attr.ngBind);
27777        element = element[0];
27778        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
27779          element.textContent = stringify(value);
27780        });
27781      };
27782    }
27783  };
27784}];
27785
27786
27787/**
27788 * @ngdoc directive
27789 * @name ngBindTemplate
27790 *
27791 * @description
27792 * The `ngBindTemplate` directive specifies that the element
27793 * text content should be replaced with the interpolation of the template
27794 * in the `ngBindTemplate` attribute.
27795 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
27796 * expressions. This directive is needed since some HTML elements
27797 * (such as TITLE and OPTION) cannot contain SPAN elements.
27798 *
27799 * @element ANY
27800 * @param {string} ngBindTemplate template of form
27801 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
27802 *
27803 * @example
27804 * Try it here: enter text in text box and watch the greeting change.
27805   <example module="bindExample" name="ng-bind-template">
27806     <file name="index.html">
27807       <script>
27808         angular.module('bindExample', [])
27809           .controller('ExampleController', ['$scope', function($scope) {
27810             $scope.salutation = 'Hello';
27811             $scope.name = 'World';
27812           }]);
27813       </script>
27814       <div ng-controller="ExampleController">
27815        <label>Salutation: <input type="text" ng-model="salutation"></label><br>
27816        <label>Name: <input type="text" ng-model="name"></label><br>
27817        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
27818       </div>
27819     </file>
27820     <file name="protractor.js" type="protractor">
27821       it('should check ng-bind', function() {
27822         var salutationElem = element(by.binding('salutation'));
27823         var salutationInput = element(by.model('salutation'));
27824         var nameInput = element(by.model('name'));
27825
27826         expect(salutationElem.getText()).toBe('Hello World!');
27827
27828         salutationInput.clear();
27829         salutationInput.sendKeys('Greetings');
27830         nameInput.clear();
27831         nameInput.sendKeys('user');
27832
27833         expect(salutationElem.getText()).toBe('Greetings user!');
27834       });
27835     </file>
27836   </example>
27837 */
27838var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
27839  return {
27840    compile: function ngBindTemplateCompile(templateElement) {
27841      $compile.$$addBindingClass(templateElement);
27842      return function ngBindTemplateLink(scope, element, attr) {
27843        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
27844        $compile.$$addBindingInfo(element, interpolateFn.expressions);
27845        element = element[0];
27846        attr.$observe('ngBindTemplate', function(value) {
27847          element.textContent = isUndefined(value) ? '' : value;
27848        });
27849      };
27850    }
27851  };
27852}];
27853
27854
27855/**
27856 * @ngdoc directive
27857 * @name ngBindHtml
27858 *
27859 * @description
27860 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
27861 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
27862 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
27863 * ngSanitize} in your module's dependencies (not in core AngularJS). In order to use {@link ngSanitize}
27864 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
27865 *
27866 * You may also bypass sanitization for values you know are safe. To do so, bind to
27867 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
27868 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
27869 *
27870 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
27871 * will have an exception (instead of an exploit.)
27872 *
27873 * @element ANY
27874 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
27875 *
27876 * @example
27877
27878   <example module="bindHtmlExample" deps="angular-sanitize.js" name="ng-bind-html">
27879     <file name="index.html">
27880       <div ng-controller="ExampleController">
27881        <p ng-bind-html="myHTML"></p>
27882       </div>
27883     </file>
27884
27885     <file name="script.js">
27886       angular.module('bindHtmlExample', ['ngSanitize'])
27887         .controller('ExampleController', ['$scope', function($scope) {
27888           $scope.myHTML =
27889              'I am an <code>HTML</code>string with ' +
27890              '<a href="#">links!</a> and other <em>stuff</em>';
27891         }]);
27892     </file>
27893
27894     <file name="protractor.js" type="protractor">
27895       it('should check ng-bind-html', function() {
27896         expect(element(by.binding('myHTML')).getText()).toBe(
27897             'I am an HTMLstring with links! and other stuff');
27898       });
27899     </file>
27900   </example>
27901 */
27902var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
27903  return {
27904    restrict: 'A',
27905    compile: function ngBindHtmlCompile(tElement, tAttrs) {
27906      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
27907      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function sceValueOf(val) {
27908        // Unwrap the value to compare the actual inner safe value, not the wrapper object.
27909        return $sce.valueOf(val);
27910      });
27911      $compile.$$addBindingClass(tElement);
27912
27913      return function ngBindHtmlLink(scope, element, attr) {
27914        $compile.$$addBindingInfo(element, attr.ngBindHtml);
27915
27916        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
27917          // The watched value is the unwrapped value. To avoid re-escaping, use the direct getter.
27918          var value = ngBindHtmlGetter(scope);
27919          element.html($sce.getTrustedHtml(value) || '');
27920        });
27921      };
27922    }
27923  };
27924}];
27925
27926/**
27927 * @ngdoc directive
27928 * @name ngChange
27929 * @restrict A
27930 *
27931 * @description
27932 * Evaluate the given expression when the user changes the input.
27933 * The expression is evaluated immediately, unlike the JavaScript onchange event
27934 * which only triggers at the end of a change (usually, when the user leaves the
27935 * form element or presses the return key).
27936 *
27937 * The `ngChange` expression is only evaluated when a change in the input value causes
27938 * a new value to be committed to the model.
27939 *
27940 * It will not be evaluated:
27941 * * if the value returned from the `$parsers` transformation pipeline has not changed
27942 * * if the input has continued to be invalid since the model will stay `null`
27943 * * if the model is changed programmatically and not by a change to the input value
27944 *
27945 *
27946 * Note, this directive requires `ngModel` to be present.
27947 *
27948 * @element ANY
27949 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
27950 * in input value.
27951 *
27952 * @example
27953 * <example name="ngChange-directive" module="changeExample">
27954 *   <file name="index.html">
27955 *     <script>
27956 *       angular.module('changeExample', [])
27957 *         .controller('ExampleController', ['$scope', function($scope) {
27958 *           $scope.counter = 0;
27959 *           $scope.change = function() {
27960 *             $scope.counter++;
27961 *           };
27962 *         }]);
27963 *     </script>
27964 *     <div ng-controller="ExampleController">
27965 *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
27966 *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
27967 *       <label for="ng-change-example2">Confirmed</label><br />
27968 *       <tt>debug = {{confirmed}}</tt><br/>
27969 *       <tt>counter = {{counter}}</tt><br/>
27970 *     </div>
27971 *   </file>
27972 *   <file name="protractor.js" type="protractor">
27973 *     var counter = element(by.binding('counter'));
27974 *     var debug = element(by.binding('confirmed'));
27975 *
27976 *     it('should evaluate the expression if changing from view', function() {
27977 *       expect(counter.getText()).toContain('0');
27978 *
27979 *       element(by.id('ng-change-example1')).click();
27980 *
27981 *       expect(counter.getText()).toContain('1');
27982 *       expect(debug.getText()).toContain('true');
27983 *     });
27984 *
27985 *     it('should not evaluate the expression if changing from model', function() {
27986 *       element(by.id('ng-change-example2')).click();
27987
27988 *       expect(counter.getText()).toContain('0');
27989 *       expect(debug.getText()).toContain('true');
27990 *     });
27991 *   </file>
27992 * </example>
27993 */
27994var ngChangeDirective = valueFn({
27995  restrict: 'A',
27996  require: 'ngModel',
27997  link: function(scope, element, attr, ctrl) {
27998    ctrl.$viewChangeListeners.push(function() {
27999      scope.$eval(attr.ngChange);
28000    });
28001  }
28002});
28003
28004/* exported
28005  ngClassDirective,
28006  ngClassEvenDirective,
28007  ngClassOddDirective
28008*/
28009
28010function classDirective(name, selector) {
28011  name = 'ngClass' + name;
28012  var indexWatchExpression;
28013
28014  return ['$parse', function($parse) {
28015    return {
28016      restrict: 'AC',
28017      link: function(scope, element, attr) {
28018        var classCounts = element.data('$classCounts');
28019        var oldModulo = true;
28020        var oldClassString;
28021
28022        if (!classCounts) {
28023          // Use createMap() to prevent class assumptions involving property
28024          // names in Object.prototype
28025          classCounts = createMap();
28026          element.data('$classCounts', classCounts);
28027        }
28028
28029        if (name !== 'ngClass') {
28030          if (!indexWatchExpression) {
28031            indexWatchExpression = $parse('$index', function moduloTwo($index) {
28032              // eslint-disable-next-line no-bitwise
28033              return $index & 1;
28034            });
28035          }
28036
28037          scope.$watch(indexWatchExpression, ngClassIndexWatchAction);
28038        }
28039
28040        scope.$watch($parse(attr[name], toClassString), ngClassWatchAction);
28041
28042        function addClasses(classString) {
28043          classString = digestClassCounts(split(classString), 1);
28044          attr.$addClass(classString);
28045        }
28046
28047        function removeClasses(classString) {
28048          classString = digestClassCounts(split(classString), -1);
28049          attr.$removeClass(classString);
28050        }
28051
28052        function updateClasses(oldClassString, newClassString) {
28053          var oldClassArray = split(oldClassString);
28054          var newClassArray = split(newClassString);
28055
28056          var toRemoveArray = arrayDifference(oldClassArray, newClassArray);
28057          var toAddArray = arrayDifference(newClassArray, oldClassArray);
28058
28059          var toRemoveString = digestClassCounts(toRemoveArray, -1);
28060          var toAddString = digestClassCounts(toAddArray, 1);
28061
28062          attr.$addClass(toAddString);
28063          attr.$removeClass(toRemoveString);
28064        }
28065
28066        function digestClassCounts(classArray, count) {
28067          var classesToUpdate = [];
28068
28069          forEach(classArray, function(className) {
28070            if (count > 0 || classCounts[className]) {
28071              classCounts[className] = (classCounts[className] || 0) + count;
28072              if (classCounts[className] === +(count > 0)) {
28073                classesToUpdate.push(className);
28074              }
28075            }
28076          });
28077
28078          return classesToUpdate.join(' ');
28079        }
28080
28081        function ngClassIndexWatchAction(newModulo) {
28082          // This watch-action should run before the `ngClassWatchAction()`, thus it
28083          // adds/removes `oldClassString`. If the `ngClass` expression has changed as well, the
28084          // `ngClassWatchAction()` will update the classes.
28085          if (newModulo === selector) {
28086            addClasses(oldClassString);
28087          } else {
28088            removeClasses(oldClassString);
28089          }
28090
28091          oldModulo = newModulo;
28092        }
28093
28094        function ngClassWatchAction(newClassString) {
28095          if (oldModulo === selector) {
28096            updateClasses(oldClassString, newClassString);
28097          }
28098
28099          oldClassString = newClassString;
28100        }
28101      }
28102    };
28103  }];
28104
28105  // Helpers
28106  function arrayDifference(tokens1, tokens2) {
28107    if (!tokens1 || !tokens1.length) return [];
28108    if (!tokens2 || !tokens2.length) return tokens1;
28109
28110    var values = [];
28111
28112    outer:
28113    for (var i = 0; i < tokens1.length; i++) {
28114      var token = tokens1[i];
28115      for (var j = 0; j < tokens2.length; j++) {
28116        if (token === tokens2[j]) continue outer;
28117      }
28118      values.push(token);
28119    }
28120
28121    return values;
28122  }
28123
28124  function split(classString) {
28125    return classString && classString.split(' ');
28126  }
28127
28128  function toClassString(classValue) {
28129    if (!classValue) return classValue;
28130
28131    var classString = classValue;
28132
28133    if (isArray(classValue)) {
28134      classString = classValue.map(toClassString).join(' ');
28135    } else if (isObject(classValue)) {
28136      classString = Object.keys(classValue).
28137        filter(function(key) { return classValue[key]; }).
28138        join(' ');
28139    } else if (!isString(classValue)) {
28140      classString = classValue + '';
28141    }
28142
28143    return classString;
28144  }
28145}
28146
28147/**
28148 * @ngdoc directive
28149 * @name ngClass
28150 * @restrict AC
28151 * @element ANY
28152 *
28153 * @description
28154 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
28155 * an expression that represents all classes to be added.
28156 *
28157 * The directive operates in three different ways, depending on which of three types the expression
28158 * evaluates to:
28159 *
28160 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
28161 * names.
28162 *
28163 * 2. If the expression evaluates to an object, then for each key-value pair of the
28164 * object with a truthy value the corresponding key is used as a class name.
28165 *
28166 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
28167 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
28168 * to give you more control over what CSS classes appear. See the code below for an example of this.
28169 *
28170 *
28171 * The directive won't add duplicate classes if a particular class was already set.
28172 *
28173 * When the expression changes, the previously added classes are removed and only then are the
28174 * new classes added.
28175 *
28176 * @knownIssue
28177 * You should not use {@link guide/interpolation interpolation} in the value of the `class`
28178 * attribute, when using the `ngClass` directive on the same element.
28179 * See {@link guide/interpolation#known-issues here} for more info.
28180 *
28181 * @animations
28182 * | Animation                        | Occurs                              |
28183 * |----------------------------------|-------------------------------------|
28184 * | {@link ng.$animate#addClass addClass}       | just before the class is applied to the element   |
28185 * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
28186 * | {@link ng.$animate#setClass setClass} | just before classes are added and classes are removed from the element at the same time |
28187 *
28188 * ### ngClass and pre-existing CSS3 Transitions/Animations
28189   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
28190   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
28191   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
28192   to view the step by step details of {@link $animate#addClass $animate.addClass} and
28193   {@link $animate#removeClass $animate.removeClass}.
28194 *
28195 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
28196 *   of the evaluation can be a string representing space delimited class
28197 *   names, an array, or a map of class names to boolean values. In the case of a map, the
28198 *   names of the properties whose values are truthy will be added as css classes to the
28199 *   element.
28200 *
28201 * @example
28202 * ### Basic
28203   <example name="ng-class">
28204     <file name="index.html">
28205       <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
28206       <label>
28207          <input type="checkbox" ng-model="deleted">
28208          deleted (apply "strike" class)
28209       </label><br>
28210       <label>
28211          <input type="checkbox" ng-model="important">
28212          important (apply "bold" class)
28213       </label><br>
28214       <label>
28215          <input type="checkbox" ng-model="error">
28216          error (apply "has-error" class)
28217       </label>
28218       <hr>
28219       <p ng-class="style">Using String Syntax</p>
28220       <input type="text" ng-model="style"
28221              placeholder="Type: bold strike red" aria-label="Type: bold strike red">
28222       <hr>
28223       <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
28224       <input ng-model="style1"
28225              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
28226       <input ng-model="style2"
28227              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
28228       <input ng-model="style3"
28229              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
28230       <hr>
28231       <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
28232       <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
28233       <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
28234     </file>
28235     <file name="style.css">
28236       .strike {
28237           text-decoration: line-through;
28238       }
28239       .bold {
28240           font-weight: bold;
28241       }
28242       .red {
28243           color: red;
28244       }
28245       .has-error {
28246           color: red;
28247           background-color: yellow;
28248       }
28249       .orange {
28250           color: orange;
28251       }
28252     </file>
28253     <file name="protractor.js" type="protractor">
28254       var ps = element.all(by.css('p'));
28255
28256       it('should let you toggle the class', function() {
28257
28258         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
28259         expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
28260
28261         element(by.model('important')).click();
28262         expect(ps.first().getAttribute('class')).toMatch(/bold/);
28263
28264         element(by.model('error')).click();
28265         expect(ps.first().getAttribute('class')).toMatch(/has-error/);
28266       });
28267
28268       it('should let you toggle string example', function() {
28269         expect(ps.get(1).getAttribute('class')).toBe('');
28270         element(by.model('style')).clear();
28271         element(by.model('style')).sendKeys('red');
28272         expect(ps.get(1).getAttribute('class')).toBe('red');
28273       });
28274
28275       it('array example should have 3 classes', function() {
28276         expect(ps.get(2).getAttribute('class')).toBe('');
28277         element(by.model('style1')).sendKeys('bold');
28278         element(by.model('style2')).sendKeys('strike');
28279         element(by.model('style3')).sendKeys('red');
28280         expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
28281       });
28282
28283       it('array with map example should have 2 classes', function() {
28284         expect(ps.last().getAttribute('class')).toBe('');
28285         element(by.model('style4')).sendKeys('bold');
28286         element(by.model('warning')).click();
28287         expect(ps.last().getAttribute('class')).toBe('bold orange');
28288       });
28289     </file>
28290   </example>
28291
28292   @example
28293   ### Animations
28294
28295   The example below demonstrates how to perform animations using ngClass.
28296
28297   <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-class">
28298     <file name="index.html">
28299      <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
28300      <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
28301      <br>
28302      <span class="base-class" ng-class="myVar">Sample Text</span>
28303     </file>
28304     <file name="style.css">
28305       .base-class {
28306         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28307       }
28308
28309       .base-class.my-class {
28310         color: red;
28311         font-size:3em;
28312       }
28313     </file>
28314     <file name="protractor.js" type="protractor">
28315       it('should check ng-class', function() {
28316         expect(element(by.css('.base-class')).getAttribute('class')).not.
28317           toMatch(/my-class/);
28318
28319         element(by.id('setbtn')).click();
28320
28321         expect(element(by.css('.base-class')).getAttribute('class')).
28322           toMatch(/my-class/);
28323
28324         element(by.id('clearbtn')).click();
28325
28326         expect(element(by.css('.base-class')).getAttribute('class')).not.
28327           toMatch(/my-class/);
28328       });
28329     </file>
28330   </example>
28331 */
28332var ngClassDirective = classDirective('', true);
28333
28334/**
28335 * @ngdoc directive
28336 * @name ngClassOdd
28337 * @restrict AC
28338 *
28339 * @description
28340 * The `ngClassOdd` and `ngClassEven` directives work exactly as
28341 * {@link ng.directive:ngClass ngClass}, except they work in
28342 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
28343 *
28344 * This directive can be applied only within the scope of an
28345 * {@link ng.directive:ngRepeat ngRepeat}.
28346 *
28347 * @animations
28348 * | Animation                        | Occurs                              |
28349 * |----------------------------------|-------------------------------------|
28350 * | {@link ng.$animate#addClass addClass}       | just before the class is applied to the element   |
28351 * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
28352 *
28353 * @element ANY
28354 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
28355 *   of the evaluation can be a string representing space delimited class names or an array.
28356 *
28357 * @example
28358   <example name="ng-class-odd">
28359     <file name="index.html">
28360        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
28361          <li ng-repeat="name in names">
28362           <span ng-class-odd="'odd'" ng-class-even="'even'">
28363             {{name}}
28364           </span>
28365          </li>
28366        </ol>
28367     </file>
28368     <file name="style.css">
28369       .odd {
28370         color: red;
28371       }
28372       .even {
28373         color: blue;
28374       }
28375     </file>
28376     <file name="protractor.js" type="protractor">
28377       it('should check ng-class-odd and ng-class-even', function() {
28378         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
28379           toMatch(/odd/);
28380         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
28381           toMatch(/even/);
28382       });
28383     </file>
28384   </example>
28385 *
28386 * <hr />
28387 * @example
28388 * An example on how to implement animations using `ngClassOdd`:
28389 *
28390   <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-class-odd-animate">
28391     <file name="index.html">
28392       <div ng-init="items=['Item 3', 'Item 2', 'Item 1', 'Item 0']">
28393         <button ng-click="items.unshift('Item ' + items.length)">Add item</button>
28394         <hr />
28395         <table>
28396           <tr ng-repeat="item in items" ng-class-odd="'odd'">
28397             <td>{{ item }}</td>
28398           </tr>
28399         </table>
28400       </div>
28401     </file>
28402     <file name="style.css">
28403       .odd {
28404         background: rgba(255, 255, 0, 0.25);
28405       }
28406
28407       .odd-add, .odd-remove {
28408         transition: 1.5s;
28409       }
28410     </file>
28411     <file name="protractor.js" type="protractor">
28412       it('should add new entries to the beginning of the list', function() {
28413         var button = element(by.buttonText('Add item'));
28414         var rows = element.all(by.repeater('item in items'));
28415
28416         expect(rows.count()).toBe(4);
28417         expect(rows.get(0).getText()).toBe('Item 3');
28418         expect(rows.get(1).getText()).toBe('Item 2');
28419
28420         button.click();
28421
28422         expect(rows.count()).toBe(5);
28423         expect(rows.get(0).getText()).toBe('Item 4');
28424         expect(rows.get(1).getText()).toBe('Item 3');
28425       });
28426
28427       it('should add odd class to odd entries', function() {
28428         var button = element(by.buttonText('Add item'));
28429         var rows = element.all(by.repeater('item in items'));
28430
28431         expect(rows.get(0).getAttribute('class')).toMatch(/odd/);
28432         expect(rows.get(1).getAttribute('class')).not.toMatch(/odd/);
28433
28434         button.click();
28435
28436         expect(rows.get(0).getAttribute('class')).toMatch(/odd/);
28437         expect(rows.get(1).getAttribute('class')).not.toMatch(/odd/);
28438       });
28439     </file>
28440   </example>
28441 */
28442var ngClassOddDirective = classDirective('Odd', 0);
28443
28444/**
28445 * @ngdoc directive
28446 * @name ngClassEven
28447 * @restrict AC
28448 *
28449 * @description
28450 * The `ngClassOdd` and `ngClassEven` directives work exactly as
28451 * {@link ng.directive:ngClass ngClass}, except they work in
28452 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
28453 *
28454 * This directive can be applied only within the scope of an
28455 * {@link ng.directive:ngRepeat ngRepeat}.
28456 *
28457 * @animations
28458 * | Animation                        | Occurs                              |
28459 * |----------------------------------|-------------------------------------|
28460 * | {@link ng.$animate#addClass addClass}       | just before the class is applied to the element   |
28461 * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
28462 *
28463 * @element ANY
28464 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
28465 *   result of the evaluation can be a string representing space delimited class names or an array.
28466 *
28467 * @example
28468   <example name="ng-class-even">
28469     <file name="index.html">
28470        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
28471          <li ng-repeat="name in names">
28472           <span ng-class-odd="'odd'" ng-class-even="'even'">
28473             {{name}} &nbsp; &nbsp; &nbsp;
28474           </span>
28475          </li>
28476        </ol>
28477     </file>
28478     <file name="style.css">
28479       .odd {
28480         color: red;
28481       }
28482       .even {
28483         color: blue;
28484       }
28485     </file>
28486     <file name="protractor.js" type="protractor">
28487       it('should check ng-class-odd and ng-class-even', function() {
28488         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
28489           toMatch(/odd/);
28490         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
28491           toMatch(/even/);
28492       });
28493     </file>
28494   </example>
28495 *
28496 * <hr />
28497 * @example
28498 * An example on how to implement animations using `ngClassEven`:
28499 *
28500   <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-class-even-animate">
28501     <file name="index.html">
28502       <div ng-init="items=['Item 3', 'Item 2', 'Item 1', 'Item 0']">
28503         <button ng-click="items.unshift('Item ' + items.length)">Add item</button>
28504         <hr />
28505         <table>
28506           <tr ng-repeat="item in items" ng-class-even="'even'">
28507             <td>{{ item }}</td>
28508           </tr>
28509         </table>
28510       </div>
28511     </file>
28512     <file name="style.css">
28513       .even {
28514         background: rgba(255, 255, 0, 0.25);
28515       }
28516
28517       .even-add, .even-remove {
28518         transition: 1.5s;
28519       }
28520     </file>
28521     <file name="protractor.js" type="protractor">
28522       it('should add new entries to the beginning of the list', function() {
28523         var button = element(by.buttonText('Add item'));
28524         var rows = element.all(by.repeater('item in items'));
28525
28526         expect(rows.count()).toBe(4);
28527         expect(rows.get(0).getText()).toBe('Item 3');
28528         expect(rows.get(1).getText()).toBe('Item 2');
28529
28530         button.click();
28531
28532         expect(rows.count()).toBe(5);
28533         expect(rows.get(0).getText()).toBe('Item 4');
28534         expect(rows.get(1).getText()).toBe('Item 3');
28535       });
28536
28537       it('should add even class to even entries', function() {
28538         var button = element(by.buttonText('Add item'));
28539         var rows = element.all(by.repeater('item in items'));
28540
28541         expect(rows.get(0).getAttribute('class')).not.toMatch(/even/);
28542         expect(rows.get(1).getAttribute('class')).toMatch(/even/);
28543
28544         button.click();
28545
28546         expect(rows.get(0).getAttribute('class')).not.toMatch(/even/);
28547         expect(rows.get(1).getAttribute('class')).toMatch(/even/);
28548       });
28549     </file>
28550   </example>
28551 */
28552var ngClassEvenDirective = classDirective('Even', 1);
28553
28554/**
28555 * @ngdoc directive
28556 * @name ngCloak
28557 * @restrict AC
28558 *
28559 * @description
28560 * The `ngCloak` directive is used to prevent the AngularJS html template from being briefly
28561 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
28562 * directive to avoid the undesirable flicker effect caused by the html template display.
28563 *
28564 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
28565 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
28566 * of the browser view.
28567 *
28568 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
28569 * `angular.min.js`.
28570 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
28571 *
28572 * ```css
28573 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
28574 *   display: none !important;
28575 * }
28576 * ```
28577 *
28578 * When this css rule is loaded by the browser, all html elements (including their children) that
28579 * are tagged with the `ngCloak` directive are hidden. When AngularJS encounters this directive
28580 * during the compilation of the template it deletes the `ngCloak` element attribute, making
28581 * the compiled element visible.
28582 *
28583 * For the best result, the `angular.js` script must be loaded in the head section of the html
28584 * document; alternatively, the css rule above must be included in the external stylesheet of the
28585 * application.
28586 *
28587 * @element ANY
28588 *
28589 * @example
28590   <example name="ng-cloak">
28591     <file name="index.html">
28592        <div id="template1" ng-cloak>{{ 'hello' }}</div>
28593        <div id="template2" class="ng-cloak">{{ 'world' }}</div>
28594     </file>
28595     <file name="protractor.js" type="protractor">
28596       it('should remove the template directive and css class', function() {
28597         expect($('#template1').getAttribute('ng-cloak')).
28598           toBeNull();
28599         expect($('#template2').getAttribute('ng-cloak')).
28600           toBeNull();
28601       });
28602     </file>
28603   </example>
28604 *
28605 */
28606var ngCloakDirective = ngDirective({
28607  compile: function(element, attr) {
28608    attr.$set('ngCloak', undefined);
28609    element.removeClass('ng-cloak');
28610  }
28611});
28612
28613/**
28614 * @ngdoc directive
28615 * @name ngController
28616 *
28617 * @description
28618 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
28619 * supports the principles behind the Model-View-Controller design pattern.
28620 *
28621 * MVC components in angular:
28622 *
28623 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
28624 *   are accessed through bindings.
28625 * * View — The template (HTML with data bindings) that is rendered into the View.
28626 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
28627 *   logic behind the application to decorate the scope with functions and values
28628 *
28629 * Note that you can also attach controllers to the DOM by declaring it in a route definition
28630 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
28631 * again using `ng-controller` in the template itself.  This will cause the controller to be attached
28632 * and executed twice.
28633 *
28634 * @element ANY
28635 * @scope
28636 * @priority 500
28637 * @param {expression} ngController Name of a constructor function registered with the current
28638 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
28639 * that on the current scope evaluates to a constructor function.
28640 *
28641 * The controller instance can be published into a scope property by specifying
28642 * `ng-controller="as propertyName"`.
28643 *
28644 * @example
28645 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
28646 * greeting are methods declared on the controller (see source tab). These methods can
28647 * easily be called from the AngularJS markup. Any changes to the data are automatically reflected
28648 * in the View without the need for a manual update.
28649 *
28650 * Two different declaration styles are included below:
28651 *
28652 * * one binds methods and properties directly onto the controller using `this`:
28653 * `ng-controller="SettingsController1 as settings"`
28654 * * one injects `$scope` into the controller:
28655 * `ng-controller="SettingsController2"`
28656 *
28657 * The second option is more common in the AngularJS community, and is generally used in boilerplates
28658 * and in this guide. However, there are advantages to binding properties directly to the controller
28659 * and avoiding scope.
28660 *
28661 * * Using `controller as` makes it obvious which controller you are accessing in the template when
28662 * multiple controllers apply to an element.
28663 * * If you are writing your controllers as classes you have easier access to the properties and
28664 * methods, which will appear on the scope, from inside the controller code.
28665 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
28666 * inheritance masking primitives.
28667 *
28668 * This example demonstrates the `controller as` syntax.
28669 *
28670 * <example name="ngControllerAs" module="controllerAsExample">
28671 *   <file name="index.html">
28672 *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
28673 *      <label>Name: <input type="text" ng-model="settings.name"/></label>
28674 *      <button ng-click="settings.greet()">greet</button><br/>
28675 *      Contact:
28676 *      <ul>
28677 *        <li ng-repeat="contact in settings.contacts">
28678 *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
28679 *             <option>phone</option>
28680 *             <option>email</option>
28681 *          </select>
28682 *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
28683 *          <button ng-click="settings.clearContact(contact)">clear</button>
28684 *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
28685 *        </li>
28686 *        <li><button ng-click="settings.addContact()">add</button></li>
28687 *     </ul>
28688 *    </div>
28689 *   </file>
28690 *   <file name="app.js">
28691 *    angular.module('controllerAsExample', [])
28692 *      .controller('SettingsController1', SettingsController1);
28693 *
28694 *    function SettingsController1() {
28695 *      this.name = 'John Smith';
28696 *      this.contacts = [
28697 *        {type: 'phone', value: '408 555 1212'},
28698 *        {type: 'email', value: '[email protected]'}
28699 *      ];
28700 *    }
28701 *
28702 *    SettingsController1.prototype.greet = function() {
28703 *      alert(this.name);
28704 *    };
28705 *
28706 *    SettingsController1.prototype.addContact = function() {
28707 *      this.contacts.push({type: 'email', value: '[email protected]'});
28708 *    };
28709 *
28710 *    SettingsController1.prototype.removeContact = function(contactToRemove) {
28711 *     var index = this.contacts.indexOf(contactToRemove);
28712 *      this.contacts.splice(index, 1);
28713 *    };
28714 *
28715 *    SettingsController1.prototype.clearContact = function(contact) {
28716 *      contact.type = 'phone';
28717 *      contact.value = '';
28718 *    };
28719 *   </file>
28720 *   <file name="protractor.js" type="protractor">
28721 *     it('should check controller as', function() {
28722 *       var container = element(by.id('ctrl-as-exmpl'));
28723 *         expect(container.element(by.model('settings.name'))
28724 *           .getAttribute('value')).toBe('John Smith');
28725 *
28726 *       var firstRepeat =
28727 *           container.element(by.repeater('contact in settings.contacts').row(0));
28728 *       var secondRepeat =
28729 *           container.element(by.repeater('contact in settings.contacts').row(1));
28730 *
28731 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
28732 *           .toBe('408 555 1212');
28733 *
28734 *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
28735 *           .toBe('[email protected]');
28736 *
28737 *       firstRepeat.element(by.buttonText('clear')).click();
28738 *
28739 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
28740 *           .toBe('');
28741 *
28742 *       container.element(by.buttonText('add')).click();
28743 *
28744 *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
28745 *           .element(by.model('contact.value'))
28746 *           .getAttribute('value'))
28747 *           .toBe('[email protected]');
28748 *     });
28749 *   </file>
28750 * </example>
28751 *
28752 * This example demonstrates the "attach to `$scope`" style of controller.
28753 *
28754 * <example name="ngController" module="controllerExample">
28755 *  <file name="index.html">
28756 *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
28757 *     <label>Name: <input type="text" ng-model="name"/></label>
28758 *     <button ng-click="greet()">greet</button><br/>
28759 *     Contact:
28760 *     <ul>
28761 *       <li ng-repeat="contact in contacts">
28762 *         <select ng-model="contact.type" id="select_{{$index}}">
28763 *            <option>phone</option>
28764 *            <option>email</option>
28765 *         </select>
28766 *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
28767 *         <button ng-click="clearContact(contact)">clear</button>
28768 *         <button ng-click="removeContact(contact)">X</button>
28769 *       </li>
28770 *       <li>[ <button ng-click="addContact()">add</button> ]</li>
28771 *    </ul>
28772 *   </div>
28773 *  </file>
28774 *  <file name="app.js">
28775 *   angular.module('controllerExample', [])
28776 *     .controller('SettingsController2', ['$scope', SettingsController2]);
28777 *
28778 *   function SettingsController2($scope) {
28779 *     $scope.name = 'John Smith';
28780 *     $scope.contacts = [
28781 *       {type:'phone', value:'408 555 1212'},
28782 *       {type:'email', value:'[email protected]'}
28783 *     ];
28784 *
28785 *     $scope.greet = function() {
28786 *       alert($scope.name);
28787 *     };
28788 *
28789 *     $scope.addContact = function() {
28790 *       $scope.contacts.push({type:'email', value:'[email protected]'});
28791 *     };
28792 *
28793 *     $scope.removeContact = function(contactToRemove) {
28794 *       var index = $scope.contacts.indexOf(contactToRemove);
28795 *       $scope.contacts.splice(index, 1);
28796 *     };
28797 *
28798 *     $scope.clearContact = function(contact) {
28799 *       contact.type = 'phone';
28800 *       contact.value = '';
28801 *     };
28802 *   }
28803 *  </file>
28804 *  <file name="protractor.js" type="protractor">
28805 *    it('should check controller', function() {
28806 *      var container = element(by.id('ctrl-exmpl'));
28807 *
28808 *      expect(container.element(by.model('name'))
28809 *          .getAttribute('value')).toBe('John Smith');
28810 *
28811 *      var firstRepeat =
28812 *          container.element(by.repeater('contact in contacts').row(0));
28813 *      var secondRepeat =
28814 *          container.element(by.repeater('contact in contacts').row(1));
28815 *
28816 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
28817 *          .toBe('408 555 1212');
28818 *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
28819 *          .toBe('[email protected]');
28820 *
28821 *      firstRepeat.element(by.buttonText('clear')).click();
28822 *
28823 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
28824 *          .toBe('');
28825 *
28826 *      container.element(by.buttonText('add')).click();
28827 *
28828 *      expect(container.element(by.repeater('contact in contacts').row(2))
28829 *          .element(by.model('contact.value'))
28830 *          .getAttribute('value'))
28831 *          .toBe('[email protected]');
28832 *    });
28833 *  </file>
28834 *</example>
28835
28836 */
28837var ngControllerDirective = [function() {
28838  return {
28839    restrict: 'A',
28840    scope: true,
28841    controller: '@',
28842    priority: 500
28843  };
28844}];
28845
28846/**
28847 * @ngdoc directive
28848 * @name ngCsp
28849 *
28850 * @restrict A
28851 * @element ANY
28852 * @description
28853 *
28854 * AngularJS has some features that can conflict with certain restrictions that are applied when using
28855 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
28856 *
28857 * If you intend to implement CSP with these rules then you must tell AngularJS not to use these
28858 * features.
28859 *
28860 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
28861 *
28862 *
28863 * The following default rules in CSP affect AngularJS:
28864 *
28865 * * The use of `eval()`, `Function(string)` and similar functions to dynamically create and execute
28866 * code from strings is forbidden. AngularJS makes use of this in the {@link $parse} service to
28867 * provide a 30% increase in the speed of evaluating AngularJS expressions. (This CSP rule can be
28868 * disabled with the CSP keyword `unsafe-eval`, but it is generally not recommended as it would
28869 * weaken the protections offered by CSP.)
28870 *
28871 * * The use of inline resources, such as inline `<script>` and `<style>` elements, are forbidden.
28872 * This prevents apps from injecting custom styles directly into the document. AngularJS makes use of
28873 * this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}). To make these
28874 * directives work when a CSP rule is blocking inline styles, you must link to the `angular-csp.css`
28875 * in your HTML manually. (This CSP rule can be disabled with the CSP keyword `unsafe-inline`, but
28876 * it is generally not recommended as it would weaken the protections offered by CSP.)
28877 *
28878 * If you do not provide `ngCsp` then AngularJS tries to autodetect if CSP is blocking dynamic code
28879 * creation from strings (e.g., `unsafe-eval` not specified in CSP header) and automatically
28880 * deactivates this feature in the {@link $parse} service. This autodetection, however, triggers a
28881 * CSP error to be logged in the console:
28882 *
28883 * ```
28884 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
28885 * script in the following Content Security Policy directive: "default-src 'self'". Note that
28886 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
28887 * ```
28888 *
28889 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
28890 * directive on an element of the HTML document that appears before the `<script>` tag that loads
28891 * the `angular.js` file.
28892 *
28893 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
28894 *
28895 * You can specify which of the CSP related AngularJS features should be deactivated by providing
28896 * a value for the `ng-csp` attribute. The options are as follows:
28897 *
28898 * * no-inline-style: this stops AngularJS from injecting CSS styles into the DOM
28899 *
28900 * * no-unsafe-eval: this stops AngularJS from optimizing $parse with unsafe eval of strings
28901 *
28902 * You can use these values in the following combinations:
28903 *
28904 *
28905 * * No declaration means that AngularJS will assume that you can do inline styles, but it will do
28906 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous
28907 * versions of AngularJS.
28908 *
28909 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell AngularJS to deactivate both inline
28910 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous
28911 * versions of AngularJS.
28912 *
28913 * * Specifying only `no-unsafe-eval` tells AngularJS that we must not use eval, but that we can
28914 * inject inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
28915 *
28916 * * Specifying only `no-inline-style` tells AngularJS that we must not inject styles, but that we can
28917 * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
28918 *
28919 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells AngularJS that we must not inject
28920 * styles nor use eval, which is the same as an empty: ng-csp.
28921 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
28922 *
28923 * @example
28924 *
28925 * This example shows how to apply the `ngCsp` directive to the `html` tag.
28926   ```html
28927     <!doctype html>
28928     <html ng-app ng-csp>
28929     ...
28930     ...
28931     </html>
28932   ```
28933
28934  <!-- Note: the `.csp` suffix in the example name triggers CSP mode in our http server! -->
28935  <example name="example.csp" module="cspExample" ng-csp="true">
28936    <file name="index.html">
28937      <div ng-controller="MainController as ctrl">
28938        <div>
28939          <button ng-click="ctrl.inc()" id="inc">Increment</button>
28940          <span id="counter">
28941            {{ctrl.counter}}
28942          </span>
28943        </div>
28944
28945        <div>
28946          <button ng-click="ctrl.evil()" id="evil">Evil</button>
28947          <span id="evilError">
28948            {{ctrl.evilError}}
28949          </span>
28950        </div>
28951      </div>
28952    </file>
28953    <file name="script.js">
28954       angular.module('cspExample', [])
28955         .controller('MainController', function MainController() {
28956            this.counter = 0;
28957            this.inc = function() {
28958              this.counter++;
28959            };
28960            this.evil = function() {
28961              try {
28962                eval('1+2'); // eslint-disable-line no-eval
28963              } catch (e) {
28964                this.evilError = e.message;
28965              }
28966            };
28967          });
28968    </file>
28969    <file name="protractor.js" type="protractor">
28970      var util, webdriver;
28971
28972      var incBtn = element(by.id('inc'));
28973      var counter = element(by.id('counter'));
28974      var evilBtn = element(by.id('evil'));
28975      var evilError = element(by.id('evilError'));
28976
28977      function getAndClearSevereErrors() {
28978        return browser.manage().logs().get('browser').then(function(browserLog) {
28979          return browserLog.filter(function(logEntry) {
28980            return logEntry.level.value > webdriver.logging.Level.WARNING.value;
28981          });
28982        });
28983      }
28984
28985      function clearErrors() {
28986        getAndClearSevereErrors();
28987      }
28988
28989      function expectNoErrors() {
28990        getAndClearSevereErrors().then(function(filteredLog) {
28991          expect(filteredLog.length).toEqual(0);
28992          if (filteredLog.length) {
28993            console.log('browser console errors: ' + util.inspect(filteredLog));
28994          }
28995        });
28996      }
28997
28998      function expectError(regex) {
28999        getAndClearSevereErrors().then(function(filteredLog) {
29000          var found = false;
29001          filteredLog.forEach(function(log) {
29002            if (log.message.match(regex)) {
29003              found = true;
29004            }
29005          });
29006          if (!found) {
29007            throw new Error('expected an error that matches ' + regex);
29008          }
29009        });
29010      }
29011
29012      beforeEach(function() {
29013        util = require('util');
29014        webdriver = require('selenium-webdriver');
29015      });
29016
29017      // For now, we only test on Chrome,
29018      // as Safari does not load the page with Protractor's injected scripts,
29019      // and Firefox webdriver always disables content security policy (#6358)
29020      if (browser.params.browser !== 'chrome') {
29021        return;
29022      }
29023
29024      it('should not report errors when the page is loaded', function() {
29025        // clear errors so we are not dependent on previous tests
29026        clearErrors();
29027        // Need to reload the page as the page is already loaded when
29028        // we come here
29029        browser.driver.getCurrentUrl().then(function(url) {
29030          browser.get(url);
29031        });
29032        expectNoErrors();
29033      });
29034
29035      it('should evaluate expressions', function() {
29036        expect(counter.getText()).toEqual('0');
29037        incBtn.click();
29038        expect(counter.getText()).toEqual('1');
29039        expectNoErrors();
29040      });
29041
29042      it('should throw and report an error when using "eval"', function() {
29043        evilBtn.click();
29044        expect(evilError.getText()).toMatch(/Content Security Policy/);
29045        expectError(/Content Security Policy/);
29046      });
29047    </file>
29048  </example>
29049  */
29050
29051// `ngCsp` is not implemented as a proper directive any more, because we need it be processed while
29052// we bootstrap the app (before `$parse` is instantiated). For this reason, we just have the `csp()`
29053// fn that looks for the `ng-csp` attribute anywhere in the current doc.
29054
29055/**
29056 * @ngdoc directive
29057 * @name ngClick
29058 * @restrict A
29059 * @element ANY
29060 * @priority 0
29061 *
29062 * @description
29063 * The ngClick directive allows you to specify custom behavior when
29064 * an element is clicked.
29065 *
29066 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
29067 * click. ({@link guide/expression#-event- Event object is available as `$event`})
29068 *
29069 * @example
29070   <example name="ng-click">
29071     <file name="index.html">
29072      <button ng-click="count = count + 1" ng-init="count=0">
29073        Increment
29074      </button>
29075      <span>
29076        count: {{count}}
29077      </span>
29078     </file>
29079     <file name="protractor.js" type="protractor">
29080       it('should check ng-click', function() {
29081         expect(element(by.binding('count')).getText()).toMatch('0');
29082         element(by.css('button')).click();
29083         expect(element(by.binding('count')).getText()).toMatch('1');
29084       });
29085     </file>
29086   </example>
29087 */
29088/*
29089 * A collection of directives that allows creation of custom event handlers that are defined as
29090 * AngularJS expressions and are compiled and executed within the current scope.
29091 */
29092var ngEventDirectives = {};
29093
29094// For events that might fire synchronously during DOM manipulation
29095// we need to execute their event handlers asynchronously using $evalAsync,
29096// so that they are not executed in an inconsistent state.
29097var forceAsyncEvents = {
29098  'blur': true,
29099  'focus': true
29100};
29101forEach(
29102  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
29103  function(eventName) {
29104    var directiveName = directiveNormalize('ng-' + eventName);
29105    ngEventDirectives[directiveName] = ['$parse', '$rootScope', '$exceptionHandler', function($parse, $rootScope, $exceptionHandler) {
29106      return createEventDirective($parse, $rootScope, $exceptionHandler, directiveName, eventName, forceAsyncEvents[eventName]);
29107    }];
29108  }
29109);
29110
29111function createEventDirective($parse, $rootScope, $exceptionHandler, directiveName, eventName, forceAsync) {
29112  return {
29113    restrict: 'A',
29114    compile: function($element, attr) {
29115      // NOTE:
29116      // We expose the powerful `$event` object on the scope that provides access to the Window,
29117      // etc. This is OK, because expressions are not sandboxed any more (and the expression
29118      // sandbox was never meant to be a security feature anyway).
29119      var fn = $parse(attr[directiveName]);
29120      return function ngEventHandler(scope, element) {
29121        element.on(eventName, function(event) {
29122          var callback = function() {
29123            fn(scope, {$event: event});
29124          };
29125
29126          if (!$rootScope.$$phase) {
29127            scope.$apply(callback);
29128          } else if (forceAsync) {
29129            scope.$evalAsync(callback);
29130          } else {
29131            try {
29132              callback();
29133            } catch (error) {
29134              $exceptionHandler(error);
29135            }
29136          }
29137        });
29138      };
29139    }
29140  };
29141}
29142
29143/**
29144 * @ngdoc directive
29145 * @name ngDblclick
29146 * @restrict A
29147 * @element ANY
29148 * @priority 0
29149 *
29150 * @description
29151 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
29152 *
29153 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
29154 * a dblclick. (The Event object is available as `$event`)
29155 *
29156 * @example
29157   <example name="ng-dblclick">
29158     <file name="index.html">
29159      <button ng-dblclick="count = count + 1" ng-init="count=0">
29160        Increment (on double click)
29161      </button>
29162      count: {{count}}
29163     </file>
29164   </example>
29165 */
29166
29167
29168/**
29169 * @ngdoc directive
29170 * @name ngMousedown
29171 * @restrict A
29172 * @element ANY
29173 * @priority 0
29174 *
29175 * @description
29176 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
29177 *
29178 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
29179 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
29180 *
29181 * @example
29182   <example name="ng-mousedown">
29183     <file name="index.html">
29184      <button ng-mousedown="count = count + 1" ng-init="count=0">
29185        Increment (on mouse down)
29186      </button>
29187      count: {{count}}
29188     </file>
29189   </example>
29190 */
29191
29192
29193/**
29194 * @ngdoc directive
29195 * @name ngMouseup
29196 * @restrict A
29197 * @element ANY
29198 * @priority 0
29199 *
29200 * @description
29201 * Specify custom behavior on mouseup event.
29202 *
29203 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
29204 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
29205 *
29206 * @example
29207   <example name="ng-mouseup">
29208     <file name="index.html">
29209      <button ng-mouseup="count = count + 1" ng-init="count=0">
29210        Increment (on mouse up)
29211      </button>
29212      count: {{count}}
29213     </file>
29214   </example>
29215 */
29216
29217/**
29218 * @ngdoc directive
29219 * @name ngMouseover
29220 * @restrict A
29221 * @element ANY
29222 * @priority 0
29223 *
29224 * @description
29225 * Specify custom behavior on mouseover event.
29226 *
29227 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
29228 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
29229 *
29230 * @example
29231   <example name="ng-mouseover">
29232     <file name="index.html">
29233      <button ng-mouseover="count = count + 1" ng-init="count=0">
29234        Increment (when mouse is over)
29235      </button>
29236      count: {{count}}
29237     </file>
29238   </example>
29239 */
29240
29241
29242/**
29243 * @ngdoc directive
29244 * @name ngMouseenter
29245 * @restrict A
29246 * @element ANY
29247 * @priority 0
29248 *
29249 * @description
29250 * Specify custom behavior on mouseenter event.
29251 *
29252 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
29253 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
29254 *
29255 * @example
29256   <example name="ng-mouseenter">
29257     <file name="index.html">
29258      <button ng-mouseenter="count = count + 1" ng-init="count=0">
29259        Increment (when mouse enters)
29260      </button>
29261      count: {{count}}
29262     </file>
29263   </example>
29264 */
29265
29266
29267/**
29268 * @ngdoc directive
29269 * @name ngMouseleave
29270 * @restrict A
29271 * @element ANY
29272 * @priority 0
29273 *
29274 * @description
29275 * Specify custom behavior on mouseleave event.
29276 *
29277 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
29278 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
29279 *
29280 * @example
29281   <example name="ng-mouseleave">
29282     <file name="index.html">
29283      <button ng-mouseleave="count = count + 1" ng-init="count=0">
29284        Increment (when mouse leaves)
29285      </button>
29286      count: {{count}}
29287     </file>
29288   </example>
29289 */
29290
29291
29292/**
29293 * @ngdoc directive
29294 * @name ngMousemove
29295 * @restrict A
29296 * @element ANY
29297 * @priority 0
29298 *
29299 * @description
29300 * Specify custom behavior on mousemove event.
29301 *
29302 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
29303 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
29304 *
29305 * @example
29306   <example name="ng-mousemove">
29307     <file name="index.html">
29308      <button ng-mousemove="count = count + 1" ng-init="count=0">
29309        Increment (when mouse moves)
29310      </button>
29311      count: {{count}}
29312     </file>
29313   </example>
29314 */
29315
29316
29317/**
29318 * @ngdoc directive
29319 * @name ngKeydown
29320 * @restrict A
29321 * @element ANY
29322 * @priority 0
29323 *
29324 * @description
29325 * Specify custom behavior on keydown event.
29326 *
29327 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
29328 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
29329 *
29330 * @example
29331   <example name="ng-keydown">
29332     <file name="index.html">
29333      <input ng-keydown="count = count + 1" ng-init="count=0">
29334      key down count: {{count}}
29335     </file>
29336   </example>
29337 */
29338
29339
29340/**
29341 * @ngdoc directive
29342 * @name ngKeyup
29343 * @restrict A
29344 * @element ANY
29345 * @priority 0
29346 *
29347 * @description
29348 * Specify custom behavior on keyup event.
29349 *
29350 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
29351 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
29352 *
29353 * @example
29354   <example name="ng-keyup">
29355     <file name="index.html">
29356       <p>Typing in the input box below updates the key count</p>
29357       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
29358
29359       <p>Typing in the input box below updates the keycode</p>
29360       <input ng-keyup="event=$event">
29361       <p>event keyCode: {{ event.keyCode }}</p>
29362       <p>event altKey: {{ event.altKey }}</p>
29363     </file>
29364   </example>
29365 */
29366
29367
29368/**
29369 * @ngdoc directive
29370 * @name ngKeypress
29371 * @restrict A
29372 * @element ANY
29373 *
29374 * @description
29375 * Specify custom behavior on keypress event.
29376 *
29377 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
29378 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
29379 * and can be interrogated for keyCode, altKey, etc.)
29380 *
29381 * @example
29382   <example name="ng-keypress">
29383     <file name="index.html">
29384      <input ng-keypress="count = count + 1" ng-init="count=0">
29385      key press count: {{count}}
29386     </file>
29387   </example>
29388 */
29389
29390
29391/**
29392 * @ngdoc directive
29393 * @name ngSubmit
29394 * @restrict A
29395 * @element form
29396 * @priority 0
29397 *
29398 * @description
29399 * Enables binding AngularJS expressions to onsubmit events.
29400 *
29401 * Additionally it prevents the default action (which for form means sending the request to the
29402 * server and reloading the current page), but only if the form does not contain `action`,
29403 * `data-action`, or `x-action` attributes.
29404 *
29405 * <div class="alert alert-warning">
29406 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
29407 * `ngSubmit` handlers together. See the
29408 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
29409 * for a detailed discussion of when `ngSubmit` may be triggered.
29410 * </div>
29411 *
29412 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
29413 * ({@link guide/expression#-event- Event object is available as `$event`})
29414 *
29415 * @example
29416   <example module="submitExample" name="ng-submit">
29417     <file name="index.html">
29418      <script>
29419        angular.module('submitExample', [])
29420          .controller('ExampleController', ['$scope', function($scope) {
29421            $scope.list = [];
29422            $scope.text = 'hello';
29423            $scope.submit = function() {
29424              if ($scope.text) {
29425                $scope.list.push(this.text);
29426                $scope.text = '';
29427              }
29428            };
29429          }]);
29430      </script>
29431      <form ng-submit="submit()" ng-controller="ExampleController">
29432        Enter text and hit enter:
29433        <input type="text" ng-model="text" name="text" />
29434        <input type="submit" id="submit" value="Submit" />
29435        <pre>list={{list}}</pre>
29436      </form>
29437     </file>
29438     <file name="protractor.js" type="protractor">
29439       it('should check ng-submit', function() {
29440         expect(element(by.binding('list')).getText()).toBe('list=[]');
29441         element(by.css('#submit')).click();
29442         expect(element(by.binding('list')).getText()).toContain('hello');
29443         expect(element(by.model('text')).getAttribute('value')).toBe('');
29444       });
29445       it('should ignore empty strings', function() {
29446         expect(element(by.binding('list')).getText()).toBe('list=[]');
29447         element(by.css('#submit')).click();
29448         element(by.css('#submit')).click();
29449         expect(element(by.binding('list')).getText()).toContain('hello');
29450        });
29451     </file>
29452   </example>
29453 */
29454
29455/**
29456 * @ngdoc directive
29457 * @name ngFocus
29458 * @restrict A
29459 * @element window, input, select, textarea, a
29460 * @priority 0
29461 *
29462 * @description
29463 * Specify custom behavior on focus event.
29464 *
29465 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
29466 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
29467 * during an `$apply` to ensure a consistent state.
29468 *
29469 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
29470 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
29471 *
29472 * @example
29473 * See {@link ng.directive:ngClick ngClick}
29474 */
29475
29476/**
29477 * @ngdoc directive
29478 * @name ngBlur
29479 * @restrict A
29480 * @element window, input, select, textarea, a
29481 * @priority 0
29482 *
29483 * @description
29484 * Specify custom behavior on blur event.
29485 *
29486 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
29487 * an element has lost focus.
29488 *
29489 * Note: As the `blur` event is executed synchronously also during DOM manipulations
29490 * (e.g. removing a focussed input),
29491 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
29492 * during an `$apply` to ensure a consistent state.
29493 *
29494 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
29495 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
29496 *
29497 * @example
29498 * See {@link ng.directive:ngClick ngClick}
29499 */
29500
29501/**
29502 * @ngdoc directive
29503 * @name ngCopy
29504 * @restrict A
29505 * @element window, input, select, textarea, a
29506 * @priority 0
29507 *
29508 * @description
29509 * Specify custom behavior on copy event.
29510 *
29511 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
29512 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
29513 *
29514 * @example
29515   <example name="ng-copy">
29516     <file name="index.html">
29517      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
29518      copied: {{copied}}
29519     </file>
29520   </example>
29521 */
29522
29523/**
29524 * @ngdoc directive
29525 * @name ngCut
29526 * @restrict A
29527 * @element window, input, select, textarea, a
29528 * @priority 0
29529 *
29530 * @description
29531 * Specify custom behavior on cut event.
29532 *
29533 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
29534 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
29535 *
29536 * @example
29537   <example name="ng-cut">
29538     <file name="index.html">
29539      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
29540      cut: {{cut}}
29541     </file>
29542   </example>
29543 */
29544
29545/**
29546 * @ngdoc directive
29547 * @name ngPaste
29548 * @restrict A
29549 * @element window, input, select, textarea, a
29550 * @priority 0
29551 *
29552 * @description
29553 * Specify custom behavior on paste event.
29554 *
29555 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
29556 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
29557 *
29558 * @example
29559   <example name="ng-paste">
29560     <file name="index.html">
29561      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
29562      pasted: {{paste}}
29563     </file>
29564   </example>
29565 */
29566
29567/**
29568 * @ngdoc directive
29569 * @name ngIf
29570 * @restrict A
29571 * @multiElement
29572 *
29573 * @description
29574 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
29575 * {expression}. If the expression assigned to `ngIf` evaluates to a false
29576 * value then the element is removed from the DOM, otherwise a clone of the
29577 * element is reinserted into the DOM.
29578 *
29579 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
29580 * element in the DOM rather than changing its visibility via the `display` css property.  A common
29581 * case when this difference is significant is when using css selectors that rely on an element's
29582 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
29583 *
29584 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
29585 * is created when the element is restored.  The scope created within `ngIf` inherits from
29586 * its parent scope using
29587 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
29588 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
29589 * a javascript primitive defined in the parent scope. In this case any modifications made to the
29590 * variable within the child scope will override (hide) the value in the parent scope.
29591 *
29592 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
29593 * is if an element's class attribute is directly modified after it's compiled, using something like
29594 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
29595 * the added class will be lost because the original compiled state is used to regenerate the element.
29596 *
29597 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
29598 * and `leave` effects.
29599 *
29600 * @animations
29601 * | Animation                        | Occurs                               |
29602 * |----------------------------------|-------------------------------------|
29603 * | {@link ng.$animate#enter enter}  | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
29604 * | {@link ng.$animate#leave leave}  | just before the `ngIf` contents are removed from the DOM |
29605 *
29606 * @element ANY
29607 * @scope
29608 * @priority 600
29609 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
29610 *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
29611 *     element is added to the DOM tree.
29612 *
29613 * @example
29614  <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-if">
29615    <file name="index.html">
29616      <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
29617      Show when checked:
29618      <span ng-if="checked" class="animate-if">
29619        This is removed when the checkbox is unchecked.
29620      </span>
29621    </file>
29622    <file name="animations.css">
29623      .animate-if {
29624        background:white;
29625        border:1px solid black;
29626        padding:10px;
29627      }
29628
29629      .animate-if.ng-enter, .animate-if.ng-leave {
29630        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
29631      }
29632
29633      .animate-if.ng-enter,
29634      .animate-if.ng-leave.ng-leave-active {
29635        opacity:0;
29636      }
29637
29638      .animate-if.ng-leave,
29639      .animate-if.ng-enter.ng-enter-active {
29640        opacity:1;
29641      }
29642    </file>
29643  </example>
29644 */
29645var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
29646  return {
29647    multiElement: true,
29648    transclude: 'element',
29649    priority: 600,
29650    terminal: true,
29651    restrict: 'A',
29652    $$tlb: true,
29653    link: function($scope, $element, $attr, ctrl, $transclude) {
29654        var block, childScope, previousElements;
29655        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
29656
29657          if (value) {
29658            if (!childScope) {
29659              $transclude(function(clone, newScope) {
29660                childScope = newScope;
29661                clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
29662                // Note: We only need the first/last node of the cloned nodes.
29663                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
29664                // by a directive with templateUrl when its template arrives.
29665                block = {
29666                  clone: clone
29667                };
29668                $animate.enter(clone, $element.parent(), $element);
29669              });
29670            }
29671          } else {
29672            if (previousElements) {
29673              previousElements.remove();
29674              previousElements = null;
29675            }
29676            if (childScope) {
29677              childScope.$destroy();
29678              childScope = null;
29679            }
29680            if (block) {
29681              previousElements = getBlockNodes(block.clone);
29682              $animate.leave(previousElements).done(function(response) {
29683                if (response !== false) previousElements = null;
29684              });
29685              block = null;
29686            }
29687          }
29688        });
29689    }
29690  };
29691}];
29692
29693/**
29694 * @ngdoc directive
29695 * @name ngInclude
29696 * @restrict ECA
29697 * @scope
29698 * @priority -400
29699 *
29700 * @description
29701 * Fetches, compiles and includes an external HTML fragment.
29702 *
29703 * By default, the template URL is restricted to the same domain and protocol as the
29704 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
29705 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
29706 * you may either add them to your {@link ng.$sceDelegateProvider#trustedResourceUrlList trusted
29707 * resource URL list} or {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to
29708 * AngularJS's {@link ng.$sce Strict Contextual Escaping}.
29709 *
29710 * In addition, the browser's
29711 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
29712 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
29713 * policy may further restrict whether the template is successfully loaded.
29714 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
29715 * access on some browsers.
29716 *
29717 * @animations
29718 * | Animation                        | Occurs                              |
29719 * |----------------------------------|-------------------------------------|
29720 * | {@link ng.$animate#enter enter}  | when the expression changes, on the new include |
29721 * | {@link ng.$animate#leave leave}  | when the expression changes, on the old include |
29722 *
29723 * The enter and leave animation occur concurrently.
29724 *
29725 * @param {string} ngInclude|src AngularJS expression evaluating to URL. If the source is a string constant,
29726 *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
29727 * @param {string=} onload Expression to evaluate when a new partial is loaded.
29728 *                  <div class="alert alert-warning">
29729 *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
29730 *                  a function with the name on the window element, which will usually throw a
29731 *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
29732 *                  different form that {@link guide/directive#normalization matches} `onload`.
29733 *                  </div>
29734   *
29735 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
29736 *                  $anchorScroll} to scroll the viewport after the content is loaded.
29737 *
29738 *                  - If the attribute is not set, disable scrolling.
29739 *                  - If the attribute is set without value, enable scrolling.
29740 *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
29741 *
29742 * @example
29743  <example module="includeExample" deps="angular-animate.js" animations="true" name="ng-include">
29744    <file name="index.html">
29745     <div ng-controller="ExampleController">
29746       <select ng-model="template" ng-options="t.name for t in templates">
29747        <option value="">(blank)</option>
29748       </select>
29749       url of the template: <code>{{template.url}}</code>
29750       <hr/>
29751       <div class="slide-animate-container">
29752         <div class="slide-animate" ng-include="template.url"></div>
29753       </div>
29754     </div>
29755    </file>
29756    <file name="script.js">
29757      angular.module('includeExample', ['ngAnimate'])
29758        .controller('ExampleController', ['$scope', function($scope) {
29759          $scope.templates =
29760            [{ name: 'template1.html', url: 'template1.html'},
29761             { name: 'template2.html', url: 'template2.html'}];
29762          $scope.template = $scope.templates[0];
29763        }]);
29764     </file>
29765    <file name="template1.html">
29766      Content of template1.html
29767    </file>
29768    <file name="template2.html">
29769      Content of template2.html
29770    </file>
29771    <file name="animations.css">
29772      .slide-animate-container {
29773        position:relative;
29774        background:white;
29775        border:1px solid black;
29776        height:40px;
29777        overflow:hidden;
29778      }
29779
29780      .slide-animate {
29781        padding:10px;
29782      }
29783
29784      .slide-animate.ng-enter, .slide-animate.ng-leave {
29785        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
29786
29787        position:absolute;
29788        top:0;
29789        left:0;
29790        right:0;
29791        bottom:0;
29792        display:block;
29793        padding:10px;
29794      }
29795
29796      .slide-animate.ng-enter {
29797        top:-50px;
29798      }
29799      .slide-animate.ng-enter.ng-enter-active {
29800        top:0;
29801      }
29802
29803      .slide-animate.ng-leave {
29804        top:0;
29805      }
29806      .slide-animate.ng-leave.ng-leave-active {
29807        top:50px;
29808      }
29809    </file>
29810    <file name="protractor.js" type="protractor">
29811      var templateSelect = element(by.model('template'));
29812      var includeElem = element(by.css('[ng-include]'));
29813
29814      it('should load template1.html', function() {
29815        expect(includeElem.getText()).toMatch(/Content of template1.html/);
29816      });
29817
29818      it('should load template2.html', function() {
29819        if (browser.params.browser === 'firefox') {
29820          // Firefox can't handle using selects
29821          // See https://github.com/angular/protractor/issues/480
29822          return;
29823        }
29824        templateSelect.click();
29825        templateSelect.all(by.css('option')).get(2).click();
29826        expect(includeElem.getText()).toMatch(/Content of template2.html/);
29827      });
29828
29829      it('should change to blank', function() {
29830        if (browser.params.browser === 'firefox') {
29831          // Firefox can't handle using selects
29832          return;
29833        }
29834        templateSelect.click();
29835        templateSelect.all(by.css('option')).get(0).click();
29836        expect(includeElem.isPresent()).toBe(false);
29837      });
29838    </file>
29839  </example>
29840 */
29841
29842
29843/**
29844 * @ngdoc event
29845 * @name ngInclude#$includeContentRequested
29846 * @eventType emit on the scope ngInclude was declared in
29847 * @description
29848 * Emitted every time the ngInclude content is requested.
29849 *
29850 * @param {Object} angularEvent Synthetic event object.
29851 * @param {String} src URL of content to load.
29852 */
29853
29854
29855/**
29856 * @ngdoc event
29857 * @name ngInclude#$includeContentLoaded
29858 * @eventType emit on the current ngInclude scope
29859 * @description
29860 * Emitted every time the ngInclude content is reloaded.
29861 *
29862 * @param {Object} angularEvent Synthetic event object.
29863 * @param {String} src URL of content to load.
29864 */
29865
29866
29867/**
29868 * @ngdoc event
29869 * @name ngInclude#$includeContentError
29870 * @eventType emit on the scope ngInclude was declared in
29871 * @description
29872 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
29873 *
29874 * @param {Object} angularEvent Synthetic event object.
29875 * @param {String} src URL of content to load.
29876 */
29877var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
29878                  function($templateRequest,   $anchorScroll,   $animate) {
29879  return {
29880    restrict: 'ECA',
29881    priority: 400,
29882    terminal: true,
29883    transclude: 'element',
29884    controller: angular.noop,
29885    compile: function(element, attr) {
29886      var srcExp = attr.ngInclude || attr.src,
29887          onloadExp = attr.onload || '',
29888          autoScrollExp = attr.autoscroll;
29889
29890      return function(scope, $element, $attr, ctrl, $transclude) {
29891        var changeCounter = 0,
29892            currentScope,
29893            previousElement,
29894            currentElement;
29895
29896        var cleanupLastIncludeContent = function() {
29897          if (previousElement) {
29898            previousElement.remove();
29899            previousElement = null;
29900          }
29901          if (currentScope) {
29902            currentScope.$destroy();
29903            currentScope = null;
29904          }
29905          if (currentElement) {
29906            $animate.leave(currentElement).done(function(response) {
29907              if (response !== false) previousElement = null;
29908            });
29909            previousElement = currentElement;
29910            currentElement = null;
29911          }
29912        };
29913
29914        scope.$watch(srcExp, function ngIncludeWatchAction(src) {
29915          var afterAnimation = function(response) {
29916            if (response !== false && isDefined(autoScrollExp) &&
29917              (!autoScrollExp || scope.$eval(autoScrollExp))) {
29918                $anchorScroll();
29919            }
29920          };
29921          var thisChangeId = ++changeCounter;
29922
29923          if (src) {
29924            //set the 2nd param to true to ignore the template request error so that the inner
29925            //contents and scope can be cleaned up.
29926            $templateRequest(src, true).then(function(response) {
29927              if (scope.$$destroyed) return;
29928
29929              if (thisChangeId !== changeCounter) return;
29930              var newScope = scope.$new();
29931              ctrl.template = response;
29932
29933              // Note: This will also link all children of ng-include that were contained in the original
29934              // html. If that content contains controllers, ... they could pollute/change the scope.
29935              // However, using ng-include on an element with additional content does not make sense...
29936              // Note: We can't remove them in the cloneAttchFn of $transclude as that
29937              // function is called before linking the content, which would apply child
29938              // directives to non existing elements.
29939              var clone = $transclude(newScope, function(clone) {
29940                cleanupLastIncludeContent();
29941                $animate.enter(clone, null, $element).done(afterAnimation);
29942              });
29943
29944              currentScope = newScope;
29945              currentElement = clone;
29946
29947              currentScope.$emit('$includeContentLoaded', src);
29948              scope.$eval(onloadExp);
29949            }, function() {
29950              if (scope.$$destroyed) return;
29951
29952              if (thisChangeId === changeCounter) {
29953                cleanupLastIncludeContent();
29954                scope.$emit('$includeContentError', src);
29955              }
29956            });
29957            scope.$emit('$includeContentRequested', src);
29958          } else {
29959            cleanupLastIncludeContent();
29960            ctrl.template = null;
29961          }
29962        });
29963      };
29964    }
29965  };
29966}];
29967
29968// This directive is called during the $transclude call of the first `ngInclude` directive.
29969// It will replace and compile the content of the element with the loaded template.
29970// We need this directive so that the element content is already filled when
29971// the link function of another directive on the same element as ngInclude
29972// is called.
29973var ngIncludeFillContentDirective = ['$compile',
29974  function($compile) {
29975    return {
29976      restrict: 'ECA',
29977      priority: -400,
29978      require: 'ngInclude',
29979      link: function(scope, $element, $attr, ctrl) {
29980        if (toString.call($element[0]).match(/SVG/)) {
29981          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
29982          // support innerHTML, so detect this here and try to generate the contents
29983          // specially.
29984          $element.empty();
29985          $compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
29986              function namespaceAdaptedClone(clone) {
29987            $element.append(clone);
29988          }, {futureParentElement: $element});
29989          return;
29990        }
29991
29992        $element.html(ctrl.template);
29993        $compile($element.contents())(scope);
29994      }
29995    };
29996  }];
29997
29998/**
29999 * @ngdoc directive
30000 * @name ngInit
30001 * @restrict AC
30002 * @priority 450
30003 * @element ANY
30004 *
30005 * @param {expression} ngInit {@link guide/expression Expression} to eval.
30006 *
30007 * @description
30008 * The `ngInit` directive allows you to evaluate an expression in the
30009 * current scope.
30010 *
30011 * <div class="alert alert-danger">
30012 * This directive can be abused to add unnecessary amounts of logic into your templates.
30013 * There are only a few appropriate uses of `ngInit`:
30014 * <ul>
30015 *   <li>aliasing special properties of {@link ng.directive:ngRepeat `ngRepeat`},
30016 *     as seen in the demo below.</li>
30017 *   <li>initializing data during development, or for examples, as seen throughout these docs.</li>
30018 *   <li>injecting data via server side scripting.</li>
30019 * </ul>
30020 *
30021 * Besides these few cases, you should use {@link guide/component Components} or
30022 * {@link guide/controller Controllers} rather than `ngInit` to initialize values on a scope.
30023 * </div>
30024 *
30025 * <div class="alert alert-warning">
30026 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
30027 * sure you have parentheses to ensure correct operator precedence:
30028 * <pre class="prettyprint">
30029 * `<div ng-init="test1 = ($index | toString)"></div>`
30030 * </pre>
30031 * </div>
30032 *
30033 * @example
30034   <example module="initExample" name="ng-init">
30035     <file name="index.html">
30036   <script>
30037     angular.module('initExample', [])
30038       .controller('ExampleController', ['$scope', function($scope) {
30039         $scope.list = [['a', 'b'], ['c', 'd']];
30040       }]);
30041   </script>
30042   <div ng-controller="ExampleController">
30043     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
30044       <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
30045          <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
30046       </div>
30047     </div>
30048   </div>
30049     </file>
30050     <file name="protractor.js" type="protractor">
30051       it('should alias index positions', function() {
30052         var elements = element.all(by.css('.example-init'));
30053         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
30054         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
30055         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
30056         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
30057       });
30058     </file>
30059   </example>
30060 */
30061var ngInitDirective = ngDirective({
30062  priority: 450,
30063  compile: function() {
30064    return {
30065      pre: function(scope, element, attrs) {
30066        scope.$eval(attrs.ngInit);
30067      }
30068    };
30069  }
30070});
30071
30072/**
30073 * @ngdoc directive
30074 * @name ngList
30075 * @restrict A
30076 * @priority 100
30077 *
30078 * @param {string=} ngList optional delimiter that should be used to split the value.
30079 *
30080 * @description
30081 * Text input that converts between a delimited string and an array of strings. The default
30082 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
30083 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
30084 *
30085 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
30086 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
30087 *   list item is respected. This implies that the user of the directive is responsible for
30088 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
30089 *   tab or newline character.
30090 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
30091 *   when joining the list items back together) and whitespace around each list item is stripped
30092 *   before it is added to the model.
30093 *
30094 * @example
30095 * ### Validation
30096 *
30097 * <example name="ngList-directive" module="listExample">
30098 *   <file name="app.js">
30099 *      angular.module('listExample', [])
30100 *        .controller('ExampleController', ['$scope', function($scope) {
30101 *          $scope.names = ['morpheus', 'neo', 'trinity'];
30102 *        }]);
30103 *   </file>
30104 *   <file name="index.html">
30105 *    <form name="myForm" ng-controller="ExampleController">
30106 *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
30107 *      <span role="alert">
30108 *        <span class="error" ng-show="myForm.namesInput.$error.required">
30109 *        Required!</span>
30110 *      </span>
30111 *      <br>
30112 *      <tt>names = {{names}}</tt><br/>
30113 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
30114 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
30115 *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
30116 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
30117 *     </form>
30118 *   </file>
30119 *   <file name="protractor.js" type="protractor">
30120 *     var listInput = element(by.model('names'));
30121 *     var names = element(by.exactBinding('names'));
30122 *     var valid = element(by.binding('myForm.namesInput.$valid'));
30123 *     var error = element(by.css('span.error'));
30124 *
30125 *     it('should initialize to model', function() {
30126 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
30127 *       expect(valid.getText()).toContain('true');
30128 *       expect(error.getCssValue('display')).toBe('none');
30129 *     });
30130 *
30131 *     it('should be invalid if empty', function() {
30132 *       listInput.clear();
30133 *       listInput.sendKeys('');
30134 *
30135 *       expect(names.getText()).toContain('');
30136 *       expect(valid.getText()).toContain('false');
30137 *       expect(error.getCssValue('display')).not.toBe('none');
30138 *     });
30139 *   </file>
30140 * </example>
30141 *
30142 * @example
30143 * ### Splitting on newline
30144 *
30145 * <example name="ngList-directive-newlines">
30146 *   <file name="index.html">
30147 *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
30148 *    <pre>{{ list | json }}</pre>
30149 *   </file>
30150 *   <file name="protractor.js" type="protractor">
30151 *     it("should split the text by newlines", function() {
30152 *       var listInput = element(by.model('list'));
30153 *       var output = element(by.binding('list | json'));
30154 *       listInput.sendKeys('abc\ndef\nghi');
30155 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
30156 *     });
30157 *   </file>
30158 * </example>
30159 *
30160 */
30161var ngListDirective = function() {
30162  return {
30163    restrict: 'A',
30164    priority: 100,
30165    require: 'ngModel',
30166    link: function(scope, element, attr, ctrl) {
30167      var ngList = attr.ngList || ', ';
30168      var trimValues = attr.ngTrim !== 'false';
30169      var separator = trimValues ? trim(ngList) : ngList;
30170
30171      var parse = function(viewValue) {
30172        // If the viewValue is invalid (say required but empty) it will be `undefined`
30173        if (isUndefined(viewValue)) return;
30174
30175        var list = [];
30176
30177        if (viewValue) {
30178          forEach(viewValue.split(separator), function(value) {
30179            if (value) list.push(trimValues ? trim(value) : value);
30180          });
30181        }
30182
30183        return list;
30184      };
30185
30186      ctrl.$parsers.push(parse);
30187      ctrl.$formatters.push(function(value) {
30188        if (isArray(value)) {
30189          return value.join(ngList);
30190        }
30191
30192        return undefined;
30193      });
30194
30195      // Override the standard $isEmpty because an empty array means the input is empty.
30196      ctrl.$isEmpty = function(value) {
30197        return !value || !value.length;
30198      };
30199    }
30200  };
30201};
30202
30203/* global VALID_CLASS: true,
30204  INVALID_CLASS: true,
30205  PRISTINE_CLASS: true,
30206  DIRTY_CLASS: true,
30207  UNTOUCHED_CLASS: true,
30208  TOUCHED_CLASS: true,
30209  PENDING_CLASS: true,
30210  addSetValidityMethod: true,
30211  setupValidity: true,
30212  defaultModelOptions: false
30213*/
30214
30215
30216var VALID_CLASS = 'ng-valid',
30217    INVALID_CLASS = 'ng-invalid',
30218    PRISTINE_CLASS = 'ng-pristine',
30219    DIRTY_CLASS = 'ng-dirty',
30220    UNTOUCHED_CLASS = 'ng-untouched',
30221    TOUCHED_CLASS = 'ng-touched',
30222    EMPTY_CLASS = 'ng-empty',
30223    NOT_EMPTY_CLASS = 'ng-not-empty';
30224
30225var ngModelMinErr = minErr('ngModel');
30226
30227/**
30228 * @ngdoc type
30229 * @name ngModel.NgModelController
30230 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
30231 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
30232 * is set.
30233 *
30234 * @property {*} $modelValue The value in the model that the control is bound to.
30235 *
30236 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
30237 *  the control updates the ngModelController with a new {@link ngModel.NgModelController#$viewValue
30238    `$viewValue`} from the DOM, usually via user input.
30239    See {@link ngModel.NgModelController#$setViewValue `$setViewValue()`} for a detailed lifecycle explanation.
30240    Note that the `$parsers` are not called when the bound ngModel expression changes programmatically.
30241
30242  The functions are called in array order, each passing
30243    its return value through to the next. The last return value is forwarded to the
30244    {@link ngModel.NgModelController#$validators `$validators`} collection.
30245
30246  Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
30247    `$viewValue`}.
30248
30249  Returning `undefined` from a parser means a parse error occurred. In that case,
30250    no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
30251    will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
30252    is set to `true`. The parse error is stored in `ngModel.$error.parse`.
30253
30254  This simple example shows a parser that would convert text input value to lowercase:
30255 * ```js
30256 * function parse(value) {
30257 *   if (value) {
30258 *     return value.toLowerCase();
30259 *   }
30260 * }
30261 * ngModelController.$parsers.push(parse);
30262 * ```
30263
30264 *
30265 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
30266    the bound ngModel expression changes programmatically. The `$formatters` are not called when the
30267    value of the control is changed by user interaction.
30268
30269  Formatters are used to format / convert the {@link ngModel.NgModelController#$modelValue
30270    `$modelValue`} for display in the control.
30271
30272  The functions are called in reverse array order, each passing the value through to the
30273    next. The last return value is used as the actual DOM value.
30274
30275  This simple example shows a formatter that would convert the model value to uppercase:
30276
30277 * ```js
30278 * function format(value) {
30279 *   if (value) {
30280 *     return value.toUpperCase();
30281 *   }
30282 * }
30283 * ngModel.$formatters.push(format);
30284 * ```
30285 *
30286 * @property {Object.<string, function>} $validators A collection of validators that are applied
30287 *      whenever the model value changes. The key value within the object refers to the name of the
30288 *      validator while the function refers to the validation operation. The validation operation is
30289 *      provided with the model value as an argument and must return a true or false value depending
30290 *      on the response of that validation.
30291 *
30292 * ```js
30293 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
30294 *   var value = modelValue || viewValue;
30295 *   return /[0-9]+/.test(value) &&
30296 *          /[a-z]+/.test(value) &&
30297 *          /[A-Z]+/.test(value) &&
30298 *          /\W+/.test(value);
30299 * };
30300 * ```
30301 *
30302 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
30303 *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
30304 *      is expected to return a promise when it is run during the model validation process. Once the promise
30305 *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
30306 *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
30307 *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
30308 *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
30309 *      will only run once all synchronous validators have passed.
30310 *
30311 * Please note that if $http is used then it is important that the server returns a success HTTP response code
30312 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
30313 *
30314 * ```js
30315 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
30316 *   var value = modelValue || viewValue;
30317 *
30318 *   // Lookup user by username
30319 *   return $http.get('/api/users/' + value).
30320 *      then(function resolved() {
30321 *        //username exists, this means validation fails
30322 *        return $q.reject('exists');
30323 *      }, function rejected() {
30324 *        //username does not exist, therefore this validation passes
30325 *        return true;
30326 *      });
30327 * };
30328 * ```
30329 *
30330 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever
30331 *     a change to {@link ngModel.NgModelController#$viewValue `$viewValue`} has caused a change
30332 *     to {@link ngModel.NgModelController#$modelValue `$modelValue`}.
30333 *     It is called with no arguments, and its return value is ignored.
30334 *     This can be used in place of additional $watches against the model value.
30335 *
30336 * @property {Object} $error An object hash with all failing validator ids as keys.
30337 * @property {Object} $pending An object hash with all pending validator ids as keys.
30338 *
30339 * @property {boolean} $untouched True if control has not lost focus yet.
30340 * @property {boolean} $touched True if control has lost focus.
30341 * @property {boolean} $pristine True if user has not interacted with the control yet.
30342 * @property {boolean} $dirty True if user has already interacted with the control.
30343 * @property {boolean} $valid True if there is no error.
30344 * @property {boolean} $invalid True if at least one error on the control.
30345 * @property {string} $name The name attribute of the control.
30346 *
30347 * @description
30348 *
30349 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
30350 * The controller contains services for data-binding, validation, CSS updates, and value formatting
30351 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
30352 * listening to DOM events.
30353 * Such DOM related logic should be provided by other directives which make use of
30354 * `NgModelController` for data-binding to control elements.
30355 * AngularJS provides this DOM logic for most {@link input `input`} elements.
30356 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
30357 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
30358 *
30359 * @example
30360 * ### Custom Control Example
30361 * This example shows how to use `NgModelController` with a custom control to achieve
30362 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
30363 * collaborate together to achieve the desired result.
30364 *
30365 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
30366 * contents be edited in place by the user.
30367 *
30368 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
30369 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
30370 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
30371 * that content using the `$sce` service.
30372 *
30373 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
30374    <file name="style.css">
30375      [contenteditable] {
30376        border: 1px solid black;
30377        background-color: white;
30378        min-height: 20px;
30379      }
30380
30381      .ng-invalid {
30382        border: 1px solid red;
30383      }
30384
30385    </file>
30386    <file name="script.js">
30387      angular.module('customControl', ['ngSanitize']).
30388        directive('contenteditable', ['$sce', function($sce) {
30389          return {
30390            restrict: 'A', // only activate on element attribute
30391            require: '?ngModel', // get a hold of NgModelController
30392            link: function(scope, element, attrs, ngModel) {
30393              if (!ngModel) return; // do nothing if no ng-model
30394
30395              // Specify how UI should be updated
30396              ngModel.$render = function() {
30397                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
30398              };
30399
30400              // Listen for change events to enable binding
30401              element.on('blur keyup change', function() {
30402                scope.$evalAsync(read);
30403              });
30404              read(); // initialize
30405
30406              // Write data to the model
30407              function read() {
30408                var html = element.html();
30409                // When we clear the content editable the browser leaves a <br> behind
30410                // If strip-br attribute is provided then we strip this out
30411                if (attrs.stripBr && html === '<br>') {
30412                  html = '';
30413                }
30414                ngModel.$setViewValue(html);
30415              }
30416            }
30417          };
30418        }]);
30419    </file>
30420    <file name="index.html">
30421      <form name="myForm">
30422       <div contenteditable
30423            name="myWidget" ng-model="userContent"
30424            strip-br="true"
30425            required>Change me!</div>
30426        <span ng-show="myForm.myWidget.$error.required">Required!</span>
30427       <hr>
30428       <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
30429      </form>
30430    </file>
30431    <file name="protractor.js" type="protractor">
30432    it('should data-bind and become invalid', function() {
30433      if (browser.params.browser === 'safari' || browser.params.browser === 'firefox') {
30434        // SafariDriver can't handle contenteditable
30435        // and Firefox driver can't clear contenteditables very well
30436        return;
30437      }
30438      var contentEditable = element(by.css('[contenteditable]'));
30439      var content = 'Change me!';
30440
30441      expect(contentEditable.getText()).toEqual(content);
30442
30443      contentEditable.clear();
30444      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
30445      expect(contentEditable.getText()).toEqual('');
30446      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
30447    });
30448    </file>
30449 * </example>
30450 *
30451 *
30452 */
30453NgModelController.$inject = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$q', '$interpolate'];
30454function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $q, $interpolate) {
30455  this.$viewValue = Number.NaN;
30456  this.$modelValue = Number.NaN;
30457  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
30458  this.$validators = {};
30459  this.$asyncValidators = {};
30460  this.$parsers = [];
30461  this.$formatters = [];
30462  this.$viewChangeListeners = [];
30463  this.$untouched = true;
30464  this.$touched = false;
30465  this.$pristine = true;
30466  this.$dirty = false;
30467  this.$valid = true;
30468  this.$invalid = false;
30469  this.$error = {}; // keep invalid keys here
30470  this.$$success = {}; // keep valid keys here
30471  this.$pending = undefined; // keep pending keys here
30472  this.$name = $interpolate($attr.name || '', false)($scope);
30473  this.$$parentForm = nullFormCtrl;
30474  this.$options = defaultModelOptions;
30475  this.$$updateEvents = '';
30476  // Attach the correct context to the event handler function for updateOn
30477  this.$$updateEventHandler = this.$$updateEventHandler.bind(this);
30478
30479  this.$$parsedNgModel = $parse($attr.ngModel);
30480  this.$$parsedNgModelAssign = this.$$parsedNgModel.assign;
30481  this.$$ngModelGet = this.$$parsedNgModel;
30482  this.$$ngModelSet = this.$$parsedNgModelAssign;
30483  this.$$pendingDebounce = null;
30484  this.$$parserValid = undefined;
30485  this.$$parserName = 'parse';
30486
30487  this.$$currentValidationRunId = 0;
30488
30489  this.$$scope = $scope;
30490  this.$$rootScope = $scope.$root;
30491  this.$$attr = $attr;
30492  this.$$element = $element;
30493  this.$$animate = $animate;
30494  this.$$timeout = $timeout;
30495  this.$$parse = $parse;
30496  this.$$q = $q;
30497  this.$$exceptionHandler = $exceptionHandler;
30498
30499  setupValidity(this);
30500  setupModelWatcher(this);
30501}
30502
30503NgModelController.prototype = {
30504  $$initGetterSetters: function() {
30505    if (this.$options.getOption('getterSetter')) {
30506      var invokeModelGetter = this.$$parse(this.$$attr.ngModel + '()'),
30507          invokeModelSetter = this.$$parse(this.$$attr.ngModel + '($$$p)');
30508
30509      this.$$ngModelGet = function($scope) {
30510        var modelValue = this.$$parsedNgModel($scope);
30511        if (isFunction(modelValue)) {
30512          modelValue = invokeModelGetter($scope);
30513        }
30514        return modelValue;
30515      };
30516      this.$$ngModelSet = function($scope, newValue) {
30517        if (isFunction(this.$$parsedNgModel($scope))) {
30518          invokeModelSetter($scope, {$$$p: newValue});
30519        } else {
30520          this.$$parsedNgModelAssign($scope, newValue);
30521        }
30522      };
30523    } else if (!this.$$parsedNgModel.assign) {
30524      throw ngModelMinErr('nonassign', 'Expression \'{0}\' is non-assignable. Element: {1}',
30525          this.$$attr.ngModel, startingTag(this.$$element));
30526    }
30527  },
30528
30529
30530  /**
30531   * @ngdoc method
30532   * @name ngModel.NgModelController#$render
30533   *
30534   * @description
30535   * Called when the view needs to be updated. It is expected that the user of the ng-model
30536   * directive will implement this method.
30537   *
30538   * The `$render()` method is invoked in the following situations:
30539   *
30540   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
30541   *   committed value then `$render()` is called to update the input control.
30542   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
30543   *   the `$viewValue` are different from last time.
30544   *
30545   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
30546   * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
30547   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
30548   * invoked if you only change a property on the objects.
30549   */
30550  $render: noop,
30551
30552  /**
30553   * @ngdoc method
30554   * @name ngModel.NgModelController#$isEmpty
30555   *
30556   * @description
30557   * This is called when we need to determine if the value of an input is empty.
30558   *
30559   * For instance, the required directive does this to work out if the input has data or not.
30560   *
30561   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
30562   *
30563   * You can override this for input directives whose concept of being empty is different from the
30564   * default. The `checkboxInputType` directive does this because in its case a value of `false`
30565   * implies empty.
30566   *
30567   * @param {*} value The value of the input to check for emptiness.
30568   * @returns {boolean} True if `value` is "empty".
30569   */
30570  $isEmpty: function(value) {
30571    // eslint-disable-next-line no-self-compare
30572    return isUndefined(value) || value === '' || value === null || value !== value;
30573  },
30574
30575  $$updateEmptyClasses: function(value) {
30576    if (this.$isEmpty(value)) {
30577      this.$$animate.removeClass(this.$$element, NOT_EMPTY_CLASS);
30578      this.$$animate.addClass(this.$$element, EMPTY_CLASS);
30579    } else {
30580      this.$$animate.removeClass(this.$$element, EMPTY_CLASS);
30581      this.$$animate.addClass(this.$$element, NOT_EMPTY_CLASS);
30582    }
30583  },
30584
30585  /**
30586   * @ngdoc method
30587   * @name ngModel.NgModelController#$setPristine
30588   *
30589   * @description
30590   * Sets the control to its pristine state.
30591   *
30592   * This method can be called to remove the `ng-dirty` class and set the control to its pristine
30593   * state (`ng-pristine` class). A model is considered to be pristine when the control
30594   * has not been changed from when first compiled.
30595   */
30596  $setPristine: function() {
30597    this.$dirty = false;
30598    this.$pristine = true;
30599    this.$$animate.removeClass(this.$$element, DIRTY_CLASS);
30600    this.$$animate.addClass(this.$$element, PRISTINE_CLASS);
30601  },
30602
30603  /**
30604   * @ngdoc method
30605   * @name ngModel.NgModelController#$setDirty
30606   *
30607   * @description
30608   * Sets the control to its dirty state.
30609   *
30610   * This method can be called to remove the `ng-pristine` class and set the control to its dirty
30611   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
30612   * from when first compiled.
30613   */
30614  $setDirty: function() {
30615    this.$dirty = true;
30616    this.$pristine = false;
30617    this.$$animate.removeClass(this.$$element, PRISTINE_CLASS);
30618    this.$$animate.addClass(this.$$element, DIRTY_CLASS);
30619    this.$$parentForm.$setDirty();
30620  },
30621
30622  /**
30623   * @ngdoc method
30624   * @name ngModel.NgModelController#$setUntouched
30625   *
30626   * @description
30627   * Sets the control to its untouched state.
30628   *
30629   * This method can be called to remove the `ng-touched` class and set the control to its
30630   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
30631   * by default, however this function can be used to restore that state if the model has
30632   * already been touched by the user.
30633   */
30634  $setUntouched: function() {
30635    this.$touched = false;
30636    this.$untouched = true;
30637    this.$$animate.setClass(this.$$element, UNTOUCHED_CLASS, TOUCHED_CLASS);
30638  },
30639
30640  /**
30641   * @ngdoc method
30642   * @name ngModel.NgModelController#$setTouched
30643   *
30644   * @description
30645   * Sets the control to its touched state.
30646   *
30647   * This method can be called to remove the `ng-untouched` class and set the control to its
30648   * touched state (`ng-touched` class). A model is considered to be touched when the user has
30649   * first focused the control element and then shifted focus away from the control (blur event).
30650   */
30651  $setTouched: function() {
30652    this.$touched = true;
30653    this.$untouched = false;
30654    this.$$animate.setClass(this.$$element, TOUCHED_CLASS, UNTOUCHED_CLASS);
30655  },
30656
30657  /**
30658   * @ngdoc method
30659   * @name ngModel.NgModelController#$rollbackViewValue
30660   *
30661   * @description
30662   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
30663   * which may be caused by a pending debounced event or because the input is waiting for some
30664   * future event.
30665   *
30666   * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
30667   * depend on special events such as `blur`, there can be a period when the `$viewValue` is out of
30668   * sync with the ngModel's `$modelValue`.
30669   *
30670   * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
30671   * and reset the input to the last committed view value.
30672   *
30673   * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
30674   * programmatically before these debounced/future events have resolved/occurred, because AngularJS's
30675   * dirty checking mechanism is not able to tell whether the model has actually changed or not.
30676   *
30677   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
30678   * input which may have such events pending. This is important in order to make sure that the
30679   * input field will be updated with the new model value and any pending operations are cancelled.
30680   *
30681   * @example
30682   * <example name="ng-model-cancel-update" module="cancel-update-example">
30683   *   <file name="app.js">
30684   *     angular.module('cancel-update-example', [])
30685   *
30686   *     .controller('CancelUpdateController', ['$scope', function($scope) {
30687   *       $scope.model = {value1: '', value2: ''};
30688   *
30689   *       $scope.setEmpty = function(e, value, rollback) {
30690   *         if (e.keyCode === 27) {
30691   *           e.preventDefault();
30692   *           if (rollback) {
30693   *             $scope.myForm[value].$rollbackViewValue();
30694   *           }
30695   *           $scope.model[value] = '';
30696   *         }
30697   *       };
30698   *     }]);
30699   *   </file>
30700   *   <file name="index.html">
30701   *     <div ng-controller="CancelUpdateController">
30702   *       <p>Both of these inputs are only updated if they are blurred. Hitting escape should
30703   *       empty them. Follow these steps and observe the difference:</p>
30704   *       <ol>
30705   *         <li>Type something in the input. You will see that the model is not yet updated</li>
30706   *         <li>Press the Escape key.
30707   *           <ol>
30708   *             <li> In the first example, nothing happens, because the model is already '', and no
30709   *             update is detected. If you blur the input, the model will be set to the current view.
30710   *             </li>
30711   *             <li> In the second example, the pending update is cancelled, and the input is set back
30712   *             to the last committed view value (''). Blurring the input does nothing.
30713   *             </li>
30714   *           </ol>
30715   *         </li>
30716   *       </ol>
30717   *
30718   *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
30719   *         <div>
30720   *           <p id="inputDescription1">Without $rollbackViewValue():</p>
30721   *           <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
30722   *                  ng-keydown="setEmpty($event, 'value1')">
30723   *           value1: "{{ model.value1 }}"
30724   *         </div>
30725   *
30726   *         <div>
30727   *           <p id="inputDescription2">With $rollbackViewValue():</p>
30728   *           <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
30729   *                  ng-keydown="setEmpty($event, 'value2', true)">
30730   *           value2: "{{ model.value2 }}"
30731   *         </div>
30732   *       </form>
30733   *     </div>
30734   *   </file>
30735       <file name="style.css">
30736          div {
30737            display: table-cell;
30738          }
30739          div:nth-child(1) {
30740            padding-right: 30px;
30741          }
30742
30743        </file>
30744   * </example>
30745   */
30746  $rollbackViewValue: function() {
30747    this.$$timeout.cancel(this.$$pendingDebounce);
30748    this.$viewValue = this.$$lastCommittedViewValue;
30749    this.$render();
30750  },
30751
30752  /**
30753   * @ngdoc method
30754   * @name ngModel.NgModelController#$validate
30755   *
30756   * @description
30757   * Runs each of the registered validators (first synchronous validators and then
30758   * asynchronous validators).
30759   * If the validity changes to invalid, the model will be set to `undefined`,
30760   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
30761   * If the validity changes to valid, it will set the model to the last available valid
30762   * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
30763   */
30764  $validate: function() {
30765
30766    // ignore $validate before model is initialized
30767    if (isNumberNaN(this.$modelValue)) {
30768      return;
30769    }
30770
30771    var viewValue = this.$$lastCommittedViewValue;
30772    // Note: we use the $$rawModelValue as $modelValue might have been
30773    // set to undefined during a view -> model update that found validation
30774    // errors. We can't parse the view here, since that could change
30775    // the model although neither viewValue nor the model on the scope changed
30776    var modelValue = this.$$rawModelValue;
30777
30778    var prevValid = this.$valid;
30779    var prevModelValue = this.$modelValue;
30780
30781    var allowInvalid = this.$options.getOption('allowInvalid');
30782
30783    var that = this;
30784    this.$$runValidators(modelValue, viewValue, function(allValid) {
30785      // If there was no change in validity, don't update the model
30786      // This prevents changing an invalid modelValue to undefined
30787      if (!allowInvalid && prevValid !== allValid) {
30788        // Note: Don't check this.$valid here, as we could have
30789        // external validators (e.g. calculated on the server),
30790        // that just call $setValidity and need the model value
30791        // to calculate their validity.
30792        that.$modelValue = allValid ? modelValue : undefined;
30793
30794        if (that.$modelValue !== prevModelValue) {
30795          that.$$writeModelToScope();
30796        }
30797      }
30798    });
30799  },
30800
30801  $$runValidators: function(modelValue, viewValue, doneCallback) {
30802    this.$$currentValidationRunId++;
30803    var localValidationRunId = this.$$currentValidationRunId;
30804    var that = this;
30805
30806    // check parser error
30807    if (!processParseErrors()) {
30808      validationDone(false);
30809      return;
30810    }
30811    if (!processSyncValidators()) {
30812      validationDone(false);
30813      return;
30814    }
30815    processAsyncValidators();
30816
30817    function processParseErrors() {
30818      var errorKey = that.$$parserName;
30819
30820      if (isUndefined(that.$$parserValid)) {
30821        setValidity(errorKey, null);
30822      } else {
30823        if (!that.$$parserValid) {
30824          forEach(that.$validators, function(v, name) {
30825            setValidity(name, null);
30826          });
30827          forEach(that.$asyncValidators, function(v, name) {
30828            setValidity(name, null);
30829          });
30830        }
30831
30832        // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
30833        setValidity(errorKey, that.$$parserValid);
30834        return that.$$parserValid;
30835      }
30836      return true;
30837    }
30838
30839    function processSyncValidators() {
30840      var syncValidatorsValid = true;
30841      forEach(that.$validators, function(validator, name) {
30842        var result = Boolean(validator(modelValue, viewValue));
30843        syncValidatorsValid = syncValidatorsValid && result;
30844        setValidity(name, result);
30845      });
30846      if (!syncValidatorsValid) {
30847        forEach(that.$asyncValidators, function(v, name) {
30848          setValidity(name, null);
30849        });
30850        return false;
30851      }
30852      return true;
30853    }
30854
30855    function processAsyncValidators() {
30856      var validatorPromises = [];
30857      var allValid = true;
30858      forEach(that.$asyncValidators, function(validator, name) {
30859        var promise = validator(modelValue, viewValue);
30860        if (!isPromiseLike(promise)) {
30861          throw ngModelMinErr('nopromise',
30862            'Expected asynchronous validator to return a promise but got \'{0}\' instead.', promise);
30863        }
30864        setValidity(name, undefined);
30865        validatorPromises.push(promise.then(function() {
30866          setValidity(name, true);
30867        }, function() {
30868          allValid = false;
30869          setValidity(name, false);
30870        }));
30871      });
30872      if (!validatorPromises.length) {
30873        validationDone(true);
30874      } else {
30875        that.$$q.all(validatorPromises).then(function() {
30876          validationDone(allValid);
30877        }, noop);
30878      }
30879    }
30880
30881    function setValidity(name, isValid) {
30882      if (localValidationRunId === that.$$currentValidationRunId) {
30883        that.$setValidity(name, isValid);
30884      }
30885    }
30886
30887    function validationDone(allValid) {
30888      if (localValidationRunId === that.$$currentValidationRunId) {
30889
30890        doneCallback(allValid);
30891      }
30892    }
30893  },
30894
30895  /**
30896   * @ngdoc method
30897   * @name ngModel.NgModelController#$commitViewValue
30898   *
30899   * @description
30900   * Commit a pending update to the `$modelValue`.
30901   *
30902   * Updates may be pending by a debounced event or because the input is waiting for a some future
30903   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
30904   * usually handles calling this in response to input events.
30905   */
30906  $commitViewValue: function() {
30907    var viewValue = this.$viewValue;
30908
30909    this.$$timeout.cancel(this.$$pendingDebounce);
30910
30911    // If the view value has not changed then we should just exit, except in the case where there is
30912    // a native validator on the element. In this case the validation state may have changed even though
30913    // the viewValue has stayed empty.
30914    if (this.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !this.$$hasNativeValidators)) {
30915      return;
30916    }
30917    this.$$updateEmptyClasses(viewValue);
30918    this.$$lastCommittedViewValue = viewValue;
30919
30920    // change to dirty
30921    if (this.$pristine) {
30922      this.$setDirty();
30923    }
30924    this.$$parseAndValidate();
30925  },
30926
30927  $$parseAndValidate: function() {
30928    var viewValue = this.$$lastCommittedViewValue;
30929    var modelValue = viewValue;
30930    var that = this;
30931
30932    this.$$parserValid = isUndefined(modelValue) ? undefined : true;
30933
30934    // Reset any previous parse error
30935    this.$setValidity(this.$$parserName, null);
30936    this.$$parserName = 'parse';
30937
30938    if (this.$$parserValid) {
30939      for (var i = 0; i < this.$parsers.length; i++) {
30940        modelValue = this.$parsers[i](modelValue);
30941        if (isUndefined(modelValue)) {
30942          this.$$parserValid = false;
30943          break;
30944        }
30945      }
30946    }
30947    if (isNumberNaN(this.$modelValue)) {
30948      // this.$modelValue has not been touched yet...
30949      this.$modelValue = this.$$ngModelGet(this.$$scope);
30950    }
30951    var prevModelValue = this.$modelValue;
30952    var allowInvalid = this.$options.getOption('allowInvalid');
30953    this.$$rawModelValue = modelValue;
30954
30955    if (allowInvalid) {
30956      this.$modelValue = modelValue;
30957      writeToModelIfNeeded();
30958    }
30959
30960    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
30961    // This can happen if e.g. $setViewValue is called from inside a parser
30962    this.$$runValidators(modelValue, this.$$lastCommittedViewValue, function(allValid) {
30963      if (!allowInvalid) {
30964        // Note: Don't check this.$valid here, as we could have
30965        // external validators (e.g. calculated on the server),
30966        // that just call $setValidity and need the model value
30967        // to calculate their validity.
30968        that.$modelValue = allValid ? modelValue : undefined;
30969        writeToModelIfNeeded();
30970      }
30971    });
30972
30973    function writeToModelIfNeeded() {
30974      if (that.$modelValue !== prevModelValue) {
30975        that.$$writeModelToScope();
30976      }
30977    }
30978  },
30979
30980  $$writeModelToScope: function() {
30981    this.$$ngModelSet(this.$$scope, this.$modelValue);
30982    forEach(this.$viewChangeListeners, function(listener) {
30983      try {
30984        listener();
30985      } catch (e) {
30986        // eslint-disable-next-line no-invalid-this
30987        this.$$exceptionHandler(e);
30988      }
30989    }, this);
30990  },
30991
30992  /**
30993   * @ngdoc method
30994   * @name ngModel.NgModelController#$setViewValue
30995   *
30996   * @description
30997   * Update the view value.
30998   *
30999   * This method should be called when a control wants to change the view value; typically,
31000   * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
31001   * directive calls it when the value of the input changes and {@link ng.directive:select select}
31002   * calls it when an option is selected.
31003   *
31004   * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
31005   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
31006   * value is sent directly for processing through the `$parsers` pipeline. After this, the `$validators` and
31007   * `$asyncValidators` are called and the value is applied to `$modelValue`.
31008   * Finally, the value is set to the **expression** specified in the `ng-model` attribute and
31009   * all the registered change listeners, in the `$viewChangeListeners` list are called.
31010   *
31011   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
31012   * and the `default` trigger is not listed, all those actions will remain pending until one of the
31013   * `updateOn` events is triggered on the DOM element.
31014   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
31015   * directive is used with a custom debounce for this particular event.
31016   * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
31017   * is specified, once the timer runs out.
31018   *
31019   * When used with standard inputs, the view value will always be a string (which is in some cases
31020   * parsed into another type, such as a `Date` object for `input[date]`.)
31021   * However, custom controls might also pass objects to this method. In this case, we should make
31022   * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
31023   * perform a deep watch of objects, it only looks for a change of identity. If you only change
31024   * the property of the object then ngModel will not realize that the object has changed and
31025   * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
31026   * not change properties of the copy once it has been passed to `$setViewValue`.
31027   * Otherwise you may cause the model value on the scope to change incorrectly.
31028   *
31029   * <div class="alert alert-info">
31030   * In any case, the value passed to the method should always reflect the current value
31031   * of the control. For example, if you are calling `$setViewValue` for an input element,
31032   * you should pass the input DOM value. Otherwise, the control and the scope model become
31033   * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
31034   * the control's DOM value in any way. If we want to change the control's DOM value
31035   * programmatically, we should update the `ngModel` scope expression. Its new value will be
31036   * picked up by the model controller, which will run it through the `$formatters`, `$render` it
31037   * to update the DOM, and finally call `$validate` on it.
31038   * </div>
31039   *
31040   * @param {*} value value from the view.
31041   * @param {string} trigger Event that triggered the update.
31042   */
31043  $setViewValue: function(value, trigger) {
31044    this.$viewValue = value;
31045    if (this.$options.getOption('updateOnDefault')) {
31046      this.$$debounceViewValueCommit(trigger);
31047    }
31048  },
31049
31050  $$debounceViewValueCommit: function(trigger) {
31051    var debounceDelay = this.$options.getOption('debounce');
31052
31053    if (isNumber(debounceDelay[trigger])) {
31054      debounceDelay = debounceDelay[trigger];
31055    } else if (isNumber(debounceDelay['default']) &&
31056      this.$options.getOption('updateOn').indexOf(trigger) === -1
31057    ) {
31058      debounceDelay = debounceDelay['default'];
31059    } else if (isNumber(debounceDelay['*'])) {
31060      debounceDelay = debounceDelay['*'];
31061    }
31062
31063    this.$$timeout.cancel(this.$$pendingDebounce);
31064    var that = this;
31065    if (debounceDelay > 0) { // this fails if debounceDelay is an object
31066      this.$$pendingDebounce = this.$$timeout(function() {
31067        that.$commitViewValue();
31068      }, debounceDelay);
31069    } else if (this.$$rootScope.$$phase) {
31070      this.$commitViewValue();
31071    } else {
31072      this.$$scope.$apply(function() {
31073        that.$commitViewValue();
31074      });
31075    }
31076  },
31077
31078  /**
31079   * @ngdoc method
31080   *
31081   * @name ngModel.NgModelController#$overrideModelOptions
31082   *
31083   * @description
31084   *
31085   * Override the current model options settings programmatically.
31086   *
31087   * The previous `ModelOptions` value will not be modified. Instead, a
31088   * new `ModelOptions` object will inherit from the previous one overriding
31089   * or inheriting settings that are defined in the given parameter.
31090   *
31091   * See {@link ngModelOptions} for information about what options can be specified
31092   * and how model option inheritance works.
31093   *
31094   * <div class="alert alert-warning">
31095   * **Note:** this function only affects the options set on the `ngModelController`,
31096   * and not the options on the {@link ngModelOptions} directive from which they might have been
31097   * obtained initially.
31098   * </div>
31099   *
31100   * <div class="alert alert-danger">
31101   * **Note:** it is not possible to override the `getterSetter` option.
31102   * </div>
31103   *
31104   * @param {Object} options a hash of settings to override the previous options
31105   *
31106   */
31107  $overrideModelOptions: function(options) {
31108    this.$options = this.$options.createChild(options);
31109    this.$$setUpdateOnEvents();
31110  },
31111
31112  /**
31113   * @ngdoc method
31114   *
31115   * @name  ngModel.NgModelController#$processModelValue
31116
31117   * @description
31118   *
31119   * Runs the model -> view pipeline on the current
31120   * {@link ngModel.NgModelController#$modelValue $modelValue}.
31121   *
31122   * The following actions are performed by this method:
31123   *
31124   * - the `$modelValue` is run through the {@link ngModel.NgModelController#$formatters $formatters}
31125   * and the result is set to the {@link ngModel.NgModelController#$viewValue $viewValue}
31126   * - the `ng-empty` or `ng-not-empty` class is set on the element
31127   * - if the `$viewValue` has changed:
31128   *   - {@link ngModel.NgModelController#$render $render} is called on the control
31129   *   - the {@link ngModel.NgModelController#$validators $validators} are run and
31130   *   the validation status is set.
31131   *
31132   * This method is called by ngModel internally when the bound scope value changes.
31133   * Application developers usually do not have to call this function themselves.
31134   *
31135   * This function can be used when the `$viewValue` or the rendered DOM value are not correctly
31136   * formatted and the `$modelValue` must be run through the `$formatters` again.
31137   *
31138   * @example
31139   * Consider a text input with an autocomplete list (for fruit), where the items are
31140   * objects with a name and an id.
31141   * A user enters `ap` and then selects `Apricot` from the list.
31142   * Based on this, the autocomplete widget will call `$setViewValue({name: 'Apricot', id: 443})`,
31143   * but the rendered value will still be `ap`.
31144   * The widget can then call `ctrl.$processModelValue()` to run the model -> view
31145   * pipeline again, which formats the object to the string `Apricot`,
31146   * then updates the `$viewValue`, and finally renders it in the DOM.
31147   *
31148   * <example module="inputExample" name="ng-model-process">
31149     <file name="index.html">
31150      <div ng-controller="inputController" style="display: flex;">
31151        <div style="margin-right: 30px;">
31152          Search Fruit:
31153          <basic-autocomplete items="items" on-select="selectedFruit = item"></basic-autocomplete>
31154        </div>
31155        <div>
31156          Model:<br>
31157          <pre>{{selectedFruit | json}}</pre>
31158        </div>
31159      </div>
31160     </file>
31161     <file name="app.js">
31162      angular.module('inputExample', [])
31163        .controller('inputController', function($scope) {
31164          $scope.items = [
31165            {name: 'Apricot', id: 443},
31166            {name: 'Clementine', id: 972},
31167            {name: 'Durian', id: 169},
31168            {name: 'Jackfruit', id: 982},
31169            {name: 'Strawberry', id: 863}
31170          ];
31171        })
31172        .component('basicAutocomplete', {
31173          bindings: {
31174            items: '<',
31175            onSelect: '&'
31176          },
31177          templateUrl: 'autocomplete.html',
31178          controller: function($element, $scope) {
31179            var that = this;
31180            var ngModel;
31181
31182            that.$postLink = function() {
31183              ngModel = $element.find('input').controller('ngModel');
31184
31185              ngModel.$formatters.push(function(value) {
31186                return (value && value.name) || value;
31187              });
31188
31189              ngModel.$parsers.push(function(value) {
31190                var match = value;
31191                for (var i = 0; i < that.items.length; i++) {
31192                  if (that.items[i].name === value) {
31193                    match = that.items[i];
31194                    break;
31195                  }
31196                }
31197
31198                return match;
31199              });
31200            };
31201
31202            that.selectItem = function(item) {
31203              ngModel.$setViewValue(item);
31204              ngModel.$processModelValue();
31205              that.onSelect({item: item});
31206            };
31207          }
31208        });
31209     </file>
31210     <file name="autocomplete.html">
31211       <div>
31212         <input type="search" ng-model="$ctrl.searchTerm" />
31213         <ul>
31214           <li ng-repeat="item in $ctrl.items | filter:$ctrl.searchTerm">
31215             <button ng-click="$ctrl.selectItem(item)">{{ item.name }}</button>
31216           </li>
31217         </ul>
31218       </div>
31219     </file>
31220   * </example>
31221   *
31222   */
31223  $processModelValue: function() {
31224    var viewValue = this.$$format();
31225
31226    if (this.$viewValue !== viewValue) {
31227      this.$$updateEmptyClasses(viewValue);
31228      this.$viewValue = this.$$lastCommittedViewValue = viewValue;
31229      this.$render();
31230      // It is possible that model and view value have been updated during render
31231      this.$$runValidators(this.$modelValue, this.$viewValue, noop);
31232    }
31233  },
31234
31235  /**
31236   * This method is called internally to run the $formatters on the $modelValue
31237   */
31238  $$format: function() {
31239    var formatters = this.$formatters,
31240        idx = formatters.length;
31241
31242    var viewValue = this.$modelValue;
31243    while (idx--) {
31244      viewValue = formatters[idx](viewValue);
31245    }
31246
31247    return viewValue;
31248  },
31249
31250  /**
31251   * This method is called internally when the bound scope value changes.
31252   */
31253  $$setModelValue: function(modelValue) {
31254    this.$modelValue = this.$$rawModelValue = modelValue;
31255    this.$$parserValid = undefined;
31256    this.$processModelValue();
31257  },
31258
31259  $$setUpdateOnEvents: function() {
31260    if (this.$$updateEvents) {
31261      this.$$element.off(this.$$updateEvents, this.$$updateEventHandler);
31262    }
31263
31264    this.$$updateEvents = this.$options.getOption('updateOn');
31265    if (this.$$updateEvents) {
31266      this.$$element.on(this.$$updateEvents, this.$$updateEventHandler);
31267    }
31268  },
31269
31270  $$updateEventHandler: function(ev) {
31271    this.$$debounceViewValueCommit(ev && ev.type);
31272  }
31273};
31274
31275function setupModelWatcher(ctrl) {
31276  // model -> value
31277  // Note: we cannot use a normal scope.$watch as we want to detect the following:
31278  // 1. scope value is 'a'
31279  // 2. user enters 'b'
31280  // 3. ng-change kicks in and reverts scope value to 'a'
31281  //    -> scope value did not change since the last digest as
31282  //       ng-change executes in apply phase
31283  // 4. view should be changed back to 'a'
31284  ctrl.$$scope.$watch(function ngModelWatch(scope) {
31285    var modelValue = ctrl.$$ngModelGet(scope);
31286
31287    // if scope model value and ngModel value are out of sync
31288    // This cannot be moved to the action function, because it would not catch the
31289    // case where the model is changed in the ngChange function or the model setter
31290    if (modelValue !== ctrl.$modelValue &&
31291      // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
31292      // eslint-disable-next-line no-self-compare
31293      (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
31294    ) {
31295      ctrl.$$setModelValue(modelValue);
31296    }
31297
31298    return modelValue;
31299  });
31300}
31301
31302/**
31303 * @ngdoc method
31304 * @name ngModel.NgModelController#$setValidity
31305 *
31306 * @description
31307 * Change the validity state, and notify the form.
31308 *
31309 * This method can be called within $parsers/$formatters or a custom validation implementation.
31310 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
31311 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
31312 *
31313 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
31314 *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
31315 *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
31316 *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
31317 *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
31318 *        classes and can be bound to as `{{ someForm.someControl.$error.myError }}`.
31319 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
31320 *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
31321 *                          Skipped is used by AngularJS when validators do not run because of parse errors and
31322 *                          when `$asyncValidators` do not run because any of the `$validators` failed.
31323 */
31324addSetValidityMethod({
31325  clazz: NgModelController,
31326  set: function(object, property) {
31327    object[property] = true;
31328  },
31329  unset: function(object, property) {
31330    delete object[property];
31331  }
31332});
31333
31334
31335/**
31336 * @ngdoc directive
31337 * @name ngModel
31338 * @restrict A
31339 * @priority 1
31340 * @param {expression} ngModel assignable {@link guide/expression Expression} to bind to.
31341 *
31342 * @description
31343 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
31344 * property on the scope using {@link ngModel.NgModelController NgModelController},
31345 * which is created and exposed by this directive.
31346 *
31347 * `ngModel` is responsible for:
31348 *
31349 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
31350 *   require.
31351 * - Providing validation behavior (i.e. required, number, email, url).
31352 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
31353 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
31354 *   `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
31355 * - Registering the control with its parent {@link ng.directive:form form}.
31356 *
31357 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
31358 * current scope. If the property doesn't already exist on this scope, it will be created
31359 * implicitly and added to the scope.
31360 *
31361 * For best practices on using `ngModel`, see:
31362 *
31363 *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
31364 *
31365 * For basic examples, how to use `ngModel`, see:
31366 *
31367 *  - {@link ng.directive:input input}
31368 *    - {@link input[text] text}
31369 *    - {@link input[checkbox] checkbox}
31370 *    - {@link input[radio] radio}
31371 *    - {@link input[number] number}
31372 *    - {@link input[email] email}
31373 *    - {@link input[url] url}
31374 *    - {@link input[date] date}
31375 *    - {@link input[datetime-local] datetime-local}
31376 *    - {@link input[time] time}
31377 *    - {@link input[month] month}
31378 *    - {@link input[week] week}
31379 *  - {@link ng.directive:select select}
31380 *  - {@link ng.directive:textarea textarea}
31381 *
31382 * ## Complex Models (objects or collections)
31383 *
31384 * By default, `ngModel` watches the model by reference, not value. This is important to know when
31385 * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
31386 * object or collection change, `ngModel` will not be notified and so the input will not be  re-rendered.
31387 *
31388 * The model must be assigned an entirely new object or collection before a re-rendering will occur.
31389 *
31390 * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
31391 * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
31392 * if the select is given the `multiple` attribute.
31393 *
31394 * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
31395 * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
31396 * not trigger a re-rendering of the model.
31397 *
31398 * ## CSS classes
31399 * The following CSS classes are added and removed on the associated input/select/textarea element
31400 * depending on the validity of the model.
31401 *
31402 *  - `ng-valid`: the model is valid
31403 *  - `ng-invalid`: the model is invalid
31404 *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
31405 *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
31406 *  - `ng-pristine`: the control hasn't been interacted with yet
31407 *  - `ng-dirty`: the control has been interacted with
31408 *  - `ng-touched`: the control has been blurred
31409 *  - `ng-untouched`: the control hasn't been blurred
31410 *  - `ng-pending`: any `$asyncValidators` are unfulfilled
31411 *  - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
31412 *     by the {@link ngModel.NgModelController#$isEmpty} method
31413 *  - `ng-not-empty`: the view contains a non-empty value
31414 *
31415 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
31416 *
31417 * @animations
31418 * Animations within models are triggered when any of the associated CSS classes are added and removed
31419 * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
31420 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
31421 * The animations that are triggered within ngModel are similar to how they work in ngClass and
31422 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
31423 *
31424 * The following example shows a simple way to utilize CSS transitions to style an input element
31425 * that has been rendered as invalid after it has been validated:
31426 *
31427 * <pre>
31428 * //be sure to include ngAnimate as a module to hook into more
31429 * //advanced animations
31430 * .my-input {
31431 *   transition:0.5s linear all;
31432 *   background: white;
31433 * }
31434 * .my-input.ng-invalid {
31435 *   background: red;
31436 *   color:white;
31437 * }
31438 * </pre>
31439 *
31440 * @example
31441 * ### Basic Usage
31442 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample" name="ng-model">
31443     <file name="index.html">
31444       <script>
31445        angular.module('inputExample', [])
31446          .controller('ExampleController', ['$scope', function($scope) {
31447            $scope.val = '1';
31448          }]);
31449       </script>
31450       <style>
31451         .my-input {
31452           transition:all linear 0.5s;
31453           background: transparent;
31454         }
31455         .my-input.ng-invalid {
31456           color:white;
31457           background: red;
31458         }
31459       </style>
31460       <p id="inputDescription">
31461        Update input to see transitions when valid/invalid.
31462        Integer is a valid value.
31463       </p>
31464       <form name="testForm" ng-controller="ExampleController">
31465         <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
31466                aria-describedby="inputDescription" />
31467       </form>
31468     </file>
31469 * </example>
31470 *
31471 * @example
31472 * ### Binding to a getter/setter
31473 *
31474 * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
31475 * function that returns a representation of the model when called with zero arguments, and sets
31476 * the internal state of a model when called with an argument. It's sometimes useful to use this
31477 * for models that have an internal representation that's different from what the model exposes
31478 * to the view.
31479 *
31480 * <div class="alert alert-success">
31481 * **Best Practice:** It's best to keep getters fast because AngularJS is likely to call them more
31482 * frequently than other parts of your code.
31483 * </div>
31484 *
31485 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
31486 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
31487 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
31488 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
31489 *
31490 * The following example shows how to use `ngModel` with a getter/setter:
31491 *
31492 * @example
31493 * <example name="ngModel-getter-setter" module="getterSetterExample">
31494     <file name="index.html">
31495       <div ng-controller="ExampleController">
31496         <form name="userForm">
31497           <label>Name:
31498             <input type="text" name="userName"
31499                    ng-model="user.name"
31500                    ng-model-options="{ getterSetter: true }" />
31501           </label>
31502         </form>
31503         <pre>user.name = <span ng-bind="user.name()"></span></pre>
31504       </div>
31505     </file>
31506     <file name="app.js">
31507       angular.module('getterSetterExample', [])
31508         .controller('ExampleController', ['$scope', function($scope) {
31509           var _name = 'Brian';
31510           $scope.user = {
31511             name: function(newName) {
31512              // Note that newName can be undefined for two reasons:
31513              // 1. Because it is called as a getter and thus called with no arguments
31514              // 2. Because the property should actually be set to undefined. This happens e.g. if the
31515              //    input is invalid
31516              return arguments.length ? (_name = newName) : _name;
31517             }
31518           };
31519         }]);
31520     </file>
31521 * </example>
31522 */
31523var ngModelDirective = ['$rootScope', function($rootScope) {
31524  return {
31525    restrict: 'A',
31526    require: ['ngModel', '^?form', '^?ngModelOptions'],
31527    controller: NgModelController,
31528    // Prelink needs to run before any input directive
31529    // so that we can set the NgModelOptions in NgModelController
31530    // before anyone else uses it.
31531    priority: 1,
31532    compile: function ngModelCompile(element) {
31533      // Setup initial state of the control
31534      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
31535
31536      return {
31537        pre: function ngModelPreLink(scope, element, attr, ctrls) {
31538          var modelCtrl = ctrls[0],
31539              formCtrl = ctrls[1] || modelCtrl.$$parentForm,
31540              optionsCtrl = ctrls[2];
31541
31542          if (optionsCtrl) {
31543            modelCtrl.$options = optionsCtrl.$options;
31544          }
31545
31546          modelCtrl.$$initGetterSetters();
31547
31548          // notify others, especially parent forms
31549          formCtrl.$addControl(modelCtrl);
31550
31551          attr.$observe('name', function(newValue) {
31552            if (modelCtrl.$name !== newValue) {
31553              modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
31554            }
31555          });
31556
31557          scope.$on('$destroy', function() {
31558            modelCtrl.$$parentForm.$removeControl(modelCtrl);
31559          });
31560        },
31561        post: function ngModelPostLink(scope, element, attr, ctrls) {
31562          var modelCtrl = ctrls[0];
31563          modelCtrl.$$setUpdateOnEvents();
31564
31565          function setTouched() {
31566            modelCtrl.$setTouched();
31567          }
31568
31569          element.on('blur', function() {
31570            if (modelCtrl.$touched) return;
31571
31572            if ($rootScope.$$phase) {
31573              scope.$evalAsync(setTouched);
31574            } else {
31575              scope.$apply(setTouched);
31576            }
31577          });
31578        }
31579      };
31580    }
31581  };
31582}];
31583
31584/* exported defaultModelOptions */
31585var defaultModelOptions;
31586var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
31587
31588/**
31589 * @ngdoc type
31590 * @name ModelOptions
31591 * @description
31592 * A container for the options set by the {@link ngModelOptions} directive
31593 */
31594function ModelOptions(options) {
31595  this.$$options = options;
31596}
31597
31598ModelOptions.prototype = {
31599
31600  /**
31601   * @ngdoc method
31602   * @name ModelOptions#getOption
31603   * @param {string} name the name of the option to retrieve
31604   * @returns {*} the value of the option
31605   * @description
31606   * Returns the value of the given option
31607   */
31608  getOption: function(name) {
31609    return this.$$options[name];
31610  },
31611
31612  /**
31613   * @ngdoc method
31614   * @name ModelOptions#createChild
31615   * @param {Object} options a hash of options for the new child that will override the parent's options
31616   * @return {ModelOptions} a new `ModelOptions` object initialized with the given options.
31617   */
31618  createChild: function(options) {
31619    var inheritAll = false;
31620
31621    // make a shallow copy
31622    options = extend({}, options);
31623
31624    // Inherit options from the parent if specified by the value `"$inherit"`
31625    forEach(options, /** @this */ function(option, key) {
31626      if (option === '$inherit') {
31627        if (key === '*') {
31628          inheritAll = true;
31629        } else {
31630          options[key] = this.$$options[key];
31631          // `updateOn` is special so we must also inherit the `updateOnDefault` option
31632          if (key === 'updateOn') {
31633            options.updateOnDefault = this.$$options.updateOnDefault;
31634          }
31635        }
31636      } else {
31637        if (key === 'updateOn') {
31638          // If the `updateOn` property contains the `default` event then we have to remove
31639          // it from the event list and set the `updateOnDefault` flag.
31640          options.updateOnDefault = false;
31641          options[key] = trim(option.replace(DEFAULT_REGEXP, function() {
31642            options.updateOnDefault = true;
31643            return ' ';
31644          }));
31645        }
31646      }
31647    }, this);
31648
31649    if (inheritAll) {
31650      // We have a property of the form: `"*": "$inherit"`
31651      delete options['*'];
31652      defaults(options, this.$$options);
31653    }
31654
31655    // Finally add in any missing defaults
31656    defaults(options, defaultModelOptions.$$options);
31657
31658    return new ModelOptions(options);
31659  }
31660};
31661
31662
31663defaultModelOptions = new ModelOptions({
31664  updateOn: '',
31665  updateOnDefault: true,
31666  debounce: 0,
31667  getterSetter: false,
31668  allowInvalid: false,
31669  timezone: null
31670});
31671
31672
31673/**
31674 * @ngdoc directive
31675 * @name ngModelOptions
31676 * @restrict A
31677 * @priority 10
31678 *
31679 * @description
31680 * This directive allows you to modify the behaviour of {@link ngModel} directives within your
31681 * application. You can specify an `ngModelOptions` directive on any element. All {@link ngModel}
31682 * directives will use the options of their nearest `ngModelOptions` ancestor.
31683 *
31684 * The `ngModelOptions` settings are found by evaluating the value of the attribute directive as
31685 * an AngularJS expression. This expression should evaluate to an object, whose properties contain
31686 * the settings. For example: `<div ng-model-options="{ debounce: 100 }"`.
31687 *
31688 * ## Inheriting Options
31689 *
31690 * You can specify that an `ngModelOptions` setting should be inherited from a parent `ngModelOptions`
31691 * directive by giving it the value of `"$inherit"`.
31692 * Then it will inherit that setting from the first `ngModelOptions` directive found by traversing up the
31693 * DOM tree. If there is no ancestor element containing an `ngModelOptions` directive then default settings
31694 * will be used.
31695 *
31696 * For example given the following fragment of HTML
31697 *
31698 *
31699 * ```html
31700 * <div ng-model-options="{ allowInvalid: true, debounce: 200 }">
31701 *   <form ng-model-options="{ updateOn: 'blur', allowInvalid: '$inherit' }">
31702 *     <input ng-model-options="{ updateOn: 'default', allowInvalid: '$inherit' }" />
31703 *   </form>
31704 * </div>
31705 * ```
31706 *
31707 * the `input` element will have the following settings
31708 *
31709 * ```js
31710 * { allowInvalid: true, updateOn: 'default', debounce: 0 }
31711 * ```
31712 *
31713 * Notice that the `debounce` setting was not inherited and used the default value instead.
31714 *
31715 * You can specify that all undefined settings are automatically inherited from an ancestor by
31716 * including a property with key of `"*"` and value of `"$inherit"`.
31717 *
31718 * For example given the following fragment of HTML
31719 *
31720 *
31721 * ```html
31722 * <div ng-model-options="{ allowInvalid: true, debounce: 200 }">
31723 *   <form ng-model-options="{ updateOn: 'blur', "*": '$inherit' }">
31724 *     <input ng-model-options="{ updateOn: 'default', "*": '$inherit' }" />
31725 *   </form>
31726 * </div>
31727 * ```
31728 *
31729 * the `input` element will have the following settings
31730 *
31731 * ```js
31732 * { allowInvalid: true, updateOn: 'default', debounce: 200 }
31733 * ```
31734 *
31735 * Notice that the `debounce` setting now inherits the value from the outer `<div>` element.
31736 *
31737 * If you are creating a reusable component then you should be careful when using `"*": "$inherit"`
31738 * since you may inadvertently inherit a setting in the future that changes the behavior of your component.
31739 *
31740 *
31741 * ## Triggering and debouncing model updates
31742 *
31743 * The `updateOn` and `debounce` properties allow you to specify a custom list of events that will
31744 * trigger a model update and/or a debouncing delay so that the actual update only takes place when
31745 * a timer expires; this timer will be reset after another change takes place.
31746 *
31747 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
31748 * be different from the value in the actual model. This means that if you update the model you
31749 * should also invoke {@link ngModel.NgModelController#$rollbackViewValue} on the relevant input field in
31750 * order to make sure it is synchronized with the model and that any debounced action is canceled.
31751 *
31752 * The easiest way to reference the control's {@link ngModel.NgModelController#$rollbackViewValue}
31753 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
31754 * important because `form` controllers are published to the related scope under the name in their
31755 * `name` attribute.
31756 *
31757 * Any pending changes will take place immediately when an enclosing form is submitted via the
31758 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
31759 * to have access to the updated model.
31760 *
31761 * ### Overriding immediate updates
31762 *
31763 * The following example shows how to override immediate updates. Changes on the inputs within the
31764 * form will update the model only when the control loses focus (blur event). If `escape` key is
31765 * pressed while the input field is focused, the value is reset to the value in the current model.
31766 *
31767 * <example name="ngModelOptions-directive-blur" module="optionsExample">
31768 *   <file name="index.html">
31769 *     <div ng-controller="ExampleController">
31770 *       <form name="userForm">
31771 *         <label>
31772 *           Name:
31773 *           <input type="text" name="userName"
31774 *                  ng-model="user.name"
31775 *                  ng-model-options="{ updateOn: 'blur' }"
31776 *                  ng-keyup="cancel($event)" />
31777 *         </label><br />
31778 *         <label>
31779 *           Other data:
31780 *           <input type="text" ng-model="user.data" />
31781 *         </label><br />
31782 *       </form>
31783 *       <pre>user.name = <span ng-bind="user.name"></span></pre>
31784 *     </div>
31785 *   </file>
31786 *   <file name="app.js">
31787 *     angular.module('optionsExample', [])
31788 *       .controller('ExampleController', ['$scope', function($scope) {
31789 *         $scope.user = { name: 'say', data: '' };
31790 *
31791 *         $scope.cancel = function(e) {
31792 *           if (e.keyCode === 27) {
31793 *             $scope.userForm.userName.$rollbackViewValue();
31794 *           }
31795 *         };
31796 *       }]);
31797 *   </file>
31798 *   <file name="protractor.js" type="protractor">
31799 *     var model = element(by.binding('user.name'));
31800 *     var input = element(by.model('user.name'));
31801 *     var other = element(by.model('user.data'));
31802 *
31803 *     it('should allow custom events', function() {
31804 *       input.sendKeys(' hello');
31805 *       input.click();
31806 *       expect(model.getText()).toEqual('say');
31807 *       other.click();
31808 *       expect(model.getText()).toEqual('say hello');
31809 *     });
31810 *
31811 *     it('should $rollbackViewValue when model changes', function() {
31812 *       input.sendKeys(' hello');
31813 *       expect(input.getAttribute('value')).toEqual('say hello');
31814 *       input.sendKeys(protractor.Key.ESCAPE);
31815 *       expect(input.getAttribute('value')).toEqual('say');
31816 *       other.click();
31817 *       expect(model.getText()).toEqual('say');
31818 *     });
31819 *   </file>
31820 * </example>
31821 *
31822 * ### Debouncing updates
31823 *
31824 * The next example shows how to debounce model changes. Model will be updated only 1 sec after last change.
31825 * If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
31826 *
31827 * <example name="ngModelOptions-directive-debounce" module="optionsExample">
31828 *   <file name="index.html">
31829 *     <div ng-controller="ExampleController">
31830 *       <form name="userForm">
31831 *         Name:
31832 *         <input type="text" name="userName"
31833 *                ng-model="user.name"
31834 *                ng-model-options="{ debounce: 1000 }" />
31835 *         <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br />
31836 *       </form>
31837 *       <pre>user.name = <span ng-bind="user.name"></span></pre>
31838 *     </div>
31839 *   </file>
31840 *   <file name="app.js">
31841 *     angular.module('optionsExample', [])
31842 *       .controller('ExampleController', ['$scope', function($scope) {
31843 *         $scope.user = { name: 'say' };
31844 *       }]);
31845 *   </file>
31846 * </example>
31847 *
31848 * ### Default events, extra triggers, and catch-all debounce values
31849 *
31850 * This example shows the relationship between "default" update events and
31851 * additional `updateOn` triggers.
31852 *
31853 * `default` events are those that are bound to the control, and when fired, update the `$viewValue`
31854 * via {@link ngModel.NgModelController#$setViewValue $setViewValue}. Every event that is not listed
31855 * in `updateOn` is considered a "default" event, since different control types have different
31856 * default events.
31857 *
31858 * The control in this example updates by "default", "click", and "blur", with different `debounce`
31859 * values. You can see that "click" doesn't have an individual `debounce` value -
31860 * therefore it uses the `*` debounce value.
31861 *
31862 * There is also a button that calls {@link ngModel.NgModelController#$setViewValue $setViewValue}
31863 * directly with a "custom" event. Since "custom" is not defined in the `updateOn` list,
31864 * it is considered a "default" event and will update the
31865 * control if "default" is defined in `updateOn`, and will receive the "default" debounce value.
31866 * Note that this is just to illustrate how custom controls would possibly call `$setViewValue`.
31867 *
31868 * You can change the `updateOn` and `debounce` configuration to test different scenarios. This
31869 * is done with {@link ngModel.NgModelController#$overrideModelOptions $overrideModelOptions}.
31870 *
31871  <example name="ngModelOptions-advanced" module="optionsExample">
31872    <file name="index.html">
31873       <model-update-demo></model-update-demo>
31874    </file>
31875    <file name="app.js">
31876      angular.module('optionsExample', [])
31877        .component('modelUpdateDemo', {
31878          templateUrl: 'template.html',
31879          controller: function() {
31880            this.name = 'Chinua';
31881
31882            this.options = {
31883              updateOn: 'default blur click',
31884              debounce: {
31885                default: 2000,
31886                blur: 0,
31887                '*': 1000
31888              }
31889            };
31890
31891            this.updateEvents = function() {
31892              var eventList = this.options.updateOn.split(' ');
31893              eventList.push('*');
31894              var events = {};
31895
31896              for (var i = 0; i < eventList.length; i++) {
31897                events[eventList[i]] = this.options.debounce[eventList[i]];
31898              }
31899
31900              this.events = events;
31901            };
31902
31903            this.updateOptions = function() {
31904              var options = angular.extend(this.options, {
31905                updateOn: Object.keys(this.events).join(' ').replace('*', ''),
31906                debounce: this.events
31907              });
31908
31909              this.form.input.$overrideModelOptions(options);
31910            };
31911
31912            // Initialize the event form
31913            this.updateEvents();
31914          }
31915        });
31916    </file>
31917    <file name="template.html">
31918      <form name="$ctrl.form">
31919        Input: <input type="text" name="input" ng-model="$ctrl.name" ng-model-options="$ctrl.options" />
31920      </form>
31921      Model: <tt>{{$ctrl.name}}</tt>
31922      <hr>
31923      <button ng-click="$ctrl.form.input.$setViewValue('some value', 'custom')">Trigger setViewValue with 'some value' and 'custom' event</button>
31924
31925      <hr>
31926      <form ng-submit="$ctrl.updateOptions()">
31927        <b>updateOn</b><br>
31928        <input type="text" ng-model="$ctrl.options.updateOn" ng-change="$ctrl.updateEvents()" ng-model-options="{debounce: 500}">
31929
31930        <table>
31931          <tr>
31932            <th>Option</th>
31933            <th>Debounce value</th>
31934          </tr>
31935          <tr ng-repeat="(key, value) in $ctrl.events">
31936            <td>{{key}}</td>
31937            <td><input type="number" ng-model="$ctrl.events[key]" /></td>
31938          </tr>
31939        </table>
31940
31941        <br>
31942        <input type="submit" value="Update options">
31943      </form>
31944    </file>
31945  </example>
31946 *
31947 *
31948 * ## Model updates and validation
31949 *
31950 * The default behaviour in `ngModel` is that the model value is set to `undefined` when the
31951 * validation determines that the value is invalid. By setting the `allowInvalid` property to true,
31952 * the model will still be updated even if the value is invalid.
31953 *
31954 *
31955 * ## Connecting to the scope
31956 *
31957 * By setting the `getterSetter` property to true you are telling ngModel that the `ngModel` expression
31958 * on the scope refers to a "getter/setter" function rather than the value itself.
31959 *
31960 * The following example shows how to bind to getter/setters:
31961 *
31962 * <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
31963 *   <file name="index.html">
31964 *     <div ng-controller="ExampleController">
31965 *       <form name="userForm">
31966 *         <label>
31967 *           Name:
31968 *           <input type="text" name="userName"
31969 *                  ng-model="user.name"
31970 *                  ng-model-options="{ getterSetter: true }" />
31971 *         </label>
31972 *       </form>
31973 *       <pre>user.name = <span ng-bind="user.name()"></span></pre>
31974 *     </div>
31975 *   </file>
31976 *   <file name="app.js">
31977 *     angular.module('getterSetterExample', [])
31978 *       .controller('ExampleController', ['$scope', function($scope) {
31979 *         var _name = 'Brian';
31980 *         $scope.user = {
31981 *           name: function(newName) {
31982 *             return angular.isDefined(newName) ? (_name = newName) : _name;
31983 *           }
31984 *         };
31985 *       }]);
31986 *   </file>
31987 * </example>
31988 *
31989 *
31990 * ## Programmatically changing options
31991 *
31992 * The `ngModelOptions` expression is only evaluated once when the directive is linked; it is not
31993 * watched for changes. However, it is possible to override the options on a single
31994 * {@link ngModel.NgModelController} instance with
31995 * {@link ngModel.NgModelController#$overrideModelOptions `NgModelController#$overrideModelOptions()`}.
31996 * See also the example for
31997 * {@link ngModelOptions#default-events-extra-triggers-and-catch-all-debounce-values
31998 * Default events, extra triggers, and catch-all debounce values}.
31999 *
32000 *
32001 * ## Specifying timezones
32002 *
32003 * You can specify the timezone that date/time input directives expect by providing its name in the
32004 * `timezone` property.
32005 *
32006 *
32007 * ## Formatting the value of `time` and `datetime-local`
32008 *
32009 * With the options `timeSecondsFormat` and `timeStripZeroSeconds` it is possible to adjust the value
32010 * that is displayed in the control. Note that browsers may apply their own formatting
32011 * in the user interface.
32012 *
32013   <example name="ngModelOptions-time-format" module="timeExample">
32014     <file name="index.html">
32015       <time-example></time-example>
32016     </file>
32017     <file name="script.js">
32018        angular.module('timeExample', [])
32019          .component('timeExample', {
32020            templateUrl: 'timeExample.html',
32021            controller: function() {
32022              this.time = new Date(1970, 0, 1, 14, 57, 0);
32023
32024              this.options = {
32025                timeSecondsFormat: 'ss',
32026                timeStripZeroSeconds: true
32027              };
32028
32029              this.optionChange = function() {
32030                this.timeForm.timeFormatted.$overrideModelOptions(this.options);
32031                this.time = new Date(this.time);
32032              };
32033            }
32034          });
32035     </file>
32036     <file name="timeExample.html">
32037       <form name="$ctrl.timeForm">
32038         <strong>Default</strong>:
32039         <input type="time" ng-model="$ctrl.time" step="any" /><br>
32040         <strong>With options</strong>:
32041         <input type="time" name="timeFormatted" ng-model="$ctrl.time" step="any" ng-model-options="$ctrl.options" />
32042         <br>
32043
32044         Options:<br>
32045         <code>timeSecondsFormat</code>:
32046         <input
32047           type="text"
32048           ng-model="$ctrl.options.timeSecondsFormat"
32049           ng-change="$ctrl.optionChange()">
32050         <br>
32051         <code>timeStripZeroSeconds</code>:
32052         <input
32053           type="checkbox"
32054           ng-model="$ctrl.options.timeStripZeroSeconds"
32055           ng-change="$ctrl.optionChange()">
32056        </form>
32057      </file>
32058 *  </example>
32059 *
32060 * @param {Object} ngModelOptions options to apply to {@link ngModel} directives on this element and
32061 *   and its descendents.
32062 *
32063 * **General options**:
32064 *
32065 *   - `updateOn`: string specifying which event should the input be bound to. You can set several
32066 *     events using an space delimited list. There is a special event called `default` that
32067 *     matches the default events belonging to the control. These are the events that are bound to
32068 *     the control, and when fired, update the `$viewValue` via `$setViewValue`.
32069 *
32070 *     `ngModelOptions` considers every event that is not listed in `updateOn` a "default" event,
32071 *     since different control types use different default events.
32072 *
32073 *     See also the section {@link ngModelOptions#triggering-and-debouncing-model-updates
32074 *     Triggering and debouncing model updates}.
32075 *
32076 *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
32077 *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
32078 *     custom value for each event. For example:
32079 *     ```
32080 *     ng-model-options="{
32081 *       updateOn: 'default blur',
32082 *       debounce: { 'default': 500, 'blur': 0 }
32083 *     }"
32084 *     ```
32085 *     You can use the `*` key to specify a debounce value that applies to all events that are not
32086 *     specifically listed. In the following example, `mouseup` would have a debounce delay of 1000:
32087 *     ```
32088 *     ng-model-options="{
32089 *       updateOn: 'default blur mouseup',
32090 *       debounce: { 'default': 500, 'blur': 0, '*': 1000 }
32091 *     }"
32092 *     ```
32093 *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
32094 *     not validate correctly instead of the default behavior of setting the model to undefined.
32095 *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
32096 *     `ngModel` as getters/setters.
32097 *
32098 *
32099 *  **Input-type specific options**:
32100 *
32101 *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
32102 *     `<input type="date" />`, `<input type="time" />`, ... . It understands UTC/GMT and the
32103 *     continental US time zone abbreviations, but for general use, use a time zone offset, for
32104 *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
32105 *     If not specified, the timezone of the browser will be used.
32106 *     Note that changing the timezone will have no effect on the current date, and is only applied after
32107 *     the next input / model change.
32108 *
32109 *   - `timeSecondsFormat`: Defines if the `time` and `datetime-local` types should show seconds and
32110 *     milliseconds. The option follows the format string of {@link date date filter}.
32111 *     By default, the options is `undefined` which is equal to `'ss.sss'` (seconds and milliseconds).
32112 *     The other options are `'ss'` (strips milliseconds), and `''` (empty string), which strips both
32113 *     seconds and milliseconds.
32114 *     Note that browsers that support `time` and `datetime-local` require the hour and minutes
32115 *     part of the time string, and may show the value differently in the user interface.
32116 *     {@link ngModelOptions#formatting-the-value-of-time-and-datetime-local- See the example}.
32117 *
32118 *   - `timeStripZeroSeconds`: Defines if the `time` and `datetime-local` types should strip the
32119 *     seconds and milliseconds from the formatted value if they are zero. This option is applied
32120 *     after `timeSecondsFormat`.
32121 *     This option can be used to make the formatting consistent over different browsers, as some
32122 *     browsers with support for `time` will natively hide the milliseconds and
32123 *     seconds if they are zero, but others won't, and browsers that don't implement these input
32124 *     types will always show the full string.
32125 *     {@link ngModelOptions#formatting-the-value-of-time-and-datetime-local- See the example}.
32126 *
32127 */
32128var ngModelOptionsDirective = function() {
32129  NgModelOptionsController.$inject = ['$attrs', '$scope'];
32130  function NgModelOptionsController($attrs, $scope) {
32131    this.$$attrs = $attrs;
32132    this.$$scope = $scope;
32133  }
32134  NgModelOptionsController.prototype = {
32135    $onInit: function() {
32136      var parentOptions = this.parentCtrl ? this.parentCtrl.$options : defaultModelOptions;
32137      var modelOptionsDefinition = this.$$scope.$eval(this.$$attrs.ngModelOptions);
32138
32139      this.$options = parentOptions.createChild(modelOptionsDefinition);
32140    }
32141  };
32142
32143  return {
32144    restrict: 'A',
32145    // ngModelOptions needs to run before ngModel and input directives
32146    priority: 10,
32147    require: {parentCtrl: '?^^ngModelOptions'},
32148    bindToController: true,
32149    controller: NgModelOptionsController
32150  };
32151};
32152
32153
32154// shallow copy over values from `src` that are not already specified on `dst`
32155function defaults(dst, src) {
32156  forEach(src, function(value, key) {
32157    if (!isDefined(dst[key])) {
32158      dst[key] = value;
32159    }
32160  });
32161}
32162
32163/**
32164 * @ngdoc directive
32165 * @name ngNonBindable
32166 * @restrict AC
32167 * @priority 1000
32168 * @element ANY
32169 *
32170 * @description
32171 * The `ngNonBindable` directive tells AngularJS not to compile or bind the contents of the current
32172 * DOM element, including directives on the element itself that have a lower priority than
32173 * `ngNonBindable`. This is useful if the element contains what appears to be AngularJS directives
32174 * and bindings but which should be ignored by AngularJS. This could be the case if you have a site
32175 * that displays snippets of code, for instance.
32176 *
32177 * @example
32178 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
32179 * but the one wrapped in `ngNonBindable` is left alone.
32180 *
32181  <example name="ng-non-bindable">
32182    <file name="index.html">
32183      <div>Normal: {{1 + 2}}</div>
32184      <div ng-non-bindable>Ignored: {{1 + 2}}</div>
32185    </file>
32186    <file name="protractor.js" type="protractor">
32187     it('should check ng-non-bindable', function() {
32188       expect(element(by.binding('1 + 2')).getText()).toContain('3');
32189       expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
32190     });
32191    </file>
32192  </example>
32193 */
32194var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
32195
32196/* exported ngOptionsDirective */
32197
32198/* global jqLiteRemove */
32199
32200var ngOptionsMinErr = minErr('ngOptions');
32201
32202/**
32203 * @ngdoc directive
32204 * @name ngOptions
32205 * @restrict A
32206 *
32207 * @description
32208 *
32209 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
32210 * elements for the `<select>` element using the array or object obtained by evaluating the
32211 * `ngOptions` comprehension expression.
32212 *
32213 * In many cases, {@link ng.directive:ngRepeat ngRepeat} can be used on `<option>` elements instead of
32214 * `ngOptions` to achieve a similar result. However, `ngOptions` provides some benefits:
32215 * - more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
32216 * comprehension expression
32217 * - reduced memory consumption by not creating a new scope for each repeated instance
32218 * - increased render speed by creating the options in a documentFragment instead of individually
32219 *
32220 * When an item in the `<select>` menu is selected, the array element or object property
32221 * represented by the selected option will be bound to the model identified by the `ngModel`
32222 * directive.
32223 *
32224 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
32225 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
32226 * option. See example below for demonstration.
32227 *
32228 * ## Complex Models (objects or collections)
32229 *
32230 * By default, `ngModel` watches the model by reference, not value. This is important to know when
32231 * binding the select to a model that is an object or a collection.
32232 *
32233 * One issue occurs if you want to preselect an option. For example, if you set
32234 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
32235 * because the objects are not identical. So by default, you should always reference the item in your collection
32236 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
32237 *
32238 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
32239 * of the item not by reference, but by the result of the `track by` expression. For example, if your
32240 * collection items have an id property, you would `track by item.id`.
32241 *
32242 * A different issue with objects or collections is that ngModel won't detect if an object property or
32243 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
32244 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
32245 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
32246 * has not changed identity, but only a property on the object or an item in the collection changes.
32247 *
32248 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
32249 * if the model is an array). This means that changing a property deeper than the first level inside the
32250 * object/collection will not trigger a re-rendering.
32251 *
32252 * ## `select` **`as`**
32253 *
32254 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
32255 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
32256 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
32257 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
32258 *
32259 *
32260 * ### `select` **`as`** and **`track by`**
32261 *
32262 * <div class="alert alert-warning">
32263 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
32264 * </div>
32265 *
32266 * Given this array of items on the $scope:
32267 *
32268 * ```js
32269 * $scope.items = [{
32270 *   id: 1,
32271 *   label: 'aLabel',
32272 *   subItem: { name: 'aSubItem' }
32273 * }, {
32274 *   id: 2,
32275 *   label: 'bLabel',
32276 *   subItem: { name: 'bSubItem' }
32277 * }];
32278 * ```
32279 *
32280 * This will work:
32281 *
32282 * ```html
32283 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
32284 * ```
32285 * ```js
32286 * $scope.selected = $scope.items[0];
32287 * ```
32288 *
32289 * but this will not work:
32290 *
32291 * ```html
32292 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
32293 * ```
32294 * ```js
32295 * $scope.selected = $scope.items[0].subItem;
32296 * ```
32297 *
32298 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
32299 * `items` array. Because the selected option has been set programmatically in the controller, the
32300 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
32301 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
32302 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
32303 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
32304 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
32305 *
32306 *
32307 * @param {string} ngModel Assignable AngularJS expression to data-bind to.
32308 * @param {comprehension_expression} ngOptions in one of the following forms:
32309 *
32310 *   * for array data sources:
32311 *     * `label` **`for`** `value` **`in`** `array`
32312 *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
32313 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
32314 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
32315 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
32316 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
32317 *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
32318 *        (for including a filter with `track by`)
32319 *   * for object data sources:
32320 *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
32321 *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
32322 *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
32323 *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
32324 *     * `select` **`as`** `label` **`group by`** `group`
32325 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
32326 *     * `select` **`as`** `label` **`disable when`** `disable`
32327 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
32328 *
32329 * Where:
32330 *
32331 *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
32332 *   * `value`: local variable which will refer to each item in the `array` or each property value
32333 *      of `object` during iteration.
32334 *   * `key`: local variable which will refer to a property name in `object` during iteration.
32335 *   * `label`: The result of this expression will be the label for `<option>` element. The
32336 *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
32337 *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
32338 *      element. If not specified, `select` expression will default to `value`.
32339 *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
32340 *      DOM element.
32341 *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
32342 *      element. Return `true` to disable.
32343 *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
32344 *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
32345 *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
32346 *      even when the options are recreated (e.g. reloaded from the server).
32347 * @param {string=} name Property name of the form under which the control is published.
32348 * @param {string=} required The control is considered valid only if value is entered.
32349 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
32350 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
32351 *    `required` when you want to data-bind to the `required` attribute.
32352 * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
32353 * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
32354 *
32355 * @example
32356    <example module="selectExample" name="select">
32357      <file name="index.html">
32358        <script>
32359        angular.module('selectExample', [])
32360          .controller('ExampleController', ['$scope', function($scope) {
32361            $scope.colors = [
32362              {name:'black', shade:'dark'},
32363              {name:'white', shade:'light', notAnOption: true},
32364              {name:'red', shade:'dark'},
32365              {name:'blue', shade:'dark', notAnOption: true},
32366              {name:'yellow', shade:'light', notAnOption: false}
32367            ];
32368            $scope.myColor = $scope.colors[2]; // red
32369          }]);
32370        </script>
32371        <div ng-controller="ExampleController">
32372          <ul>
32373            <li ng-repeat="color in colors">
32374              <label>Name: <input ng-model="color.name"></label>
32375              <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
32376              <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
32377            </li>
32378            <li>
32379              <button ng-click="colors.push({})">add</button>
32380            </li>
32381          </ul>
32382          <hr/>
32383          <label>Color (null not allowed):
32384            <select ng-model="myColor" ng-options="color.name for color in colors"></select>
32385          </label><br/>
32386          <label>Color (null allowed):
32387          <span  class="nullable">
32388            <select ng-model="myColor" ng-options="color.name for color in colors">
32389              <option value="">-- choose color --</option>
32390            </select>
32391          </span></label><br/>
32392
32393          <label>Color grouped by shade:
32394            <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
32395            </select>
32396          </label><br/>
32397
32398          <label>Color grouped by shade, with some disabled:
32399            <select ng-model="myColor"
32400                  ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
32401            </select>
32402          </label><br/>
32403
32404
32405
32406          Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
32407          <br/>
32408          <hr/>
32409          Currently selected: {{ {selected_color:myColor} }}
32410          <div style="border:solid 1px black; height:20px"
32411               ng-style="{'background-color':myColor.name}">
32412          </div>
32413        </div>
32414      </file>
32415      <file name="protractor.js" type="protractor">
32416         it('should check ng-options', function() {
32417           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
32418           element.all(by.model('myColor')).first().click();
32419           element.all(by.css('select[ng-model="myColor"] option')).first().click();
32420           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
32421           element(by.css('.nullable select[ng-model="myColor"]')).click();
32422           element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
32423           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
32424         });
32425      </file>
32426    </example>
32427 */
32428
32429/* eslint-disable max-len */
32430//                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555000000000666666666666600000007777777777777000000000000000888888888800000000000000000009999999999
32431var 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]+?))?$/;
32432                        // 1: value expression (valueFn)
32433                        // 2: label expression (displayFn)
32434                        // 3: group by expression (groupByFn)
32435                        // 4: disable when expression (disableWhenFn)
32436                        // 5: array item variable name
32437                        // 6: object item key variable name
32438                        // 7: object item value variable name
32439                        // 8: collection expression
32440                        // 9: track by expression
32441/* eslint-enable */
32442
32443
32444var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
32445
32446  function parseOptionsExpression(optionsExp, selectElement, scope) {
32447
32448    var match = optionsExp.match(NG_OPTIONS_REGEXP);
32449    if (!(match)) {
32450      throw ngOptionsMinErr('iexp',
32451        'Expected expression in form of ' +
32452        '\'_select_ (as _label_)? for (_key_,)?_value_ in _collection_\'' +
32453        ' but got \'{0}\'. Element: {1}',
32454        optionsExp, startingTag(selectElement));
32455    }
32456
32457    // Extract the parts from the ngOptions expression
32458
32459    // The variable name for the value of the item in the collection
32460    var valueName = match[5] || match[7];
32461    // The variable name for the key of the item in the collection
32462    var keyName = match[6];
32463
32464    // An expression that generates the viewValue for an option if there is a label expression
32465    var selectAs = / as /.test(match[0]) && match[1];
32466    // An expression that is used to track the id of each object in the options collection
32467    var trackBy = match[9];
32468    // An expression that generates the viewValue for an option if there is no label expression
32469    var valueFn = $parse(match[2] ? match[1] : valueName);
32470    var selectAsFn = selectAs && $parse(selectAs);
32471    var viewValueFn = selectAsFn || valueFn;
32472    var trackByFn = trackBy && $parse(trackBy);
32473
32474    // Get the value by which we are going to track the option
32475    // if we have a trackFn then use that (passing scope and locals)
32476    // otherwise just hash the given viewValue
32477    var getTrackByValueFn = trackBy ?
32478                              function(value, locals) { return trackByFn(scope, locals); } :
32479                              function getHashOfValue(value) { return hashKey(value); };
32480    var getTrackByValue = function(value, key) {
32481      return getTrackByValueFn(value, getLocals(value, key));
32482    };
32483
32484    var displayFn = $parse(match[2] || match[1]);
32485    var groupByFn = $parse(match[3] || '');
32486    var disableWhenFn = $parse(match[4] || '');
32487    var valuesFn = $parse(match[8]);
32488
32489    var locals = {};
32490    var getLocals = keyName ? function(value, key) {
32491      locals[keyName] = key;
32492      locals[valueName] = value;
32493      return locals;
32494    } : function(value) {
32495      locals[valueName] = value;
32496      return locals;
32497    };
32498
32499
32500    function Option(selectValue, viewValue, label, group, disabled) {
32501      this.selectValue = selectValue;
32502      this.viewValue = viewValue;
32503      this.label = label;
32504      this.group = group;
32505      this.disabled = disabled;
32506    }
32507
32508    function getOptionValuesKeys(optionValues) {
32509      var optionValuesKeys;
32510
32511      if (!keyName && isArrayLike(optionValues)) {
32512        optionValuesKeys = optionValues;
32513      } else {
32514        // if object, extract keys, in enumeration order, unsorted
32515        optionValuesKeys = [];
32516        for (var itemKey in optionValues) {
32517          if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
32518            optionValuesKeys.push(itemKey);
32519          }
32520        }
32521      }
32522      return optionValuesKeys;
32523    }
32524
32525    return {
32526      trackBy: trackBy,
32527      getTrackByValue: getTrackByValue,
32528      getWatchables: $parse(valuesFn, function(optionValues) {
32529        // Create a collection of things that we would like to watch (watchedArray)
32530        // so that they can all be watched using a single $watchCollection
32531        // that only runs the handler once if anything changes
32532        var watchedArray = [];
32533        optionValues = optionValues || [];
32534
32535        var optionValuesKeys = getOptionValuesKeys(optionValues);
32536        var optionValuesLength = optionValuesKeys.length;
32537        for (var index = 0; index < optionValuesLength; index++) {
32538          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
32539          var value = optionValues[key];
32540
32541          var locals = getLocals(value, key);
32542          var selectValue = getTrackByValueFn(value, locals);
32543          watchedArray.push(selectValue);
32544
32545          // Only need to watch the displayFn if there is a specific label expression
32546          if (match[2] || match[1]) {
32547            var label = displayFn(scope, locals);
32548            watchedArray.push(label);
32549          }
32550
32551          // Only need to watch the disableWhenFn if there is a specific disable expression
32552          if (match[4]) {
32553            var disableWhen = disableWhenFn(scope, locals);
32554            watchedArray.push(disableWhen);
32555          }
32556        }
32557        return watchedArray;
32558      }),
32559
32560      getOptions: function() {
32561
32562        var optionItems = [];
32563        var selectValueMap = {};
32564
32565        // The option values were already computed in the `getWatchables` fn,
32566        // which must have been called to trigger `getOptions`
32567        var optionValues = valuesFn(scope) || [];
32568        var optionValuesKeys = getOptionValuesKeys(optionValues);
32569        var optionValuesLength = optionValuesKeys.length;
32570
32571        for (var index = 0; index < optionValuesLength; index++) {
32572          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
32573          var value = optionValues[key];
32574          var locals = getLocals(value, key);
32575          var viewValue = viewValueFn(scope, locals);
32576          var selectValue = getTrackByValueFn(viewValue, locals);
32577          var label = displayFn(scope, locals);
32578          var group = groupByFn(scope, locals);
32579          var disabled = disableWhenFn(scope, locals);
32580          var optionItem = new Option(selectValue, viewValue, label, group, disabled);
32581
32582          optionItems.push(optionItem);
32583          selectValueMap[selectValue] = optionItem;
32584        }
32585
32586        return {
32587          items: optionItems,
32588          selectValueMap: selectValueMap,
32589          getOptionFromViewValue: function(value) {
32590            return selectValueMap[getTrackByValue(value)];
32591          },
32592          getViewValueFromOption: function(option) {
32593            // If the viewValue could be an object that may be mutated by the application,
32594            // we need to make a copy and not return the reference to the value on the option.
32595            return trackBy ? copy(option.viewValue) : option.viewValue;
32596          }
32597        };
32598      }
32599    };
32600  }
32601
32602
32603  // Support: IE 9 only
32604  // We can't just jqLite('<option>') since jqLite is not smart enough
32605  // to create it in <select> and IE barfs otherwise.
32606  var optionTemplate = window.document.createElement('option'),
32607      optGroupTemplate = window.document.createElement('optgroup');
32608
32609    function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
32610
32611      var selectCtrl = ctrls[0];
32612      var ngModelCtrl = ctrls[1];
32613      var multiple = attr.multiple;
32614
32615      // The emptyOption allows the application developer to provide their own custom "empty"
32616      // option when the viewValue does not match any of the option values.
32617      for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
32618        if (children[i].value === '') {
32619          selectCtrl.hasEmptyOption = true;
32620          selectCtrl.emptyOption = children.eq(i);
32621          break;
32622        }
32623      }
32624
32625      // The empty option will be compiled and rendered before we first generate the options
32626      selectElement.empty();
32627
32628      var providedEmptyOption = !!selectCtrl.emptyOption;
32629
32630      var unknownOption = jqLite(optionTemplate.cloneNode(false));
32631      unknownOption.val('?');
32632
32633      var options;
32634      var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
32635      // This stores the newly created options before they are appended to the select.
32636      // Since the contents are removed from the fragment when it is appended,
32637      // we only need to create it once.
32638      var listFragment = $document[0].createDocumentFragment();
32639
32640      // Overwrite the implementation. ngOptions doesn't use hashes
32641      selectCtrl.generateUnknownOptionValue = function(val) {
32642        return '?';
32643      };
32644
32645      // Update the controller methods for multiple selectable options
32646      if (!multiple) {
32647
32648        selectCtrl.writeValue = function writeNgOptionsValue(value) {
32649          // The options might not be defined yet when ngModel tries to render
32650          if (!options) return;
32651
32652          var selectedOption = selectElement[0].options[selectElement[0].selectedIndex];
32653          var option = options.getOptionFromViewValue(value);
32654
32655          // Make sure to remove the selected attribute from the previously selected option
32656          // Otherwise, screen readers might get confused
32657          if (selectedOption) selectedOption.removeAttribute('selected');
32658
32659          if (option) {
32660            // Don't update the option when it is already selected.
32661            // For example, the browser will select the first option by default. In that case,
32662            // most properties are set automatically - except the `selected` attribute, which we
32663            // set always
32664
32665            if (selectElement[0].value !== option.selectValue) {
32666              selectCtrl.removeUnknownOption();
32667
32668              selectElement[0].value = option.selectValue;
32669              option.element.selected = true;
32670            }
32671
32672            option.element.setAttribute('selected', 'selected');
32673          } else {
32674            selectCtrl.selectUnknownOrEmptyOption(value);
32675          }
32676        };
32677
32678        selectCtrl.readValue = function readNgOptionsValue() {
32679
32680          var selectedOption = options.selectValueMap[selectElement.val()];
32681
32682          if (selectedOption && !selectedOption.disabled) {
32683            selectCtrl.unselectEmptyOption();
32684            selectCtrl.removeUnknownOption();
32685            return options.getViewValueFromOption(selectedOption);
32686          }
32687          return null;
32688        };
32689
32690        // If we are using `track by` then we must watch the tracked value on the model
32691        // since ngModel only watches for object identity change
32692        // FIXME: When a user selects an option, this watch will fire needlessly
32693        if (ngOptions.trackBy) {
32694          scope.$watch(
32695            function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
32696            function() { ngModelCtrl.$render(); }
32697          );
32698        }
32699
32700      } else {
32701
32702        selectCtrl.writeValue = function writeNgOptionsMultiple(values) {
32703          // The options might not be defined yet when ngModel tries to render
32704          if (!options) return;
32705
32706          // Only set `<option>.selected` if necessary, in order to prevent some browsers from
32707          // scrolling to `<option>` elements that are outside the `<select>` element's viewport.
32708          var selectedOptions = values && values.map(getAndUpdateSelectedOption) || [];
32709
32710          options.items.forEach(function(option) {
32711            if (option.element.selected && !includes(selectedOptions, option)) {
32712              option.element.selected = false;
32713            }
32714          });
32715        };
32716
32717
32718        selectCtrl.readValue = function readNgOptionsMultiple() {
32719          var selectedValues = selectElement.val() || [],
32720              selections = [];
32721
32722          forEach(selectedValues, function(value) {
32723            var option = options.selectValueMap[value];
32724            if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
32725          });
32726
32727          return selections;
32728        };
32729
32730        // If we are using `track by` then we must watch these tracked values on the model
32731        // since ngModel only watches for object identity change
32732        if (ngOptions.trackBy) {
32733
32734          scope.$watchCollection(function() {
32735            if (isArray(ngModelCtrl.$viewValue)) {
32736              return ngModelCtrl.$viewValue.map(function(value) {
32737                return ngOptions.getTrackByValue(value);
32738              });
32739            }
32740          }, function() {
32741            ngModelCtrl.$render();
32742          });
32743
32744        }
32745      }
32746
32747      if (providedEmptyOption) {
32748
32749        // compile the element since there might be bindings in it
32750        $compile(selectCtrl.emptyOption)(scope);
32751
32752        selectElement.prepend(selectCtrl.emptyOption);
32753
32754        if (selectCtrl.emptyOption[0].nodeType === NODE_TYPE_COMMENT) {
32755          // This means the empty option has currently no actual DOM node, probably because
32756          // it has been modified by a transclusion directive.
32757          selectCtrl.hasEmptyOption = false;
32758
32759          // Redefine the registerOption function, which will catch
32760          // options that are added by ngIf etc. (rendering of the node is async because of
32761          // lazy transclusion)
32762          selectCtrl.registerOption = function(optionScope, optionEl) {
32763            if (optionEl.val() === '') {
32764              selectCtrl.hasEmptyOption = true;
32765              selectCtrl.emptyOption = optionEl;
32766              selectCtrl.emptyOption.removeClass('ng-scope');
32767              // This ensures the new empty option is selected if previously no option was selected
32768              ngModelCtrl.$render();
32769
32770              optionEl.on('$destroy', function() {
32771                var needsRerender = selectCtrl.$isEmptyOptionSelected();
32772
32773                selectCtrl.hasEmptyOption = false;
32774                selectCtrl.emptyOption = undefined;
32775
32776                if (needsRerender) ngModelCtrl.$render();
32777              });
32778            }
32779          };
32780
32781        } else {
32782          // remove the class, which is added automatically because we recompile the element and it
32783          // becomes the compilation root
32784          selectCtrl.emptyOption.removeClass('ng-scope');
32785        }
32786
32787      }
32788
32789      // We will re-render the option elements if the option values or labels change
32790      scope.$watchCollection(ngOptions.getWatchables, updateOptions);
32791
32792      // ------------------------------------------------------------------ //
32793
32794      function addOptionElement(option, parent) {
32795        var optionElement = optionTemplate.cloneNode(false);
32796        parent.appendChild(optionElement);
32797        updateOptionElement(option, optionElement);
32798      }
32799
32800      function getAndUpdateSelectedOption(viewValue) {
32801        var option = options.getOptionFromViewValue(viewValue);
32802        var element = option && option.element;
32803
32804        if (element && !element.selected) element.selected = true;
32805
32806        return option;
32807      }
32808
32809      function updateOptionElement(option, element) {
32810        option.element = element;
32811        element.disabled = option.disabled;
32812        // Support: IE 11 only, Edge 12-13 only
32813        // NOTE: The label must be set before the value, otherwise IE 11 & Edge create unresponsive
32814        // selects in certain circumstances when multiple selects are next to each other and display
32815        // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
32816        // See https://github.com/angular/angular.js/issues/11314 for more info.
32817        // This is unfortunately untestable with unit / e2e tests
32818        if (option.label !== element.label) {
32819          element.label = option.label;
32820          element.textContent = option.label;
32821        }
32822        element.value = option.selectValue;
32823      }
32824
32825      function updateOptions() {
32826        var previousValue = options && selectCtrl.readValue();
32827
32828        // We must remove all current options, but cannot simply set innerHTML = null
32829        // since the providedEmptyOption might have an ngIf on it that inserts comments which we
32830        // must preserve.
32831        // Instead, iterate over the current option elements and remove them or their optgroup
32832        // parents
32833        if (options) {
32834
32835          for (var i = options.items.length - 1; i >= 0; i--) {
32836            var option = options.items[i];
32837            if (isDefined(option.group)) {
32838              jqLiteRemove(option.element.parentNode);
32839            } else {
32840              jqLiteRemove(option.element);
32841            }
32842          }
32843        }
32844
32845        options = ngOptions.getOptions();
32846
32847        var groupElementMap = {};
32848
32849        options.items.forEach(function addOption(option) {
32850          var groupElement;
32851
32852          if (isDefined(option.group)) {
32853
32854            // This option is to live in a group
32855            // See if we have already created this group
32856            groupElement = groupElementMap[option.group];
32857
32858            if (!groupElement) {
32859
32860              groupElement = optGroupTemplate.cloneNode(false);
32861              listFragment.appendChild(groupElement);
32862
32863              // Update the label on the group element
32864              // "null" is special cased because of Safari
32865              groupElement.label = option.group === null ? 'null' : option.group;
32866
32867              // Store it for use later
32868              groupElementMap[option.group] = groupElement;
32869            }
32870
32871            addOptionElement(option, groupElement);
32872
32873          } else {
32874
32875            // This option is not in a group
32876            addOptionElement(option, listFragment);
32877          }
32878        });
32879
32880        selectElement[0].appendChild(listFragment);
32881
32882        ngModelCtrl.$render();
32883
32884        // Check to see if the value has changed due to the update to the options
32885        if (!ngModelCtrl.$isEmpty(previousValue)) {
32886          var nextValue = selectCtrl.readValue();
32887          var isNotPrimitive = ngOptions.trackBy || multiple;
32888          if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
32889            ngModelCtrl.$setViewValue(nextValue);
32890            ngModelCtrl.$render();
32891          }
32892        }
32893      }
32894  }
32895
32896  return {
32897    restrict: 'A',
32898    terminal: true,
32899    require: ['select', 'ngModel'],
32900    link: {
32901      pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
32902        // Deactivate the SelectController.register method to prevent
32903        // option directives from accidentally registering themselves
32904        // (and unwanted $destroy handlers etc.)
32905        ctrls[0].registerOption = noop;
32906      },
32907      post: ngOptionsPostLink
32908    }
32909  };
32910}];
32911
32912/**
32913 * @ngdoc directive
32914 * @name ngPluralize
32915 * @restrict EA
32916 *
32917 * @description
32918 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
32919 * These rules are bundled with angular.js, but can be overridden
32920 * (see {@link guide/i18n AngularJS i18n} dev guide). You configure ngPluralize directive
32921 * by specifying the mappings between
32922 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
32923 * and the strings to be displayed.
32924 *
32925 * ## Plural categories and explicit number rules
32926 * There are two
32927 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
32928 * in AngularJS's default en-US locale: "one" and "other".
32929 *
32930 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
32931 * any number that is not 1), an explicit number rule can only match one number. For example, the
32932 * explicit number rule for "3" matches the number 3. There are examples of plural categories
32933 * and explicit number rules throughout the rest of this documentation.
32934 *
32935 * ## Configuring ngPluralize
32936 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
32937 * You can also provide an optional attribute, `offset`.
32938 *
32939 * The value of the `count` attribute can be either a string or an {@link guide/expression
32940 * AngularJS expression}; these are evaluated on the current scope for its bound value.
32941 *
32942 * The `when` attribute specifies the mappings between plural categories and the actual
32943 * string to be displayed. The value of the attribute should be a JSON object.
32944 *
32945 * The following example shows how to configure ngPluralize:
32946 *
32947 * ```html
32948 * <ng-pluralize count="personCount"
32949                 when="{'0': 'Nobody is viewing.',
32950 *                      'one': '1 person is viewing.',
32951 *                      'other': '{} people are viewing.'}">
32952 * </ng-pluralize>
32953 *```
32954 *
32955 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
32956 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
32957 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
32958 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
32959 * show "a dozen people are viewing".
32960 *
32961 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
32962 * into pluralized strings. In the previous example, AngularJS will replace `{}` with
32963 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
32964 * for <span ng-non-bindable>{{numberExpression}}</span>.
32965 *
32966 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
32967 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
32968 *
32969 * ## Configuring ngPluralize with offset
32970 * The `offset` attribute allows further customization of pluralized text, which can result in
32971 * a better user experience. For example, instead of the message "4 people are viewing this document",
32972 * you might display "John, Kate and 2 others are viewing this document".
32973 * The offset attribute allows you to offset a number by any desired value.
32974 * Let's take a look at an example:
32975 *
32976 * ```html
32977 * <ng-pluralize count="personCount" offset=2
32978 *               when="{'0': 'Nobody is viewing.',
32979 *                      '1': '{{person1}} is viewing.',
32980 *                      '2': '{{person1}} and {{person2}} are viewing.',
32981 *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
32982 *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
32983 * </ng-pluralize>
32984 * ```
32985 *
32986 * Notice that we are still using two plural categories(one, other), but we added
32987 * three explicit number rules 0, 1 and 2.
32988 * When one person, perhaps John, views the document, "John is viewing" will be shown.
32989 * When three people view the document, no explicit number rule is found, so
32990 * an offset of 2 is taken off 3, and AngularJS uses 1 to decide the plural category.
32991 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
32992 * is shown.
32993 *
32994 * Note that when you specify offsets, you must provide explicit number rules for
32995 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
32996 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
32997 * plural categories "one" and "other".
32998 *
32999 * @param {string|expression} count The variable to be bound to.
33000 * @param {string} when The mapping between plural category to its corresponding strings.
33001 * @param {number=} offset Offset to deduct from the total number.
33002 *
33003 * @example
33004    <example module="pluralizeExample" name="ng-pluralize">
33005      <file name="index.html">
33006        <script>
33007          angular.module('pluralizeExample', [])
33008            .controller('ExampleController', ['$scope', function($scope) {
33009              $scope.person1 = 'Igor';
33010              $scope.person2 = 'Misko';
33011              $scope.personCount = 1;
33012            }]);
33013        </script>
33014        <div ng-controller="ExampleController">
33015          <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
33016          <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
33017          <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
33018
33019          <!--- Example with simple pluralization rules for en locale --->
33020          Without Offset:
33021          <ng-pluralize count="personCount"
33022                        when="{'0': 'Nobody is viewing.',
33023                               'one': '1 person is viewing.',
33024                               'other': '{} people are viewing.'}">
33025          </ng-pluralize><br>
33026
33027          <!--- Example with offset --->
33028          With Offset(2):
33029          <ng-pluralize count="personCount" offset=2
33030                        when="{'0': 'Nobody is viewing.',
33031                               '1': '{{person1}} is viewing.',
33032                               '2': '{{person1}} and {{person2}} are viewing.',
33033                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
33034                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
33035          </ng-pluralize>
33036        </div>
33037      </file>
33038      <file name="protractor.js" type="protractor">
33039        it('should show correct pluralized string', function() {
33040          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
33041          var withOffset = element.all(by.css('ng-pluralize')).get(1);
33042          var countInput = element(by.model('personCount'));
33043
33044          expect(withoutOffset.getText()).toEqual('1 person is viewing.');
33045          expect(withOffset.getText()).toEqual('Igor is viewing.');
33046
33047          countInput.clear();
33048          countInput.sendKeys('0');
33049
33050          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
33051          expect(withOffset.getText()).toEqual('Nobody is viewing.');
33052
33053          countInput.clear();
33054          countInput.sendKeys('2');
33055
33056          expect(withoutOffset.getText()).toEqual('2 people are viewing.');
33057          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
33058
33059          countInput.clear();
33060          countInput.sendKeys('3');
33061
33062          expect(withoutOffset.getText()).toEqual('3 people are viewing.');
33063          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
33064
33065          countInput.clear();
33066          countInput.sendKeys('4');
33067
33068          expect(withoutOffset.getText()).toEqual('4 people are viewing.');
33069          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
33070        });
33071        it('should show data-bound names', function() {
33072          var withOffset = element.all(by.css('ng-pluralize')).get(1);
33073          var personCount = element(by.model('personCount'));
33074          var person1 = element(by.model('person1'));
33075          var person2 = element(by.model('person2'));
33076          personCount.clear();
33077          personCount.sendKeys('4');
33078          person1.clear();
33079          person1.sendKeys('Di');
33080          person2.clear();
33081          person2.sendKeys('Vojta');
33082          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
33083        });
33084      </file>
33085    </example>
33086 */
33087var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
33088  var BRACE = /{}/g,
33089      IS_WHEN = /^when(Minus)?(.+)$/;
33090
33091  return {
33092    link: function(scope, element, attr) {
33093      var numberExp = attr.count,
33094          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
33095          offset = attr.offset || 0,
33096          whens = scope.$eval(whenExp) || {},
33097          whensExpFns = {},
33098          startSymbol = $interpolate.startSymbol(),
33099          endSymbol = $interpolate.endSymbol(),
33100          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
33101          watchRemover = angular.noop,
33102          lastCount;
33103
33104      forEach(attr, function(expression, attributeName) {
33105        var tmpMatch = IS_WHEN.exec(attributeName);
33106        if (tmpMatch) {
33107          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
33108          whens[whenKey] = element.attr(attr.$attr[attributeName]);
33109        }
33110      });
33111      forEach(whens, function(expression, key) {
33112        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
33113
33114      });
33115
33116      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
33117        var count = parseFloat(newVal);
33118        var countIsNaN = isNumberNaN(count);
33119
33120        if (!countIsNaN && !(count in whens)) {
33121          // If an explicit number rule such as 1, 2, 3... is defined, just use it.
33122          // Otherwise, check it against pluralization rules in $locale service.
33123          count = $locale.pluralCat(count - offset);
33124        }
33125
33126        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
33127        // In JS `NaN !== NaN`, so we have to explicitly check.
33128        if ((count !== lastCount) && !(countIsNaN && isNumberNaN(lastCount))) {
33129          watchRemover();
33130          var whenExpFn = whensExpFns[count];
33131          if (isUndefined(whenExpFn)) {
33132            if (newVal != null) {
33133              $log.debug('ngPluralize: no rule defined for \'' + count + '\' in ' + whenExp);
33134            }
33135            watchRemover = noop;
33136            updateElementText();
33137          } else {
33138            watchRemover = scope.$watch(whenExpFn, updateElementText);
33139          }
33140          lastCount = count;
33141        }
33142      });
33143
33144      function updateElementText(newText) {
33145        element.text(newText || '');
33146      }
33147    }
33148  };
33149}];
33150
33151/**
33152 * @ngdoc directive
33153 * @name ngRef
33154 * @restrict A
33155 *
33156 * @description
33157 * The `ngRef` attribute tells AngularJS to assign the controller of a component (or a directive)
33158 * to the given property in the current scope. It is also possible to add the jqlite-wrapped DOM
33159 * element to the scope.
33160 *
33161 * If the element with `ngRef` is destroyed `null` is assigned to the property.
33162 *
33163 * Note that if you want to assign from a child into the parent scope, you must initialize the
33164 * target property on the parent scope, otherwise `ngRef` will assign on the child scope.
33165 * This commonly happens when assigning elements or components wrapped in {@link ngIf} or
33166 * {@link ngRepeat}. See the second example below.
33167 *
33168 *
33169 * @element ANY
33170 * @param {string} ngRef property name - A valid AngularJS expression identifier to which the
33171 *                       controller or jqlite-wrapped DOM element will be bound.
33172 * @param {string=} ngRefRead read value - The name of a directive (or component) on this element,
33173 *                            or the special string `$element`. If a name is provided, `ngRef` will
33174 *                            assign the matching controller. If `$element` is provided, the element
33175 *                            itself is assigned (even if a controller is available).
33176 *
33177 *
33178 * @example
33179 * ### Simple toggle
33180 * This example shows how the controller of the component toggle
33181 * is reused in the template through the scope to use its logic.
33182 * <example name="ng-ref-component" module="myApp">
33183 *   <file name="index.html">
33184 *     <my-toggle ng-ref="myToggle"></my-toggle>
33185 *     <button ng-click="myToggle.toggle()">Toggle</button>
33186 *     <div ng-show="myToggle.isOpen()">
33187 *       You are using a component in the same template to show it.
33188 *     </div>
33189 *   </file>
33190 *   <file name="index.js">
33191 *     angular.module('myApp', [])
33192 *     .component('myToggle', {
33193 *       controller: function ToggleController() {
33194 *         var opened = false;
33195 *         this.isOpen = function() { return opened; };
33196 *         this.toggle = function() { opened = !opened; };
33197 *       }
33198 *     });
33199 *   </file>
33200 *   <file name="protractor.js" type="protractor">
33201 *      it('should publish the toggle into the scope', function() {
33202 *        var toggle = element(by.buttonText('Toggle'));
33203 *        expect(toggle.evaluate('myToggle.isOpen()')).toEqual(false);
33204 *        toggle.click();
33205 *        expect(toggle.evaluate('myToggle.isOpen()')).toEqual(true);
33206 *      });
33207 *   </file>
33208 * </example>
33209 *
33210 * @example
33211 * ### ngRef inside scopes
33212 * This example shows how `ngRef` works with child scopes. The `ngRepeat`-ed `myWrapper` components
33213 * are assigned to the scope of `myRoot`, because the `toggles` property has been initialized.
33214 * The repeated `myToggle` components are published to the child scopes created by `ngRepeat`.
33215 * `ngIf` behaves similarly - the assignment of `myToggle` happens in the `ngIf` child scope,
33216 * because the target property has not been initialized on the `myRoot` component controller.
33217 *
33218 * <example name="ng-ref-scopes" module="myApp">
33219 *   <file name="index.html">
33220 *     <my-root></my-root>
33221 *   </file>
33222 *   <file name="index.js">
33223 *     angular.module('myApp', [])
33224 *     .component('myRoot', {
33225 *       templateUrl: 'root.html',
33226 *       controller: function() {
33227 *         this.wrappers = []; // initialize the array so that the wrappers are assigned into the parent scope
33228 *       }
33229 *     })
33230 *     .component('myToggle', {
33231 *       template: '<strong>myToggle</strong><button ng-click="$ctrl.toggle()" ng-transclude></button>',
33232 *       transclude: true,
33233 *       controller: function ToggleController() {
33234 *         var opened = false;
33235 *         this.isOpen = function() { return opened; };
33236 *         this.toggle = function() { opened = !opened; };
33237 *       }
33238 *     })
33239 *     .component('myWrapper', {
33240 *       transclude: true,
33241 *       template: '<strong>myWrapper</strong>' +
33242 *         '<div>ngRepeatToggle.isOpen(): {{$ctrl.ngRepeatToggle.isOpen() | json}}</div>' +
33243 *         '<my-toggle ng-ref="$ctrl.ngRepeatToggle"><ng-transclude></ng-transclude></my-toggle>'
33244 *     });
33245 *   </file>
33246 *   <file name="root.html">
33247 *     <strong>myRoot</strong>
33248 *     <my-toggle ng-ref="$ctrl.outerToggle">Outer Toggle</my-toggle>
33249 *     <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
33250 *     <div><em>wrappers assigned to root</em><br>
33251 *     <div ng-repeat="wrapper in $ctrl.wrappers">
33252 *       wrapper.ngRepeatToggle.isOpen(): {{wrapper.ngRepeatToggle.isOpen() | json}}
33253 *     </div>
33254 *
33255 *     <ul>
33256 *       <li ng-repeat="(index, value) in [1,2,3]">
33257 *         <strong>ngRepeat</strong>
33258 *         <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
33259 *         <my-wrapper ng-ref="$ctrl.wrappers[index]">ngRepeat Toggle {{$index + 1}}</my-wrapper>
33260 *       </li>
33261 *     </ul>
33262 *
33263 *     <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen()}} // This is always undefined because it's
33264 *       assigned to the child scope created by ngIf.
33265 *     </div>
33266 *     <div ng-if="true">
33267          <strong>ngIf</strong>
33268 *        <my-toggle ng-ref="ngIfToggle">ngIf Toggle</my-toggle>
33269 *        <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen() | json}}</div>
33270 *        <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
33271 *     </div>
33272 *   </file>
33273 *   <file name="styles.css">
33274 *     ul {
33275 *       list-style: none;
33276 *       padding-left: 0;
33277 *     }
33278 *
33279 *     li[ng-repeat] {
33280 *       background: lightgreen;
33281 *       padding: 8px;
33282 *       margin: 8px;
33283 *     }
33284 *
33285 *     [ng-if] {
33286 *       background: lightgrey;
33287 *       padding: 8px;
33288 *     }
33289 *
33290 *     my-root {
33291 *       background: lightgoldenrodyellow;
33292 *       padding: 8px;
33293 *       display: block;
33294 *     }
33295 *
33296 *     my-wrapper {
33297 *       background: lightsalmon;
33298 *       padding: 8px;
33299 *       display: block;
33300 *     }
33301 *
33302 *     my-toggle {
33303 *       background: lightblue;
33304 *       padding: 8px;
33305 *       display: block;
33306 *     }
33307 *   </file>
33308 *   <file name="protractor.js" type="protractor">
33309 *      var OuterToggle = function() {
33310 *        this.toggle = function() {
33311 *          element(by.buttonText('Outer Toggle')).click();
33312 *        };
33313 *        this.isOpen = function() {
33314 *          return element.all(by.binding('outerToggle.isOpen()')).first().getText();
33315 *        };
33316 *      };
33317 *      var NgRepeatToggle = function(i) {
33318 *        var parent = element.all(by.repeater('(index, value) in [1,2,3]')).get(i - 1);
33319 *        this.toggle = function() {
33320 *          element(by.buttonText('ngRepeat Toggle ' + i)).click();
33321 *        };
33322 *        this.isOpen = function() {
33323 *          return parent.element(by.binding('ngRepeatToggle.isOpen() | json')).getText();
33324 *        };
33325 *        this.isOuterOpen = function() {
33326 *          return parent.element(by.binding('outerToggle.isOpen() | json')).getText();
33327 *        };
33328 *      };
33329 *      var NgRepeatToggles = function() {
33330 *        var toggles = [1,2,3].map(function(i) { return new NgRepeatToggle(i); });
33331 *        this.forEach = function(fn) {
33332 *          toggles.forEach(fn);
33333 *        };
33334 *        this.isOuterOpen = function(i) {
33335 *          return toggles[i - 1].isOuterOpen();
33336 *        };
33337 *      };
33338 *      var NgIfToggle = function() {
33339 *        var parent = element(by.css('[ng-if]'));
33340 *        this.toggle = function() {
33341 *          element(by.buttonText('ngIf Toggle')).click();
33342 *        };
33343 *        this.isOpen = function() {
33344 *          return by.binding('ngIfToggle.isOpen() | json').getText();
33345 *        };
33346 *        this.isOuterOpen = function() {
33347 *          return parent.element(by.binding('outerToggle.isOpen() | json')).getText();
33348 *        };
33349 *      };
33350 *
33351 *      it('should toggle the outer toggle', function() {
33352 *        var outerToggle = new OuterToggle();
33353 *        expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): false');
33354 *        outerToggle.toggle();
33355 *        expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): true');
33356 *      });
33357 *
33358 *      it('should toggle all outer toggles', function() {
33359 *        var outerToggle = new OuterToggle();
33360 *        var repeatToggles = new NgRepeatToggles();
33361 *        var ifToggle = new NgIfToggle();
33362 *        expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): false');
33363 *        expect(repeatToggles.isOuterOpen(1)).toEqual('outerToggle.isOpen(): false');
33364 *        expect(repeatToggles.isOuterOpen(2)).toEqual('outerToggle.isOpen(): false');
33365 *        expect(repeatToggles.isOuterOpen(3)).toEqual('outerToggle.isOpen(): false');
33366 *        expect(ifToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
33367 *        outerToggle.toggle();
33368 *        expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): true');
33369 *        expect(repeatToggles.isOuterOpen(1)).toEqual('outerToggle.isOpen(): true');
33370 *        expect(repeatToggles.isOuterOpen(2)).toEqual('outerToggle.isOpen(): true');
33371 *        expect(repeatToggles.isOuterOpen(3)).toEqual('outerToggle.isOpen(): true');
33372 *        expect(ifToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): true');
33373 *      });
33374 *
33375 *      it('should toggle each repeat iteration separately', function() {
33376 *        var repeatToggles = new NgRepeatToggles();
33377 *
33378 *        repeatToggles.forEach(function(repeatToggle) {
33379 *          expect(repeatToggle.isOpen()).toEqual('ngRepeatToggle.isOpen(): false');
33380 *          expect(repeatToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
33381 *          repeatToggle.toggle();
33382 *          expect(repeatToggle.isOpen()).toEqual('ngRepeatToggle.isOpen(): true');
33383 *          expect(repeatToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
33384 *        });
33385 *      });
33386 *   </file>
33387 * </example>
33388 *
33389 */
33390
33391var ngRefMinErr = minErr('ngRef');
33392
33393var ngRefDirective = ['$parse', function($parse) {
33394  return {
33395    priority: -1, // Needed for compatibility with element transclusion on the same element
33396    restrict: 'A',
33397    compile: function(tElement, tAttrs) {
33398      // Get the expected controller name, converts <data-some-thing> into "someThing"
33399      var controllerName = directiveNormalize(nodeName_(tElement));
33400
33401      // Get the expression for value binding
33402      var getter = $parse(tAttrs.ngRef);
33403      var setter = getter.assign || function() {
33404        throw ngRefMinErr('nonassign', 'Expression in ngRef="{0}" is non-assignable!', tAttrs.ngRef);
33405      };
33406
33407      return function(scope, element, attrs) {
33408        var refValue;
33409
33410        if (attrs.hasOwnProperty('ngRefRead')) {
33411          if (attrs.ngRefRead === '$element') {
33412            refValue = element;
33413          } else {
33414            refValue = element.data('$' + attrs.ngRefRead + 'Controller');
33415
33416            if (!refValue) {
33417              throw ngRefMinErr(
33418                'noctrl',
33419                'The controller for ngRefRead="{0}" could not be found on ngRef="{1}"',
33420                attrs.ngRefRead,
33421                tAttrs.ngRef
33422              );
33423            }
33424          }
33425        } else {
33426          refValue = element.data('$' + controllerName + 'Controller');
33427        }
33428
33429        refValue = refValue || element;
33430
33431        setter(scope, refValue);
33432
33433        // when the element is removed, remove it (nullify it)
33434        element.on('$destroy', function() {
33435          // only remove it if value has not changed,
33436          // because animations (and other procedures) may duplicate elements
33437          if (getter(scope) === refValue) {
33438            setter(scope, null);
33439          }
33440        });
33441      };
33442    }
33443  };
33444}];
33445
33446/* exported ngRepeatDirective */
33447
33448/**
33449 * @ngdoc directive
33450 * @name ngRepeat
33451 * @multiElement
33452 * @restrict A
33453 *
33454 * @description
33455 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
33456 * instance gets its own scope, where the given loop variable is set to the current collection item,
33457 * and `$index` is set to the item index or key.
33458 *
33459 * Special properties are exposed on the local scope of each template instance, including:
33460 *
33461 * | Variable  | Type            | Details                                                                     |
33462 * |-----------|-----------------|-----------------------------------------------------------------------------|
33463 * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
33464 * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
33465 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
33466 * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
33467 * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
33468 * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
33469 *
33470 * <div class="alert alert-info">
33471 *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
33472 *   This may be useful when, for instance, nesting ngRepeats.
33473 * </div>
33474 *
33475 *
33476 * ## Iterating over object properties
33477 *
33478 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
33479 * syntax:
33480 *
33481 * ```js
33482 * <div ng-repeat="(key, value) in myObj"> ... </div>
33483 * ```
33484 *
33485 * However, there are a few limitations compared to array iteration:
33486 *
33487 * - The JavaScript specification does not define the order of keys
33488 *   returned for an object, so AngularJS relies on the order returned by the browser
33489 *   when running `for key in myObj`. Browsers generally follow the strategy of providing
33490 *   keys in the order in which they were defined, although there are exceptions when keys are deleted
33491 *   and reinstated. See the
33492 *   [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
33493 *
33494 * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
33495 *   it's a prefix used by AngularJS for public (`$`) and private (`$$`) properties.
33496 *
33497 * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
33498 *   objects, and will throw an error if used with one.
33499 *
33500 * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
33501 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
33502 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
33503 * or implement a `$watch` on the object yourself.
33504 *
33505 *
33506 * ## Tracking and Duplicates
33507 *
33508 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
33509 * the collection. When a change happens, `ngRepeat` then makes the corresponding changes to the DOM:
33510 *
33511 * * When an item is added, a new instance of the template is added to the DOM.
33512 * * When an item is removed, its template instance is removed from the DOM.
33513 * * When items are reordered, their respective templates are reordered in the DOM.
33514 *
33515 * To minimize creation of DOM elements, `ngRepeat` uses a function
33516 * to "keep track" of all items in the collection and their corresponding DOM elements.
33517 * For example, if an item is added to the collection, `ngRepeat` will know that all other items
33518 * already have DOM elements, and will not re-render them.
33519 *
33520 * All different types of tracking functions, their syntax, and their support for duplicate
33521 * items in collections can be found in the
33522 * {@link ngRepeat#ngRepeat-arguments ngRepeat expression description}.
33523 *
33524 * <div class="alert alert-success">
33525 * **Best Practice:** If you are working with objects that have a unique identifier property, you
33526 * should track by this identifier instead of the object instance,
33527 * e.g. `item in items track by item.id`.
33528 * Should you reload your data later, `ngRepeat` will not have to rebuild the DOM elements for items
33529 * it has already rendered, even if the JavaScript objects in the collection have been substituted
33530 * for new ones. For large collections, this significantly improves rendering performance.
33531 * </div>
33532 *
33533 * ### Effects of DOM Element re-use
33534 *
33535 * When DOM elements are re-used, ngRepeat updates the scope for the element, which will
33536 * automatically update any active bindings on the template. However, other
33537 * functionality will not be updated, because the element is not re-created:
33538 *
33539 * - Directives are not re-compiled
33540 * - {@link guide/expression#one-time-binding one-time expressions} on the repeated template are not
33541 * updated if they have stabilized.
33542 *
33543 * The above affects all kinds of element re-use due to tracking, but may be especially visible
33544 * when tracking by `$index` due to the way ngRepeat re-uses elements.
33545 *
33546 * The following example shows the effects of different actions with tracking:
33547
33548  <example module="ngRepeat" name="ngRepeat-tracking" deps="angular-animate.js" animations="true">
33549    <file name="script.js">
33550      angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
33551        var friends = [
33552          {name:'John', age:25},
33553          {name:'Mary', age:40},
33554          {name:'Peter', age:85}
33555        ];
33556
33557        $scope.removeFirst = function() {
33558          $scope.friends.shift();
33559        };
33560
33561        $scope.updateAge = function() {
33562          $scope.friends.forEach(function(el) {
33563            el.age = el.age + 5;
33564          });
33565        };
33566
33567        $scope.copy = function() {
33568          $scope.friends = angular.copy($scope.friends);
33569        };
33570
33571        $scope.reset = function() {
33572          $scope.friends = angular.copy(friends);
33573        };
33574
33575        $scope.reset();
33576      });
33577    </file>
33578    <file name="index.html">
33579      <div ng-controller="repeatController">
33580        <ol>
33581          <li>When you click "Update Age", only the first list updates the age, because all others have
33582          a one-time binding on the age property. If you then click "Copy", the current friend list
33583          is copied, and now the second list updates the age, because the identity of the collection items
33584          has changed and the list must be re-rendered. The 3rd and 4th list stay the same, because all the
33585          items are already known according to their tracking functions.
33586          </li>
33587          <li>When you click "Remove First", the 4th list has the wrong age on both remaining items. This is
33588          due to tracking by $index: when the first collection item is removed, ngRepeat reuses the first
33589          DOM element for the new first collection item, and so on. Since the age property is one-time
33590          bound, the value remains from the collection item which was previously at this index.
33591          </li>
33592        </ol>
33593
33594        <button ng-click="removeFirst()">Remove First</button>
33595        <button ng-click="updateAge()">Update Age</button>
33596        <button ng-click="copy()">Copy</button>
33597        <br><button ng-click="reset()">Reset List</button>
33598        <br>
33599        <code>track by $id(friend)</code> (default):
33600        <ul class="example-animate-container">
33601          <li class="animate-repeat" ng-repeat="friend in friends">
33602            {{friend.name}} is {{friend.age}} years old.
33603          </li>
33604        </ul>
33605        <code>track by $id(friend)</code> (default), with age one-time binding:
33606        <ul class="example-animate-container">
33607          <li class="animate-repeat" ng-repeat="friend in friends">
33608            {{friend.name}} is {{::friend.age}} years old.
33609          </li>
33610        </ul>
33611        <code>track by friend.name</code>, with age one-time binding:
33612        <ul class="example-animate-container">
33613          <li class="animate-repeat" ng-repeat="friend in friends track by friend.name">
33614            {{friend.name}}  is {{::friend.age}} years old.
33615          </li>
33616        </ul>
33617        <code>track by $index</code>, with age one-time binding:
33618        <ul class="example-animate-container">
33619          <li class="animate-repeat" ng-repeat="friend in friends track by $index">
33620            {{friend.name}} is {{::friend.age}} years old.
33621          </li>
33622        </ul>
33623      </div>
33624    </file>
33625    <file name="animations.css">
33626      .example-animate-container {
33627        background:white;
33628        border:1px solid black;
33629        list-style:none;
33630        margin:0;
33631        padding:0 10px;
33632      }
33633
33634      .animate-repeat {
33635        line-height:30px;
33636        list-style:none;
33637        box-sizing:border-box;
33638      }
33639
33640      .animate-repeat.ng-move,
33641      .animate-repeat.ng-enter,
33642      .animate-repeat.ng-leave {
33643        transition:all linear 0.5s;
33644      }
33645
33646      .animate-repeat.ng-leave.ng-leave-active,
33647      .animate-repeat.ng-move,
33648      .animate-repeat.ng-enter {
33649        opacity:0;
33650        max-height:0;
33651      }
33652
33653      .animate-repeat.ng-leave,
33654      .animate-repeat.ng-move.ng-move-active,
33655      .animate-repeat.ng-enter.ng-enter-active {
33656        opacity:1;
33657        max-height:30px;
33658      }
33659    </file>
33660  </example>
33661
33662 *
33663 * ## Special repeat start and end points
33664 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
33665 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
33666 * 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)
33667 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
33668 *
33669 * The example below makes use of this feature:
33670 * ```html
33671 *   <header ng-repeat-start="item in items">
33672 *     Header {{ item }}
33673 *   </header>
33674 *   <div class="body">
33675 *     Body {{ item }}
33676 *   </div>
33677 *   <footer ng-repeat-end>
33678 *     Footer {{ item }}
33679 *   </footer>
33680 * ```
33681 *
33682 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
33683 * ```html
33684 *   <header>
33685 *     Header A
33686 *   </header>
33687 *   <div class="body">
33688 *     Body A
33689 *   </div>
33690 *   <footer>
33691 *     Footer A
33692 *   </footer>
33693 *   <header>
33694 *     Header B
33695 *   </header>
33696 *   <div class="body">
33697 *     Body B
33698 *   </div>
33699 *   <footer>
33700 *     Footer B
33701 *   </footer>
33702 * ```
33703 *
33704 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
33705 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
33706 *
33707 * @animations
33708 * | Animation                        | Occurs                              |
33709 * |----------------------------------|-------------------------------------|
33710 * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
33711 * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
33712 * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
33713 *
33714 * See the example below for defining CSS animations with ngRepeat.
33715 *
33716 * @element ANY
33717 * @scope
33718 * @priority 1000
33719 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
33720 *   formats are currently supported:
33721 *
33722 *   * `variable in expression` – where variable is the user defined loop variable and `expression`
33723 *     is a scope expression giving the collection to enumerate.
33724 *
33725 *     For example: `album in artist.albums`.
33726 *
33727 *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
33728 *     and `expression` is the scope expression giving the collection to enumerate.
33729 *
33730 *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
33731 *
33732 *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
33733 *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
33734 *     is specified, ng-repeat associates elements by identity. It is an error to have
33735 *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
33736 *     mapped to the same DOM element, which is not possible.)
33737 *
33738 *     *Default tracking: $id()*: `item in items` is equivalent to `item in items track by $id(item)`.
33739 *     This implies that the DOM elements will be associated by item identity in the collection.
33740 *
33741 *     The built-in `$id()` function can be used to assign a unique
33742 *     `$$hashKey` property to each item in the collection. This property is then used as a key to associated DOM elements
33743 *     with the corresponding item in the collection by identity. Moving the same object would move
33744 *     the DOM element in the same way in the DOM.
33745 *     Note that the default id function does not support duplicate primitive values (`number`, `string`),
33746 *     but supports duplictae non-primitive values (`object`) that are *equal* in shape.
33747 *
33748 *     *Custom Expression*: It is possible to use any AngularJS expression to compute the tracking
33749 *     id, for example with a function, or using a property on the collection items.
33750 *     `item in items track by item.id` is a typical pattern when the items have a unique identifier,
33751 *     e.g. database id. In this case the object identity does not matter. Two objects are considered
33752 *     equivalent as long as their `id` property is same.
33753 *     Tracking by unique identifier is the most performant way and should be used whenever possible.
33754 *
33755 *     *$index*: This special property tracks the collection items by their index, and
33756 *     re-uses the DOM elements that match that index, e.g. `item in items track by $index`. This can
33757 *     be used for a performance improvement if no unique identfier is available and the identity of
33758 *     the collection items cannot be easily computed. It also allows duplicates.
33759 *
33760 *     <div class="alert alert-warning">
33761 *       <strong>Note:</strong> Re-using DOM elements can have unforeseen effects. Read the
33762 *       {@link ngRepeat#tracking-and-duplicates section on tracking and duplicates} for
33763 *       more info.
33764 *     </div>
33765 *
33766 *     <div class="alert alert-warning">
33767 *       <strong>Note:</strong> the `track by` expression must come last - after any filters, and the alias expression:
33768 *       `item in items | filter:searchText as results  track by item.id`
33769 *     </div>
33770 *
33771 *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
33772 *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
33773 *     when a filter is active on the repeater, but the filtered result set is empty.
33774 *
33775 *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
33776 *     the items have been processed through the filter.
33777 *
33778 *     Please note that `as [variable name]` is not an operator but rather a part of ngRepeat
33779 *     micro-syntax so it can be used only after all filters (and not as operator, inside an expression).
33780 *
33781 *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results track by item.id` .
33782 *
33783 * @example
33784 * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
33785 * results by name or by age. New (entering) and removed (leaving) items are animated.
33786  <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true">
33787    <file name="index.html">
33788      <div ng-controller="repeatController">
33789        I have {{friends.length}} friends. They are:
33790        <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
33791        <ul class="example-animate-container">
33792          <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results track by friend.name">
33793            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
33794          </li>
33795          <li class="animate-repeat" ng-if="results.length === 0">
33796            <strong>No results found...</strong>
33797          </li>
33798        </ul>
33799      </div>
33800    </file>
33801    <file name="script.js">
33802      angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
33803        $scope.friends = [
33804          {name:'John', age:25, gender:'boy'},
33805          {name:'Jessie', age:30, gender:'girl'},
33806          {name:'Johanna', age:28, gender:'girl'},
33807          {name:'Joy', age:15, gender:'girl'},
33808          {name:'Mary', age:28, gender:'girl'},
33809          {name:'Peter', age:95, gender:'boy'},
33810          {name:'Sebastian', age:50, gender:'boy'},
33811          {name:'Erika', age:27, gender:'girl'},
33812          {name:'Patrick', age:40, gender:'boy'},
33813          {name:'Samantha', age:60, gender:'girl'}
33814        ];
33815      });
33816    </file>
33817    <file name="animations.css">
33818      .example-animate-container {
33819        background:white;
33820        border:1px solid black;
33821        list-style:none;
33822        margin:0;
33823        padding:0 10px;
33824      }
33825
33826      .animate-repeat {
33827        line-height:30px;
33828        list-style:none;
33829        box-sizing:border-box;
33830      }
33831
33832      .animate-repeat.ng-move,
33833      .animate-repeat.ng-enter,
33834      .animate-repeat.ng-leave {
33835        transition:all linear 0.5s;
33836      }
33837
33838      .animate-repeat.ng-leave.ng-leave-active,
33839      .animate-repeat.ng-move,
33840      .animate-repeat.ng-enter {
33841        opacity:0;
33842        max-height:0;
33843      }
33844
33845      .animate-repeat.ng-leave,
33846      .animate-repeat.ng-move.ng-move-active,
33847      .animate-repeat.ng-enter.ng-enter-active {
33848        opacity:1;
33849        max-height:30px;
33850      }
33851    </file>
33852    <file name="protractor.js" type="protractor">
33853      var friends = element.all(by.repeater('friend in friends'));
33854
33855      it('should render initial data set', function() {
33856        expect(friends.count()).toBe(10);
33857        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
33858        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
33859        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
33860        expect(element(by.binding('friends.length')).getText())
33861            .toMatch("I have 10 friends. They are:");
33862      });
33863
33864       it('should update repeater when filter predicate changes', function() {
33865         expect(friends.count()).toBe(10);
33866
33867         element(by.model('q')).sendKeys('ma');
33868
33869         expect(friends.count()).toBe(2);
33870         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
33871         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
33872       });
33873      </file>
33874    </example>
33875 */
33876var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
33877  var NG_REMOVED = '$$NG_REMOVED';
33878  var ngRepeatMinErr = minErr('ngRepeat');
33879
33880  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
33881    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
33882    scope[valueIdentifier] = value;
33883    if (keyIdentifier) scope[keyIdentifier] = key;
33884    scope.$index = index;
33885    scope.$first = (index === 0);
33886    scope.$last = (index === (arrayLength - 1));
33887    scope.$middle = !(scope.$first || scope.$last);
33888    // eslint-disable-next-line no-bitwise
33889    scope.$odd = !(scope.$even = (index & 1) === 0);
33890  };
33891
33892  var getBlockStart = function(block) {
33893    return block.clone[0];
33894  };
33895
33896  var getBlockEnd = function(block) {
33897    return block.clone[block.clone.length - 1];
33898  };
33899
33900  var trackByIdArrayFn = function($scope, key, value) {
33901    return hashKey(value);
33902  };
33903
33904  var trackByIdObjFn = function($scope, key) {
33905    return key;
33906  };
33907
33908  return {
33909    restrict: 'A',
33910    multiElement: true,
33911    transclude: 'element',
33912    priority: 1000,
33913    terminal: true,
33914    $$tlb: true,
33915    compile: function ngRepeatCompile($element, $attr) {
33916      var expression = $attr.ngRepeat;
33917      var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
33918
33919      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*$/);
33920
33921      if (!match) {
33922        throw ngRepeatMinErr('iexp', 'Expected expression in form of \'_item_ in _collection_[ track by _id_]\' but got \'{0}\'.',
33923            expression);
33924      }
33925
33926      var lhs = match[1];
33927      var rhs = match[2];
33928      var aliasAs = match[3];
33929      var trackByExp = match[4];
33930
33931      match = lhs.match(/^(?:(\s*[$\w]+)|\(\s*([$\w]+)\s*,\s*([$\w]+)\s*\))$/);
33932
33933      if (!match) {
33934        throw ngRepeatMinErr('iidexp', '\'_item_\' in \'_item_ in _collection_\' should be an identifier or \'(_key_, _value_)\' expression, but got \'{0}\'.',
33935            lhs);
33936      }
33937      var valueIdentifier = match[3] || match[1];
33938      var keyIdentifier = match[2];
33939
33940      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
33941          /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
33942        throw ngRepeatMinErr('badident', 'alias \'{0}\' is invalid --- must be a valid JS identifier which is not a reserved name.',
33943          aliasAs);
33944      }
33945
33946      var trackByIdExpFn;
33947
33948      if (trackByExp) {
33949        var hashFnLocals = {$id: hashKey};
33950        var trackByExpGetter = $parse(trackByExp);
33951
33952        trackByIdExpFn = function($scope, key, value, index) {
33953          // assign key, value, and $index to the locals so that they can be used in hash functions
33954          if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
33955          hashFnLocals[valueIdentifier] = value;
33956          hashFnLocals.$index = index;
33957          return trackByExpGetter($scope, hashFnLocals);
33958        };
33959      }
33960
33961      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
33962
33963        // Store a list of elements from previous run. This is a hash where key is the item from the
33964        // iterator, and the value is objects with following properties.
33965        //   - scope: bound scope
33966        //   - clone: previous element.
33967        //   - index: position
33968        //
33969        // We are using no-proto object so that we don't need to guard against inherited props via
33970        // hasOwnProperty.
33971        var lastBlockMap = createMap();
33972
33973        //watch props
33974        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
33975          var index, length,
33976              previousNode = $element[0],     // node that cloned nodes should be inserted after
33977                                              // initialized to the comment node anchor
33978              nextNode,
33979              // Same as lastBlockMap but it has the current state. It will become the
33980              // lastBlockMap on the next iteration.
33981              nextBlockMap = createMap(),
33982              collectionLength,
33983              key, value, // key/value of iteration
33984              trackById,
33985              trackByIdFn,
33986              collectionKeys,
33987              block,       // last object information {scope, element, id}
33988              nextBlockOrder,
33989              elementsToRemove;
33990
33991          if (aliasAs) {
33992            $scope[aliasAs] = collection;
33993          }
33994
33995          if (isArrayLike(collection)) {
33996            collectionKeys = collection;
33997            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
33998          } else {
33999            trackByIdFn = trackByIdExpFn || trackByIdObjFn;
34000            // if object, extract keys, in enumeration order, unsorted
34001            collectionKeys = [];
34002            for (var itemKey in collection) {
34003              if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
34004                collectionKeys.push(itemKey);
34005              }
34006            }
34007          }
34008
34009          collectionLength = collectionKeys.length;
34010          nextBlockOrder = new Array(collectionLength);
34011
34012          // locate existing items
34013          for (index = 0; index < collectionLength; index++) {
34014            key = (collection === collectionKeys) ? index : collectionKeys[index];
34015            value = collection[key];
34016            trackById = trackByIdFn($scope, key, value, index);
34017            if (lastBlockMap[trackById]) {
34018              // found previously seen block
34019              block = lastBlockMap[trackById];
34020              delete lastBlockMap[trackById];
34021              nextBlockMap[trackById] = block;
34022              nextBlockOrder[index] = block;
34023            } else if (nextBlockMap[trackById]) {
34024              // if collision detected. restore lastBlockMap and throw an error
34025              forEach(nextBlockOrder, function(block) {
34026                if (block && block.scope) lastBlockMap[block.id] = block;
34027              });
34028              throw ngRepeatMinErr('dupes',
34029                  'Duplicates in a repeater are not allowed. Use \'track by\' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}',
34030                  expression, trackById, value);
34031            } else {
34032              // new never before seen block
34033              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
34034              nextBlockMap[trackById] = true;
34035            }
34036          }
34037
34038          // Clear the value property from the hashFnLocals object to prevent a reference to the last value
34039          // being leaked into the ngRepeatCompile function scope
34040          if (hashFnLocals) {
34041            hashFnLocals[valueIdentifier] = undefined;
34042          }
34043
34044          // remove leftover items
34045          for (var blockKey in lastBlockMap) {
34046            block = lastBlockMap[blockKey];
34047            elementsToRemove = getBlockNodes(block.clone);
34048            $animate.leave(elementsToRemove);
34049            if (elementsToRemove[0].parentNode) {
34050              // if the element was not removed yet because of pending animation, mark it as deleted
34051              // so that we can ignore it later
34052              for (index = 0, length = elementsToRemove.length; index < length; index++) {
34053                elementsToRemove[index][NG_REMOVED] = true;
34054              }
34055            }
34056            block.scope.$destroy();
34057          }
34058
34059          // we are not using forEach for perf reasons (trying to avoid #call)
34060          for (index = 0; index < collectionLength; index++) {
34061            key = (collection === collectionKeys) ? index : collectionKeys[index];
34062            value = collection[key];
34063            block = nextBlockOrder[index];
34064
34065            if (block.scope) {
34066              // if we have already seen this object, then we need to reuse the
34067              // associated scope/element
34068
34069              nextNode = previousNode;
34070
34071              // skip nodes that are already pending removal via leave animation
34072              do {
34073                nextNode = nextNode.nextSibling;
34074              } while (nextNode && nextNode[NG_REMOVED]);
34075
34076              if (getBlockStart(block) !== nextNode) {
34077                // existing item which got moved
34078                $animate.move(getBlockNodes(block.clone), null, previousNode);
34079              }
34080              previousNode = getBlockEnd(block);
34081              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
34082            } else {
34083              // new item which we don't know about
34084              $transclude(function ngRepeatTransclude(clone, scope) {
34085                block.scope = scope;
34086                // http://jsperf.com/clone-vs-createcomment
34087                var endNode = ngRepeatEndComment.cloneNode(false);
34088                clone[clone.length++] = endNode;
34089
34090                $animate.enter(clone, null, previousNode);
34091                previousNode = endNode;
34092                // Note: We only need the first/last node of the cloned nodes.
34093                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
34094                // by a directive with templateUrl when its template arrives.
34095                block.clone = clone;
34096                nextBlockMap[block.id] = block;
34097                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
34098              });
34099            }
34100          }
34101          lastBlockMap = nextBlockMap;
34102        });
34103      };
34104    }
34105  };
34106}];
34107
34108var NG_HIDE_CLASS = 'ng-hide';
34109var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
34110/**
34111 * @ngdoc directive
34112 * @name ngShow
34113 * @multiElement
34114 *
34115 * @description
34116 * The `ngShow` directive shows or hides the given HTML element based on the expression provided to
34117 * the `ngShow` attribute.
34118 *
34119 * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
34120 * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
34121 * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
34122 * {@link ng.directive:ngCsp ngCsp}).
34123 *
34124 * ```html
34125 * <!-- when $scope.myValue is truthy (element is visible) -->
34126 * <div ng-show="myValue"></div>
34127 *
34128 * <!-- when $scope.myValue is falsy (element is hidden) -->
34129 * <div ng-show="myValue" class="ng-hide"></div>
34130 * ```
34131 *
34132 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added
34133 * to the class attribute on the element causing it to become hidden. When truthy, the `.ng-hide`
34134 * CSS class is removed from the element causing the element not to appear hidden.
34135 *
34136 * ## Why is `!important` used?
34137 *
34138 * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
34139 * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
34140 * simple as changing the display style on a HTML list item would make hidden elements appear
34141 * visible. This also becomes a bigger issue when dealing with CSS frameworks.
34142 *
34143 * By using `!important`, the show and hide behavior will work as expected despite any clash between
34144 * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
34145 * developer chooses to override the styling to change how to hide an element then it is just a
34146 * matter of using `!important` in their own CSS code.
34147 *
34148 * ### Overriding `.ng-hide`
34149 *
34150 * By default, the `.ng-hide` class will style the element with `display: none !important`. If you
34151 * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
34152 * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
34153 * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
34154 *
34155 * ```css
34156 * .ng-hide:not(.ng-hide-animate) {
34157 *   /&#42; These are just alternative ways of hiding an element &#42;/
34158 *   display: block!important;
34159 *   position: absolute;
34160 *   top: -9999px;
34161 *   left: -9999px;
34162 * }
34163 * ```
34164 *
34165 * By default you don't need to override anything in CSS and the animations will work around the
34166 * display style.
34167 *
34168 * @animations
34169 * | Animation                                           | Occurs                                                                                                        |
34170 * |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
34171 * | {@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. |
34172 * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngShow` expression evaluates to a truthy value and just before contents are set to visible.        |
34173 *
34174 * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
34175 * directive expression is true and false. This system works like the animation system present with
34176 * `ngClass` except that you must also include the `!important` flag to override the display
34177 * property so that the elements are not actually hidden during the animation.
34178 *
34179 * ```css
34180 * /&#42; A working example can be found at the bottom of this page. &#42;/
34181 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
34182 *   transition: all 0.5s linear;
34183 * }
34184 *
34185 * .my-element.ng-hide-add { ... }
34186 * .my-element.ng-hide-add.ng-hide-add-active { ... }
34187 * .my-element.ng-hide-remove { ... }
34188 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
34189 * ```
34190 *
34191 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
34192 * to block during animation states - ngAnimate will automatically handle the style toggling for you.
34193 *
34194 * @element ANY
34195 * @param {expression} ngShow If the {@link guide/expression expression} is truthy/falsy then the
34196 *                            element is shown/hidden respectively.
34197 *
34198 * @example
34199 * A simple example, animating the element's opacity:
34200 *
34201  <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-simple">
34202    <file name="index.html">
34203      Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
34204      <div class="check-element animate-show-hide" ng-show="checked">
34205        I show up when your checkbox is checked.
34206      </div>
34207    </file>
34208    <file name="animations.css">
34209      .animate-show-hide.ng-hide {
34210        opacity: 0;
34211      }
34212
34213      .animate-show-hide.ng-hide-add,
34214      .animate-show-hide.ng-hide-remove {
34215        transition: all linear 0.5s;
34216      }
34217
34218      .check-element {
34219        border: 1px solid black;
34220        opacity: 1;
34221        padding: 10px;
34222      }
34223    </file>
34224    <file name="protractor.js" type="protractor">
34225      it('should check ngShow', function() {
34226        var checkbox = element(by.model('checked'));
34227        var checkElem = element(by.css('.check-element'));
34228
34229        expect(checkElem.isDisplayed()).toBe(false);
34230        checkbox.click();
34231        expect(checkElem.isDisplayed()).toBe(true);
34232      });
34233    </file>
34234  </example>
34235 *
34236 * <hr />
34237 * @example
34238 * A more complex example, featuring different show/hide animations:
34239 *
34240  <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-complex">
34241    <file name="index.html">
34242      Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
34243      <div class="check-element funky-show-hide" ng-show="checked">
34244        I show up when your checkbox is checked.
34245      </div>
34246    </file>
34247    <file name="animations.css">
34248      body {
34249        overflow: hidden;
34250        perspective: 1000px;
34251      }
34252
34253      .funky-show-hide.ng-hide-add {
34254        transform: rotateZ(0);
34255        transform-origin: right;
34256        transition: all 0.5s ease-in-out;
34257      }
34258
34259      .funky-show-hide.ng-hide-add.ng-hide-add-active {
34260        transform: rotateZ(-135deg);
34261      }
34262
34263      .funky-show-hide.ng-hide-remove {
34264        transform: rotateY(90deg);
34265        transform-origin: left;
34266        transition: all 0.5s ease;
34267      }
34268
34269      .funky-show-hide.ng-hide-remove.ng-hide-remove-active {
34270        transform: rotateY(0);
34271      }
34272
34273      .check-element {
34274        border: 1px solid black;
34275        opacity: 1;
34276        padding: 10px;
34277      }
34278    </file>
34279    <file name="protractor.js" type="protractor">
34280      it('should check ngShow', function() {
34281        var checkbox = element(by.model('checked'));
34282        var checkElem = element(by.css('.check-element'));
34283
34284        expect(checkElem.isDisplayed()).toBe(false);
34285        checkbox.click();
34286        expect(checkElem.isDisplayed()).toBe(true);
34287      });
34288    </file>
34289  </example>
34290 *
34291 * @knownIssue
34292 *
34293 * ### Flickering when using ngShow to toggle between elements
34294 *
34295 * When using {@link ngShow} and / or {@link ngHide} to toggle between elements, it can
34296 * happen that both the element to show and the element to hide are visible for a very short time.
34297 *
34298 * This usually happens when the {@link ngAnimate ngAnimate module} is included, but no actual animations
34299 * are defined for {@link ngShow} / {@link ngHide}. Internet Explorer is affected more often than
34300 * other browsers.
34301 *
34302 * There are several way to mitigate this problem:
34303 *
34304 * - {@link guide/animations#how-to-selectively-enable-disable-and-skip-animations Disable animations on the affected elements}.
34305 * - Use {@link ngIf} or {@link ngSwitch} instead of {@link ngShow} / {@link ngHide}.
34306 * - Use the special CSS selector `ng-hide.ng-hide-animate` to set `{display: none}` or similar on the affected elements.
34307 * - Use `ng-class="{'ng-hide': expression}` instead of instead of {@link ngShow} / {@link ngHide}.
34308 * - Define an animation on the affected elements.
34309 */
34310var ngShowDirective = ['$animate', function($animate) {
34311  return {
34312    restrict: 'A',
34313    multiElement: true,
34314    link: function(scope, element, attr) {
34315      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
34316        // we're adding a temporary, animation-specific class for ng-hide since this way
34317        // we can control when the element is actually displayed on screen without having
34318        // to have a global/greedy CSS selector that breaks when other animations are run.
34319        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
34320        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
34321          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
34322        });
34323      });
34324    }
34325  };
34326}];
34327
34328
34329/**
34330 * @ngdoc directive
34331 * @name ngHide
34332 * @multiElement
34333 *
34334 * @description
34335 * The `ngHide` directive shows or hides the given HTML element based on the expression provided to
34336 * the `ngHide` attribute.
34337 *
34338 * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
34339 * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
34340 * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
34341 * {@link ng.directive:ngCsp ngCsp}).
34342 *
34343 * ```html
34344 * <!-- when $scope.myValue is truthy (element is hidden) -->
34345 * <div ng-hide="myValue" class="ng-hide"></div>
34346 *
34347 * <!-- when $scope.myValue is falsy (element is visible) -->
34348 * <div ng-hide="myValue"></div>
34349 * ```
34350 *
34351 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added
34352 * to the class attribute on the element causing it to become hidden. When falsy, the `.ng-hide`
34353 * CSS class is removed from the element causing the element not to appear hidden.
34354 *
34355 * ## Why is `!important` used?
34356 *
34357 * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
34358 * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
34359 * simple as changing the display style on a HTML list item would make hidden elements appear
34360 * visible. This also becomes a bigger issue when dealing with CSS frameworks.
34361 *
34362 * By using `!important`, the show and hide behavior will work as expected despite any clash between
34363 * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
34364 * developer chooses to override the styling to change how to hide an element then it is just a
34365 * matter of using `!important` in their own CSS code.
34366 *
34367 * ### Overriding `.ng-hide`
34368 *
34369 * By default, the `.ng-hide` class will style the element with `display: none !important`. If you
34370 * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
34371 * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
34372 * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
34373 *
34374 * ```css
34375 * .ng-hide:not(.ng-hide-animate) {
34376 *   /&#42; These are just alternative ways of hiding an element &#42;/
34377 *   display: block!important;
34378 *   position: absolute;
34379 *   top: -9999px;
34380 *   left: -9999px;
34381 * }
34382 * ```
34383 *
34384 * By default you don't need to override in CSS anything and the animations will work around the
34385 * display style.
34386 *
34387 * @animations
34388 * | Animation                                           | Occurs                                                                                                     |
34389 * |-----------------------------------------------------|------------------------------------------------------------------------------------------------------------|
34390 * | {@link $animate#addClass addClass} `.ng-hide`       | After the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden.  |
34391 * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible. |
34392 *
34393 * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
34394 * directive expression is true and false. This system works like the animation system present with
34395 * `ngClass` except that you must also include the `!important` flag to override the display
34396 * property so that the elements are not actually hidden during the animation.
34397 *
34398 * ```css
34399 * /&#42; A working example can be found at the bottom of this page. &#42;/
34400 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
34401 *   transition: all 0.5s linear;
34402 * }
34403 *
34404 * .my-element.ng-hide-add { ... }
34405 * .my-element.ng-hide-add.ng-hide-add-active { ... }
34406 * .my-element.ng-hide-remove { ... }
34407 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
34408 * ```
34409 *
34410 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
34411 * to block during animation states - ngAnimate will automatically handle the style toggling for you.
34412 *
34413 * @element ANY
34414 * @param {expression} ngHide If the {@link guide/expression expression} is truthy/falsy then the
34415 *                            element is hidden/shown respectively.
34416 *
34417 * @example
34418 * A simple example, animating the element's opacity:
34419 *
34420  <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-simple">
34421    <file name="index.html">
34422      Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
34423      <div class="check-element animate-show-hide" ng-hide="checked">
34424        I hide when your checkbox is checked.
34425      </div>
34426    </file>
34427    <file name="animations.css">
34428      .animate-show-hide.ng-hide {
34429        opacity: 0;
34430      }
34431
34432      .animate-show-hide.ng-hide-add,
34433      .animate-show-hide.ng-hide-remove {
34434        transition: all linear 0.5s;
34435      }
34436
34437      .check-element {
34438        border: 1px solid black;
34439        opacity: 1;
34440        padding: 10px;
34441      }
34442    </file>
34443    <file name="protractor.js" type="protractor">
34444      it('should check ngHide', function() {
34445        var checkbox = element(by.model('checked'));
34446        var checkElem = element(by.css('.check-element'));
34447
34448        expect(checkElem.isDisplayed()).toBe(true);
34449        checkbox.click();
34450        expect(checkElem.isDisplayed()).toBe(false);
34451      });
34452    </file>
34453  </example>
34454 *
34455 * <hr />
34456 * @example
34457 * A more complex example, featuring different show/hide animations:
34458 *
34459  <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-complex">
34460    <file name="index.html">
34461      Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
34462      <div class="check-element funky-show-hide" ng-hide="checked">
34463        I hide when your checkbox is checked.
34464      </div>
34465    </file>
34466    <file name="animations.css">
34467      body {
34468        overflow: hidden;
34469        perspective: 1000px;
34470      }
34471
34472      .funky-show-hide.ng-hide-add {
34473        transform: rotateZ(0);
34474        transform-origin: right;
34475        transition: all 0.5s ease-in-out;
34476      }
34477
34478      .funky-show-hide.ng-hide-add.ng-hide-add-active {
34479        transform: rotateZ(-135deg);
34480      }
34481
34482      .funky-show-hide.ng-hide-remove {
34483        transform: rotateY(90deg);
34484        transform-origin: left;
34485        transition: all 0.5s ease;
34486      }
34487
34488      .funky-show-hide.ng-hide-remove.ng-hide-remove-active {
34489        transform: rotateY(0);
34490      }
34491
34492      .check-element {
34493        border: 1px solid black;
34494        opacity: 1;
34495        padding: 10px;
34496      }
34497    </file>
34498    <file name="protractor.js" type="protractor">
34499      it('should check ngHide', function() {
34500        var checkbox = element(by.model('checked'));
34501        var checkElem = element(by.css('.check-element'));
34502
34503        expect(checkElem.isDisplayed()).toBe(true);
34504        checkbox.click();
34505        expect(checkElem.isDisplayed()).toBe(false);
34506      });
34507    </file>
34508  </example>
34509 *
34510 * @knownIssue
34511 *
34512 * ### Flickering when using ngHide to toggle between elements
34513 *
34514 * When using {@link ngShow} and / or {@link ngHide} to toggle between elements, it can
34515 * happen that both the element to show and the element to hide are visible for a very short time.
34516 *
34517 * This usually happens when the {@link ngAnimate ngAnimate module} is included, but no actual animations
34518 * are defined for {@link ngShow} / {@link ngHide}. Internet Explorer is affected more often than
34519 * other browsers.
34520 *
34521 * There are several way to mitigate this problem:
34522 *
34523 * - {@link guide/animations#how-to-selectively-enable-disable-and-skip-animations Disable animations on the affected elements}.
34524 * - Use {@link ngIf} or {@link ngSwitch} instead of {@link ngShow} / {@link ngHide}.
34525 * - Use the special CSS selector `ng-hide.ng-hide-animate` to set `{display: none}` or similar on the affected elements.
34526 * - Use `ng-class="{'ng-hide': expression}` instead of instead of {@link ngShow} / {@link ngHide}.
34527 * - Define an animation on the affected elements.
34528 */
34529var ngHideDirective = ['$animate', function($animate) {
34530  return {
34531    restrict: 'A',
34532    multiElement: true,
34533    link: function(scope, element, attr) {
34534      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
34535        // The comment inside of the ngShowDirective explains why we add and
34536        // remove a temporary class for the show/hide animation
34537        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
34538          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
34539        });
34540      });
34541    }
34542  };
34543}];
34544
34545/**
34546 * @ngdoc directive
34547 * @name ngStyle
34548 * @restrict AC
34549 *
34550 * @description
34551 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
34552 *
34553 * @knownIssue
34554 * You should not use {@link guide/interpolation interpolation} in the value of the `style`
34555 * attribute, when using the `ngStyle` directive on the same element.
34556 * See {@link guide/interpolation#known-issues here} for more info.
34557 *
34558 * @element ANY
34559 * @param {expression} ngStyle
34560 *
34561 * {@link guide/expression Expression} which evals to an
34562 * object whose keys are CSS style names and values are corresponding values for those CSS
34563 * keys.
34564 *
34565 * Since some CSS style names are not valid keys for an object, they must be quoted.
34566 * See the 'background-color' style in the example below.
34567 *
34568 * @example
34569   <example name="ng-style">
34570     <file name="index.html">
34571        <input type="button" value="set color" ng-click="myStyle={color:'red'}">
34572        <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
34573        <input type="button" value="clear" ng-click="myStyle={}">
34574        <br/>
34575        <span ng-style="myStyle">Sample Text</span>
34576        <pre>myStyle={{myStyle}}</pre>
34577     </file>
34578     <file name="style.css">
34579       span {
34580         color: black;
34581       }
34582     </file>
34583     <file name="protractor.js" type="protractor">
34584       var colorSpan = element(by.css('span'));
34585
34586       it('should check ng-style', function() {
34587         expect(colorSpan.getCssValue('color')).toMatch(/rgba\(0, 0, 0, 1\)|rgb\(0, 0, 0\)/);
34588         element(by.css('input[value=\'set color\']')).click();
34589         expect(colorSpan.getCssValue('color')).toMatch(/rgba\(255, 0, 0, 1\)|rgb\(255, 0, 0\)/);
34590         element(by.css('input[value=clear]')).click();
34591         expect(colorSpan.getCssValue('color')).toMatch(/rgba\(0, 0, 0, 1\)|rgb\(0, 0, 0\)/);
34592       });
34593     </file>
34594   </example>
34595 */
34596var ngStyleDirective = ngDirective(function(scope, element, attr) {
34597  scope.$watchCollection(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
34598    if (oldStyles && (newStyles !== oldStyles)) {
34599      forEach(oldStyles, function(val, style) { element.css(style, ''); });
34600    }
34601    if (newStyles) element.css(newStyles);
34602  });
34603});
34604
34605/**
34606 * @ngdoc directive
34607 * @name ngSwitch
34608 * @restrict EA
34609 *
34610 * @description
34611 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
34612 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
34613 * as specified in the template.
34614 *
34615 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
34616 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
34617 * matches the value obtained from the evaluated expression. In other words, you define a container element
34618 * (where you place the directive), place an expression on the **`on="..."` attribute**
34619 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
34620 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
34621 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
34622 * attribute is displayed.
34623 *
34624 * <div class="alert alert-info">
34625 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
34626 * as literal string values to match against.
34627 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
34628 * value of the expression `$scope.someVal`.
34629 * </div>
34630
34631 * @animations
34632 * | Animation                        | Occurs                              |
34633 * |----------------------------------|-------------------------------------|
34634 * | {@link ng.$animate#enter enter}  | after the ngSwitch contents change and the matched child element is placed inside the container |
34635 * | {@link ng.$animate#leave leave}  | after the ngSwitch contents change and just before the former contents are removed from the DOM |
34636 *
34637 * @usage
34638 *
34639 * ```
34640 * <ANY ng-switch="expression">
34641 *   <ANY ng-switch-when="matchValue1">...</ANY>
34642 *   <ANY ng-switch-when="matchValue2">...</ANY>
34643 *   <ANY ng-switch-default>...</ANY>
34644 * </ANY>
34645 * ```
34646 *
34647 *
34648 * @scope
34649 * @priority 1200
34650 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
34651 * On child elements add:
34652 *
34653 * * `ngSwitchWhen`: the case statement to match against. If match then this
34654 *   case will be displayed. If the same match appears multiple times, all the
34655 *   elements will be displayed. It is possible to associate multiple values to
34656 *   the same `ngSwitchWhen` by defining the optional attribute
34657 *   `ngSwitchWhenSeparator`. The separator will be used to split the value of
34658 *   the `ngSwitchWhen` attribute into multiple tokens, and the element will show
34659 *   if any of the `ngSwitch` evaluates to any of these tokens.
34660 * * `ngSwitchDefault`: the default case when no other case match. If there
34661 *   are multiple default cases, all of them will be displayed when no other
34662 *   case match.
34663 *
34664 *
34665 * @example
34666  <example module="switchExample" deps="angular-animate.js" animations="true" name="ng-switch">
34667    <file name="index.html">
34668      <div ng-controller="ExampleController">
34669        <select ng-model="selection" ng-options="item for item in items">
34670        </select>
34671        <code>selection={{selection}}</code>
34672        <hr/>
34673        <div class="animate-switch-container"
34674          ng-switch on="selection">
34675            <div class="animate-switch" ng-switch-when="settings|options" ng-switch-when-separator="|">Settings Div</div>
34676            <div class="animate-switch" ng-switch-when="home">Home Span</div>
34677            <div class="animate-switch" ng-switch-default>default</div>
34678        </div>
34679      </div>
34680    </file>
34681    <file name="script.js">
34682      angular.module('switchExample', ['ngAnimate'])
34683        .controller('ExampleController', ['$scope', function($scope) {
34684          $scope.items = ['settings', 'home', 'options', 'other'];
34685          $scope.selection = $scope.items[0];
34686        }]);
34687    </file>
34688    <file name="animations.css">
34689      .animate-switch-container {
34690        position:relative;
34691        background:white;
34692        border:1px solid black;
34693        height:40px;
34694        overflow:hidden;
34695      }
34696
34697      .animate-switch {
34698        padding:10px;
34699      }
34700
34701      .animate-switch.ng-animate {
34702        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
34703
34704        position:absolute;
34705        top:0;
34706        left:0;
34707        right:0;
34708        bottom:0;
34709      }
34710
34711      .animate-switch.ng-leave.ng-leave-active,
34712      .animate-switch.ng-enter {
34713        top:-50px;
34714      }
34715      .animate-switch.ng-leave,
34716      .animate-switch.ng-enter.ng-enter-active {
34717        top:0;
34718      }
34719    </file>
34720    <file name="protractor.js" type="protractor">
34721      var switchElem = element(by.css('[ng-switch]'));
34722      var select = element(by.model('selection'));
34723
34724      it('should start in settings', function() {
34725        expect(switchElem.getText()).toMatch(/Settings Div/);
34726      });
34727      it('should change to home', function() {
34728        select.all(by.css('option')).get(1).click();
34729        expect(switchElem.getText()).toMatch(/Home Span/);
34730      });
34731      it('should change to settings via "options"', function() {
34732        select.all(by.css('option')).get(2).click();
34733        expect(switchElem.getText()).toMatch(/Settings Div/);
34734      });
34735      it('should select default', function() {
34736        select.all(by.css('option')).get(3).click();
34737        expect(switchElem.getText()).toMatch(/default/);
34738      });
34739    </file>
34740  </example>
34741 */
34742var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
34743  return {
34744    require: 'ngSwitch',
34745
34746    // asks for $scope to fool the BC controller module
34747    controller: ['$scope', function NgSwitchController() {
34748     this.cases = {};
34749    }],
34750    link: function(scope, element, attr, ngSwitchController) {
34751      var watchExpr = attr.ngSwitch || attr.on,
34752          selectedTranscludes = [],
34753          selectedElements = [],
34754          previousLeaveAnimations = [],
34755          selectedScopes = [];
34756
34757      var spliceFactory = function(array, index) {
34758          return function(response) {
34759            if (response !== false) array.splice(index, 1);
34760          };
34761      };
34762
34763      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
34764        var i, ii;
34765
34766        // Start with the last, in case the array is modified during the loop
34767        while (previousLeaveAnimations.length) {
34768          $animate.cancel(previousLeaveAnimations.pop());
34769        }
34770
34771        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
34772          var selected = getBlockNodes(selectedElements[i].clone);
34773          selectedScopes[i].$destroy();
34774          var runner = previousLeaveAnimations[i] = $animate.leave(selected);
34775          runner.done(spliceFactory(previousLeaveAnimations, i));
34776        }
34777
34778        selectedElements.length = 0;
34779        selectedScopes.length = 0;
34780
34781        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
34782          forEach(selectedTranscludes, function(selectedTransclude) {
34783            selectedTransclude.transclude(function(caseElement, selectedScope) {
34784              selectedScopes.push(selectedScope);
34785              var anchor = selectedTransclude.element;
34786              caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
34787              var block = { clone: caseElement };
34788
34789              selectedElements.push(block);
34790              $animate.enter(caseElement, anchor.parent(), anchor);
34791            });
34792          });
34793        }
34794      });
34795    }
34796  };
34797}];
34798
34799var ngSwitchWhenDirective = ngDirective({
34800  transclude: 'element',
34801  priority: 1200,
34802  require: '^ngSwitch',
34803  multiElement: true,
34804  link: function(scope, element, attrs, ctrl, $transclude) {
34805
34806    var cases = attrs.ngSwitchWhen.split(attrs.ngSwitchWhenSeparator).sort().filter(
34807      // Filter duplicate cases
34808      function(element, index, array) { return array[index - 1] !== element; }
34809    );
34810
34811    forEach(cases, function(whenCase) {
34812      ctrl.cases['!' + whenCase] = (ctrl.cases['!' + whenCase] || []);
34813      ctrl.cases['!' + whenCase].push({ transclude: $transclude, element: element });
34814    });
34815  }
34816});
34817
34818var ngSwitchDefaultDirective = ngDirective({
34819  transclude: 'element',
34820  priority: 1200,
34821  require: '^ngSwitch',
34822  multiElement: true,
34823  link: function(scope, element, attr, ctrl, $transclude) {
34824    ctrl.cases['?'] = (ctrl.cases['?'] || []);
34825    ctrl.cases['?'].push({ transclude: $transclude, element: element });
34826   }
34827});
34828
34829/**
34830 * @ngdoc directive
34831 * @name ngTransclude
34832 * @restrict EAC
34833 *
34834 * @description
34835 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
34836 *
34837 * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
34838 * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
34839 *
34840 * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
34841 * content of this element will be removed before the transcluded content is inserted.
34842 * If the transcluded content is empty (or only whitespace), the existing content is left intact. This lets you provide fallback
34843 * content in the case that no transcluded content is provided.
34844 *
34845 * @element ANY
34846 *
34847 * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
34848 *                                               or its value is the same as the name of the attribute then the default slot is used.
34849 *
34850 * @example
34851 * ### Basic transclusion
34852 * This example demonstrates basic transclusion of content into a component directive.
34853 * <example name="simpleTranscludeExample" module="transcludeExample">
34854 *   <file name="index.html">
34855 *     <script>
34856 *       angular.module('transcludeExample', [])
34857 *        .directive('pane', function(){
34858 *           return {
34859 *             restrict: 'E',
34860 *             transclude: true,
34861 *             scope: { title:'@' },
34862 *             template: '<div style="border: 1px solid black;">' +
34863 *                         '<div style="background-color: gray">{{title}}</div>' +
34864 *                         '<ng-transclude></ng-transclude>' +
34865 *                       '</div>'
34866 *           };
34867 *       })
34868 *       .controller('ExampleController', ['$scope', function($scope) {
34869 *         $scope.title = 'Lorem Ipsum';
34870 *         $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
34871 *       }]);
34872 *     </script>
34873 *     <div ng-controller="ExampleController">
34874 *       <input ng-model="title" aria-label="title"> <br/>
34875 *       <textarea ng-model="text" aria-label="text"></textarea> <br/>
34876 *       <pane title="{{title}}"><span>{{text}}</span></pane>
34877 *     </div>
34878 *   </file>
34879 *   <file name="protractor.js" type="protractor">
34880 *      it('should have transcluded', function() {
34881 *        var titleElement = element(by.model('title'));
34882 *        titleElement.clear();
34883 *        titleElement.sendKeys('TITLE');
34884 *        var textElement = element(by.model('text'));
34885 *        textElement.clear();
34886 *        textElement.sendKeys('TEXT');
34887 *        expect(element(by.binding('title')).getText()).toEqual('TITLE');
34888 *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
34889 *      });
34890 *   </file>
34891 * </example>
34892 *
34893 * @example
34894 * ### Transclude fallback content
34895 * This example shows how to use `NgTransclude` with fallback content, that
34896 * is displayed if no transcluded content is provided.
34897 *
34898 * <example module="transcludeFallbackContentExample" name="ng-transclude">
34899 * <file name="index.html">
34900 * <script>
34901 * angular.module('transcludeFallbackContentExample', [])
34902 * .directive('myButton', function(){
34903 *             return {
34904 *               restrict: 'E',
34905 *               transclude: true,
34906 *               scope: true,
34907 *               template: '<button style="cursor: pointer;">' +
34908 *                           '<ng-transclude>' +
34909 *                             '<b style="color: red;">Button1</b>' +
34910 *                           '</ng-transclude>' +
34911 *                         '</button>'
34912 *             };
34913 *         });
34914 * </script>
34915 * <!-- fallback button content -->
34916 * <my-button id="fallback"></my-button>
34917 * <!-- modified button content -->
34918 * <my-button id="modified">
34919 *   <i style="color: green;">Button2</i>
34920 * </my-button>
34921 * </file>
34922 * <file name="protractor.js" type="protractor">
34923 * it('should have different transclude element content', function() {
34924 *          expect(element(by.id('fallback')).getText()).toBe('Button1');
34925 *          expect(element(by.id('modified')).getText()).toBe('Button2');
34926 *        });
34927 * </file>
34928 * </example>
34929 *
34930 * @example
34931 * ### Multi-slot transclusion
34932 * This example demonstrates using multi-slot transclusion in a component directive.
34933 * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
34934 *   <file name="index.html">
34935 *    <style>
34936 *      .title, .footer {
34937 *        background-color: gray
34938 *      }
34939 *    </style>
34940 *    <div ng-controller="ExampleController">
34941 *      <input ng-model="title" aria-label="title"> <br/>
34942 *      <textarea ng-model="text" aria-label="text"></textarea> <br/>
34943 *      <pane>
34944 *        <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
34945 *        <pane-body><p>{{text}}</p></pane-body>
34946 *      </pane>
34947 *    </div>
34948 *   </file>
34949 *   <file name="app.js">
34950 *    angular.module('multiSlotTranscludeExample', [])
34951 *     .directive('pane', function() {
34952 *        return {
34953 *          restrict: 'E',
34954 *          transclude: {
34955 *            'title': '?paneTitle',
34956 *            'body': 'paneBody',
34957 *            'footer': '?paneFooter'
34958 *          },
34959 *          template: '<div style="border: 1px solid black;">' +
34960 *                      '<div class="title" ng-transclude="title">Fallback Title</div>' +
34961 *                      '<div ng-transclude="body"></div>' +
34962 *                      '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
34963 *                    '</div>'
34964 *        };
34965 *    })
34966 *    .controller('ExampleController', ['$scope', function($scope) {
34967 *      $scope.title = 'Lorem Ipsum';
34968 *      $scope.link = 'https://google.com';
34969 *      $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
34970 *    }]);
34971 *   </file>
34972 *   <file name="protractor.js" type="protractor">
34973 *      it('should have transcluded the title and the body', function() {
34974 *        var titleElement = element(by.model('title'));
34975 *        titleElement.clear();
34976 *        titleElement.sendKeys('TITLE');
34977 *        var textElement = element(by.model('text'));
34978 *        textElement.clear();
34979 *        textElement.sendKeys('TEXT');
34980 *        expect(element(by.css('.title')).getText()).toEqual('TITLE');
34981 *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
34982 *        expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
34983 *      });
34984 *   </file>
34985 * </example>
34986 */
34987var ngTranscludeMinErr = minErr('ngTransclude');
34988var ngTranscludeDirective = ['$compile', function($compile) {
34989  return {
34990    restrict: 'EAC',
34991    compile: function ngTranscludeCompile(tElement) {
34992
34993      // Remove and cache any original content to act as a fallback
34994      var fallbackLinkFn = $compile(tElement.contents());
34995      tElement.empty();
34996
34997      return function ngTranscludePostLink($scope, $element, $attrs, controller, $transclude) {
34998
34999        if (!$transclude) {
35000          throw ngTranscludeMinErr('orphan',
35001          'Illegal use of ngTransclude directive in the template! ' +
35002          'No parent directive that requires a transclusion found. ' +
35003          'Element: {0}',
35004          startingTag($element));
35005        }
35006
35007
35008        // If the attribute is of the form: `ng-transclude="ng-transclude"` then treat it like the default
35009        if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
35010          $attrs.ngTransclude = '';
35011        }
35012        var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
35013
35014        // If the slot is required and no transclusion content is provided then this call will throw an error
35015        $transclude(ngTranscludeCloneAttachFn, null, slotName);
35016
35017        // If the slot is optional and no transclusion content is provided then use the fallback content
35018        if (slotName && !$transclude.isSlotFilled(slotName)) {
35019          useFallbackContent();
35020        }
35021
35022        function ngTranscludeCloneAttachFn(clone, transcludedScope) {
35023          if (clone.length && notWhitespace(clone)) {
35024            $element.append(clone);
35025          } else {
35026            useFallbackContent();
35027            // There is nothing linked against the transcluded scope since no content was available,
35028            // so it should be safe to clean up the generated scope.
35029            transcludedScope.$destroy();
35030          }
35031        }
35032
35033        function useFallbackContent() {
35034          // Since this is the fallback content rather than the transcluded content,
35035          // we link against the scope of this directive rather than the transcluded scope
35036          fallbackLinkFn($scope, function(clone) {
35037            $element.append(clone);
35038          });
35039        }
35040
35041        function notWhitespace(nodes) {
35042          for (var i = 0, ii = nodes.length; i < ii; i++) {
35043            var node = nodes[i];
35044            if (node.nodeType !== NODE_TYPE_TEXT || node.nodeValue.trim()) {
35045              return true;
35046            }
35047          }
35048        }
35049      };
35050    }
35051  };
35052}];
35053
35054/**
35055 * @ngdoc directive
35056 * @name script
35057 * @restrict E
35058 *
35059 * @description
35060 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
35061 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
35062 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
35063 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
35064 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
35065 *
35066 * @param {string} type Must be set to `'text/ng-template'`.
35067 * @param {string} id Cache name of the template.
35068 *
35069 * @example
35070  <example  name="script-tag">
35071    <file name="index.html">
35072      <script type="text/ng-template" id="/tpl.html">
35073        Content of the template.
35074      </script>
35075
35076      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
35077      <div id="tpl-content" ng-include src="currentTpl"></div>
35078    </file>
35079    <file name="protractor.js" type="protractor">
35080      it('should load template defined inside script tag', function() {
35081        element(by.css('#tpl-link')).click();
35082        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
35083      });
35084    </file>
35085  </example>
35086 */
35087var scriptDirective = ['$templateCache', function($templateCache) {
35088  return {
35089    restrict: 'E',
35090    terminal: true,
35091    compile: function(element, attr) {
35092      if (attr.type === 'text/ng-template') {
35093        var templateUrl = attr.id,
35094            text = element[0].text;
35095
35096        $templateCache.put(templateUrl, text);
35097      }
35098    }
35099  };
35100}];
35101
35102/* exported selectDirective, optionDirective */
35103
35104var noopNgModelController = { $setViewValue: noop, $render: noop };
35105
35106function setOptionSelectedStatus(optionEl, value) {
35107  optionEl.prop('selected', value);
35108  /**
35109   * When unselecting an option, setting the property to null / false should be enough
35110   * However, screenreaders might react to the selected attribute instead, see
35111   * https://github.com/angular/angular.js/issues/14419
35112   * Note: "selected" is a boolean attr and will be removed when the "value" arg in attr() is false
35113   * or null
35114   */
35115  optionEl.attr('selected', value);
35116}
35117
35118/**
35119 * @ngdoc type
35120 * @name  select.SelectController
35121 *
35122 * @description
35123 * The controller for the {@link ng.select select} directive. The controller exposes
35124 * a few utility methods that can be used to augment the behavior of a regular or an
35125 * {@link ng.ngOptions ngOptions} select element.
35126 *
35127 * @example
35128 * ### Set a custom error when the unknown option is selected
35129 *
35130 * This example sets a custom error "unknownValue" on the ngModelController
35131 * when the select element's unknown option is selected, i.e. when the model is set to a value
35132 * that is not matched by any option.
35133 *
35134 * <example name="select-unknown-value-error" module="staticSelect">
35135 * <file name="index.html">
35136 * <div ng-controller="ExampleController">
35137 *   <form name="myForm">
35138 *     <label for="testSelect"> Single select: </label><br>
35139 *     <select name="testSelect" ng-model="selected" unknown-value-error>
35140 *       <option value="option-1">Option 1</option>
35141 *       <option value="option-2">Option 2</option>
35142 *     </select><br>
35143 *     <span class="error" ng-if="myForm.testSelect.$error.unknownValue">
35144 *       Error: The current model doesn't match any option</span><br>
35145 *
35146 *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
35147 *   </form>
35148 * </div>
35149 * </file>
35150 * <file name="app.js">
35151 *  angular.module('staticSelect', [])
35152 *    .controller('ExampleController', ['$scope', function($scope) {
35153 *      $scope.selected = null;
35154 *
35155 *      $scope.forceUnknownOption = function() {
35156 *        $scope.selected = 'nonsense';
35157 *      };
35158 *   }])
35159 *   .directive('unknownValueError', function() {
35160 *     return {
35161 *       require: ['ngModel', 'select'],
35162 *       link: function(scope, element, attrs, ctrls) {
35163 *         var ngModelCtrl = ctrls[0];
35164 *         var selectCtrl = ctrls[1];
35165 *
35166 *         ngModelCtrl.$validators.unknownValue = function(modelValue, viewValue) {
35167 *           if (selectCtrl.$isUnknownOptionSelected()) {
35168 *             return false;
35169 *           }
35170 *
35171 *           return true;
35172 *         };
35173 *       }
35174 *
35175 *     };
35176 *   });
35177 * </file>
35178 *</example>
35179 *
35180 *
35181 * @example
35182 * ### Set the "required" error when the unknown option is selected.
35183 *
35184 * By default, the "required" error on the ngModelController is only set on a required select
35185 * when the empty option is selected. This example adds a custom directive that also sets the
35186 * error when the unknown option is selected.
35187 *
35188 * <example name="select-unknown-value-required" module="staticSelect">
35189 * <file name="index.html">
35190 * <div ng-controller="ExampleController">
35191 *   <form name="myForm">
35192 *     <label for="testSelect"> Select: </label><br>
35193 *     <select name="testSelect" ng-model="selected" required unknown-value-required>
35194 *       <option value="option-1">Option 1</option>
35195 *       <option value="option-2">Option 2</option>
35196 *     </select><br>
35197 *     <span class="error" ng-if="myForm.testSelect.$error.required">Error: Please select a value</span><br>
35198 *
35199 *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
35200 *   </form>
35201 * </div>
35202 * </file>
35203 * <file name="app.js">
35204 *  angular.module('staticSelect', [])
35205 *    .controller('ExampleController', ['$scope', function($scope) {
35206 *      $scope.selected = null;
35207 *
35208 *      $scope.forceUnknownOption = function() {
35209 *        $scope.selected = 'nonsense';
35210 *      };
35211 *   }])
35212 *   .directive('unknownValueRequired', function() {
35213 *     return {
35214 *       priority: 1, // This directive must run after the required directive has added its validator
35215 *       require: ['ngModel', 'select'],
35216 *       link: function(scope, element, attrs, ctrls) {
35217 *         var ngModelCtrl = ctrls[0];
35218 *         var selectCtrl = ctrls[1];
35219 *
35220 *         var originalRequiredValidator = ngModelCtrl.$validators.required;
35221 *
35222 *         ngModelCtrl.$validators.required = function() {
35223 *           if (attrs.required && selectCtrl.$isUnknownOptionSelected()) {
35224 *             return false;
35225 *           }
35226 *
35227 *           return originalRequiredValidator.apply(this, arguments);
35228 *         };
35229 *       }
35230 *     };
35231 *   });
35232 * </file>
35233 * <file name="protractor.js" type="protractor">
35234 *  it('should show the error message when the unknown option is selected', function() {
35235
35236      var error = element(by.className('error'));
35237
35238      expect(error.getText()).toBe('Error: Please select a value');
35239
35240      element(by.cssContainingText('option', 'Option 1')).click();
35241
35242      expect(error.isPresent()).toBe(false);
35243
35244      element(by.tagName('button')).click();
35245
35246      expect(error.getText()).toBe('Error: Please select a value');
35247    });
35248 * </file>
35249 *</example>
35250 *
35251 *
35252 */
35253var SelectController =
35254        ['$element', '$scope', /** @this */ function($element, $scope) {
35255
35256  var self = this,
35257      optionsMap = new NgMap();
35258
35259  self.selectValueMap = {}; // Keys are the hashed values, values the original values
35260
35261  // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
35262  self.ngModelCtrl = noopNgModelController;
35263  self.multiple = false;
35264
35265  // The "unknown" option is one that is prepended to the list if the viewValue
35266  // does not match any of the options. When it is rendered the value of the unknown
35267  // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
35268  //
35269  // Support: IE 9 only
35270  // We can't just jqLite('<option>') since jqLite is not smart enough
35271  // to create it in <select> and IE barfs otherwise.
35272  self.unknownOption = jqLite(window.document.createElement('option'));
35273
35274  // The empty option is an option with the value '' that the application developer can
35275  // provide inside the select. It is always selectable and indicates that a "null" selection has
35276  // been made by the user.
35277  // If the select has an empty option, and the model of the select is set to "undefined" or "null",
35278  // the empty option is selected.
35279  // If the model is set to a different unmatched value, the unknown option is rendered and
35280  // selected, i.e both are present, because a "null" selection and an unknown value are different.
35281  self.hasEmptyOption = false;
35282  self.emptyOption = undefined;
35283
35284  self.renderUnknownOption = function(val) {
35285    var unknownVal = self.generateUnknownOptionValue(val);
35286    self.unknownOption.val(unknownVal);
35287    $element.prepend(self.unknownOption);
35288    setOptionSelectedStatus(self.unknownOption, true);
35289    $element.val(unknownVal);
35290  };
35291
35292  self.updateUnknownOption = function(val) {
35293    var unknownVal = self.generateUnknownOptionValue(val);
35294    self.unknownOption.val(unknownVal);
35295    setOptionSelectedStatus(self.unknownOption, true);
35296    $element.val(unknownVal);
35297  };
35298
35299  self.generateUnknownOptionValue = function(val) {
35300    return '? ' + hashKey(val) + ' ?';
35301  };
35302
35303  self.removeUnknownOption = function() {
35304    if (self.unknownOption.parent()) self.unknownOption.remove();
35305  };
35306
35307  self.selectEmptyOption = function() {
35308    if (self.emptyOption) {
35309      $element.val('');
35310      setOptionSelectedStatus(self.emptyOption, true);
35311    }
35312  };
35313
35314  self.unselectEmptyOption = function() {
35315    if (self.hasEmptyOption) {
35316      setOptionSelectedStatus(self.emptyOption, false);
35317    }
35318  };
35319
35320  $scope.$on('$destroy', function() {
35321    // disable unknown option so that we don't do work when the whole select is being destroyed
35322    self.renderUnknownOption = noop;
35323  });
35324
35325  // Read the value of the select control, the implementation of this changes depending
35326  // upon whether the select can have multiple values and whether ngOptions is at work.
35327  self.readValue = function readSingleValue() {
35328    var val = $element.val();
35329    // ngValue added option values are stored in the selectValueMap, normal interpolations are not
35330    var realVal = val in self.selectValueMap ? self.selectValueMap[val] : val;
35331
35332    if (self.hasOption(realVal)) {
35333      return realVal;
35334    }
35335
35336    return null;
35337  };
35338
35339
35340  // Write the value to the select control, the implementation of this changes depending
35341  // upon whether the select can have multiple values and whether ngOptions is at work.
35342  self.writeValue = function writeSingleValue(value) {
35343    // Make sure to remove the selected attribute from the previously selected option
35344    // Otherwise, screen readers might get confused
35345    var currentlySelectedOption = $element[0].options[$element[0].selectedIndex];
35346    if (currentlySelectedOption) setOptionSelectedStatus(jqLite(currentlySelectedOption), false);
35347
35348    if (self.hasOption(value)) {
35349      self.removeUnknownOption();
35350
35351      var hashedVal = hashKey(value);
35352      $element.val(hashedVal in self.selectValueMap ? hashedVal : value);
35353
35354      // Set selected attribute and property on selected option for screen readers
35355      var selectedOption = $element[0].options[$element[0].selectedIndex];
35356      setOptionSelectedStatus(jqLite(selectedOption), true);
35357    } else {
35358      self.selectUnknownOrEmptyOption(value);
35359    }
35360  };
35361
35362
35363  // Tell the select control that an option, with the given value, has been added
35364  self.addOption = function(value, element) {
35365    // Skip comment nodes, as they only pollute the `optionsMap`
35366    if (element[0].nodeType === NODE_TYPE_COMMENT) return;
35367
35368    assertNotHasOwnProperty(value, '"option value"');
35369    if (value === '') {
35370      self.hasEmptyOption = true;
35371      self.emptyOption = element;
35372    }
35373    var count = optionsMap.get(value) || 0;
35374    optionsMap.set(value, count + 1);
35375    // Only render at the end of a digest. This improves render performance when many options
35376    // are added during a digest and ensures all relevant options are correctly marked as selected
35377    scheduleRender();
35378  };
35379
35380  // Tell the select control that an option, with the given value, has been removed
35381  self.removeOption = function(value) {
35382    var count = optionsMap.get(value);
35383    if (count) {
35384      if (count === 1) {
35385        optionsMap.delete(value);
35386        if (value === '') {
35387          self.hasEmptyOption = false;
35388          self.emptyOption = undefined;
35389        }
35390      } else {
35391        optionsMap.set(value, count - 1);
35392      }
35393    }
35394  };
35395
35396  // Check whether the select control has an option matching the given value
35397  self.hasOption = function(value) {
35398    return !!optionsMap.get(value);
35399  };
35400
35401  /**
35402   * @ngdoc method
35403   * @name select.SelectController#$hasEmptyOption
35404   *
35405   * @description
35406   *
35407   * Returns `true` if the select element currently has an empty option
35408   * element, i.e. an option that signifies that the select is empty / the selection is null.
35409   *
35410   */
35411  self.$hasEmptyOption = function() {
35412    return self.hasEmptyOption;
35413  };
35414
35415  /**
35416   * @ngdoc method
35417   * @name select.SelectController#$isUnknownOptionSelected
35418   *
35419   * @description
35420   *
35421   * Returns `true` if the select element's unknown option is selected. The unknown option is added
35422   * and automatically selected whenever the select model doesn't match any option.
35423   *
35424   */
35425  self.$isUnknownOptionSelected = function() {
35426    // Presence of the unknown option means it is selected
35427    return $element[0].options[0] === self.unknownOption[0];
35428  };
35429
35430  /**
35431   * @ngdoc method
35432   * @name select.SelectController#$isEmptyOptionSelected
35433   *
35434   * @description
35435   *
35436   * Returns `true` if the select element has an empty option and this empty option is currently
35437   * selected. Returns `false` if the select element has no empty option or it is not selected.
35438   *
35439   */
35440  self.$isEmptyOptionSelected = function() {
35441    return self.hasEmptyOption && $element[0].options[$element[0].selectedIndex] === self.emptyOption[0];
35442  };
35443
35444  self.selectUnknownOrEmptyOption = function(value) {
35445    if (value == null && self.emptyOption) {
35446      self.removeUnknownOption();
35447      self.selectEmptyOption();
35448    } else if (self.unknownOption.parent().length) {
35449      self.updateUnknownOption(value);
35450    } else {
35451      self.renderUnknownOption(value);
35452    }
35453  };
35454
35455  var renderScheduled = false;
35456  function scheduleRender() {
35457    if (renderScheduled) return;
35458    renderScheduled = true;
35459    $scope.$$postDigest(function() {
35460      renderScheduled = false;
35461      self.ngModelCtrl.$render();
35462    });
35463  }
35464
35465  var updateScheduled = false;
35466  function scheduleViewValueUpdate(renderAfter) {
35467    if (updateScheduled) return;
35468
35469    updateScheduled = true;
35470
35471    $scope.$$postDigest(function() {
35472      if ($scope.$$destroyed) return;
35473
35474      updateScheduled = false;
35475      self.ngModelCtrl.$setViewValue(self.readValue());
35476      if (renderAfter) self.ngModelCtrl.$render();
35477    });
35478  }
35479
35480
35481  self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
35482
35483    if (optionAttrs.$attr.ngValue) {
35484      // The value attribute is set by ngValue
35485      var oldVal, hashedVal;
35486      optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
35487
35488        var removal;
35489        var previouslySelected = optionElement.prop('selected');
35490
35491        if (isDefined(hashedVal)) {
35492          self.removeOption(oldVal);
35493          delete self.selectValueMap[hashedVal];
35494          removal = true;
35495        }
35496
35497        hashedVal = hashKey(newVal);
35498        oldVal = newVal;
35499        self.selectValueMap[hashedVal] = newVal;
35500        self.addOption(newVal, optionElement);
35501        // Set the attribute directly instead of using optionAttrs.$set - this stops the observer
35502        // from firing a second time. Other $observers on value will also get the result of the
35503        // ngValue expression, not the hashed value
35504        optionElement.attr('value', hashedVal);
35505
35506        if (removal && previouslySelected) {
35507          scheduleViewValueUpdate();
35508        }
35509
35510      });
35511    } else if (interpolateValueFn) {
35512      // The value attribute is interpolated
35513      optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
35514        // This method is overwritten in ngOptions and has side-effects!
35515        self.readValue();
35516
35517        var removal;
35518        var previouslySelected = optionElement.prop('selected');
35519
35520        if (isDefined(oldVal)) {
35521          self.removeOption(oldVal);
35522          removal = true;
35523        }
35524        oldVal = newVal;
35525        self.addOption(newVal, optionElement);
35526
35527        if (removal && previouslySelected) {
35528          scheduleViewValueUpdate();
35529        }
35530      });
35531    } else if (interpolateTextFn) {
35532      // The text content is interpolated
35533      optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
35534        optionAttrs.$set('value', newVal);
35535        var previouslySelected = optionElement.prop('selected');
35536        if (oldVal !== newVal) {
35537          self.removeOption(oldVal);
35538        }
35539        self.addOption(newVal, optionElement);
35540
35541        if (oldVal && previouslySelected) {
35542          scheduleViewValueUpdate();
35543        }
35544      });
35545    } else {
35546      // The value attribute is static
35547      self.addOption(optionAttrs.value, optionElement);
35548    }
35549
35550
35551    optionAttrs.$observe('disabled', function(newVal) {
35552
35553      // Since model updates will also select disabled options (like ngOptions),
35554      // we only have to handle options becoming disabled, not enabled
35555
35556      if (newVal === 'true' || newVal && optionElement.prop('selected')) {
35557        if (self.multiple) {
35558          scheduleViewValueUpdate(true);
35559        } else {
35560          self.ngModelCtrl.$setViewValue(null);
35561          self.ngModelCtrl.$render();
35562        }
35563      }
35564    });
35565
35566    optionElement.on('$destroy', function() {
35567      var currentValue = self.readValue();
35568      var removeValue = optionAttrs.value;
35569
35570      self.removeOption(removeValue);
35571      scheduleRender();
35572
35573      if (self.multiple && currentValue && currentValue.indexOf(removeValue) !== -1 ||
35574          currentValue === removeValue
35575      ) {
35576        // When multiple (selected) options are destroyed at the same time, we don't want
35577        // to run a model update for each of them. Instead, run a single update in the $$postDigest
35578        scheduleViewValueUpdate(true);
35579      }
35580    });
35581  };
35582}];
35583
35584/**
35585 * @ngdoc directive
35586 * @name select
35587 * @restrict E
35588 *
35589 * @description
35590 * HTML `select` element with AngularJS data-binding.
35591 *
35592 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
35593 * between the scope and the `<select>` control (including setting default values).
35594 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
35595 * {@link ngOptions `ngOptions`} directives.
35596 *
35597 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
35598 * to the model identified by the `ngModel` directive. With static or repeated options, this is
35599 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
35600 * Value and textContent can be interpolated.
35601 *
35602 * The {@link select.SelectController select controller} exposes utility functions that can be used
35603 * to manipulate the select's behavior.
35604 *
35605 * ## Matching model and option values
35606 *
35607 * In general, the match between the model and an option is evaluated by strictly comparing the model
35608 * value against the value of the available options.
35609 *
35610 * If you are setting the option value with the option's `value` attribute, or textContent, the
35611 * value will always be a `string` which means that the model value must also be a string.
35612 * Otherwise the `select` directive cannot match them correctly.
35613 *
35614 * To bind the model to a non-string value, you can use one of the following strategies:
35615 * - the {@link ng.ngOptions `ngOptions`} directive
35616 *   ({@link ng.select#using-select-with-ngoptions-and-setting-a-default-value})
35617 * - the {@link ng.ngValue `ngValue`} directive, which allows arbitrary expressions to be
35618 *   option values ({@link ng.select#using-ngvalue-to-bind-the-model-to-an-array-of-objects Example})
35619 * - model $parsers / $formatters to convert the string value
35620 *   ({@link ng.select#binding-select-to-a-non-string-value-via-ngmodel-parsing-formatting Example})
35621 *
35622 * If the viewValue of `ngModel` does not match any of the options, then the control
35623 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
35624 *
35625 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
35626 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
35627 * option. See example below for demonstration.
35628 *
35629 * ## Choosing between `ngRepeat` and `ngOptions`
35630 *
35631 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
35632 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits:
35633 * - more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
35634 * comprehension expression
35635 * - reduced memory consumption by not creating a new scope for each repeated instance
35636 * - increased render speed by creating the options in a documentFragment instead of individually
35637 *
35638 * Specifically, select with repeated options slows down significantly starting at 2000 options in
35639 * Chrome and Internet Explorer / Edge.
35640 *
35641 *
35642 * @param {string} ngModel Assignable AngularJS expression to data-bind to.
35643 * @param {string=} name Property name of the form under which the control is published.
35644 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
35645 *     bound to the model as an array.
35646 * @param {string=} required Sets `required` validation error key if the value is not entered.
35647 * @param {string=} ngRequired Adds required attribute and required validation constraint to
35648 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
35649 * when you want to data-bind to the required attribute.
35650 * @param {string=} ngChange AngularJS expression to be executed when selected option(s) changes due to user
35651 *    interaction with the select element.
35652 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
35653 * set on the model on selection. See {@link ngOptions `ngOptions`}.
35654 * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
35655 * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
35656 *
35657 *
35658 * @example
35659 * ### Simple `select` elements with static options
35660 *
35661 * <example name="static-select" module="staticSelect">
35662 * <file name="index.html">
35663 * <div ng-controller="ExampleController">
35664 *   <form name="myForm">
35665 *     <label for="singleSelect"> Single select: </label><br>
35666 *     <select name="singleSelect" ng-model="data.singleSelect">
35667 *       <option value="option-1">Option 1</option>
35668 *       <option value="option-2">Option 2</option>
35669 *     </select><br>
35670 *
35671 *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
35672 *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
35673 *       <option value="">---Please select---</option> <!-- not selected / blank option -->
35674 *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
35675 *       <option value="option-2">Option 2</option>
35676 *     </select><br>
35677 *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
35678 *     <tt>singleSelect = {{data.singleSelect}}</tt>
35679 *
35680 *     <hr>
35681 *     <label for="multipleSelect"> Multiple select: </label><br>
35682 *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
35683 *       <option value="option-1">Option 1</option>
35684 *       <option value="option-2">Option 2</option>
35685 *       <option value="option-3">Option 3</option>
35686 *     </select><br>
35687 *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
35688 *   </form>
35689 * </div>
35690 * </file>
35691 * <file name="app.js">
35692 *  angular.module('staticSelect', [])
35693 *    .controller('ExampleController', ['$scope', function($scope) {
35694 *      $scope.data = {
35695 *       singleSelect: null,
35696 *       multipleSelect: [],
35697 *       option1: 'option-1'
35698 *      };
35699 *
35700 *      $scope.forceUnknownOption = function() {
35701 *        $scope.data.singleSelect = 'nonsense';
35702 *      };
35703 *   }]);
35704 * </file>
35705 *</example>
35706 *
35707 * @example
35708 * ### Using `ngRepeat` to generate `select` options
35709 * <example name="select-ngrepeat" module="ngrepeatSelect">
35710 * <file name="index.html">
35711 * <div ng-controller="ExampleController">
35712 *   <form name="myForm">
35713 *     <label for="repeatSelect"> Repeat select: </label>
35714 *     <select name="repeatSelect" id="repeatSelect" ng-model="data.model">
35715 *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
35716 *     </select>
35717 *   </form>
35718 *   <hr>
35719 *   <tt>model = {{data.model}}</tt><br/>
35720 * </div>
35721 * </file>
35722 * <file name="app.js">
35723 *  angular.module('ngrepeatSelect', [])
35724 *    .controller('ExampleController', ['$scope', function($scope) {
35725 *      $scope.data = {
35726 *       model: null,
35727 *       availableOptions: [
35728 *         {id: '1', name: 'Option A'},
35729 *         {id: '2', name: 'Option B'},
35730 *         {id: '3', name: 'Option C'}
35731 *       ]
35732 *      };
35733 *   }]);
35734 * </file>
35735 *</example>
35736 *
35737 * @example
35738 * ### Using `ngValue` to bind the model to an array of objects
35739 * <example name="select-ngvalue" module="ngvalueSelect">
35740 * <file name="index.html">
35741 * <div ng-controller="ExampleController">
35742 *   <form name="myForm">
35743 *     <label for="ngvalueselect"> ngvalue select: </label>
35744 *     <select size="6" name="ngvalueselect" ng-model="data.model" multiple>
35745 *       <option ng-repeat="option in data.availableOptions" ng-value="option.value">{{option.name}}</option>
35746 *     </select>
35747 *   </form>
35748 *   <hr>
35749 *   <pre>model = {{data.model | json}}</pre><br/>
35750 * </div>
35751 * </file>
35752 * <file name="app.js">
35753 *  angular.module('ngvalueSelect', [])
35754 *    .controller('ExampleController', ['$scope', function($scope) {
35755 *      $scope.data = {
35756 *       model: null,
35757 *       availableOptions: [
35758           {value: 'myString', name: 'string'},
35759           {value: 1, name: 'integer'},
35760           {value: true, name: 'boolean'},
35761           {value: null, name: 'null'},
35762           {value: {prop: 'value'}, name: 'object'},
35763           {value: ['a'], name: 'array'}
35764 *       ]
35765 *      };
35766 *   }]);
35767 * </file>
35768 *</example>
35769 *
35770 * @example
35771 * ### Using `select` with `ngOptions` and setting a default value
35772 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
35773 *
35774 * <example name="select-with-default-values" module="defaultValueSelect">
35775 * <file name="index.html">
35776 * <div ng-controller="ExampleController">
35777 *   <form name="myForm">
35778 *     <label for="mySelect">Make a choice:</label>
35779 *     <select name="mySelect" id="mySelect"
35780 *       ng-options="option.name for option in data.availableOptions track by option.id"
35781 *       ng-model="data.selectedOption"></select>
35782 *   </form>
35783 *   <hr>
35784 *   <tt>option = {{data.selectedOption}}</tt><br/>
35785 * </div>
35786 * </file>
35787 * <file name="app.js">
35788 *  angular.module('defaultValueSelect', [])
35789 *    .controller('ExampleController', ['$scope', function($scope) {
35790 *      $scope.data = {
35791 *       availableOptions: [
35792 *         {id: '1', name: 'Option A'},
35793 *         {id: '2', name: 'Option B'},
35794 *         {id: '3', name: 'Option C'}
35795 *       ],
35796 *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
35797 *       };
35798 *   }]);
35799 * </file>
35800 *</example>
35801 *
35802 * @example
35803 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
35804 *
35805 * <example name="select-with-non-string-options" module="nonStringSelect">
35806 *   <file name="index.html">
35807 *     <select ng-model="model.id" convert-to-number>
35808 *       <option value="0">Zero</option>
35809 *       <option value="1">One</option>
35810 *       <option value="2">Two</option>
35811 *     </select>
35812 *     {{ model }}
35813 *   </file>
35814 *   <file name="app.js">
35815 *     angular.module('nonStringSelect', [])
35816 *       .run(function($rootScope) {
35817 *         $rootScope.model = { id: 2 };
35818 *       })
35819 *       .directive('convertToNumber', function() {
35820 *         return {
35821 *           require: 'ngModel',
35822 *           link: function(scope, element, attrs, ngModel) {
35823 *             ngModel.$parsers.push(function(val) {
35824 *               return parseInt(val, 10);
35825 *             });
35826 *             ngModel.$formatters.push(function(val) {
35827 *               return '' + val;
35828 *             });
35829 *           }
35830 *         };
35831 *       });
35832 *   </file>
35833 *   <file name="protractor.js" type="protractor">
35834 *     it('should initialize to model', function() {
35835 *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
35836 *     });
35837 *   </file>
35838 * </example>
35839 *
35840 */
35841var selectDirective = function() {
35842
35843  return {
35844    restrict: 'E',
35845    require: ['select', '?ngModel'],
35846    controller: SelectController,
35847    priority: 1,
35848    link: {
35849      pre: selectPreLink,
35850      post: selectPostLink
35851    }
35852  };
35853
35854  function selectPreLink(scope, element, attr, ctrls) {
35855
35856      var selectCtrl = ctrls[0];
35857      var ngModelCtrl = ctrls[1];
35858
35859      // if ngModel is not defined, we don't need to do anything but set the registerOption
35860      // function to noop, so options don't get added internally
35861      if (!ngModelCtrl) {
35862        selectCtrl.registerOption = noop;
35863        return;
35864      }
35865
35866
35867      selectCtrl.ngModelCtrl = ngModelCtrl;
35868
35869      // When the selected item(s) changes we delegate getting the value of the select control
35870      // to the `readValue` method, which can be changed if the select can have multiple
35871      // selected values or if the options are being generated by `ngOptions`
35872      element.on('change', function() {
35873        selectCtrl.removeUnknownOption();
35874        scope.$apply(function() {
35875          ngModelCtrl.$setViewValue(selectCtrl.readValue());
35876        });
35877      });
35878
35879      // If the select allows multiple values then we need to modify how we read and write
35880      // values from and to the control; also what it means for the value to be empty and
35881      // we have to add an extra watch since ngModel doesn't work well with arrays - it
35882      // doesn't trigger rendering if only an item in the array changes.
35883      if (attr.multiple) {
35884        selectCtrl.multiple = true;
35885
35886        // Read value now needs to check each option to see if it is selected
35887        selectCtrl.readValue = function readMultipleValue() {
35888          var array = [];
35889          forEach(element.find('option'), function(option) {
35890            if (option.selected && !option.disabled) {
35891              var val = option.value;
35892              array.push(val in selectCtrl.selectValueMap ? selectCtrl.selectValueMap[val] : val);
35893            }
35894          });
35895          return array;
35896        };
35897
35898        // Write value now needs to set the selected property of each matching option
35899        selectCtrl.writeValue = function writeMultipleValue(value) {
35900          forEach(element.find('option'), function(option) {
35901            var shouldBeSelected = !!value && (includes(value, option.value) ||
35902                                               includes(value, selectCtrl.selectValueMap[option.value]));
35903            var currentlySelected = option.selected;
35904
35905            // Support: IE 9-11 only, Edge 12-15+
35906            // In IE and Edge adding options to the selection via shift+click/UP/DOWN
35907            // will de-select already selected options if "selected" on those options was set
35908            // more than once (i.e. when the options were already selected)
35909            // So we only modify the selected property if necessary.
35910            // Note: this behavior cannot be replicated via unit tests because it only shows in the
35911            // actual user interface.
35912            if (shouldBeSelected !== currentlySelected) {
35913              setOptionSelectedStatus(jqLite(option), shouldBeSelected);
35914            }
35915
35916          });
35917        };
35918
35919        // we have to do it on each watch since ngModel watches reference, but
35920        // we need to work of an array, so we need to see if anything was inserted/removed
35921        var lastView, lastViewRef = NaN;
35922        scope.$watch(function selectMultipleWatch() {
35923          if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
35924            lastView = shallowCopy(ngModelCtrl.$viewValue);
35925            ngModelCtrl.$render();
35926          }
35927          lastViewRef = ngModelCtrl.$viewValue;
35928        });
35929
35930        // If we are a multiple select then value is now a collection
35931        // so the meaning of $isEmpty changes
35932        ngModelCtrl.$isEmpty = function(value) {
35933          return !value || value.length === 0;
35934        };
35935
35936      }
35937    }
35938
35939    function selectPostLink(scope, element, attrs, ctrls) {
35940      // if ngModel is not defined, we don't need to do anything
35941      var ngModelCtrl = ctrls[1];
35942      if (!ngModelCtrl) return;
35943
35944      var selectCtrl = ctrls[0];
35945
35946      // We delegate rendering to the `writeValue` method, which can be changed
35947      // if the select can have multiple selected values or if the options are being
35948      // generated by `ngOptions`.
35949      // This must be done in the postLink fn to prevent $render to be called before
35950      // all nodes have been linked correctly.
35951      ngModelCtrl.$render = function() {
35952        selectCtrl.writeValue(ngModelCtrl.$viewValue);
35953      };
35954    }
35955};
35956
35957
35958// The option directive is purely designed to communicate the existence (or lack of)
35959// of dynamically created (and destroyed) option elements to their containing select
35960// directive via its controller.
35961var optionDirective = ['$interpolate', function($interpolate) {
35962  return {
35963    restrict: 'E',
35964    priority: 100,
35965    compile: function(element, attr) {
35966      var interpolateValueFn, interpolateTextFn;
35967
35968      if (isDefined(attr.ngValue)) {
35969        // Will be handled by registerOption
35970      } else if (isDefined(attr.value)) {
35971        // If the value attribute is defined, check if it contains an interpolation
35972        interpolateValueFn = $interpolate(attr.value, true);
35973      } else {
35974        // If the value attribute is not defined then we fall back to the
35975        // text content of the option element, which may be interpolated
35976        interpolateTextFn = $interpolate(element.text(), true);
35977        if (!interpolateTextFn) {
35978          attr.$set('value', element.text());
35979        }
35980      }
35981
35982      return function(scope, element, attr) {
35983        // This is an optimization over using ^^ since we don't want to have to search
35984        // all the way to the root of the DOM for every single option element
35985        var selectCtrlName = '$selectController',
35986            parent = element.parent(),
35987            selectCtrl = parent.data(selectCtrlName) ||
35988              parent.parent().data(selectCtrlName); // in case we are in optgroup
35989
35990        if (selectCtrl) {
35991          selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
35992        }
35993      };
35994    }
35995  };
35996}];
35997
35998/**
35999 * @ngdoc directive
36000 * @name ngRequired
36001 * @restrict A
36002 *
36003 * @param {expression} ngRequired AngularJS expression. If it evaluates to `true`, it sets the
36004 *                                `required` attribute to the element and adds the `required`
36005 *                                {@link ngModel.NgModelController#$validators `validator`}.
36006 *
36007 * @description
36008 *
36009 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
36010 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
36011 * applied to custom controls.
36012 *
36013 * The directive sets the `required` attribute on the element if the AngularJS expression inside
36014 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
36015 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
36016 * for more info.
36017 *
36018 * The validator will set the `required` error key to true if the `required` attribute is set and
36019 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
36020 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
36021 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
36022 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
36023 *
36024 * @example
36025 * <example name="ngRequiredDirective" module="ngRequiredExample">
36026 *   <file name="index.html">
36027 *     <script>
36028 *       angular.module('ngRequiredExample', [])
36029 *         .controller('ExampleController', ['$scope', function($scope) {
36030 *           $scope.required = true;
36031 *         }]);
36032 *     </script>
36033 *     <div ng-controller="ExampleController">
36034 *       <form name="form">
36035 *         <label for="required">Toggle required: </label>
36036 *         <input type="checkbox" ng-model="required" id="required" />
36037 *         <br>
36038 *         <label for="input">This input must be filled if `required` is true: </label>
36039 *         <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
36040 *         <hr>
36041 *         required error set? = <code>{{form.input.$error.required}}</code><br>
36042 *         model = <code>{{model}}</code>
36043 *       </form>
36044 *     </div>
36045 *   </file>
36046 *   <file name="protractor.js" type="protractor">
36047       var required = element(by.binding('form.input.$error.required'));
36048       var model = element(by.binding('model'));
36049       var input = element(by.id('input'));
36050
36051       it('should set the required error', function() {
36052         expect(required.getText()).toContain('true');
36053
36054         input.sendKeys('123');
36055         expect(required.getText()).not.toContain('true');
36056         expect(model.getText()).toContain('123');
36057       });
36058 *   </file>
36059 * </example>
36060 */
36061var requiredDirective = ['$parse', function($parse) {
36062  return {
36063    restrict: 'A',
36064    require: '?ngModel',
36065    link: function(scope, elm, attr, ctrl) {
36066      if (!ctrl) return;
36067      // For boolean attributes like required, presence means true
36068      var value = attr.hasOwnProperty('required') || $parse(attr.ngRequired)(scope);
36069
36070      if (!attr.ngRequired) {
36071        // force truthy in case we are on non input element
36072        // (input elements do this automatically for boolean attributes like required)
36073        attr.required = true;
36074      }
36075
36076      ctrl.$validators.required = function(modelValue, viewValue) {
36077        return !value || !ctrl.$isEmpty(viewValue);
36078      };
36079
36080      attr.$observe('required', function(newVal) {
36081
36082        if (value !== newVal) {
36083          value = newVal;
36084          ctrl.$validate();
36085        }
36086      });
36087    }
36088  };
36089}];
36090
36091/**
36092 * @ngdoc directive
36093 * @name ngPattern
36094 * @restrict A
36095 *
36096 * @param {expression|RegExp} ngPattern AngularJS expression that must evaluate to a `RegExp` or a `String`
36097 *                                      parsable into a `RegExp`, or a `RegExp` literal. See above for
36098 *                                      more details.
36099 *
36100 * @description
36101 *
36102 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
36103 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
36104 *
36105 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
36106 * does not match a RegExp which is obtained from the `ngPattern` attribute value:
36107 * - the value is an AngularJS expression:
36108 *   - If the expression evaluates to a RegExp object, then this is used directly.
36109 *   - If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
36110 *     in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
36111 * - If the value is a RegExp literal, e.g. `ngPattern="/^\d+$/"`, it is used directly.
36112 *
36113 * <div class="alert alert-info">
36114 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
36115 * start at the index of the last search's match, thus not taking the whole input value into
36116 * account.
36117 * </div>
36118 *
36119 * <div class="alert alert-info">
36120 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
36121 * differences:
36122 * <ol>
36123 *   <li>
36124 *     `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
36125 *     not available.
36126 *   </li>
36127 *   <li>
36128 *     The `ngPattern` attribute must be an expression, while the `pattern` value must be
36129 *     interpolated.
36130 *   </li>
36131 * </ol>
36132 * </div>
36133 *
36134 * @example
36135 * <example name="ngPatternDirective" module="ngPatternExample">
36136 *   <file name="index.html">
36137 *     <script>
36138 *       angular.module('ngPatternExample', [])
36139 *         .controller('ExampleController', ['$scope', function($scope) {
36140 *           $scope.regex = '\\d+';
36141 *         }]);
36142 *     </script>
36143 *     <div ng-controller="ExampleController">
36144 *       <form name="form">
36145 *         <label for="regex">Set a pattern (regex string): </label>
36146 *         <input type="text" ng-model="regex" id="regex" />
36147 *         <br>
36148 *         <label for="input">This input is restricted by the current pattern: </label>
36149 *         <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
36150 *         <hr>
36151 *         input valid? = <code>{{form.input.$valid}}</code><br>
36152 *         model = <code>{{model}}</code>
36153 *       </form>
36154 *     </div>
36155 *   </file>
36156 *   <file name="protractor.js" type="protractor">
36157       var model = element(by.binding('model'));
36158       var input = element(by.id('input'));
36159
36160       it('should validate the input with the default pattern', function() {
36161         input.sendKeys('aaa');
36162         expect(model.getText()).not.toContain('aaa');
36163
36164         input.clear().then(function() {
36165           input.sendKeys('123');
36166           expect(model.getText()).toContain('123');
36167         });
36168       });
36169 *   </file>
36170 * </example>
36171 */
36172var patternDirective = ['$parse', function($parse) {
36173  return {
36174    restrict: 'A',
36175    require: '?ngModel',
36176    compile: function(tElm, tAttr) {
36177      var patternExp;
36178      var parseFn;
36179
36180      if (tAttr.ngPattern) {
36181        patternExp = tAttr.ngPattern;
36182
36183        // ngPattern might be a scope expression, or an inlined regex, which is not parsable.
36184        // We get value of the attribute here, so we can compare the old and the new value
36185        // in the observer to avoid unnecessary validations
36186        if (tAttr.ngPattern.charAt(0) === '/' && REGEX_STRING_REGEXP.test(tAttr.ngPattern)) {
36187          parseFn = function() { return tAttr.ngPattern; };
36188        } else {
36189          parseFn = $parse(tAttr.ngPattern);
36190        }
36191      }
36192
36193      return function(scope, elm, attr, ctrl) {
36194        if (!ctrl) return;
36195
36196        var attrVal = attr.pattern;
36197
36198        if (attr.ngPattern) {
36199          attrVal = parseFn(scope);
36200        } else {
36201          patternExp = attr.pattern;
36202        }
36203
36204        var regexp = parsePatternAttr(attrVal, patternExp, elm);
36205
36206        attr.$observe('pattern', function(newVal) {
36207          var oldRegexp = regexp;
36208
36209          regexp = parsePatternAttr(newVal, patternExp, elm);
36210
36211          if ((oldRegexp && oldRegexp.toString()) !== (regexp && regexp.toString())) {
36212            ctrl.$validate();
36213          }
36214        });
36215
36216        ctrl.$validators.pattern = function(modelValue, viewValue) {
36217          // HTML5 pattern constraint validates the input value, so we validate the viewValue
36218          return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
36219        };
36220      };
36221    }
36222
36223  };
36224}];
36225
36226/**
36227 * @ngdoc directive
36228 * @name ngMaxlength
36229 * @restrict A
36230 *
36231 * @param {expression} ngMaxlength AngularJS expression that must evaluate to a `Number` or `String`
36232 *                                 parsable into a `Number`. Used as value for the `maxlength`
36233 *                                 {@link ngModel.NgModelController#$validators validator}.
36234 *
36235 * @description
36236 *
36237 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
36238 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
36239 *
36240 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
36241 * is longer than the integer obtained by evaluating the AngularJS expression given in the
36242 * `ngMaxlength` attribute value.
36243 *
36244 * <div class="alert alert-info">
36245 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
36246 * differences:
36247 * <ol>
36248 *   <li>
36249 *     `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
36250 *     validation is not available.
36251 *   </li>
36252 *   <li>
36253 *     The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
36254 *     interpolated.
36255 *   </li>
36256 * </ol>
36257 * </div>
36258 *
36259 * @example
36260 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
36261 *   <file name="index.html">
36262 *     <script>
36263 *       angular.module('ngMaxlengthExample', [])
36264 *         .controller('ExampleController', ['$scope', function($scope) {
36265 *           $scope.maxlength = 5;
36266 *         }]);
36267 *     </script>
36268 *     <div ng-controller="ExampleController">
36269 *       <form name="form">
36270 *         <label for="maxlength">Set a maxlength: </label>
36271 *         <input type="number" ng-model="maxlength" id="maxlength" />
36272 *         <br>
36273 *         <label for="input">This input is restricted by the current maxlength: </label>
36274 *         <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
36275 *         <hr>
36276 *         input valid? = <code>{{form.input.$valid}}</code><br>
36277 *         model = <code>{{model}}</code>
36278 *       </form>
36279 *     </div>
36280 *   </file>
36281 *   <file name="protractor.js" type="protractor">
36282       var model = element(by.binding('model'));
36283       var input = element(by.id('input'));
36284
36285       it('should validate the input with the default maxlength', function() {
36286         input.sendKeys('abcdef');
36287         expect(model.getText()).not.toContain('abcdef');
36288
36289         input.clear().then(function() {
36290           input.sendKeys('abcde');
36291           expect(model.getText()).toContain('abcde');
36292         });
36293       });
36294 *   </file>
36295 * </example>
36296 */
36297var maxlengthDirective = ['$parse', function($parse) {
36298  return {
36299    restrict: 'A',
36300    require: '?ngModel',
36301    link: function(scope, elm, attr, ctrl) {
36302      if (!ctrl) return;
36303
36304      var maxlength = attr.maxlength || $parse(attr.ngMaxlength)(scope);
36305      var maxlengthParsed = parseLength(maxlength);
36306
36307      attr.$observe('maxlength', function(value) {
36308        if (maxlength !== value) {
36309          maxlengthParsed = parseLength(value);
36310          maxlength = value;
36311          ctrl.$validate();
36312        }
36313      });
36314      ctrl.$validators.maxlength = function(modelValue, viewValue) {
36315        return (maxlengthParsed < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlengthParsed);
36316      };
36317    }
36318  };
36319}];
36320
36321/**
36322 * @ngdoc directive
36323 * @name ngMinlength
36324 * @restrict A
36325 *
36326 * @param {expression} ngMinlength AngularJS expression that must evaluate to a `Number` or `String`
36327 *                                 parsable into a `Number`. Used as value for the `minlength`
36328 *                                 {@link ngModel.NgModelController#$validators validator}.
36329 *
36330 * @description
36331 *
36332 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
36333 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
36334 *
36335 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
36336 * is shorter than the integer obtained by evaluating the AngularJS expression given in the
36337 * `ngMinlength` attribute value.
36338 *
36339 * <div class="alert alert-info">
36340 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
36341 * differences:
36342 * <ol>
36343 *   <li>
36344 *     `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
36345 *     validation is not available.
36346 *   </li>
36347 *   <li>
36348 *     The `ngMinlength` value must be an expression, while the `minlength` value must be
36349 *     interpolated.
36350 *   </li>
36351 * </ol>
36352 * </div>
36353 *
36354 * @example
36355 * <example name="ngMinlengthDirective" module="ngMinlengthExample">
36356 *   <file name="index.html">
36357 *     <script>
36358 *       angular.module('ngMinlengthExample', [])
36359 *         .controller('ExampleController', ['$scope', function($scope) {
36360 *           $scope.minlength = 3;
36361 *         }]);
36362 *     </script>
36363 *     <div ng-controller="ExampleController">
36364 *       <form name="form">
36365 *         <label for="minlength">Set a minlength: </label>
36366 *         <input type="number" ng-model="minlength" id="minlength" />
36367 *         <br>
36368 *         <label for="input">This input is restricted by the current minlength: </label>
36369 *         <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
36370 *         <hr>
36371 *         input valid? = <code>{{form.input.$valid}}</code><br>
36372 *         model = <code>{{model}}</code>
36373 *       </form>
36374 *     </div>
36375 *   </file>
36376 *   <file name="protractor.js" type="protractor">
36377       var model = element(by.binding('model'));
36378       var input = element(by.id('input'));
36379
36380       it('should validate the input with the default minlength', function() {
36381         input.sendKeys('ab');
36382         expect(model.getText()).not.toContain('ab');
36383
36384         input.sendKeys('abc');
36385         expect(model.getText()).toContain('abc');
36386       });
36387 *   </file>
36388 * </example>
36389 */
36390var minlengthDirective = ['$parse', function($parse) {
36391  return {
36392    restrict: 'A',
36393    require: '?ngModel',
36394    link: function(scope, elm, attr, ctrl) {
36395      if (!ctrl) return;
36396
36397      var minlength = attr.minlength || $parse(attr.ngMinlength)(scope);
36398      var minlengthParsed = parseLength(minlength) || -1;
36399
36400      attr.$observe('minlength', function(value) {
36401        if (minlength !== value) {
36402          minlengthParsed = parseLength(value) || -1;
36403          minlength = value;
36404          ctrl.$validate();
36405        }
36406
36407      });
36408      ctrl.$validators.minlength = function(modelValue, viewValue) {
36409        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlengthParsed;
36410      };
36411    }
36412  };
36413}];
36414
36415
36416function parsePatternAttr(regex, patternExp, elm) {
36417  if (!regex) return undefined;
36418
36419  if (isString(regex)) {
36420    regex = new RegExp('^' + regex + '$');
36421  }
36422
36423  if (!regex.test) {
36424    throw minErr('ngPattern')('noregexp',
36425      'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
36426      regex, startingTag(elm));
36427  }
36428
36429  return regex;
36430}
36431
36432function parseLength(val) {
36433  var intVal = toInt(val);
36434  return isNumberNaN(intVal) ? -1 : intVal;
36435}
36436
36437if (window.angular.bootstrap) {
36438  // AngularJS is already loaded, so we can return here...
36439  if (window.console) {
36440    console.log('WARNING: Tried to load AngularJS more than once.');
36441  }
36442  return;
36443}
36444
36445// try to bind to jquery now so that one can write jqLite(fn)
36446// but we will rebind on bootstrap again.
36447bindJQuery();
36448
36449publishExternalAPI(angular);
36450
36451angular.module("ngLocale", [], ["$provide", function($provide) {
36452var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
36453function getDecimals(n) {
36454  n = n + '';
36455  var i = n.indexOf('.');
36456  return (i == -1) ? 0 : n.length - i - 1;
36457}
36458
36459function getVF(n, opt_precision) {
36460  var v = opt_precision;
36461
36462  if (undefined === v) {
36463    v = Math.min(getDecimals(n), 3);
36464  }
36465
36466  var base = Math.pow(10, v);
36467  var f = ((n * base) | 0) % base;
36468  return {v: v, f: f};
36469}
36470
36471$provide.value("$locale", {
36472  "DATETIME_FORMATS": {
36473    "AMPMS": [
36474      "AM",
36475      "PM"
36476    ],
36477    "DAY": [
36478      "Sunday",
36479      "Monday",
36480      "Tuesday",
36481      "Wednesday",
36482      "Thursday",
36483      "Friday",
36484      "Saturday"
36485    ],
36486    "ERANAMES": [
36487      "Before Christ",
36488      "Anno Domini"
36489    ],
36490    "ERAS": [
36491      "BC",
36492      "AD"
36493    ],
36494    "FIRSTDAYOFWEEK": 6,
36495    "MONTH": [
36496      "January",
36497      "February",
36498      "March",
36499      "April",
36500      "May",
36501      "June",
36502      "July",
36503      "August",
36504      "September",
36505      "October",
36506      "November",
36507      "December"
36508    ],
36509    "SHORTDAY": [
36510      "Sun",
36511      "Mon",
36512      "Tue",
36513      "Wed",
36514      "Thu",
36515      "Fri",
36516      "Sat"
36517    ],
36518    "SHORTMONTH": [
36519      "Jan",
36520      "Feb",
36521      "Mar",
36522      "Apr",
36523      "May",
36524      "Jun",
36525      "Jul",
36526      "Aug",
36527      "Sep",
36528      "Oct",
36529      "Nov",
36530      "Dec"
36531    ],
36532    "STANDALONEMONTH": [
36533      "January",
36534      "February",
36535      "March",
36536      "April",
36537      "May",
36538      "June",
36539      "July",
36540      "August",
36541      "September",
36542      "October",
36543      "November",
36544      "December"
36545    ],
36546    "WEEKENDRANGE": [
36547      5,
36548      6
36549    ],
36550    "fullDate": "EEEE, MMMM d, y",
36551    "longDate": "MMMM d, y",
36552    "medium": "MMM d, y h:mm:ss a",
36553    "mediumDate": "MMM d, y",
36554    "mediumTime": "h:mm:ss a",
36555    "short": "M/d/yy h:mm a",
36556    "shortDate": "M/d/yy",
36557    "shortTime": "h:mm a"
36558  },
36559  "NUMBER_FORMATS": {
36560    "CURRENCY_SYM": "$",
36561    "DECIMAL_SEP": ".",
36562    "GROUP_SEP": ",",
36563    "PATTERNS": [
36564      {
36565        "gSize": 3,
36566        "lgSize": 3,
36567        "maxFrac": 3,
36568        "minFrac": 0,
36569        "minInt": 1,
36570        "negPre": "-",
36571        "negSuf": "",
36572        "posPre": "",
36573        "posSuf": ""
36574      },
36575      {
36576        "gSize": 3,
36577        "lgSize": 3,
36578        "maxFrac": 2,
36579        "minFrac": 2,
36580        "minInt": 1,
36581        "negPre": "-\u00a4",
36582        "negSuf": "",
36583        "posPre": "\u00a4",
36584        "posSuf": ""
36585      }
36586    ]
36587  },
36588  "id": "en-us",
36589  "localeID": "en_US",
36590  "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;}
36591});
36592}]);
36593
36594  jqLite(function() {
36595    angularInit(window.document, bootstrap);
36596  });
36597
36598})(window);
36599
36600!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend(window.angular.element('<style>').text('@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;}'));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.