Built motion from commit 503e72f.|0.0.143
[motion.git] / public / bower_components / angular / angular.js
1 /**
2  * @license AngularJS v1.4.10
3  * (c) 2010-2015 Google, Inc. http://angularjs.org
4  * License: MIT
5  */
6 (function(window, document, undefined) {'use strict';
7
8 /**
9  * @description
10  *
11  * This object provides a utility for producing rich Error messages within
12  * Angular. It can be called as follows:
13  *
14  * var exampleMinErr = minErr('example');
15  * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
16  *
17  * The above creates an instance of minErr in the example namespace. The
18  * resulting error will have a namespaced error code of example.one.  The
19  * resulting error will replace {0} with the value of foo, and {1} with the
20  * value of bar. The object is not restricted in the number of arguments it can
21  * take.
22  *
23  * If fewer arguments are specified than necessary for interpolation, the extra
24  * interpolation markers will be preserved in the final string.
25  *
26  * Since data will be parsed statically during a build step, some restrictions
27  * are applied with respect to how minErr instances are created and called.
28  * Instances should have names of the form namespaceMinErr for a minErr created
29  * using minErr('namespace') . Error codes, namespaces and template strings
30  * should all be static strings, not variables or general expressions.
31  *
32  * @param {string} module The namespace to use for the new minErr instance.
33  * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
34  *   error from returned function, for cases when a particular type of error is useful.
35  * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
36  */
37
38 function minErr(module, ErrorConstructor) {
39   ErrorConstructor = ErrorConstructor || Error;
40   return function() {
41     var SKIP_INDEXES = 2;
42
43     var templateArgs = arguments,
44       code = templateArgs[0],
45       message = '[' + (module ? module + ':' : '') + code + '] ',
46       template = templateArgs[1],
47       paramPrefix, i;
48
49     message += template.replace(/\{\d+\}/g, function(match) {
50       var index = +match.slice(1, -1),
51         shiftedIndex = index + SKIP_INDEXES;
52
53       if (shiftedIndex < templateArgs.length) {
54         return toDebugString(templateArgs[shiftedIndex]);
55       }
56
57       return match;
58     });
59
60     message += '\nhttp://errors.angularjs.org/1.4.10/' +
61       (module ? module + '/' : '') + code;
62
63     for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
64       message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
65         encodeURIComponent(toDebugString(templateArgs[i]));
66     }
67
68     return new ErrorConstructor(message);
69   };
70 }
71
72 /* We need to tell jshint what variables are being exported */
73 /* global angular: true,
74   msie: true,
75   jqLite: true,
76   jQuery: true,
77   slice: true,
78   splice: true,
79   push: true,
80   toString: true,
81   ngMinErr: true,
82   angularModule: true,
83   uid: true,
84   REGEX_STRING_REGEXP: true,
85   VALIDITY_STATE_PROPERTY: true,
86
87   lowercase: true,
88   uppercase: true,
89   manualLowercase: true,
90   manualUppercase: true,
91   nodeName_: true,
92   isArrayLike: true,
93   forEach: true,
94   forEachSorted: true,
95   reverseParams: true,
96   nextUid: true,
97   setHashKey: true,
98   extend: true,
99   toInt: true,
100   inherit: true,
101   merge: true,
102   noop: true,
103   identity: true,
104   valueFn: true,
105   isUndefined: true,
106   isDefined: true,
107   isObject: true,
108   isBlankObject: true,
109   isString: true,
110   isNumber: true,
111   isDate: true,
112   isArray: true,
113   isFunction: true,
114   isRegExp: true,
115   isWindow: true,
116   isScope: true,
117   isFile: true,
118   isFormData: true,
119   isBlob: true,
120   isBoolean: true,
121   isPromiseLike: true,
122   trim: true,
123   escapeForRegexp: true,
124   isElement: true,
125   makeMap: true,
126   includes: true,
127   arrayRemove: true,
128   copy: true,
129   shallowCopy: true,
130   equals: true,
131   csp: true,
132   jq: true,
133   concat: true,
134   sliceArgs: true,
135   bind: true,
136   toJsonReplacer: true,
137   toJson: true,
138   fromJson: true,
139   convertTimezoneToLocal: true,
140   timezoneToOffset: true,
141   startingTag: true,
142   tryDecodeURIComponent: true,
143   parseKeyValue: true,
144   toKeyValue: true,
145   encodeUriSegment: true,
146   encodeUriQuery: true,
147   angularInit: true,
148   bootstrap: true,
149   getTestability: true,
150   snake_case: true,
151   bindJQuery: true,
152   assertArg: true,
153   assertArgFn: true,
154   assertNotHasOwnProperty: true,
155   getter: true,
156   getBlockNodes: true,
157   hasOwnProperty: true,
158   createMap: true,
159
160   NODE_TYPE_ELEMENT: true,
161   NODE_TYPE_ATTRIBUTE: true,
162   NODE_TYPE_TEXT: true,
163   NODE_TYPE_COMMENT: true,
164   NODE_TYPE_DOCUMENT: true,
165   NODE_TYPE_DOCUMENT_FRAGMENT: true,
166 */
167
168 ////////////////////////////////////
169
170 /**
171  * @ngdoc module
172  * @name ng
173  * @module ng
174  * @description
175  *
176  * # ng (core module)
177  * The ng module is loaded by default when an AngularJS application is started. The module itself
178  * contains the essential components for an AngularJS application to function. The table below
179  * lists a high level breakdown of each of the services/factories, filters, directives and testing
180  * components available within this core module.
181  *
182  * <div doc-module-components="ng"></div>
183  */
184
185 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
186
187 // The name of a form control's ValidityState property.
188 // This is used so that it's possible for internal tests to create mock ValidityStates.
189 var VALIDITY_STATE_PROPERTY = 'validity';
190
191 /**
192  * @ngdoc function
193  * @name angular.lowercase
194  * @module ng
195  * @kind function
196  *
197  * @description Converts the specified string to lowercase.
198  * @param {string} string String to be converted to lowercase.
199  * @returns {string} Lowercased string.
200  */
201 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
202 var hasOwnProperty = Object.prototype.hasOwnProperty;
203
204 /**
205  * @ngdoc function
206  * @name angular.uppercase
207  * @module ng
208  * @kind function
209  *
210  * @description Converts the specified string to uppercase.
211  * @param {string} string String to be converted to uppercase.
212  * @returns {string} Uppercased string.
213  */
214 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
215
216
217 var manualLowercase = function(s) {
218   /* jshint bitwise: false */
219   return isString(s)
220       ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
221       : s;
222 };
223 var manualUppercase = function(s) {
224   /* jshint bitwise: false */
225   return isString(s)
226       ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
227       : s;
228 };
229
230
231 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
232 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
233 // with correct but slower alternatives.
234 if ('i' !== 'I'.toLowerCase()) {
235   lowercase = manualLowercase;
236   uppercase = manualUppercase;
237 }
238
239
240 var
241     msie,             // holds major version number for IE, or NaN if UA is not IE.
242     jqLite,           // delay binding since jQuery could be loaded after us.
243     jQuery,           // delay binding
244     slice             = [].slice,
245     splice            = [].splice,
246     push              = [].push,
247     toString          = Object.prototype.toString,
248     getPrototypeOf    = Object.getPrototypeOf,
249     ngMinErr          = minErr('ng'),
250
251     /** @name angular */
252     angular           = window.angular || (window.angular = {}),
253     angularModule,
254     uid               = 0;
255
256 /**
257  * documentMode is an IE-only property
258  * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
259  */
260 msie = document.documentMode;
261
262
263 /**
264  * @private
265  * @param {*} obj
266  * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
267  *                   String ...)
268  */
269 function isArrayLike(obj) {
270
271   // `null`, `undefined` and `window` are not array-like
272   if (obj == null || isWindow(obj)) return false;
273
274   // arrays, strings and jQuery/jqLite objects are array like
275   // * jqLite is either the jQuery or jqLite constructor function
276   // * we have to check the existance of jqLite first as this method is called
277   //   via the forEach method when constructing the jqLite object in the first place
278   if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
279
280   // Support: iOS 8.2 (not reproducible in simulator)
281   // "length" in obj used to prevent JIT error (gh-11508)
282   var length = "length" in Object(obj) && obj.length;
283
284   // NodeList objects (with `item` method) and
285   // other objects with suitable length characteristics are array-like
286   return isNumber(length) &&
287     (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
288
289 }
290
291 /**
292  * @ngdoc function
293  * @name angular.forEach
294  * @module ng
295  * @kind function
296  *
297  * @description
298  * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
299  * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
300  * is the value of an object property or an array element, `key` is the object property key or
301  * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
302  *
303  * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
304  * using the `hasOwnProperty` method.
305  *
306  * Unlike ES262's
307  * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
308  * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
309  * return the value provided.
310  *
311    ```js
312      var values = {name: 'misko', gender: 'male'};
313      var log = [];
314      angular.forEach(values, function(value, key) {
315        this.push(key + ': ' + value);
316      }, log);
317      expect(log).toEqual(['name: misko', 'gender: male']);
318    ```
319  *
320  * @param {Object|Array} obj Object to iterate over.
321  * @param {Function} iterator Iterator function.
322  * @param {Object=} context Object to become context (`this`) for the iterator function.
323  * @returns {Object|Array} Reference to `obj`.
324  */
325
326 function forEach(obj, iterator, context) {
327   var key, length;
328   if (obj) {
329     if (isFunction(obj)) {
330       for (key in obj) {
331         // Need to check if hasOwnProperty exists,
332         // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
333         if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
334           iterator.call(context, obj[key], key, obj);
335         }
336       }
337     } else if (isArray(obj) || isArrayLike(obj)) {
338       var isPrimitive = typeof obj !== 'object';
339       for (key = 0, length = obj.length; key < length; key++) {
340         if (isPrimitive || key in obj) {
341           iterator.call(context, obj[key], key, obj);
342         }
343       }
344     } else if (obj.forEach && obj.forEach !== forEach) {
345         obj.forEach(iterator, context, obj);
346     } else if (isBlankObject(obj)) {
347       // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
348       for (key in obj) {
349         iterator.call(context, obj[key], key, obj);
350       }
351     } else if (typeof obj.hasOwnProperty === 'function') {
352       // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
353       for (key in obj) {
354         if (obj.hasOwnProperty(key)) {
355           iterator.call(context, obj[key], key, obj);
356         }
357       }
358     } else {
359       // Slow path for objects which do not have a method `hasOwnProperty`
360       for (key in obj) {
361         if (hasOwnProperty.call(obj, key)) {
362           iterator.call(context, obj[key], key, obj);
363         }
364       }
365     }
366   }
367   return obj;
368 }
369
370 function forEachSorted(obj, iterator, context) {
371   var keys = Object.keys(obj).sort();
372   for (var i = 0; i < keys.length; i++) {
373     iterator.call(context, obj[keys[i]], keys[i]);
374   }
375   return keys;
376 }
377
378
379 /**
380  * when using forEach the params are value, key, but it is often useful to have key, value.
381  * @param {function(string, *)} iteratorFn
382  * @returns {function(*, string)}
383  */
384 function reverseParams(iteratorFn) {
385   return function(value, key) {iteratorFn(key, value);};
386 }
387
388 /**
389  * A consistent way of creating unique IDs in angular.
390  *
391  * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
392  * we hit number precision issues in JavaScript.
393  *
394  * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
395  *
396  * @returns {number} an unique alpha-numeric string
397  */
398 function nextUid() {
399   return ++uid;
400 }
401
402
403 /**
404  * Set or clear the hashkey for an object.
405  * @param obj object
406  * @param h the hashkey (!truthy to delete the hashkey)
407  */
408 function setHashKey(obj, h) {
409   if (h) {
410     obj.$$hashKey = h;
411   } else {
412     delete obj.$$hashKey;
413   }
414 }
415
416
417 function baseExtend(dst, objs, deep) {
418   var h = dst.$$hashKey;
419
420   for (var i = 0, ii = objs.length; i < ii; ++i) {
421     var obj = objs[i];
422     if (!isObject(obj) && !isFunction(obj)) continue;
423     var keys = Object.keys(obj);
424     for (var j = 0, jj = keys.length; j < jj; j++) {
425       var key = keys[j];
426       var src = obj[key];
427
428       if (deep && isObject(src)) {
429         if (isDate(src)) {
430           dst[key] = new Date(src.valueOf());
431         } else if (isRegExp(src)) {
432           dst[key] = new RegExp(src);
433         } else if (src.nodeName) {
434           dst[key] = src.cloneNode(true);
435         } else if (isElement(src)) {
436           dst[key] = src.clone();
437         } else {
438           if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
439           baseExtend(dst[key], [src], true);
440         }
441       } else {
442         dst[key] = src;
443       }
444     }
445   }
446
447   setHashKey(dst, h);
448   return dst;
449 }
450
451 /**
452  * @ngdoc function
453  * @name angular.extend
454  * @module ng
455  * @kind function
456  *
457  * @description
458  * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
459  * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
460  * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
461  *
462  * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
463  * {@link angular.merge} for this.
464  *
465  * @param {Object} dst Destination object.
466  * @param {...Object} src Source object(s).
467  * @returns {Object} Reference to `dst`.
468  */
469 function extend(dst) {
470   return baseExtend(dst, slice.call(arguments, 1), false);
471 }
472
473
474 /**
475 * @ngdoc function
476 * @name angular.merge
477 * @module ng
478 * @kind function
479 *
480 * @description
481 * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
482 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
483 * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
484 *
485 * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
486 * objects, performing a deep copy.
487 *
488 * @param {Object} dst Destination object.
489 * @param {...Object} src Source object(s).
490 * @returns {Object} Reference to `dst`.
491 */
492 function merge(dst) {
493   return baseExtend(dst, slice.call(arguments, 1), true);
494 }
495
496
497
498 function toInt(str) {
499   return parseInt(str, 10);
500 }
501
502
503 function inherit(parent, extra) {
504   return extend(Object.create(parent), extra);
505 }
506
507 /**
508  * @ngdoc function
509  * @name angular.noop
510  * @module ng
511  * @kind function
512  *
513  * @description
514  * A function that performs no operations. This function can be useful when writing code in the
515  * functional style.
516    ```js
517      function foo(callback) {
518        var result = calculateResult();
519        (callback || angular.noop)(result);
520      }
521    ```
522  */
523 function noop() {}
524 noop.$inject = [];
525
526
527 /**
528  * @ngdoc function
529  * @name angular.identity
530  * @module ng
531  * @kind function
532  *
533  * @description
534  * A function that returns its first argument. This function is useful when writing code in the
535  * functional style.
536  *
537    ```js
538      function transformer(transformationFn, value) {
539        return (transformationFn || angular.identity)(value);
540      };
541    ```
542   * @param {*} value to be returned.
543   * @returns {*} the value passed in.
544  */
545 function identity($) {return $;}
546 identity.$inject = [];
547
548
549 function valueFn(value) {return function() {return value;};}
550
551 function hasCustomToString(obj) {
552   return isFunction(obj.toString) && obj.toString !== toString;
553 }
554
555
556 /**
557  * @ngdoc function
558  * @name angular.isUndefined
559  * @module ng
560  * @kind function
561  *
562  * @description
563  * Determines if a reference is undefined.
564  *
565  * @param {*} value Reference to check.
566  * @returns {boolean} True if `value` is undefined.
567  */
568 function isUndefined(value) {return typeof value === 'undefined';}
569
570
571 /**
572  * @ngdoc function
573  * @name angular.isDefined
574  * @module ng
575  * @kind function
576  *
577  * @description
578  * Determines if a reference is defined.
579  *
580  * @param {*} value Reference to check.
581  * @returns {boolean} True if `value` is defined.
582  */
583 function isDefined(value) {return typeof value !== 'undefined';}
584
585
586 /**
587  * @ngdoc function
588  * @name angular.isObject
589  * @module ng
590  * @kind function
591  *
592  * @description
593  * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
594  * considered to be objects. Note that JavaScript arrays are objects.
595  *
596  * @param {*} value Reference to check.
597  * @returns {boolean} True if `value` is an `Object` but not `null`.
598  */
599 function isObject(value) {
600   // http://jsperf.com/isobject4
601   return value !== null && typeof value === 'object';
602 }
603
604
605 /**
606  * Determine if a value is an object with a null prototype
607  *
608  * @returns {boolean} True if `value` is an `Object` with a null prototype
609  */
610 function isBlankObject(value) {
611   return value !== null && typeof value === 'object' && !getPrototypeOf(value);
612 }
613
614
615 /**
616  * @ngdoc function
617  * @name angular.isString
618  * @module ng
619  * @kind function
620  *
621  * @description
622  * Determines if a reference is a `String`.
623  *
624  * @param {*} value Reference to check.
625  * @returns {boolean} True if `value` is a `String`.
626  */
627 function isString(value) {return typeof value === 'string';}
628
629
630 /**
631  * @ngdoc function
632  * @name angular.isNumber
633  * @module ng
634  * @kind function
635  *
636  * @description
637  * Determines if a reference is a `Number`.
638  *
639  * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
640  *
641  * If you wish to exclude these then you can use the native
642  * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
643  * method.
644  *
645  * @param {*} value Reference to check.
646  * @returns {boolean} True if `value` is a `Number`.
647  */
648 function isNumber(value) {return typeof value === 'number';}
649
650
651 /**
652  * @ngdoc function
653  * @name angular.isDate
654  * @module ng
655  * @kind function
656  *
657  * @description
658  * Determines if a value is a date.
659  *
660  * @param {*} value Reference to check.
661  * @returns {boolean} True if `value` is a `Date`.
662  */
663 function isDate(value) {
664   return toString.call(value) === '[object Date]';
665 }
666
667
668 /**
669  * @ngdoc function
670  * @name angular.isArray
671  * @module ng
672  * @kind function
673  *
674  * @description
675  * Determines if a reference is an `Array`.
676  *
677  * @param {*} value Reference to check.
678  * @returns {boolean} True if `value` is an `Array`.
679  */
680 var isArray = Array.isArray;
681
682 /**
683  * @ngdoc function
684  * @name angular.isFunction
685  * @module ng
686  * @kind function
687  *
688  * @description
689  * Determines if a reference is a `Function`.
690  *
691  * @param {*} value Reference to check.
692  * @returns {boolean} True if `value` is a `Function`.
693  */
694 function isFunction(value) {return typeof value === 'function';}
695
696
697 /**
698  * Determines if a value is a regular expression object.
699  *
700  * @private
701  * @param {*} value Reference to check.
702  * @returns {boolean} True if `value` is a `RegExp`.
703  */
704 function isRegExp(value) {
705   return toString.call(value) === '[object RegExp]';
706 }
707
708
709 /**
710  * Checks if `obj` is a window object.
711  *
712  * @private
713  * @param {*} obj Object to check
714  * @returns {boolean} True if `obj` is a window obj.
715  */
716 function isWindow(obj) {
717   return obj && obj.window === obj;
718 }
719
720
721 function isScope(obj) {
722   return obj && obj.$evalAsync && obj.$watch;
723 }
724
725
726 function isFile(obj) {
727   return toString.call(obj) === '[object File]';
728 }
729
730
731 function isFormData(obj) {
732   return toString.call(obj) === '[object FormData]';
733 }
734
735
736 function isBlob(obj) {
737   return toString.call(obj) === '[object Blob]';
738 }
739
740
741 function isBoolean(value) {
742   return typeof value === 'boolean';
743 }
744
745
746 function isPromiseLike(obj) {
747   return obj && isFunction(obj.then);
748 }
749
750
751 var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
752 function isTypedArray(value) {
753   return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
754 }
755
756
757 var trim = function(value) {
758   return isString(value) ? value.trim() : value;
759 };
760
761 // Copied from:
762 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
763 // Prereq: s is a string.
764 var escapeForRegexp = function(s) {
765   return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
766            replace(/\x08/g, '\\x08');
767 };
768
769
770 /**
771  * @ngdoc function
772  * @name angular.isElement
773  * @module ng
774  * @kind function
775  *
776  * @description
777  * Determines if a reference is a DOM element (or wrapped jQuery element).
778  *
779  * @param {*} value Reference to check.
780  * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
781  */
782 function isElement(node) {
783   return !!(node &&
784     (node.nodeName  // we are a direct element
785     || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
786 }
787
788 /**
789  * @param str 'key1,key2,...'
790  * @returns {object} in the form of {key1:true, key2:true, ...}
791  */
792 function makeMap(str) {
793   var obj = {}, items = str.split(','), i;
794   for (i = 0; i < items.length; i++) {
795     obj[items[i]] = true;
796   }
797   return obj;
798 }
799
800
801 function nodeName_(element) {
802   return lowercase(element.nodeName || (element[0] && element[0].nodeName));
803 }
804
805 function includes(array, obj) {
806   return Array.prototype.indexOf.call(array, obj) != -1;
807 }
808
809 function arrayRemove(array, value) {
810   var index = array.indexOf(value);
811   if (index >= 0) {
812     array.splice(index, 1);
813   }
814   return index;
815 }
816
817 /**
818  * @ngdoc function
819  * @name angular.copy
820  * @module ng
821  * @kind function
822  *
823  * @description
824  * Creates a deep copy of `source`, which should be an object or an array.
825  *
826  * * If no destination is supplied, a copy of the object or array is created.
827  * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
828  *   are deleted and then all elements/properties from the source are copied to it.
829  * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
830  * * If `source` is identical to 'destination' an exception will be thrown.
831  *
832  * @param {*} source The source that will be used to make a copy.
833  *                   Can be any type, including primitives, `null`, and `undefined`.
834  * @param {(Object|Array)=} destination Destination into which the source is copied. If
835  *     provided, must be of the same type as `source`.
836  * @returns {*} The copy or updated `destination`, if `destination` was specified.
837  *
838  * @example
839  <example module="copyExample">
840  <file name="index.html">
841  <div ng-controller="ExampleController">
842  <form novalidate class="simple-form">
843  Name: <input type="text" ng-model="user.name" /><br />
844  E-mail: <input type="email" ng-model="user.email" /><br />
845  Gender: <input type="radio" ng-model="user.gender" value="male" />male
846  <input type="radio" ng-model="user.gender" value="female" />female<br />
847  <button ng-click="reset()">RESET</button>
848  <button ng-click="update(user)">SAVE</button>
849  </form>
850  <pre>form = {{user | json}}</pre>
851  <pre>master = {{master | json}}</pre>
852  </div>
853
854  <script>
855   angular.module('copyExample', [])
856     .controller('ExampleController', ['$scope', function($scope) {
857       $scope.master= {};
858
859       $scope.update = function(user) {
860         // Example with 1 argument
861         $scope.master= angular.copy(user);
862       };
863
864       $scope.reset = function() {
865         // Example with 2 arguments
866         angular.copy($scope.master, $scope.user);
867       };
868
869       $scope.reset();
870     }]);
871  </script>
872  </file>
873  </example>
874  */
875 function copy(source, destination) {
876   var stackSource = [];
877   var stackDest = [];
878
879   if (destination) {
880     if (isTypedArray(destination)) {
881       throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
882     }
883     if (source === destination) {
884       throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
885     }
886
887     // Empty the destination object
888     if (isArray(destination)) {
889       destination.length = 0;
890     } else {
891       forEach(destination, function(value, key) {
892         if (key !== '$$hashKey') {
893           delete destination[key];
894         }
895       });
896     }
897
898     stackSource.push(source);
899     stackDest.push(destination);
900     return copyRecurse(source, destination);
901   }
902
903   return copyElement(source);
904
905   function copyRecurse(source, destination) {
906     var h = destination.$$hashKey;
907     var result, key;
908     if (isArray(source)) {
909       for (var i = 0, ii = source.length; i < ii; i++) {
910         destination.push(copyElement(source[i]));
911       }
912     } else if (isBlankObject(source)) {
913       // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
914       for (key in source) {
915         destination[key] = copyElement(source[key]);
916       }
917     } else if (source && typeof source.hasOwnProperty === 'function') {
918       // Slow path, which must rely on hasOwnProperty
919       for (key in source) {
920         if (source.hasOwnProperty(key)) {
921           destination[key] = copyElement(source[key]);
922         }
923       }
924     } else {
925       // Slowest path --- hasOwnProperty can't be called as a method
926       for (key in source) {
927         if (hasOwnProperty.call(source, key)) {
928           destination[key] = copyElement(source[key]);
929         }
930       }
931     }
932     setHashKey(destination, h);
933     return destination;
934   }
935
936   function copyElement(source) {
937     // Simple values
938     if (!isObject(source)) {
939       return source;
940     }
941
942     // Already copied values
943     var index = stackSource.indexOf(source);
944     if (index !== -1) {
945       return stackDest[index];
946     }
947
948     if (isWindow(source) || isScope(source)) {
949       throw ngMinErr('cpws',
950         "Can't copy! Making copies of Window or Scope instances is not supported.");
951     }
952
953     var needsRecurse = false;
954     var destination;
955
956     if (isArray(source)) {
957       destination = [];
958       needsRecurse = true;
959     } else if (isTypedArray(source)) {
960       destination = new source.constructor(source);
961     } else if (isDate(source)) {
962       destination = new Date(source.getTime());
963     } else if (isRegExp(source)) {
964       destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
965       destination.lastIndex = source.lastIndex;
966     } else if (isBlob(source)) {
967       destination = new source.constructor([source], {type: source.type});
968     } else if (isFunction(source.cloneNode)) {
969         destination = source.cloneNode(true);
970     } else {
971       destination = Object.create(getPrototypeOf(source));
972       needsRecurse = true;
973     }
974
975     stackSource.push(source);
976     stackDest.push(destination);
977
978     return needsRecurse
979       ? copyRecurse(source, destination)
980       : destination;
981   }
982 }
983
984 /**
985  * Creates a shallow copy of an object, an array or a primitive.
986  *
987  * Assumes that there are no proto properties for objects.
988  */
989 function shallowCopy(src, dst) {
990   if (isArray(src)) {
991     dst = dst || [];
992
993     for (var i = 0, ii = src.length; i < ii; i++) {
994       dst[i] = src[i];
995     }
996   } else if (isObject(src)) {
997     dst = dst || {};
998
999     for (var key in src) {
1000       if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1001         dst[key] = src[key];
1002       }
1003     }
1004   }
1005
1006   return dst || src;
1007 }
1008
1009
1010 /**
1011  * @ngdoc function
1012  * @name angular.equals
1013  * @module ng
1014  * @kind function
1015  *
1016  * @description
1017  * Determines if two objects or two values are equivalent. Supports value types, regular
1018  * expressions, arrays and objects.
1019  *
1020  * Two objects or values are considered equivalent if at least one of the following is true:
1021  *
1022  * * Both objects or values pass `===` comparison.
1023  * * Both objects or values are of the same type and all of their properties are equal by
1024  *   comparing them with `angular.equals`.
1025  * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1026  * * Both values represent the same regular expression (In JavaScript,
1027  *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1028  *   representation matches).
1029  *
1030  * During a property comparison, properties of `function` type and properties with names
1031  * that begin with `$` are ignored.
1032  *
1033  * Scope and DOMWindow objects are being compared only by identify (`===`).
1034  *
1035  * @param {*} o1 Object or value to compare.
1036  * @param {*} o2 Object or value to compare.
1037  * @returns {boolean} True if arguments are equal.
1038  */
1039 function equals(o1, o2) {
1040   if (o1 === o2) return true;
1041   if (o1 === null || o2 === null) return false;
1042   if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1043   var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1044   if (t1 == t2) {
1045     if (t1 == 'object') {
1046       if (isArray(o1)) {
1047         if (!isArray(o2)) return false;
1048         if ((length = o1.length) == o2.length) {
1049           for (key = 0; key < length; key++) {
1050             if (!equals(o1[key], o2[key])) return false;
1051           }
1052           return true;
1053         }
1054       } else if (isDate(o1)) {
1055         if (!isDate(o2)) return false;
1056         return equals(o1.getTime(), o2.getTime());
1057       } else if (isRegExp(o1)) {
1058         return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1059       } else {
1060         if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1061           isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1062         keySet = createMap();
1063         for (key in o1) {
1064           if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1065           if (!equals(o1[key], o2[key])) return false;
1066           keySet[key] = true;
1067         }
1068         for (key in o2) {
1069           if (!(key in keySet) &&
1070               key.charAt(0) !== '$' &&
1071               isDefined(o2[key]) &&
1072               !isFunction(o2[key])) return false;
1073         }
1074         return true;
1075       }
1076     }
1077   }
1078   return false;
1079 }
1080
1081 var csp = function() {
1082   if (!isDefined(csp.rules)) {
1083
1084
1085     var ngCspElement = (document.querySelector('[ng-csp]') ||
1086                     document.querySelector('[data-ng-csp]'));
1087
1088     if (ngCspElement) {
1089       var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1090                     ngCspElement.getAttribute('data-ng-csp');
1091       csp.rules = {
1092         noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1093         noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1094       };
1095     } else {
1096       csp.rules = {
1097         noUnsafeEval: noUnsafeEval(),
1098         noInlineStyle: false
1099       };
1100     }
1101   }
1102
1103   return csp.rules;
1104
1105   function noUnsafeEval() {
1106     try {
1107       /* jshint -W031, -W054 */
1108       new Function('');
1109       /* jshint +W031, +W054 */
1110       return false;
1111     } catch (e) {
1112       return true;
1113     }
1114   }
1115 };
1116
1117 /**
1118  * @ngdoc directive
1119  * @module ng
1120  * @name ngJq
1121  *
1122  * @element ANY
1123  * @param {string=} ngJq the name of the library available under `window`
1124  * to be used for angular.element
1125  * @description
1126  * Use this directive to force the angular.element library.  This should be
1127  * used to force either jqLite by leaving ng-jq blank or setting the name of
1128  * the jquery variable under window (eg. jQuery).
1129  *
1130  * Since angular looks for this directive when it is loaded (doesn't wait for the
1131  * DOMContentLoaded event), it must be placed on an element that comes before the script
1132  * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1133  * others ignored.
1134  *
1135  * @example
1136  * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1137  ```html
1138  <!doctype html>
1139  <html ng-app ng-jq>
1140  ...
1141  ...
1142  </html>
1143  ```
1144  * @example
1145  * This example shows how to use a jQuery based library of a different name.
1146  * The library name must be available at the top most 'window'.
1147  ```html
1148  <!doctype html>
1149  <html ng-app ng-jq="jQueryLib">
1150  ...
1151  ...
1152  </html>
1153  ```
1154  */
1155 var jq = function() {
1156   if (isDefined(jq.name_)) return jq.name_;
1157   var el;
1158   var i, ii = ngAttrPrefixes.length, prefix, name;
1159   for (i = 0; i < ii; ++i) {
1160     prefix = ngAttrPrefixes[i];
1161     if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1162       name = el.getAttribute(prefix + 'jq');
1163       break;
1164     }
1165   }
1166
1167   return (jq.name_ = name);
1168 };
1169
1170 function concat(array1, array2, index) {
1171   return array1.concat(slice.call(array2, index));
1172 }
1173
1174 function sliceArgs(args, startIndex) {
1175   return slice.call(args, startIndex || 0);
1176 }
1177
1178
1179 /* jshint -W101 */
1180 /**
1181  * @ngdoc function
1182  * @name angular.bind
1183  * @module ng
1184  * @kind function
1185  *
1186  * @description
1187  * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1188  * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1189  * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1190  * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1191  *
1192  * @param {Object} self Context which `fn` should be evaluated in.
1193  * @param {function()} fn Function to be bound.
1194  * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1195  * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1196  */
1197 /* jshint +W101 */
1198 function bind(self, fn) {
1199   var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1200   if (isFunction(fn) && !(fn instanceof RegExp)) {
1201     return curryArgs.length
1202       ? function() {
1203           return arguments.length
1204             ? fn.apply(self, concat(curryArgs, arguments, 0))
1205             : fn.apply(self, curryArgs);
1206         }
1207       : function() {
1208           return arguments.length
1209             ? fn.apply(self, arguments)
1210             : fn.call(self);
1211         };
1212   } else {
1213     // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1214     return fn;
1215   }
1216 }
1217
1218
1219 function toJsonReplacer(key, value) {
1220   var val = value;
1221
1222   if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1223     val = undefined;
1224   } else if (isWindow(value)) {
1225     val = '$WINDOW';
1226   } else if (value &&  document === value) {
1227     val = '$DOCUMENT';
1228   } else if (isScope(value)) {
1229     val = '$SCOPE';
1230   }
1231
1232   return val;
1233 }
1234
1235
1236 /**
1237  * @ngdoc function
1238  * @name angular.toJson
1239  * @module ng
1240  * @kind function
1241  *
1242  * @description
1243  * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1244  * stripped since angular uses this notation internally.
1245  *
1246  * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1247  * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1248  *    If set to an integer, the JSON output will contain that many spaces per indentation.
1249  * @returns {string|undefined} JSON-ified string representing `obj`.
1250  */
1251 function toJson(obj, pretty) {
1252   if (isUndefined(obj)) return undefined;
1253   if (!isNumber(pretty)) {
1254     pretty = pretty ? 2 : null;
1255   }
1256   return JSON.stringify(obj, toJsonReplacer, pretty);
1257 }
1258
1259
1260 /**
1261  * @ngdoc function
1262  * @name angular.fromJson
1263  * @module ng
1264  * @kind function
1265  *
1266  * @description
1267  * Deserializes a JSON string.
1268  *
1269  * @param {string} json JSON string to deserialize.
1270  * @returns {Object|Array|string|number} Deserialized JSON string.
1271  */
1272 function fromJson(json) {
1273   return isString(json)
1274       ? JSON.parse(json)
1275       : json;
1276 }
1277
1278
1279 var ALL_COLONS = /:/g;
1280 function timezoneToOffset(timezone, fallback) {
1281   // IE/Edge do not "understand" colon (`:`) in timezone
1282   timezone = timezone.replace(ALL_COLONS, '');
1283   var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1284   return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1285 }
1286
1287
1288 function addDateMinutes(date, minutes) {
1289   date = new Date(date.getTime());
1290   date.setMinutes(date.getMinutes() + minutes);
1291   return date;
1292 }
1293
1294
1295 function convertTimezoneToLocal(date, timezone, reverse) {
1296   reverse = reverse ? -1 : 1;
1297   var dateTimezoneOffset = date.getTimezoneOffset();
1298   var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
1299   return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
1300 }
1301
1302
1303 /**
1304  * @returns {string} Returns the string representation of the element.
1305  */
1306 function startingTag(element) {
1307   element = jqLite(element).clone();
1308   try {
1309     // turns out IE does not let you set .html() on elements which
1310     // are not allowed to have children. So we just ignore it.
1311     element.empty();
1312   } catch (e) {}
1313   var elemHtml = jqLite('<div>').append(element).html();
1314   try {
1315     return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1316         elemHtml.
1317           match(/^(<[^>]+>)/)[1].
1318           replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
1319   } catch (e) {
1320     return lowercase(elemHtml);
1321   }
1322
1323 }
1324
1325
1326 /////////////////////////////////////////////////
1327
1328 /**
1329  * Tries to decode the URI component without throwing an exception.
1330  *
1331  * @private
1332  * @param str value potential URI component to check.
1333  * @returns {boolean} True if `value` can be decoded
1334  * with the decodeURIComponent function.
1335  */
1336 function tryDecodeURIComponent(value) {
1337   try {
1338     return decodeURIComponent(value);
1339   } catch (e) {
1340     // Ignore any invalid uri component
1341   }
1342 }
1343
1344
1345 /**
1346  * Parses an escaped url query string into key-value pairs.
1347  * @returns {Object.<string,boolean|Array>}
1348  */
1349 function parseKeyValue(/**string*/keyValue) {
1350   var obj = {};
1351   forEach((keyValue || "").split('&'), function(keyValue) {
1352     var splitPoint, key, val;
1353     if (keyValue) {
1354       key = keyValue = keyValue.replace(/\+/g,'%20');
1355       splitPoint = keyValue.indexOf('=');
1356       if (splitPoint !== -1) {
1357         key = keyValue.substring(0, splitPoint);
1358         val = keyValue.substring(splitPoint + 1);
1359       }
1360       key = tryDecodeURIComponent(key);
1361       if (isDefined(key)) {
1362         val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1363         if (!hasOwnProperty.call(obj, key)) {
1364           obj[key] = val;
1365         } else if (isArray(obj[key])) {
1366           obj[key].push(val);
1367         } else {
1368           obj[key] = [obj[key],val];
1369         }
1370       }
1371     }
1372   });
1373   return obj;
1374 }
1375
1376 function toKeyValue(obj) {
1377   var parts = [];
1378   forEach(obj, function(value, key) {
1379     if (isArray(value)) {
1380       forEach(value, function(arrayValue) {
1381         parts.push(encodeUriQuery(key, true) +
1382                    (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1383       });
1384     } else {
1385     parts.push(encodeUriQuery(key, true) +
1386                (value === true ? '' : '=' + encodeUriQuery(value, true)));
1387     }
1388   });
1389   return parts.length ? parts.join('&') : '';
1390 }
1391
1392
1393 /**
1394  * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1395  * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1396  * segments:
1397  *    segment       = *pchar
1398  *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1399  *    pct-encoded   = "%" HEXDIG HEXDIG
1400  *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1401  *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1402  *                     / "*" / "+" / "," / ";" / "="
1403  */
1404 function encodeUriSegment(val) {
1405   return encodeUriQuery(val, true).
1406              replace(/%26/gi, '&').
1407              replace(/%3D/gi, '=').
1408              replace(/%2B/gi, '+');
1409 }
1410
1411
1412 /**
1413  * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1414  * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1415  * encoded per http://tools.ietf.org/html/rfc3986:
1416  *    query       = *( pchar / "/" / "?" )
1417  *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1418  *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1419  *    pct-encoded   = "%" HEXDIG HEXDIG
1420  *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1421  *                     / "*" / "+" / "," / ";" / "="
1422  */
1423 function encodeUriQuery(val, pctEncodeSpaces) {
1424   return encodeURIComponent(val).
1425              replace(/%40/gi, '@').
1426              replace(/%3A/gi, ':').
1427              replace(/%24/g, '$').
1428              replace(/%2C/gi, ',').
1429              replace(/%3B/gi, ';').
1430              replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1431 }
1432
1433 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1434
1435 function getNgAttribute(element, ngAttr) {
1436   var attr, i, ii = ngAttrPrefixes.length;
1437   for (i = 0; i < ii; ++i) {
1438     attr = ngAttrPrefixes[i] + ngAttr;
1439     if (isString(attr = element.getAttribute(attr))) {
1440       return attr;
1441     }
1442   }
1443   return null;
1444 }
1445
1446 /**
1447  * @ngdoc directive
1448  * @name ngApp
1449  * @module ng
1450  *
1451  * @element ANY
1452  * @param {angular.Module} ngApp an optional application
1453  *   {@link angular.module module} name to load.
1454  * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1455  *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1456  *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1457  *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1458  *   tracking down the root of these bugs.
1459  *
1460  * @description
1461  *
1462  * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1463  * designates the **root element** of the application and is typically placed near the root element
1464  * of the page - e.g. on the `<body>` or `<html>` tags.
1465  *
1466  * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1467  * found in the document will be used to define the root element to auto-bootstrap as an
1468  * application. To run multiple applications in an HTML document you must manually bootstrap them using
1469  * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1470  *
1471  * You can specify an **AngularJS module** to be used as the root module for the application.  This
1472  * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1473  * should contain the application code needed or have dependencies on other modules that will
1474  * contain the code. See {@link angular.module} for more information.
1475  *
1476  * In the example below if the `ngApp` directive were not placed on the `html` element then the
1477  * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1478  * would not be resolved to `3`.
1479  *
1480  * `ngApp` is the easiest, and most common way to bootstrap an application.
1481  *
1482  <example module="ngAppDemo">
1483    <file name="index.html">
1484    <div ng-controller="ngAppDemoController">
1485      I can add: {{a}} + {{b}} =  {{ a+b }}
1486    </div>
1487    </file>
1488    <file name="script.js">
1489    angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1490      $scope.a = 1;
1491      $scope.b = 2;
1492    });
1493    </file>
1494  </example>
1495  *
1496  * Using `ngStrictDi`, you would see something like this:
1497  *
1498  <example ng-app-included="true">
1499    <file name="index.html">
1500    <div ng-app="ngAppStrictDemo" ng-strict-di>
1501        <div ng-controller="GoodController1">
1502            I can add: {{a}} + {{b}} =  {{ a+b }}
1503
1504            <p>This renders because the controller does not fail to
1505               instantiate, by using explicit annotation style (see
1506               script.js for details)
1507            </p>
1508        </div>
1509
1510        <div ng-controller="GoodController2">
1511            Name: <input ng-model="name"><br />
1512            Hello, {{name}}!
1513
1514            <p>This renders because the controller does not fail to
1515               instantiate, by using explicit annotation style
1516               (see script.js for details)
1517            </p>
1518        </div>
1519
1520        <div ng-controller="BadController">
1521            I can add: {{a}} + {{b}} =  {{ a+b }}
1522
1523            <p>The controller could not be instantiated, due to relying
1524               on automatic function annotations (which are disabled in
1525               strict mode). As such, the content of this section is not
1526               interpolated, and there should be an error in your web console.
1527            </p>
1528        </div>
1529    </div>
1530    </file>
1531    <file name="script.js">
1532    angular.module('ngAppStrictDemo', [])
1533      // BadController will fail to instantiate, due to relying on automatic function annotation,
1534      // rather than an explicit annotation
1535      .controller('BadController', function($scope) {
1536        $scope.a = 1;
1537        $scope.b = 2;
1538      })
1539      // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1540      // due to using explicit annotations using the array style and $inject property, respectively.
1541      .controller('GoodController1', ['$scope', function($scope) {
1542        $scope.a = 1;
1543        $scope.b = 2;
1544      }])
1545      .controller('GoodController2', GoodController2);
1546      function GoodController2($scope) {
1547        $scope.name = "World";
1548      }
1549      GoodController2.$inject = ['$scope'];
1550    </file>
1551    <file name="style.css">
1552    div[ng-controller] {
1553        margin-bottom: 1em;
1554        -webkit-border-radius: 4px;
1555        border-radius: 4px;
1556        border: 1px solid;
1557        padding: .5em;
1558    }
1559    div[ng-controller^=Good] {
1560        border-color: #d6e9c6;
1561        background-color: #dff0d8;
1562        color: #3c763d;
1563    }
1564    div[ng-controller^=Bad] {
1565        border-color: #ebccd1;
1566        background-color: #f2dede;
1567        color: #a94442;
1568        margin-bottom: 0;
1569    }
1570    </file>
1571  </example>
1572  */
1573 function angularInit(element, bootstrap) {
1574   var appElement,
1575       module,
1576       config = {};
1577
1578   // The element `element` has priority over any other element
1579   forEach(ngAttrPrefixes, function(prefix) {
1580     var name = prefix + 'app';
1581
1582     if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1583       appElement = element;
1584       module = element.getAttribute(name);
1585     }
1586   });
1587   forEach(ngAttrPrefixes, function(prefix) {
1588     var name = prefix + 'app';
1589     var candidate;
1590
1591     if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1592       appElement = candidate;
1593       module = candidate.getAttribute(name);
1594     }
1595   });
1596   if (appElement) {
1597     config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1598     bootstrap(appElement, module ? [module] : [], config);
1599   }
1600 }
1601
1602 /**
1603  * @ngdoc function
1604  * @name angular.bootstrap
1605  * @module ng
1606  * @description
1607  * Use this function to manually start up angular application.
1608  *
1609  * See: {@link guide/bootstrap Bootstrap}
1610  *
1611  * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1612  * They must use {@link ng.directive:ngApp ngApp}.
1613  *
1614  * Angular will detect if it has been loaded into the browser more than once and only allow the
1615  * first loaded script to be bootstrapped and will report a warning to the browser console for
1616  * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1617  * multiple instances of Angular try to work on the DOM.
1618  *
1619  * ```html
1620  * <!doctype html>
1621  * <html>
1622  * <body>
1623  * <div ng-controller="WelcomeController">
1624  *   {{greeting}}
1625  * </div>
1626  *
1627  * <script src="angular.js"></script>
1628  * <script>
1629  *   var app = angular.module('demo', [])
1630  *   .controller('WelcomeController', function($scope) {
1631  *       $scope.greeting = 'Welcome!';
1632  *   });
1633  *   angular.bootstrap(document, ['demo']);
1634  * </script>
1635  * </body>
1636  * </html>
1637  * ```
1638  *
1639  * @param {DOMElement} element DOM element which is the root of angular application.
1640  * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1641  *     Each item in the array should be the name of a predefined module or a (DI annotated)
1642  *     function that will be invoked by the injector as a `config` block.
1643  *     See: {@link angular.module modules}
1644  * @param {Object=} config an object for defining configuration options for the application. The
1645  *     following keys are supported:
1646  *
1647  * * `strictDi` - disable automatic function annotation for the application. This is meant to
1648  *   assist in finding bugs which break minified code. Defaults to `false`.
1649  *
1650  * @returns {auto.$injector} Returns the newly created injector for this app.
1651  */
1652 function bootstrap(element, modules, config) {
1653   if (!isObject(config)) config = {};
1654   var defaultConfig = {
1655     strictDi: false
1656   };
1657   config = extend(defaultConfig, config);
1658   var doBootstrap = function() {
1659     element = jqLite(element);
1660
1661     if (element.injector()) {
1662       var tag = (element[0] === document) ? 'document' : startingTag(element);
1663       //Encode angle brackets to prevent input from being sanitized to empty string #8683
1664       throw ngMinErr(
1665           'btstrpd',
1666           "App Already Bootstrapped with this Element '{0}'",
1667           tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1668     }
1669
1670     modules = modules || [];
1671     modules.unshift(['$provide', function($provide) {
1672       $provide.value('$rootElement', element);
1673     }]);
1674
1675     if (config.debugInfoEnabled) {
1676       // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1677       modules.push(['$compileProvider', function($compileProvider) {
1678         $compileProvider.debugInfoEnabled(true);
1679       }]);
1680     }
1681
1682     modules.unshift('ng');
1683     var injector = createInjector(modules, config.strictDi);
1684     injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1685        function bootstrapApply(scope, element, compile, injector) {
1686         scope.$apply(function() {
1687           element.data('$injector', injector);
1688           compile(element)(scope);
1689         });
1690       }]
1691     );
1692     return injector;
1693   };
1694
1695   var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1696   var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1697
1698   if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1699     config.debugInfoEnabled = true;
1700     window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1701   }
1702
1703   if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1704     return doBootstrap();
1705   }
1706
1707   window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1708   angular.resumeBootstrap = function(extraModules) {
1709     forEach(extraModules, function(module) {
1710       modules.push(module);
1711     });
1712     return doBootstrap();
1713   };
1714
1715   if (isFunction(angular.resumeDeferredBootstrap)) {
1716     angular.resumeDeferredBootstrap();
1717   }
1718 }
1719
1720 /**
1721  * @ngdoc function
1722  * @name angular.reloadWithDebugInfo
1723  * @module ng
1724  * @description
1725  * Use this function to reload the current application with debug information turned on.
1726  * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1727  *
1728  * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1729  */
1730 function reloadWithDebugInfo() {
1731   window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1732   window.location.reload();
1733 }
1734
1735 /**
1736  * @name angular.getTestability
1737  * @module ng
1738  * @description
1739  * Get the testability service for the instance of Angular on the given
1740  * element.
1741  * @param {DOMElement} element DOM element which is the root of angular application.
1742  */
1743 function getTestability(rootElement) {
1744   var injector = angular.element(rootElement).injector();
1745   if (!injector) {
1746     throw ngMinErr('test',
1747       'no injector found for element argument to getTestability');
1748   }
1749   return injector.get('$$testability');
1750 }
1751
1752 var SNAKE_CASE_REGEXP = /[A-Z]/g;
1753 function snake_case(name, separator) {
1754   separator = separator || '_';
1755   return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1756     return (pos ? separator : '') + letter.toLowerCase();
1757   });
1758 }
1759
1760 var bindJQueryFired = false;
1761 var skipDestroyOnNextJQueryCleanData;
1762 function bindJQuery() {
1763   var originalCleanData;
1764
1765   if (bindJQueryFired) {
1766     return;
1767   }
1768
1769   // bind to jQuery if present;
1770   var jqName = jq();
1771   jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
1772            !jqName             ? undefined     :   // use jqLite
1773                                  window[jqName];   // use jQuery specified by `ngJq`
1774
1775   // Use jQuery if it exists with proper functionality, otherwise default to us.
1776   // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1777   // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1778   // versions. It will not work for sure with jQuery <1.7, though.
1779   if (jQuery && jQuery.fn.on) {
1780     jqLite = jQuery;
1781     extend(jQuery.fn, {
1782       scope: JQLitePrototype.scope,
1783       isolateScope: JQLitePrototype.isolateScope,
1784       controller: JQLitePrototype.controller,
1785       injector: JQLitePrototype.injector,
1786       inheritedData: JQLitePrototype.inheritedData
1787     });
1788
1789     // All nodes removed from the DOM via various jQuery APIs like .remove()
1790     // are passed through jQuery.cleanData. Monkey-patch this method to fire
1791     // the $destroy event on all removed nodes.
1792     originalCleanData = jQuery.cleanData;
1793     jQuery.cleanData = function(elems) {
1794       var events;
1795       if (!skipDestroyOnNextJQueryCleanData) {
1796         for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1797           events = jQuery._data(elem, "events");
1798           if (events && events.$destroy) {
1799             jQuery(elem).triggerHandler('$destroy');
1800           }
1801         }
1802       } else {
1803         skipDestroyOnNextJQueryCleanData = false;
1804       }
1805       originalCleanData(elems);
1806     };
1807   } else {
1808     jqLite = JQLite;
1809   }
1810
1811   angular.element = jqLite;
1812
1813   // Prevent double-proxying.
1814   bindJQueryFired = true;
1815 }
1816
1817 /**
1818  * throw error if the argument is falsy.
1819  */
1820 function assertArg(arg, name, reason) {
1821   if (!arg) {
1822     throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1823   }
1824   return arg;
1825 }
1826
1827 function assertArgFn(arg, name, acceptArrayAnnotation) {
1828   if (acceptArrayAnnotation && isArray(arg)) {
1829       arg = arg[arg.length - 1];
1830   }
1831
1832   assertArg(isFunction(arg), name, 'not a function, got ' +
1833       (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1834   return arg;
1835 }
1836
1837 /**
1838  * throw error if the name given is hasOwnProperty
1839  * @param  {String} name    the name to test
1840  * @param  {String} context the context in which the name is used, such as module or directive
1841  */
1842 function assertNotHasOwnProperty(name, context) {
1843   if (name === 'hasOwnProperty') {
1844     throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1845   }
1846 }
1847
1848 /**
1849  * Return the value accessible from the object by path. Any undefined traversals are ignored
1850  * @param {Object} obj starting object
1851  * @param {String} path path to traverse
1852  * @param {boolean} [bindFnToScope=true]
1853  * @returns {Object} value as accessible by path
1854  */
1855 //TODO(misko): this function needs to be removed
1856 function getter(obj, path, bindFnToScope) {
1857   if (!path) return obj;
1858   var keys = path.split('.');
1859   var key;
1860   var lastInstance = obj;
1861   var len = keys.length;
1862
1863   for (var i = 0; i < len; i++) {
1864     key = keys[i];
1865     if (obj) {
1866       obj = (lastInstance = obj)[key];
1867     }
1868   }
1869   if (!bindFnToScope && isFunction(obj)) {
1870     return bind(lastInstance, obj);
1871   }
1872   return obj;
1873 }
1874
1875 /**
1876  * Return the DOM siblings between the first and last node in the given array.
1877  * @param {Array} array like object
1878  * @returns {Array} the inputted object or a jqLite collection containing the nodes
1879  */
1880 function getBlockNodes(nodes) {
1881   // TODO(perf): update `nodes` instead of creating a new object?
1882   var node = nodes[0];
1883   var endNode = nodes[nodes.length - 1];
1884   var blockNodes;
1885
1886   for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1887     if (blockNodes || nodes[i] !== node) {
1888       if (!blockNodes) {
1889         blockNodes = jqLite(slice.call(nodes, 0, i));
1890       }
1891       blockNodes.push(node);
1892     }
1893   }
1894
1895   return blockNodes || nodes;
1896 }
1897
1898
1899 /**
1900  * Creates a new object without a prototype. This object is useful for lookup without having to
1901  * guard against prototypically inherited properties via hasOwnProperty.
1902  *
1903  * Related micro-benchmarks:
1904  * - http://jsperf.com/object-create2
1905  * - http://jsperf.com/proto-map-lookup/2
1906  * - http://jsperf.com/for-in-vs-object-keys2
1907  *
1908  * @returns {Object}
1909  */
1910 function createMap() {
1911   return Object.create(null);
1912 }
1913
1914 var NODE_TYPE_ELEMENT = 1;
1915 var NODE_TYPE_ATTRIBUTE = 2;
1916 var NODE_TYPE_TEXT = 3;
1917 var NODE_TYPE_COMMENT = 8;
1918 var NODE_TYPE_DOCUMENT = 9;
1919 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1920
1921 /**
1922  * @ngdoc type
1923  * @name angular.Module
1924  * @module ng
1925  * @description
1926  *
1927  * Interface for configuring angular {@link angular.module modules}.
1928  */
1929
1930 function setupModuleLoader(window) {
1931
1932   var $injectorMinErr = minErr('$injector');
1933   var ngMinErr = minErr('ng');
1934
1935   function ensure(obj, name, factory) {
1936     return obj[name] || (obj[name] = factory());
1937   }
1938
1939   var angular = ensure(window, 'angular', Object);
1940
1941   // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1942   angular.$$minErr = angular.$$minErr || minErr;
1943
1944   return ensure(angular, 'module', function() {
1945     /** @type {Object.<string, angular.Module>} */
1946     var modules = {};
1947
1948     /**
1949      * @ngdoc function
1950      * @name angular.module
1951      * @module ng
1952      * @description
1953      *
1954      * The `angular.module` is a global place for creating, registering and retrieving Angular
1955      * modules.
1956      * All modules (angular core or 3rd party) that should be available to an application must be
1957      * registered using this mechanism.
1958      *
1959      * Passing one argument retrieves an existing {@link angular.Module},
1960      * whereas passing more than one argument creates a new {@link angular.Module}
1961      *
1962      *
1963      * # Module
1964      *
1965      * A module is a collection of services, directives, controllers, filters, and configuration information.
1966      * `angular.module` is used to configure the {@link auto.$injector $injector}.
1967      *
1968      * ```js
1969      * // Create a new module
1970      * var myModule = angular.module('myModule', []);
1971      *
1972      * // register a new service
1973      * myModule.value('appName', 'MyCoolApp');
1974      *
1975      * // configure existing services inside initialization blocks.
1976      * myModule.config(['$locationProvider', function($locationProvider) {
1977      *   // Configure existing providers
1978      *   $locationProvider.hashPrefix('!');
1979      * }]);
1980      * ```
1981      *
1982      * Then you can create an injector and load your modules like this:
1983      *
1984      * ```js
1985      * var injector = angular.injector(['ng', 'myModule'])
1986      * ```
1987      *
1988      * However it's more likely that you'll just use
1989      * {@link ng.directive:ngApp ngApp} or
1990      * {@link angular.bootstrap} to simplify this process for you.
1991      *
1992      * @param {!string} name The name of the module to create or retrieve.
1993      * @param {!Array.<string>=} requires If specified then new module is being created. If
1994      *        unspecified then the module is being retrieved for further configuration.
1995      * @param {Function=} configFn Optional configuration function for the module. Same as
1996      *        {@link angular.Module#config Module#config()}.
1997      * @returns {angular.Module} new module with the {@link angular.Module} api.
1998      */
1999     return function module(name, requires, configFn) {
2000       var assertNotHasOwnProperty = function(name, context) {
2001         if (name === 'hasOwnProperty') {
2002           throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2003         }
2004       };
2005
2006       assertNotHasOwnProperty(name, 'module');
2007       if (requires && modules.hasOwnProperty(name)) {
2008         modules[name] = null;
2009       }
2010       return ensure(modules, name, function() {
2011         if (!requires) {
2012           throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2013              "the module name or forgot to load it. If registering a module ensure that you " +
2014              "specify the dependencies as the second argument.", name);
2015         }
2016
2017         /** @type {!Array.<Array.<*>>} */
2018         var invokeQueue = [];
2019
2020         /** @type {!Array.<Function>} */
2021         var configBlocks = [];
2022
2023         /** @type {!Array.<Function>} */
2024         var runBlocks = [];
2025
2026         var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2027
2028         /** @type {angular.Module} */
2029         var moduleInstance = {
2030           // Private state
2031           _invokeQueue: invokeQueue,
2032           _configBlocks: configBlocks,
2033           _runBlocks: runBlocks,
2034
2035           /**
2036            * @ngdoc property
2037            * @name angular.Module#requires
2038            * @module ng
2039            *
2040            * @description
2041            * Holds the list of modules which the injector will load before the current module is
2042            * loaded.
2043            */
2044           requires: requires,
2045
2046           /**
2047            * @ngdoc property
2048            * @name angular.Module#name
2049            * @module ng
2050            *
2051            * @description
2052            * Name of the module.
2053            */
2054           name: name,
2055
2056
2057           /**
2058            * @ngdoc method
2059            * @name angular.Module#provider
2060            * @module ng
2061            * @param {string} name service name
2062            * @param {Function} providerType Construction function for creating new instance of the
2063            *                                service.
2064            * @description
2065            * See {@link auto.$provide#provider $provide.provider()}.
2066            */
2067           provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2068
2069           /**
2070            * @ngdoc method
2071            * @name angular.Module#factory
2072            * @module ng
2073            * @param {string} name service name
2074            * @param {Function} providerFunction Function for creating new instance of the service.
2075            * @description
2076            * See {@link auto.$provide#factory $provide.factory()}.
2077            */
2078           factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2079
2080           /**
2081            * @ngdoc method
2082            * @name angular.Module#service
2083            * @module ng
2084            * @param {string} name service name
2085            * @param {Function} constructor A constructor function that will be instantiated.
2086            * @description
2087            * See {@link auto.$provide#service $provide.service()}.
2088            */
2089           service: invokeLaterAndSetModuleName('$provide', 'service'),
2090
2091           /**
2092            * @ngdoc method
2093            * @name angular.Module#value
2094            * @module ng
2095            * @param {string} name service name
2096            * @param {*} object Service instance object.
2097            * @description
2098            * See {@link auto.$provide#value $provide.value()}.
2099            */
2100           value: invokeLater('$provide', 'value'),
2101
2102           /**
2103            * @ngdoc method
2104            * @name angular.Module#constant
2105            * @module ng
2106            * @param {string} name constant name
2107            * @param {*} object Constant value.
2108            * @description
2109            * Because the constants are fixed, they get applied before other provide methods.
2110            * See {@link auto.$provide#constant $provide.constant()}.
2111            */
2112           constant: invokeLater('$provide', 'constant', 'unshift'),
2113
2114            /**
2115            * @ngdoc method
2116            * @name angular.Module#decorator
2117            * @module ng
2118            * @param {string} The name of the service to decorate.
2119            * @param {Function} This function will be invoked when the service needs to be
2120            *                                    instantiated and should return the decorated service instance.
2121            * @description
2122            * See {@link auto.$provide#decorator $provide.decorator()}.
2123            */
2124           decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2125
2126           /**
2127            * @ngdoc method
2128            * @name angular.Module#animation
2129            * @module ng
2130            * @param {string} name animation name
2131            * @param {Function} animationFactory Factory function for creating new instance of an
2132            *                                    animation.
2133            * @description
2134            *
2135            * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2136            *
2137            *
2138            * Defines an animation hook that can be later used with
2139            * {@link $animate $animate} service and directives that use this service.
2140            *
2141            * ```js
2142            * module.animation('.animation-name', function($inject1, $inject2) {
2143            *   return {
2144            *     eventName : function(element, done) {
2145            *       //code to run the animation
2146            *       //once complete, then run done()
2147            *       return function cancellationFunction(element) {
2148            *         //code to cancel the animation
2149            *       }
2150            *     }
2151            *   }
2152            * })
2153            * ```
2154            *
2155            * See {@link ng.$animateProvider#register $animateProvider.register()} and
2156            * {@link ngAnimate ngAnimate module} for more information.
2157            */
2158           animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2159
2160           /**
2161            * @ngdoc method
2162            * @name angular.Module#filter
2163            * @module ng
2164            * @param {string} name Filter name - this must be a valid angular expression identifier
2165            * @param {Function} filterFactory Factory function for creating new instance of filter.
2166            * @description
2167            * See {@link ng.$filterProvider#register $filterProvider.register()}.
2168            *
2169            * <div class="alert alert-warning">
2170            * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2171            * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2172            * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2173            * (`myapp_subsection_filterx`).
2174            * </div>
2175            */
2176           filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2177
2178           /**
2179            * @ngdoc method
2180            * @name angular.Module#controller
2181            * @module ng
2182            * @param {string|Object} name Controller name, or an object map of controllers where the
2183            *    keys are the names and the values are the constructors.
2184            * @param {Function} constructor Controller constructor function.
2185            * @description
2186            * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2187            */
2188           controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2189
2190           /**
2191            * @ngdoc method
2192            * @name angular.Module#directive
2193            * @module ng
2194            * @param {string|Object} name Directive name, or an object map of directives where the
2195            *    keys are the names and the values are the factories.
2196            * @param {Function} directiveFactory Factory function for creating new instance of
2197            * directives.
2198            * @description
2199            * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2200            */
2201           directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2202
2203           /**
2204            * @ngdoc method
2205            * @name angular.Module#config
2206            * @module ng
2207            * @param {Function} configFn Execute this function on module load. Useful for service
2208            *    configuration.
2209            * @description
2210            * Use this method to register work which needs to be performed on module loading.
2211            * For more about how to configure services, see
2212            * {@link providers#provider-recipe Provider Recipe}.
2213            */
2214           config: config,
2215
2216           /**
2217            * @ngdoc method
2218            * @name angular.Module#run
2219            * @module ng
2220            * @param {Function} initializationFn Execute this function after injector creation.
2221            *    Useful for application initialization.
2222            * @description
2223            * Use this method to register work which should be performed when the injector is done
2224            * loading all modules.
2225            */
2226           run: function(block) {
2227             runBlocks.push(block);
2228             return this;
2229           }
2230         };
2231
2232         if (configFn) {
2233           config(configFn);
2234         }
2235
2236         return moduleInstance;
2237
2238         /**
2239          * @param {string} provider
2240          * @param {string} method
2241          * @param {String=} insertMethod
2242          * @returns {angular.Module}
2243          */
2244         function invokeLater(provider, method, insertMethod, queue) {
2245           if (!queue) queue = invokeQueue;
2246           return function() {
2247             queue[insertMethod || 'push']([provider, method, arguments]);
2248             return moduleInstance;
2249           };
2250         }
2251
2252         /**
2253          * @param {string} provider
2254          * @param {string} method
2255          * @returns {angular.Module}
2256          */
2257         function invokeLaterAndSetModuleName(provider, method) {
2258           return function(recipeName, factoryFunction) {
2259             if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2260             invokeQueue.push([provider, method, arguments]);
2261             return moduleInstance;
2262           };
2263         }
2264       });
2265     };
2266   });
2267
2268 }
2269
2270 /* global: toDebugString: true */
2271
2272 function serializeObject(obj) {
2273   var seen = [];
2274
2275   return JSON.stringify(obj, function(key, val) {
2276     val = toJsonReplacer(key, val);
2277     if (isObject(val)) {
2278
2279       if (seen.indexOf(val) >= 0) return '...';
2280
2281       seen.push(val);
2282     }
2283     return val;
2284   });
2285 }
2286
2287 function toDebugString(obj) {
2288   if (typeof obj === 'function') {
2289     return obj.toString().replace(/ \{[\s\S]*$/, '');
2290   } else if (isUndefined(obj)) {
2291     return 'undefined';
2292   } else if (typeof obj !== 'string') {
2293     return serializeObject(obj);
2294   }
2295   return obj;
2296 }
2297
2298 /* global angularModule: true,
2299   version: true,
2300
2301   $CompileProvider,
2302
2303   htmlAnchorDirective,
2304   inputDirective,
2305   inputDirective,
2306   formDirective,
2307   scriptDirective,
2308   selectDirective,
2309   styleDirective,
2310   optionDirective,
2311   ngBindDirective,
2312   ngBindHtmlDirective,
2313   ngBindTemplateDirective,
2314   ngClassDirective,
2315   ngClassEvenDirective,
2316   ngClassOddDirective,
2317   ngCloakDirective,
2318   ngControllerDirective,
2319   ngFormDirective,
2320   ngHideDirective,
2321   ngIfDirective,
2322   ngIncludeDirective,
2323   ngIncludeFillContentDirective,
2324   ngInitDirective,
2325   ngNonBindableDirective,
2326   ngPluralizeDirective,
2327   ngRepeatDirective,
2328   ngShowDirective,
2329   ngStyleDirective,
2330   ngSwitchDirective,
2331   ngSwitchWhenDirective,
2332   ngSwitchDefaultDirective,
2333   ngOptionsDirective,
2334   ngTranscludeDirective,
2335   ngModelDirective,
2336   ngListDirective,
2337   ngChangeDirective,
2338   patternDirective,
2339   patternDirective,
2340   requiredDirective,
2341   requiredDirective,
2342   minlengthDirective,
2343   minlengthDirective,
2344   maxlengthDirective,
2345   maxlengthDirective,
2346   ngValueDirective,
2347   ngModelOptionsDirective,
2348   ngAttributeAliasDirectives,
2349   ngEventDirectives,
2350
2351   $AnchorScrollProvider,
2352   $AnimateProvider,
2353   $CoreAnimateCssProvider,
2354   $$CoreAnimateJsProvider,
2355   $$CoreAnimateQueueProvider,
2356   $$AnimateRunnerFactoryProvider,
2357   $$AnimateAsyncRunFactoryProvider,
2358   $BrowserProvider,
2359   $CacheFactoryProvider,
2360   $ControllerProvider,
2361   $DocumentProvider,
2362   $ExceptionHandlerProvider,
2363   $FilterProvider,
2364   $$ForceReflowProvider,
2365   $InterpolateProvider,
2366   $IntervalProvider,
2367   $$HashMapProvider,
2368   $HttpProvider,
2369   $HttpParamSerializerProvider,
2370   $HttpParamSerializerJQLikeProvider,
2371   $HttpBackendProvider,
2372   $xhrFactoryProvider,
2373   $LocationProvider,
2374   $LogProvider,
2375   $ParseProvider,
2376   $RootScopeProvider,
2377   $QProvider,
2378   $$QProvider,
2379   $$SanitizeUriProvider,
2380   $SceProvider,
2381   $SceDelegateProvider,
2382   $SnifferProvider,
2383   $TemplateCacheProvider,
2384   $TemplateRequestProvider,
2385   $$TestabilityProvider,
2386   $TimeoutProvider,
2387   $$RAFProvider,
2388   $WindowProvider,
2389   $$jqLiteProvider,
2390   $$CookieReaderProvider
2391 */
2392
2393
2394 /**
2395  * @ngdoc object
2396  * @name angular.version
2397  * @module ng
2398  * @description
2399  * An object that contains information about the current AngularJS version.
2400  *
2401  * This object has the following properties:
2402  *
2403  * - `full` – `{string}` – Full version string, such as "0.9.18".
2404  * - `major` – `{number}` – Major version number, such as "0".
2405  * - `minor` – `{number}` – Minor version number, such as "9".
2406  * - `dot` – `{number}` – Dot version number, such as "18".
2407  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2408  */
2409 var version = {
2410   full: '1.4.10',    // all of these placeholder strings will be replaced by grunt's
2411   major: 1,    // package task
2412   minor: 4,
2413   dot: 10,
2414   codeName: 'benignant-oscillation'
2415 };
2416
2417
2418 function publishExternalAPI(angular) {
2419   extend(angular, {
2420     'bootstrap': bootstrap,
2421     'copy': copy,
2422     'extend': extend,
2423     'merge': merge,
2424     'equals': equals,
2425     'element': jqLite,
2426     'forEach': forEach,
2427     'injector': createInjector,
2428     'noop': noop,
2429     'bind': bind,
2430     'toJson': toJson,
2431     'fromJson': fromJson,
2432     'identity': identity,
2433     'isUndefined': isUndefined,
2434     'isDefined': isDefined,
2435     'isString': isString,
2436     'isFunction': isFunction,
2437     'isObject': isObject,
2438     'isNumber': isNumber,
2439     'isElement': isElement,
2440     'isArray': isArray,
2441     'version': version,
2442     'isDate': isDate,
2443     'lowercase': lowercase,
2444     'uppercase': uppercase,
2445     'callbacks': {counter: 0},
2446     'getTestability': getTestability,
2447     '$$minErr': minErr,
2448     '$$csp': csp,
2449     'reloadWithDebugInfo': reloadWithDebugInfo
2450   });
2451
2452   angularModule = setupModuleLoader(window);
2453
2454   angularModule('ng', ['ngLocale'], ['$provide',
2455     function ngModule($provide) {
2456       // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2457       $provide.provider({
2458         $$sanitizeUri: $$SanitizeUriProvider
2459       });
2460       $provide.provider('$compile', $CompileProvider).
2461         directive({
2462             a: htmlAnchorDirective,
2463             input: inputDirective,
2464             textarea: inputDirective,
2465             form: formDirective,
2466             script: scriptDirective,
2467             select: selectDirective,
2468             style: styleDirective,
2469             option: optionDirective,
2470             ngBind: ngBindDirective,
2471             ngBindHtml: ngBindHtmlDirective,
2472             ngBindTemplate: ngBindTemplateDirective,
2473             ngClass: ngClassDirective,
2474             ngClassEven: ngClassEvenDirective,
2475             ngClassOdd: ngClassOddDirective,
2476             ngCloak: ngCloakDirective,
2477             ngController: ngControllerDirective,
2478             ngForm: ngFormDirective,
2479             ngHide: ngHideDirective,
2480             ngIf: ngIfDirective,
2481             ngInclude: ngIncludeDirective,
2482             ngInit: ngInitDirective,
2483             ngNonBindable: ngNonBindableDirective,
2484             ngPluralize: ngPluralizeDirective,
2485             ngRepeat: ngRepeatDirective,
2486             ngShow: ngShowDirective,
2487             ngStyle: ngStyleDirective,
2488             ngSwitch: ngSwitchDirective,
2489             ngSwitchWhen: ngSwitchWhenDirective,
2490             ngSwitchDefault: ngSwitchDefaultDirective,
2491             ngOptions: ngOptionsDirective,
2492             ngTransclude: ngTranscludeDirective,
2493             ngModel: ngModelDirective,
2494             ngList: ngListDirective,
2495             ngChange: ngChangeDirective,
2496             pattern: patternDirective,
2497             ngPattern: patternDirective,
2498             required: requiredDirective,
2499             ngRequired: requiredDirective,
2500             minlength: minlengthDirective,
2501             ngMinlength: minlengthDirective,
2502             maxlength: maxlengthDirective,
2503             ngMaxlength: maxlengthDirective,
2504             ngValue: ngValueDirective,
2505             ngModelOptions: ngModelOptionsDirective
2506         }).
2507         directive({
2508           ngInclude: ngIncludeFillContentDirective
2509         }).
2510         directive(ngAttributeAliasDirectives).
2511         directive(ngEventDirectives);
2512       $provide.provider({
2513         $anchorScroll: $AnchorScrollProvider,
2514         $animate: $AnimateProvider,
2515         $animateCss: $CoreAnimateCssProvider,
2516         $$animateJs: $$CoreAnimateJsProvider,
2517         $$animateQueue: $$CoreAnimateQueueProvider,
2518         $$AnimateRunner: $$AnimateRunnerFactoryProvider,
2519         $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
2520         $browser: $BrowserProvider,
2521         $cacheFactory: $CacheFactoryProvider,
2522         $controller: $ControllerProvider,
2523         $document: $DocumentProvider,
2524         $exceptionHandler: $ExceptionHandlerProvider,
2525         $filter: $FilterProvider,
2526         $$forceReflow: $$ForceReflowProvider,
2527         $interpolate: $InterpolateProvider,
2528         $interval: $IntervalProvider,
2529         $http: $HttpProvider,
2530         $httpParamSerializer: $HttpParamSerializerProvider,
2531         $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2532         $httpBackend: $HttpBackendProvider,
2533         $xhrFactory: $xhrFactoryProvider,
2534         $location: $LocationProvider,
2535         $log: $LogProvider,
2536         $parse: $ParseProvider,
2537         $rootScope: $RootScopeProvider,
2538         $q: $QProvider,
2539         $$q: $$QProvider,
2540         $sce: $SceProvider,
2541         $sceDelegate: $SceDelegateProvider,
2542         $sniffer: $SnifferProvider,
2543         $templateCache: $TemplateCacheProvider,
2544         $templateRequest: $TemplateRequestProvider,
2545         $$testability: $$TestabilityProvider,
2546         $timeout: $TimeoutProvider,
2547         $window: $WindowProvider,
2548         $$rAF: $$RAFProvider,
2549         $$jqLite: $$jqLiteProvider,
2550         $$HashMap: $$HashMapProvider,
2551         $$cookieReader: $$CookieReaderProvider
2552       });
2553     }
2554   ]);
2555 }
2556
2557 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2558  *     Any commits to this file should be reviewed with security in mind.  *
2559  *   Changes to this file can potentially create security vulnerabilities. *
2560  *          An approval from 2 Core members with history of modifying      *
2561  *                         this file is required.                          *
2562  *                                                                         *
2563  *  Does the change somehow allow for arbitrary javascript to be executed? *
2564  *    Or allows for someone to change the prototype of built-in objects?   *
2565  *     Or gives undesired access to variables likes document or window?    *
2566  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2567
2568 /* global JQLitePrototype: true,
2569   addEventListenerFn: true,
2570   removeEventListenerFn: true,
2571   BOOLEAN_ATTR: true,
2572   ALIASED_ATTR: true,
2573 */
2574
2575 //////////////////////////////////
2576 //JQLite
2577 //////////////////////////////////
2578
2579 /**
2580  * @ngdoc function
2581  * @name angular.element
2582  * @module ng
2583  * @kind function
2584  *
2585  * @description
2586  * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2587  *
2588  * If jQuery is available, `angular.element` is an alias for the
2589  * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2590  * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
2591  *
2592  * jqLite is a tiny, API-compatible subset of jQuery that allows
2593  * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
2594  * commonly needed functionality with the goal of having a very small footprint.
2595  *
2596  * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
2597  * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
2598  * specific version of jQuery if multiple versions exist on the page.
2599  *
2600  * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
2601  * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
2602  *
2603  * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
2604  * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
2605  * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
2606  *
2607  * ## Angular's jqLite
2608  * jqLite provides only the following jQuery methods:
2609  *
2610  * - [`addClass()`](http://api.jquery.com/addClass/)
2611  * - [`after()`](http://api.jquery.com/after/)
2612  * - [`append()`](http://api.jquery.com/append/)
2613  * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2614  * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2615  * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2616  * - [`clone()`](http://api.jquery.com/clone/)
2617  * - [`contents()`](http://api.jquery.com/contents/)
2618  * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
2619  *   As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
2620  * - [`data()`](http://api.jquery.com/data/)
2621  * - [`detach()`](http://api.jquery.com/detach/)
2622  * - [`empty()`](http://api.jquery.com/empty/)
2623  * - [`eq()`](http://api.jquery.com/eq/)
2624  * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2625  * - [`hasClass()`](http://api.jquery.com/hasClass/)
2626  * - [`html()`](http://api.jquery.com/html/)
2627  * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2628  * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2629  * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2630  * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2631  * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2632  * - [`prepend()`](http://api.jquery.com/prepend/)
2633  * - [`prop()`](http://api.jquery.com/prop/)
2634  * - [`ready()`](http://api.jquery.com/ready/)
2635  * - [`remove()`](http://api.jquery.com/remove/)
2636  * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2637  * - [`removeClass()`](http://api.jquery.com/removeClass/)
2638  * - [`removeData()`](http://api.jquery.com/removeData/)
2639  * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2640  * - [`text()`](http://api.jquery.com/text/)
2641  * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2642  * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2643  * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2644  * - [`val()`](http://api.jquery.com/val/)
2645  * - [`wrap()`](http://api.jquery.com/wrap/)
2646  *
2647  * ## jQuery/jqLite Extras
2648  * Angular also provides the following additional methods and events to both jQuery and jqLite:
2649  *
2650  * ### Events
2651  * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2652  *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2653  *    element before it is removed.
2654  *
2655  * ### Methods
2656  * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2657  *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2658  *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2659  *   `'ngModel'`).
2660  * - `injector()` - retrieves the injector of the current element or its parent.
2661  * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2662  *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2663  *   be enabled.
2664  * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2665  *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2666  *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2667  *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2668  * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2669  *   parent element is reached.
2670  *
2671  * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2672  * @returns {Object} jQuery object.
2673  */
2674
2675 JQLite.expando = 'ng339';
2676
2677 var jqCache = JQLite.cache = {},
2678     jqId = 1,
2679     addEventListenerFn = function(element, type, fn) {
2680       element.addEventListener(type, fn, false);
2681     },
2682     removeEventListenerFn = function(element, type, fn) {
2683       element.removeEventListener(type, fn, false);
2684     };
2685
2686 /*
2687  * !!! This is an undocumented "private" function !!!
2688  */
2689 JQLite._data = function(node) {
2690   //jQuery always returns an object on cache miss
2691   return this.cache[node[this.expando]] || {};
2692 };
2693
2694 function jqNextId() { return ++jqId; }
2695
2696
2697 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2698 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2699 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2700 var jqLiteMinErr = minErr('jqLite');
2701
2702 /**
2703  * Converts snake_case to camelCase.
2704  * Also there is special case for Moz prefix starting with upper case letter.
2705  * @param name Name to normalize
2706  */
2707 function camelCase(name) {
2708   return name.
2709     replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2710       return offset ? letter.toUpperCase() : letter;
2711     }).
2712     replace(MOZ_HACK_REGEXP, 'Moz$1');
2713 }
2714
2715 var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2716 var HTML_REGEXP = /<|&#?\w+;/;
2717 var TAG_NAME_REGEXP = /<([\w:-]+)/;
2718 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2719
2720 var wrapMap = {
2721   'option': [1, '<select multiple="multiple">', '</select>'],
2722
2723   'thead': [1, '<table>', '</table>'],
2724   'col': [2, '<table><colgroup>', '</colgroup></table>'],
2725   'tr': [2, '<table><tbody>', '</tbody></table>'],
2726   'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2727   '_default': [0, "", ""]
2728 };
2729
2730 wrapMap.optgroup = wrapMap.option;
2731 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2732 wrapMap.th = wrapMap.td;
2733
2734
2735 function jqLiteIsTextNode(html) {
2736   return !HTML_REGEXP.test(html);
2737 }
2738
2739 function jqLiteAcceptsData(node) {
2740   // The window object can accept data but has no nodeType
2741   // Otherwise we are only interested in elements (1) and documents (9)
2742   var nodeType = node.nodeType;
2743   return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2744 }
2745
2746 function jqLiteHasData(node) {
2747   for (var key in jqCache[node.ng339]) {
2748     return true;
2749   }
2750   return false;
2751 }
2752
2753 function jqLiteBuildFragment(html, context) {
2754   var tmp, tag, wrap,
2755       fragment = context.createDocumentFragment(),
2756       nodes = [], i;
2757
2758   if (jqLiteIsTextNode(html)) {
2759     // Convert non-html into a text node
2760     nodes.push(context.createTextNode(html));
2761   } else {
2762     // Convert html into DOM nodes
2763     tmp = tmp || fragment.appendChild(context.createElement("div"));
2764     tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2765     wrap = wrapMap[tag] || wrapMap._default;
2766     tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2767
2768     // Descend through wrappers to the right content
2769     i = wrap[0];
2770     while (i--) {
2771       tmp = tmp.lastChild;
2772     }
2773
2774     nodes = concat(nodes, tmp.childNodes);
2775
2776     tmp = fragment.firstChild;
2777     tmp.textContent = "";
2778   }
2779
2780   // Remove wrapper from fragment
2781   fragment.textContent = "";
2782   fragment.innerHTML = ""; // Clear inner HTML
2783   forEach(nodes, function(node) {
2784     fragment.appendChild(node);
2785   });
2786
2787   return fragment;
2788 }
2789
2790 function jqLiteParseHTML(html, context) {
2791   context = context || document;
2792   var parsed;
2793
2794   if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2795     return [context.createElement(parsed[1])];
2796   }
2797
2798   if ((parsed = jqLiteBuildFragment(html, context))) {
2799     return parsed.childNodes;
2800   }
2801
2802   return [];
2803 }
2804
2805 function jqLiteWrapNode(node, wrapper) {
2806   var parent = node.parentNode;
2807
2808   if (parent) {
2809     parent.replaceChild(wrapper, node);
2810   }
2811
2812   wrapper.appendChild(node);
2813 }
2814
2815
2816 // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2817 var jqLiteContains = Node.prototype.contains || function(arg) {
2818   // jshint bitwise: false
2819   return !!(this.compareDocumentPosition(arg) & 16);
2820   // jshint bitwise: true
2821 };
2822
2823 /////////////////////////////////////////////
2824 function JQLite(element) {
2825   if (element instanceof JQLite) {
2826     return element;
2827   }
2828
2829   var argIsString;
2830
2831   if (isString(element)) {
2832     element = trim(element);
2833     argIsString = true;
2834   }
2835   if (!(this instanceof JQLite)) {
2836     if (argIsString && element.charAt(0) != '<') {
2837       throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2838     }
2839     return new JQLite(element);
2840   }
2841
2842   if (argIsString) {
2843     jqLiteAddNodes(this, jqLiteParseHTML(element));
2844   } else {
2845     jqLiteAddNodes(this, element);
2846   }
2847 }
2848
2849 function jqLiteClone(element) {
2850   return element.cloneNode(true);
2851 }
2852
2853 function jqLiteDealoc(element, onlyDescendants) {
2854   if (!onlyDescendants) jqLiteRemoveData(element);
2855
2856   if (element.querySelectorAll) {
2857     var descendants = element.querySelectorAll('*');
2858     for (var i = 0, l = descendants.length; i < l; i++) {
2859       jqLiteRemoveData(descendants[i]);
2860     }
2861   }
2862 }
2863
2864 function jqLiteOff(element, type, fn, unsupported) {
2865   if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2866
2867   var expandoStore = jqLiteExpandoStore(element);
2868   var events = expandoStore && expandoStore.events;
2869   var handle = expandoStore && expandoStore.handle;
2870
2871   if (!handle) return; //no listeners registered
2872
2873   if (!type) {
2874     for (type in events) {
2875       if (type !== '$destroy') {
2876         removeEventListenerFn(element, type, handle);
2877       }
2878       delete events[type];
2879     }
2880   } else {
2881
2882     var removeHandler = function(type) {
2883       var listenerFns = events[type];
2884       if (isDefined(fn)) {
2885         arrayRemove(listenerFns || [], fn);
2886       }
2887       if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2888         removeEventListenerFn(element, type, handle);
2889         delete events[type];
2890       }
2891     };
2892
2893     forEach(type.split(' '), function(type) {
2894       removeHandler(type);
2895       if (MOUSE_EVENT_MAP[type]) {
2896         removeHandler(MOUSE_EVENT_MAP[type]);
2897       }
2898     });
2899   }
2900 }
2901
2902 function jqLiteRemoveData(element, name) {
2903   var expandoId = element.ng339;
2904   var expandoStore = expandoId && jqCache[expandoId];
2905
2906   if (expandoStore) {
2907     if (name) {
2908       delete expandoStore.data[name];
2909       return;
2910     }
2911
2912     if (expandoStore.handle) {
2913       if (expandoStore.events.$destroy) {
2914         expandoStore.handle({}, '$destroy');
2915       }
2916       jqLiteOff(element);
2917     }
2918     delete jqCache[expandoId];
2919     element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2920   }
2921 }
2922
2923
2924 function jqLiteExpandoStore(element, createIfNecessary) {
2925   var expandoId = element.ng339,
2926       expandoStore = expandoId && jqCache[expandoId];
2927
2928   if (createIfNecessary && !expandoStore) {
2929     element.ng339 = expandoId = jqNextId();
2930     expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2931   }
2932
2933   return expandoStore;
2934 }
2935
2936
2937 function jqLiteData(element, key, value) {
2938   if (jqLiteAcceptsData(element)) {
2939
2940     var isSimpleSetter = isDefined(value);
2941     var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2942     var massGetter = !key;
2943     var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2944     var data = expandoStore && expandoStore.data;
2945
2946     if (isSimpleSetter) { // data('key', value)
2947       data[key] = value;
2948     } else {
2949       if (massGetter) {  // data()
2950         return data;
2951       } else {
2952         if (isSimpleGetter) { // data('key')
2953           // don't force creation of expandoStore if it doesn't exist yet
2954           return data && data[key];
2955         } else { // mass-setter: data({key1: val1, key2: val2})
2956           extend(data, key);
2957         }
2958       }
2959     }
2960   }
2961 }
2962
2963 function jqLiteHasClass(element, selector) {
2964   if (!element.getAttribute) return false;
2965   return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2966       indexOf(" " + selector + " ") > -1);
2967 }
2968
2969 function jqLiteRemoveClass(element, cssClasses) {
2970   if (cssClasses && element.setAttribute) {
2971     forEach(cssClasses.split(' '), function(cssClass) {
2972       element.setAttribute('class', trim(
2973           (" " + (element.getAttribute('class') || '') + " ")
2974           .replace(/[\n\t]/g, " ")
2975           .replace(" " + trim(cssClass) + " ", " "))
2976       );
2977     });
2978   }
2979 }
2980
2981 function jqLiteAddClass(element, cssClasses) {
2982   if (cssClasses && element.setAttribute) {
2983     var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2984                             .replace(/[\n\t]/g, " ");
2985
2986     forEach(cssClasses.split(' '), function(cssClass) {
2987       cssClass = trim(cssClass);
2988       if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2989         existingClasses += cssClass + ' ';
2990       }
2991     });
2992
2993     element.setAttribute('class', trim(existingClasses));
2994   }
2995 }
2996
2997
2998 function jqLiteAddNodes(root, elements) {
2999   // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3000
3001   if (elements) {
3002
3003     // if a Node (the most common case)
3004     if (elements.nodeType) {
3005       root[root.length++] = elements;
3006     } else {
3007       var length = elements.length;
3008
3009       // if an Array or NodeList and not a Window
3010       if (typeof length === 'number' && elements.window !== elements) {
3011         if (length) {
3012           for (var i = 0; i < length; i++) {
3013             root[root.length++] = elements[i];
3014           }
3015         }
3016       } else {
3017         root[root.length++] = elements;
3018       }
3019     }
3020   }
3021 }
3022
3023
3024 function jqLiteController(element, name) {
3025   return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3026 }
3027
3028 function jqLiteInheritedData(element, name, value) {
3029   // if element is the document object work with the html element instead
3030   // this makes $(document).scope() possible
3031   if (element.nodeType == NODE_TYPE_DOCUMENT) {
3032     element = element.documentElement;
3033   }
3034   var names = isArray(name) ? name : [name];
3035
3036   while (element) {
3037     for (var i = 0, ii = names.length; i < ii; i++) {
3038       if (isDefined(value = jqLite.data(element, names[i]))) return value;
3039     }
3040
3041     // If dealing with a document fragment node with a host element, and no parent, use the host
3042     // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3043     // to lookup parent controllers.
3044     element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3045   }
3046 }
3047
3048 function jqLiteEmpty(element) {
3049   jqLiteDealoc(element, true);
3050   while (element.firstChild) {
3051     element.removeChild(element.firstChild);
3052   }
3053 }
3054
3055 function jqLiteRemove(element, keepData) {
3056   if (!keepData) jqLiteDealoc(element);
3057   var parent = element.parentNode;
3058   if (parent) parent.removeChild(element);
3059 }
3060
3061
3062 function jqLiteDocumentLoaded(action, win) {
3063   win = win || window;
3064   if (win.document.readyState === 'complete') {
3065     // Force the action to be run async for consistent behaviour
3066     // from the action's point of view
3067     // i.e. it will definitely not be in a $apply
3068     win.setTimeout(action);
3069   } else {
3070     // No need to unbind this handler as load is only ever called once
3071     jqLite(win).on('load', action);
3072   }
3073 }
3074
3075 //////////////////////////////////////////
3076 // Functions which are declared directly.
3077 //////////////////////////////////////////
3078 var JQLitePrototype = JQLite.prototype = {
3079   ready: function(fn) {
3080     var fired = false;
3081
3082     function trigger() {
3083       if (fired) return;
3084       fired = true;
3085       fn();
3086     }
3087
3088     // check if document is already loaded
3089     if (document.readyState === 'complete') {
3090       setTimeout(trigger);
3091     } else {
3092       this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3093       // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3094       // jshint -W064
3095       JQLite(window).on('load', trigger); // fallback to window.onload for others
3096       // jshint +W064
3097     }
3098   },
3099   toString: function() {
3100     var value = [];
3101     forEach(this, function(e) { value.push('' + e);});
3102     return '[' + value.join(', ') + ']';
3103   },
3104
3105   eq: function(index) {
3106       return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3107   },
3108
3109   length: 0,
3110   push: push,
3111   sort: [].sort,
3112   splice: [].splice
3113 };
3114
3115 //////////////////////////////////////////
3116 // Functions iterating getter/setters.
3117 // these functions return self on setter and
3118 // value on get.
3119 //////////////////////////////////////////
3120 var BOOLEAN_ATTR = {};
3121 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3122   BOOLEAN_ATTR[lowercase(value)] = value;
3123 });
3124 var BOOLEAN_ELEMENTS = {};
3125 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3126   BOOLEAN_ELEMENTS[value] = true;
3127 });
3128 var ALIASED_ATTR = {
3129   'ngMinlength': 'minlength',
3130   'ngMaxlength': 'maxlength',
3131   'ngMin': 'min',
3132   'ngMax': 'max',
3133   'ngPattern': 'pattern'
3134 };
3135
3136 function getBooleanAttrName(element, name) {
3137   // check dom last since we will most likely fail on name
3138   var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3139
3140   // booleanAttr is here twice to minimize DOM access
3141   return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3142 }
3143
3144 function getAliasedAttrName(name) {
3145   return ALIASED_ATTR[name];
3146 }
3147
3148 forEach({
3149   data: jqLiteData,
3150   removeData: jqLiteRemoveData,
3151   hasData: jqLiteHasData
3152 }, function(fn, name) {
3153   JQLite[name] = fn;
3154 });
3155
3156 forEach({
3157   data: jqLiteData,
3158   inheritedData: jqLiteInheritedData,
3159
3160   scope: function(element) {
3161     // Can't use jqLiteData here directly so we stay compatible with jQuery!
3162     return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3163   },
3164
3165   isolateScope: function(element) {
3166     // Can't use jqLiteData here directly so we stay compatible with jQuery!
3167     return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3168   },
3169
3170   controller: jqLiteController,
3171
3172   injector: function(element) {
3173     return jqLiteInheritedData(element, '$injector');
3174   },
3175
3176   removeAttr: function(element, name) {
3177     element.removeAttribute(name);
3178   },
3179
3180   hasClass: jqLiteHasClass,
3181
3182   css: function(element, name, value) {
3183     name = camelCase(name);
3184
3185     if (isDefined(value)) {
3186       element.style[name] = value;
3187     } else {
3188       return element.style[name];
3189     }
3190   },
3191
3192   attr: function(element, name, value) {
3193     var nodeType = element.nodeType;
3194     if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3195       return;
3196     }
3197     var lowercasedName = lowercase(name);
3198     if (BOOLEAN_ATTR[lowercasedName]) {
3199       if (isDefined(value)) {
3200         if (!!value) {
3201           element[name] = true;
3202           element.setAttribute(name, lowercasedName);
3203         } else {
3204           element[name] = false;
3205           element.removeAttribute(lowercasedName);
3206         }
3207       } else {
3208         return (element[name] ||
3209                  (element.attributes.getNamedItem(name) || noop).specified)
3210                ? lowercasedName
3211                : undefined;
3212       }
3213     } else if (isDefined(value)) {
3214       element.setAttribute(name, value);
3215     } else if (element.getAttribute) {
3216       // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3217       // some elements (e.g. Document) don't have get attribute, so return undefined
3218       var ret = element.getAttribute(name, 2);
3219       // normalize non-existing attributes to undefined (as jQuery)
3220       return ret === null ? undefined : ret;
3221     }
3222   },
3223
3224   prop: function(element, name, value) {
3225     if (isDefined(value)) {
3226       element[name] = value;
3227     } else {
3228       return element[name];
3229     }
3230   },
3231
3232   text: (function() {
3233     getText.$dv = '';
3234     return getText;
3235
3236     function getText(element, value) {
3237       if (isUndefined(value)) {
3238         var nodeType = element.nodeType;
3239         return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3240       }
3241       element.textContent = value;
3242     }
3243   })(),
3244
3245   val: function(element, value) {
3246     if (isUndefined(value)) {
3247       if (element.multiple && nodeName_(element) === 'select') {
3248         var result = [];
3249         forEach(element.options, function(option) {
3250           if (option.selected) {
3251             result.push(option.value || option.text);
3252           }
3253         });
3254         return result.length === 0 ? null : result;
3255       }
3256       return element.value;
3257     }
3258     element.value = value;
3259   },
3260
3261   html: function(element, value) {
3262     if (isUndefined(value)) {
3263       return element.innerHTML;
3264     }
3265     jqLiteDealoc(element, true);
3266     element.innerHTML = value;
3267   },
3268
3269   empty: jqLiteEmpty
3270 }, function(fn, name) {
3271   /**
3272    * Properties: writes return selection, reads return first value
3273    */
3274   JQLite.prototype[name] = function(arg1, arg2) {
3275     var i, key;
3276     var nodeCount = this.length;
3277
3278     // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3279     // in a way that survives minification.
3280     // jqLiteEmpty takes no arguments but is a setter.
3281     if (fn !== jqLiteEmpty &&
3282         (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3283       if (isObject(arg1)) {
3284
3285         // we are a write, but the object properties are the key/values
3286         for (i = 0; i < nodeCount; i++) {
3287           if (fn === jqLiteData) {
3288             // data() takes the whole object in jQuery
3289             fn(this[i], arg1);
3290           } else {
3291             for (key in arg1) {
3292               fn(this[i], key, arg1[key]);
3293             }
3294           }
3295         }
3296         // return self for chaining
3297         return this;
3298       } else {
3299         // we are a read, so read the first child.
3300         // TODO: do we still need this?
3301         var value = fn.$dv;
3302         // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3303         var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3304         for (var j = 0; j < jj; j++) {
3305           var nodeValue = fn(this[j], arg1, arg2);
3306           value = value ? value + nodeValue : nodeValue;
3307         }
3308         return value;
3309       }
3310     } else {
3311       // we are a write, so apply to all children
3312       for (i = 0; i < nodeCount; i++) {
3313         fn(this[i], arg1, arg2);
3314       }
3315       // return self for chaining
3316       return this;
3317     }
3318   };
3319 });
3320
3321 function createEventHandler(element, events) {
3322   var eventHandler = function(event, type) {
3323     // jQuery specific api
3324     event.isDefaultPrevented = function() {
3325       return event.defaultPrevented;
3326     };
3327
3328     var eventFns = events[type || event.type];
3329     var eventFnsLength = eventFns ? eventFns.length : 0;
3330
3331     if (!eventFnsLength) return;
3332
3333     if (isUndefined(event.immediatePropagationStopped)) {
3334       var originalStopImmediatePropagation = event.stopImmediatePropagation;
3335       event.stopImmediatePropagation = function() {
3336         event.immediatePropagationStopped = true;
3337
3338         if (event.stopPropagation) {
3339           event.stopPropagation();
3340         }
3341
3342         if (originalStopImmediatePropagation) {
3343           originalStopImmediatePropagation.call(event);
3344         }
3345       };
3346     }
3347
3348     event.isImmediatePropagationStopped = function() {
3349       return event.immediatePropagationStopped === true;
3350     };
3351
3352     // Some events have special handlers that wrap the real handler
3353     var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3354
3355     // Copy event handlers in case event handlers array is modified during execution.
3356     if ((eventFnsLength > 1)) {
3357       eventFns = shallowCopy(eventFns);
3358     }
3359
3360     for (var i = 0; i < eventFnsLength; i++) {
3361       if (!event.isImmediatePropagationStopped()) {
3362         handlerWrapper(element, event, eventFns[i]);
3363       }
3364     }
3365   };
3366
3367   // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3368   //       events on `element`
3369   eventHandler.elem = element;
3370   return eventHandler;
3371 }
3372
3373 function defaultHandlerWrapper(element, event, handler) {
3374   handler.call(element, event);
3375 }
3376
3377 function specialMouseHandlerWrapper(target, event, handler) {
3378   // Refer to jQuery's implementation of mouseenter & mouseleave
3379   // Read about mouseenter and mouseleave:
3380   // http://www.quirksmode.org/js/events_mouse.html#link8
3381   var related = event.relatedTarget;
3382   // For mousenter/leave call the handler if related is outside the target.
3383   // NB: No relatedTarget if the mouse left/entered the browser window
3384   if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3385     handler.call(target, event);
3386   }
3387 }
3388
3389 //////////////////////////////////////////
3390 // Functions iterating traversal.
3391 // These functions chain results into a single
3392 // selector.
3393 //////////////////////////////////////////
3394 forEach({
3395   removeData: jqLiteRemoveData,
3396
3397   on: function jqLiteOn(element, type, fn, unsupported) {
3398     if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3399
3400     // Do not add event handlers to non-elements because they will not be cleaned up.
3401     if (!jqLiteAcceptsData(element)) {
3402       return;
3403     }
3404
3405     var expandoStore = jqLiteExpandoStore(element, true);
3406     var events = expandoStore.events;
3407     var handle = expandoStore.handle;
3408
3409     if (!handle) {
3410       handle = expandoStore.handle = createEventHandler(element, events);
3411     }
3412
3413     // http://jsperf.com/string-indexof-vs-split
3414     var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3415     var i = types.length;
3416
3417     var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3418       var eventFns = events[type];
3419
3420       if (!eventFns) {
3421         eventFns = events[type] = [];
3422         eventFns.specialHandlerWrapper = specialHandlerWrapper;
3423         if (type !== '$destroy' && !noEventListener) {
3424           addEventListenerFn(element, type, handle);
3425         }
3426       }
3427
3428       eventFns.push(fn);
3429     };
3430
3431     while (i--) {
3432       type = types[i];
3433       if (MOUSE_EVENT_MAP[type]) {
3434         addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3435         addHandler(type, undefined, true);
3436       } else {
3437         addHandler(type);
3438       }
3439     }
3440   },
3441
3442   off: jqLiteOff,
3443
3444   one: function(element, type, fn) {
3445     element = jqLite(element);
3446
3447     //add the listener twice so that when it is called
3448     //you can remove the original function and still be
3449     //able to call element.off(ev, fn) normally
3450     element.on(type, function onFn() {
3451       element.off(type, fn);
3452       element.off(type, onFn);
3453     });
3454     element.on(type, fn);
3455   },
3456
3457   replaceWith: function(element, replaceNode) {
3458     var index, parent = element.parentNode;
3459     jqLiteDealoc(element);
3460     forEach(new JQLite(replaceNode), function(node) {
3461       if (index) {
3462         parent.insertBefore(node, index.nextSibling);
3463       } else {
3464         parent.replaceChild(node, element);
3465       }
3466       index = node;
3467     });
3468   },
3469
3470   children: function(element) {
3471     var children = [];
3472     forEach(element.childNodes, function(element) {
3473       if (element.nodeType === NODE_TYPE_ELEMENT) {
3474         children.push(element);
3475       }
3476     });
3477     return children;
3478   },
3479
3480   contents: function(element) {
3481     return element.contentDocument || element.childNodes || [];
3482   },
3483
3484   append: function(element, node) {
3485     var nodeType = element.nodeType;
3486     if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3487
3488     node = new JQLite(node);
3489
3490     for (var i = 0, ii = node.length; i < ii; i++) {
3491       var child = node[i];
3492       element.appendChild(child);
3493     }
3494   },
3495
3496   prepend: function(element, node) {
3497     if (element.nodeType === NODE_TYPE_ELEMENT) {
3498       var index = element.firstChild;
3499       forEach(new JQLite(node), function(child) {
3500         element.insertBefore(child, index);
3501       });
3502     }
3503   },
3504
3505   wrap: function(element, wrapNode) {
3506     jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
3507   },
3508
3509   remove: jqLiteRemove,
3510
3511   detach: function(element) {
3512     jqLiteRemove(element, true);
3513   },
3514
3515   after: function(element, newElement) {
3516     var index = element, parent = element.parentNode;
3517     newElement = new JQLite(newElement);
3518
3519     for (var i = 0, ii = newElement.length; i < ii; i++) {
3520       var node = newElement[i];
3521       parent.insertBefore(node, index.nextSibling);
3522       index = node;
3523     }
3524   },
3525
3526   addClass: jqLiteAddClass,
3527   removeClass: jqLiteRemoveClass,
3528
3529   toggleClass: function(element, selector, condition) {
3530     if (selector) {
3531       forEach(selector.split(' '), function(className) {
3532         var classCondition = condition;
3533         if (isUndefined(classCondition)) {
3534           classCondition = !jqLiteHasClass(element, className);
3535         }
3536         (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3537       });
3538     }
3539   },
3540
3541   parent: function(element) {
3542     var parent = element.parentNode;
3543     return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3544   },
3545
3546   next: function(element) {
3547     return element.nextElementSibling;
3548   },
3549
3550   find: function(element, selector) {
3551     if (element.getElementsByTagName) {
3552       return element.getElementsByTagName(selector);
3553     } else {
3554       return [];
3555     }
3556   },
3557
3558   clone: jqLiteClone,
3559
3560   triggerHandler: function(element, event, extraParameters) {
3561
3562     var dummyEvent, eventFnsCopy, handlerArgs;
3563     var eventName = event.type || event;
3564     var expandoStore = jqLiteExpandoStore(element);
3565     var events = expandoStore && expandoStore.events;
3566     var eventFns = events && events[eventName];
3567
3568     if (eventFns) {
3569       // Create a dummy event to pass to the handlers
3570       dummyEvent = {
3571         preventDefault: function() { this.defaultPrevented = true; },
3572         isDefaultPrevented: function() { return this.defaultPrevented === true; },
3573         stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3574         isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3575         stopPropagation: noop,
3576         type: eventName,
3577         target: element
3578       };
3579
3580       // If a custom event was provided then extend our dummy event with it
3581       if (event.type) {
3582         dummyEvent = extend(dummyEvent, event);
3583       }
3584
3585       // Copy event handlers in case event handlers array is modified during execution.
3586       eventFnsCopy = shallowCopy(eventFns);
3587       handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3588
3589       forEach(eventFnsCopy, function(fn) {
3590         if (!dummyEvent.isImmediatePropagationStopped()) {
3591           fn.apply(element, handlerArgs);
3592         }
3593       });
3594     }
3595   }
3596 }, function(fn, name) {
3597   /**
3598    * chaining functions
3599    */
3600   JQLite.prototype[name] = function(arg1, arg2, arg3) {
3601     var value;
3602
3603     for (var i = 0, ii = this.length; i < ii; i++) {
3604       if (isUndefined(value)) {
3605         value = fn(this[i], arg1, arg2, arg3);
3606         if (isDefined(value)) {
3607           // any function which returns a value needs to be wrapped
3608           value = jqLite(value);
3609         }
3610       } else {
3611         jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3612       }
3613     }
3614     return isDefined(value) ? value : this;
3615   };
3616
3617   // bind legacy bind/unbind to on/off
3618   JQLite.prototype.bind = JQLite.prototype.on;
3619   JQLite.prototype.unbind = JQLite.prototype.off;
3620 });
3621
3622
3623 // Provider for private $$jqLite service
3624 function $$jqLiteProvider() {
3625   this.$get = function $$jqLite() {
3626     return extend(JQLite, {
3627       hasClass: function(node, classes) {
3628         if (node.attr) node = node[0];
3629         return jqLiteHasClass(node, classes);
3630       },
3631       addClass: function(node, classes) {
3632         if (node.attr) node = node[0];
3633         return jqLiteAddClass(node, classes);
3634       },
3635       removeClass: function(node, classes) {
3636         if (node.attr) node = node[0];
3637         return jqLiteRemoveClass(node, classes);
3638       }
3639     });
3640   };
3641 }
3642
3643 /**
3644  * Computes a hash of an 'obj'.
3645  * Hash of a:
3646  *  string is string
3647  *  number is number as string
3648  *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3649  *         that is also assigned to the $$hashKey property of the object.
3650  *
3651  * @param obj
3652  * @returns {string} hash string such that the same input will have the same hash string.
3653  *         The resulting string key is in 'type:hashKey' format.
3654  */
3655 function hashKey(obj, nextUidFn) {
3656   var key = obj && obj.$$hashKey;
3657
3658   if (key) {
3659     if (typeof key === 'function') {
3660       key = obj.$$hashKey();
3661     }
3662     return key;
3663   }
3664
3665   var objType = typeof obj;
3666   if (objType == 'function' || (objType == 'object' && obj !== null)) {
3667     key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3668   } else {
3669     key = objType + ':' + obj;
3670   }
3671
3672   return key;
3673 }
3674
3675 /**
3676  * HashMap which can use objects as keys
3677  */
3678 function HashMap(array, isolatedUid) {
3679   if (isolatedUid) {
3680     var uid = 0;
3681     this.nextUid = function() {
3682       return ++uid;
3683     };
3684   }
3685   forEach(array, this.put, this);
3686 }
3687 HashMap.prototype = {
3688   /**
3689    * Store key value pair
3690    * @param key key to store can be any type
3691    * @param value value to store can be any type
3692    */
3693   put: function(key, value) {
3694     this[hashKey(key, this.nextUid)] = value;
3695   },
3696
3697   /**
3698    * @param key
3699    * @returns {Object} the value for the key
3700    */
3701   get: function(key) {
3702     return this[hashKey(key, this.nextUid)];
3703   },
3704
3705   /**
3706    * Remove the key/value pair
3707    * @param key
3708    */
3709   remove: function(key) {
3710     var value = this[key = hashKey(key, this.nextUid)];
3711     delete this[key];
3712     return value;
3713   }
3714 };
3715
3716 var $$HashMapProvider = [function() {
3717   this.$get = [function() {
3718     return HashMap;
3719   }];
3720 }];
3721
3722 /**
3723  * @ngdoc function
3724  * @module ng
3725  * @name angular.injector
3726  * @kind function
3727  *
3728  * @description
3729  * Creates an injector object that can be used for retrieving services as well as for
3730  * dependency injection (see {@link guide/di dependency injection}).
3731  *
3732  * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3733  *     {@link angular.module}. The `ng` module must be explicitly added.
3734  * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3735  *     disallows argument name annotation inference.
3736  * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3737  *
3738  * @example
3739  * Typical usage
3740  * ```js
3741  *   // create an injector
3742  *   var $injector = angular.injector(['ng']);
3743  *
3744  *   // use the injector to kick off your application
3745  *   // use the type inference to auto inject arguments, or use implicit injection
3746  *   $injector.invoke(function($rootScope, $compile, $document) {
3747  *     $compile($document)($rootScope);
3748  *     $rootScope.$digest();
3749  *   });
3750  * ```
3751  *
3752  * Sometimes you want to get access to the injector of a currently running Angular app
3753  * from outside Angular. Perhaps, you want to inject and compile some markup after the
3754  * application has been bootstrapped. You can do this using the extra `injector()` added
3755  * to JQuery/jqLite elements. See {@link angular.element}.
3756  *
3757  * *This is fairly rare but could be the case if a third party library is injecting the
3758  * markup.*
3759  *
3760  * In the following example a new block of HTML containing a `ng-controller`
3761  * directive is added to the end of the document body by JQuery. We then compile and link
3762  * it into the current AngularJS scope.
3763  *
3764  * ```js
3765  * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3766  * $(document.body).append($div);
3767  *
3768  * angular.element(document).injector().invoke(function($compile) {
3769  *   var scope = angular.element($div).scope();
3770  *   $compile($div)(scope);
3771  * });
3772  * ```
3773  */
3774
3775
3776 /**
3777  * @ngdoc module
3778  * @name auto
3779  * @description
3780  *
3781  * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3782  */
3783
3784 var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3785 var FN_ARG_SPLIT = /,/;
3786 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3787 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3788 var $injectorMinErr = minErr('$injector');
3789
3790 function anonFn(fn) {
3791   // For anonymous functions, showing at the very least the function signature can help in
3792   // debugging.
3793   var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3794       args = fnText.match(FN_ARGS);
3795   if (args) {
3796     return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3797   }
3798   return 'fn';
3799 }
3800
3801 function annotate(fn, strictDi, name) {
3802   var $inject,
3803       fnText,
3804       argDecl,
3805       last;
3806
3807   if (typeof fn === 'function') {
3808     if (!($inject = fn.$inject)) {
3809       $inject = [];
3810       if (fn.length) {
3811         if (strictDi) {
3812           if (!isString(name) || !name) {
3813             name = fn.name || anonFn(fn);
3814           }
3815           throw $injectorMinErr('strictdi',
3816             '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3817         }
3818         fnText = fn.toString().replace(STRIP_COMMENTS, '');
3819         argDecl = fnText.match(FN_ARGS);
3820         forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3821           arg.replace(FN_ARG, function(all, underscore, name) {
3822             $inject.push(name);
3823           });
3824         });
3825       }
3826       fn.$inject = $inject;
3827     }
3828   } else if (isArray(fn)) {
3829     last = fn.length - 1;
3830     assertArgFn(fn[last], 'fn');
3831     $inject = fn.slice(0, last);
3832   } else {
3833     assertArgFn(fn, 'fn', true);
3834   }
3835   return $inject;
3836 }
3837
3838 ///////////////////////////////////////
3839
3840 /**
3841  * @ngdoc service
3842  * @name $injector
3843  *
3844  * @description
3845  *
3846  * `$injector` is used to retrieve object instances as defined by
3847  * {@link auto.$provide provider}, instantiate types, invoke methods,
3848  * and load modules.
3849  *
3850  * The following always holds true:
3851  *
3852  * ```js
3853  *   var $injector = angular.injector();
3854  *   expect($injector.get('$injector')).toBe($injector);
3855  *   expect($injector.invoke(function($injector) {
3856  *     return $injector;
3857  *   })).toBe($injector);
3858  * ```
3859  *
3860  * # Injection Function Annotation
3861  *
3862  * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3863  * following are all valid ways of annotating function with injection arguments and are equivalent.
3864  *
3865  * ```js
3866  *   // inferred (only works if code not minified/obfuscated)
3867  *   $injector.invoke(function(serviceA){});
3868  *
3869  *   // annotated
3870  *   function explicit(serviceA) {};
3871  *   explicit.$inject = ['serviceA'];
3872  *   $injector.invoke(explicit);
3873  *
3874  *   // inline
3875  *   $injector.invoke(['serviceA', function(serviceA){}]);
3876  * ```
3877  *
3878  * ## Inference
3879  *
3880  * In JavaScript calling `toString()` on a function returns the function definition. The definition
3881  * can then be parsed and the function arguments can be extracted. This method of discovering
3882  * annotations is disallowed when the injector is in strict mode.
3883  * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3884  * argument names.
3885  *
3886  * ## `$inject` Annotation
3887  * By adding an `$inject` property onto a function the injection parameters can be specified.
3888  *
3889  * ## Inline
3890  * As an array of injection names, where the last item in the array is the function to call.
3891  */
3892
3893 /**
3894  * @ngdoc method
3895  * @name $injector#get
3896  *
3897  * @description
3898  * Return an instance of the service.
3899  *
3900  * @param {string} name The name of the instance to retrieve.
3901  * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3902  * @return {*} The instance.
3903  */
3904
3905 /**
3906  * @ngdoc method
3907  * @name $injector#invoke
3908  *
3909  * @description
3910  * Invoke the method and supply the method arguments from the `$injector`.
3911  *
3912  * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3913  *   injected according to the {@link guide/di $inject Annotation} rules.
3914  * @param {Object=} self The `this` for the invoked method.
3915  * @param {Object=} locals Optional object. If preset then any argument names are read from this
3916  *                         object first, before the `$injector` is consulted.
3917  * @returns {*} the value returned by the invoked `fn` function.
3918  */
3919
3920 /**
3921  * @ngdoc method
3922  * @name $injector#has
3923  *
3924  * @description
3925  * Allows the user to query if the particular service exists.
3926  *
3927  * @param {string} name Name of the service to query.
3928  * @returns {boolean} `true` if injector has given service.
3929  */
3930
3931 /**
3932  * @ngdoc method
3933  * @name $injector#instantiate
3934  * @description
3935  * Create a new instance of JS type. The method takes a constructor function, invokes the new
3936  * operator, and supplies all of the arguments to the constructor function as specified by the
3937  * constructor annotation.
3938  *
3939  * @param {Function} Type Annotated constructor function.
3940  * @param {Object=} locals Optional object. If preset then any argument names are read from this
3941  * object first, before the `$injector` is consulted.
3942  * @returns {Object} new instance of `Type`.
3943  */
3944
3945 /**
3946  * @ngdoc method
3947  * @name $injector#annotate
3948  *
3949  * @description
3950  * Returns an array of service names which the function is requesting for injection. This API is
3951  * used by the injector to determine which services need to be injected into the function when the
3952  * function is invoked. There are three ways in which the function can be annotated with the needed
3953  * dependencies.
3954  *
3955  * # Argument names
3956  *
3957  * The simplest form is to extract the dependencies from the arguments of the function. This is done
3958  * by converting the function into a string using `toString()` method and extracting the argument
3959  * names.
3960  * ```js
3961  *   // Given
3962  *   function MyController($scope, $route) {
3963  *     // ...
3964  *   }
3965  *
3966  *   // Then
3967  *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3968  * ```
3969  *
3970  * You can disallow this method by using strict injection mode.
3971  *
3972  * This method does not work with code minification / obfuscation. For this reason the following
3973  * annotation strategies are supported.
3974  *
3975  * # The `$inject` property
3976  *
3977  * If a function has an `$inject` property and its value is an array of strings, then the strings
3978  * represent names of services to be injected into the function.
3979  * ```js
3980  *   // Given
3981  *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
3982  *     // ...
3983  *   }
3984  *   // Define function dependencies
3985  *   MyController['$inject'] = ['$scope', '$route'];
3986  *
3987  *   // Then
3988  *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3989  * ```
3990  *
3991  * # The array notation
3992  *
3993  * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3994  * is very inconvenient. In these situations using the array notation to specify the dependencies in
3995  * a way that survives minification is a better choice:
3996  *
3997  * ```js
3998  *   // We wish to write this (not minification / obfuscation safe)
3999  *   injector.invoke(function($compile, $rootScope) {
4000  *     // ...
4001  *   });
4002  *
4003  *   // We are forced to write break inlining
4004  *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4005  *     // ...
4006  *   };
4007  *   tmpFn.$inject = ['$compile', '$rootScope'];
4008  *   injector.invoke(tmpFn);
4009  *
4010  *   // To better support inline function the inline annotation is supported
4011  *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4012  *     // ...
4013  *   }]);
4014  *
4015  *   // Therefore
4016  *   expect(injector.annotate(
4017  *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4018  *    ).toEqual(['$compile', '$rootScope']);
4019  * ```
4020  *
4021  * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4022  * be retrieved as described above.
4023  *
4024  * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4025  *
4026  * @returns {Array.<string>} The names of the services which the function requires.
4027  */
4028
4029
4030
4031
4032 /**
4033  * @ngdoc service
4034  * @name $provide
4035  *
4036  * @description
4037  *
4038  * The {@link auto.$provide $provide} service has a number of methods for registering components
4039  * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4040  * {@link angular.Module}.
4041  *
4042  * An Angular **service** is a singleton object created by a **service factory**.  These **service
4043  * factories** are functions which, in turn, are created by a **service provider**.
4044  * The **service providers** are constructor functions. When instantiated they must contain a
4045  * property called `$get`, which holds the **service factory** function.
4046  *
4047  * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4048  * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4049  * function to get the instance of the **service**.
4050  *
4051  * Often services have no configuration options and there is no need to add methods to the service
4052  * provider.  The provider will be no more than a constructor function with a `$get` property. For
4053  * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4054  * services without specifying a provider.
4055  *
4056  * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4057  *     {@link auto.$injector $injector}
4058  * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4059  *     providers and services.
4060  * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4061  *     services, not providers.
4062  * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4063  *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
4064  *     given factory function.
4065  * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4066  *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4067  *      a new object using the given constructor function.
4068  *
4069  * See the individual methods for more information and examples.
4070  */
4071
4072 /**
4073  * @ngdoc method
4074  * @name $provide#provider
4075  * @description
4076  *
4077  * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4078  * are constructor functions, whose instances are responsible for "providing" a factory for a
4079  * service.
4080  *
4081  * Service provider names start with the name of the service they provide followed by `Provider`.
4082  * For example, the {@link ng.$log $log} service has a provider called
4083  * {@link ng.$logProvider $logProvider}.
4084  *
4085  * Service provider objects can have additional methods which allow configuration of the provider
4086  * and its service. Importantly, you can configure what kind of service is created by the `$get`
4087  * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4088  * method {@link ng.$logProvider#debugEnabled debugEnabled}
4089  * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4090  * console or not.
4091  *
4092  * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4093                         'Provider'` key.
4094  * @param {(Object|function())} provider If the provider is:
4095  *
4096  *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4097  *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4098  *   - `Constructor`: a new instance of the provider will be created using
4099  *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4100  *
4101  * @returns {Object} registered provider instance
4102
4103  * @example
4104  *
4105  * The following example shows how to create a simple event tracking service and register it using
4106  * {@link auto.$provide#provider $provide.provider()}.
4107  *
4108  * ```js
4109  *  // Define the eventTracker provider
4110  *  function EventTrackerProvider() {
4111  *    var trackingUrl = '/track';
4112  *
4113  *    // A provider method for configuring where the tracked events should been saved
4114  *    this.setTrackingUrl = function(url) {
4115  *      trackingUrl = url;
4116  *    };
4117  *
4118  *    // The service factory function
4119  *    this.$get = ['$http', function($http) {
4120  *      var trackedEvents = {};
4121  *      return {
4122  *        // Call this to track an event
4123  *        event: function(event) {
4124  *          var count = trackedEvents[event] || 0;
4125  *          count += 1;
4126  *          trackedEvents[event] = count;
4127  *          return count;
4128  *        },
4129  *        // Call this to save the tracked events to the trackingUrl
4130  *        save: function() {
4131  *          $http.post(trackingUrl, trackedEvents);
4132  *        }
4133  *      };
4134  *    }];
4135  *  }
4136  *
4137  *  describe('eventTracker', function() {
4138  *    var postSpy;
4139  *
4140  *    beforeEach(module(function($provide) {
4141  *      // Register the eventTracker provider
4142  *      $provide.provider('eventTracker', EventTrackerProvider);
4143  *    }));
4144  *
4145  *    beforeEach(module(function(eventTrackerProvider) {
4146  *      // Configure eventTracker provider
4147  *      eventTrackerProvider.setTrackingUrl('/custom-track');
4148  *    }));
4149  *
4150  *    it('tracks events', inject(function(eventTracker) {
4151  *      expect(eventTracker.event('login')).toEqual(1);
4152  *      expect(eventTracker.event('login')).toEqual(2);
4153  *    }));
4154  *
4155  *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4156  *      postSpy = spyOn($http, 'post');
4157  *      eventTracker.event('login');
4158  *      eventTracker.save();
4159  *      expect(postSpy).toHaveBeenCalled();
4160  *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4161  *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4162  *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4163  *    }));
4164  *  });
4165  * ```
4166  */
4167
4168 /**
4169  * @ngdoc method
4170  * @name $provide#factory
4171  * @description
4172  *
4173  * Register a **service factory**, which will be called to return the service instance.
4174  * This is short for registering a service where its provider consists of only a `$get` property,
4175  * which is the given service factory function.
4176  * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4177  * configure your service in a provider.
4178  *
4179  * @param {string} name The name of the instance.
4180  * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4181  *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4182  * @returns {Object} registered provider instance
4183  *
4184  * @example
4185  * Here is an example of registering a service
4186  * ```js
4187  *   $provide.factory('ping', ['$http', function($http) {
4188  *     return function ping() {
4189  *       return $http.send('/ping');
4190  *     };
4191  *   }]);
4192  * ```
4193  * You would then inject and use this service like this:
4194  * ```js
4195  *   someModule.controller('Ctrl', ['ping', function(ping) {
4196  *     ping();
4197  *   }]);
4198  * ```
4199  */
4200
4201
4202 /**
4203  * @ngdoc method
4204  * @name $provide#service
4205  * @description
4206  *
4207  * Register a **service constructor**, which will be invoked with `new` to create the service
4208  * instance.
4209  * This is short for registering a service where its provider's `$get` property is a factory
4210  * function that returns an instance instantiated by the injector from the service constructor
4211  * function.
4212  *
4213  * Internally it looks a bit like this:
4214  *
4215  * ```
4216  * {
4217  *   $get: function() {
4218  *     return $injector.instantiate(constructor);
4219  *   }
4220  * }
4221  * ```
4222  *
4223  *
4224  * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4225  * as a type/class.
4226  *
4227  * @param {string} name The name of the instance.
4228  * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4229  *     that will be instantiated.
4230  * @returns {Object} registered provider instance
4231  *
4232  * @example
4233  * Here is an example of registering a service using
4234  * {@link auto.$provide#service $provide.service(class)}.
4235  * ```js
4236  *   var Ping = function($http) {
4237  *     this.$http = $http;
4238  *   };
4239  *
4240  *   Ping.$inject = ['$http'];
4241  *
4242  *   Ping.prototype.send = function() {
4243  *     return this.$http.get('/ping');
4244  *   };
4245  *   $provide.service('ping', Ping);
4246  * ```
4247  * You would then inject and use this service like this:
4248  * ```js
4249  *   someModule.controller('Ctrl', ['ping', function(ping) {
4250  *     ping.send();
4251  *   }]);
4252  * ```
4253  */
4254
4255
4256 /**
4257  * @ngdoc method
4258  * @name $provide#value
4259  * @description
4260  *
4261  * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4262  * number, an array, an object or a function. This is short for registering a service where its
4263  * provider's `$get` property is a factory function that takes no arguments and returns the **value
4264  * service**. That also means it is not possible to inject other services into a value service.
4265  *
4266  * Value services are similar to constant services, except that they cannot be injected into a
4267  * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4268  * an Angular {@link auto.$provide#decorator decorator}.
4269  *
4270  * @param {string} name The name of the instance.
4271  * @param {*} value The value.
4272  * @returns {Object} registered provider instance
4273  *
4274  * @example
4275  * Here are some examples of creating value services.
4276  * ```js
4277  *   $provide.value('ADMIN_USER', 'admin');
4278  *
4279  *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4280  *
4281  *   $provide.value('halfOf', function(value) {
4282  *     return value / 2;
4283  *   });
4284  * ```
4285  */
4286
4287
4288 /**
4289  * @ngdoc method
4290  * @name $provide#constant
4291  * @description
4292  *
4293  * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
4294  * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
4295  * possible to inject other services into a constant.
4296  *
4297  * But unlike {@link auto.$provide#value value}, a constant can be
4298  * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4299  * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4300  *
4301  * @param {string} name The name of the constant.
4302  * @param {*} value The constant value.
4303  * @returns {Object} registered instance
4304  *
4305  * @example
4306  * Here a some examples of creating constants:
4307  * ```js
4308  *   $provide.constant('SHARD_HEIGHT', 306);
4309  *
4310  *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4311  *
4312  *   $provide.constant('double', function(value) {
4313  *     return value * 2;
4314  *   });
4315  * ```
4316  */
4317
4318
4319 /**
4320  * @ngdoc method
4321  * @name $provide#decorator
4322  * @description
4323  *
4324  * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4325  * intercepts the creation of a service, allowing it to override or modify the behavior of the
4326  * service. The object returned by the decorator may be the original service, or a new service
4327  * object which replaces or wraps and delegates to the original service.
4328  *
4329  * @param {string} name The name of the service to decorate.
4330  * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4331  *    instantiated and should return the decorated service instance. The function is called using
4332  *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4333  *    Local injection arguments:
4334  *
4335  *    * `$delegate` - The original service instance, which can be monkey patched, configured,
4336  *      decorated or delegated to.
4337  *
4338  * @example
4339  * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4340  * calls to {@link ng.$log#error $log.warn()}.
4341  * ```js
4342  *   $provide.decorator('$log', ['$delegate', function($delegate) {
4343  *     $delegate.warn = $delegate.error;
4344  *     return $delegate;
4345  *   }]);
4346  * ```
4347  */
4348
4349
4350 function createInjector(modulesToLoad, strictDi) {
4351   strictDi = (strictDi === true);
4352   var INSTANTIATING = {},
4353       providerSuffix = 'Provider',
4354       path = [],
4355       loadedModules = new HashMap([], true),
4356       providerCache = {
4357         $provide: {
4358             provider: supportObject(provider),
4359             factory: supportObject(factory),
4360             service: supportObject(service),
4361             value: supportObject(value),
4362             constant: supportObject(constant),
4363             decorator: decorator
4364           }
4365       },
4366       providerInjector = (providerCache.$injector =
4367           createInternalInjector(providerCache, function(serviceName, caller) {
4368             if (angular.isString(caller)) {
4369               path.push(caller);
4370             }
4371             throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4372           })),
4373       instanceCache = {},
4374       instanceInjector = (instanceCache.$injector =
4375           createInternalInjector(instanceCache, function(serviceName, caller) {
4376             var provider = providerInjector.get(serviceName + providerSuffix, caller);
4377             return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4378           }));
4379
4380
4381   forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4382
4383   return instanceInjector;
4384
4385   ////////////////////////////////////
4386   // $provider
4387   ////////////////////////////////////
4388
4389   function supportObject(delegate) {
4390     return function(key, value) {
4391       if (isObject(key)) {
4392         forEach(key, reverseParams(delegate));
4393       } else {
4394         return delegate(key, value);
4395       }
4396     };
4397   }
4398
4399   function provider(name, provider_) {
4400     assertNotHasOwnProperty(name, 'service');
4401     if (isFunction(provider_) || isArray(provider_)) {
4402       provider_ = providerInjector.instantiate(provider_);
4403     }
4404     if (!provider_.$get) {
4405       throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4406     }
4407     return providerCache[name + providerSuffix] = provider_;
4408   }
4409
4410   function enforceReturnValue(name, factory) {
4411     return function enforcedReturnValue() {
4412       var result = instanceInjector.invoke(factory, this);
4413       if (isUndefined(result)) {
4414         throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4415       }
4416       return result;
4417     };
4418   }
4419
4420   function factory(name, factoryFn, enforce) {
4421     return provider(name, {
4422       $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4423     });
4424   }
4425
4426   function service(name, constructor) {
4427     return factory(name, ['$injector', function($injector) {
4428       return $injector.instantiate(constructor);
4429     }]);
4430   }
4431
4432   function value(name, val) { return factory(name, valueFn(val), false); }
4433
4434   function constant(name, value) {
4435     assertNotHasOwnProperty(name, 'constant');
4436     providerCache[name] = value;
4437     instanceCache[name] = value;
4438   }
4439
4440   function decorator(serviceName, decorFn) {
4441     var origProvider = providerInjector.get(serviceName + providerSuffix),
4442         orig$get = origProvider.$get;
4443
4444     origProvider.$get = function() {
4445       var origInstance = instanceInjector.invoke(orig$get, origProvider);
4446       return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4447     };
4448   }
4449
4450   ////////////////////////////////////
4451   // Module Loading
4452   ////////////////////////////////////
4453   function loadModules(modulesToLoad) {
4454     assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4455     var runBlocks = [], moduleFn;
4456     forEach(modulesToLoad, function(module) {
4457       if (loadedModules.get(module)) return;
4458       loadedModules.put(module, true);
4459
4460       function runInvokeQueue(queue) {
4461         var i, ii;
4462         for (i = 0, ii = queue.length; i < ii; i++) {
4463           var invokeArgs = queue[i],
4464               provider = providerInjector.get(invokeArgs[0]);
4465
4466           provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4467         }
4468       }
4469
4470       try {
4471         if (isString(module)) {
4472           moduleFn = angularModule(module);
4473           runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4474           runInvokeQueue(moduleFn._invokeQueue);
4475           runInvokeQueue(moduleFn._configBlocks);
4476         } else if (isFunction(module)) {
4477             runBlocks.push(providerInjector.invoke(module));
4478         } else if (isArray(module)) {
4479             runBlocks.push(providerInjector.invoke(module));
4480         } else {
4481           assertArgFn(module, 'module');
4482         }
4483       } catch (e) {
4484         if (isArray(module)) {
4485           module = module[module.length - 1];
4486         }
4487         if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4488           // Safari & FF's stack traces don't contain error.message content
4489           // unlike those of Chrome and IE
4490           // So if stack doesn't contain message, we create a new string that contains both.
4491           // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4492           /* jshint -W022 */
4493           e = e.message + '\n' + e.stack;
4494         }
4495         throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4496                   module, e.stack || e.message || e);
4497       }
4498     });
4499     return runBlocks;
4500   }
4501
4502   ////////////////////////////////////
4503   // internal Injector
4504   ////////////////////////////////////
4505
4506   function createInternalInjector(cache, factory) {
4507
4508     function getService(serviceName, caller) {
4509       if (cache.hasOwnProperty(serviceName)) {
4510         if (cache[serviceName] === INSTANTIATING) {
4511           throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4512                     serviceName + ' <- ' + path.join(' <- '));
4513         }
4514         return cache[serviceName];
4515       } else {
4516         try {
4517           path.unshift(serviceName);
4518           cache[serviceName] = INSTANTIATING;
4519           return cache[serviceName] = factory(serviceName, caller);
4520         } catch (err) {
4521           if (cache[serviceName] === INSTANTIATING) {
4522             delete cache[serviceName];
4523           }
4524           throw err;
4525         } finally {
4526           path.shift();
4527         }
4528       }
4529     }
4530
4531     function invoke(fn, self, locals, serviceName) {
4532       if (typeof locals === 'string') {
4533         serviceName = locals;
4534         locals = null;
4535       }
4536
4537       var args = [],
4538           $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4539           length, i,
4540           key;
4541
4542       for (i = 0, length = $inject.length; i < length; i++) {
4543         key = $inject[i];
4544         if (typeof key !== 'string') {
4545           throw $injectorMinErr('itkn',
4546                   'Incorrect injection token! Expected service name as string, got {0}', key);
4547         }
4548         args.push(
4549           locals && locals.hasOwnProperty(key)
4550           ? locals[key]
4551           : getService(key, serviceName)
4552         );
4553       }
4554       if (isArray(fn)) {
4555         fn = fn[length];
4556       }
4557
4558       // http://jsperf.com/angularjs-invoke-apply-vs-switch
4559       // #5388
4560       return fn.apply(self, args);
4561     }
4562
4563     function instantiate(Type, locals, serviceName) {
4564       // Check if Type is annotated and use just the given function at n-1 as parameter
4565       // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4566       // Object creation: http://jsperf.com/create-constructor/2
4567       var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4568       var returnedValue = invoke(Type, instance, locals, serviceName);
4569
4570       return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4571     }
4572
4573     return {
4574       invoke: invoke,
4575       instantiate: instantiate,
4576       get: getService,
4577       annotate: createInjector.$$annotate,
4578       has: function(name) {
4579         return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4580       }
4581     };
4582   }
4583 }
4584
4585 createInjector.$$annotate = annotate;
4586
4587 /**
4588  * @ngdoc provider
4589  * @name $anchorScrollProvider
4590  *
4591  * @description
4592  * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4593  * {@link ng.$location#hash $location.hash()} changes.
4594  */
4595 function $AnchorScrollProvider() {
4596
4597   var autoScrollingEnabled = true;
4598
4599   /**
4600    * @ngdoc method
4601    * @name $anchorScrollProvider#disableAutoScrolling
4602    *
4603    * @description
4604    * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4605    * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4606    * Use this method to disable automatic scrolling.
4607    *
4608    * If automatic scrolling is disabled, one must explicitly call
4609    * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4610    * current hash.
4611    */
4612   this.disableAutoScrolling = function() {
4613     autoScrollingEnabled = false;
4614   };
4615
4616   /**
4617    * @ngdoc service
4618    * @name $anchorScroll
4619    * @kind function
4620    * @requires $window
4621    * @requires $location
4622    * @requires $rootScope
4623    *
4624    * @description
4625    * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4626    * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4627    * in the
4628    * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4629    *
4630    * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4631    * match any anchor whenever it changes. This can be disabled by calling
4632    * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4633    *
4634    * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4635    * vertical scroll-offset (either fixed or dynamic).
4636    *
4637    * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4638    *                       {@link ng.$location#hash $location.hash()} will be used.
4639    *
4640    * @property {(number|function|jqLite)} yOffset
4641    * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4642    * positioned elements at the top of the page, such as navbars, headers etc.
4643    *
4644    * `yOffset` can be specified in various ways:
4645    * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4646    * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4647    *   a number representing the offset (in pixels).<br /><br />
4648    * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4649    *   the top of the page to the element's bottom will be used as offset.<br />
4650    *   **Note**: The element will be taken into account only as long as its `position` is set to
4651    *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4652    *   their height and/or positioning according to the viewport's size.
4653    *
4654    * <br />
4655    * <div class="alert alert-warning">
4656    * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4657    * not some child element.
4658    * </div>
4659    *
4660    * @example
4661      <example module="anchorScrollExample">
4662        <file name="index.html">
4663          <div id="scrollArea" ng-controller="ScrollController">
4664            <a ng-click="gotoBottom()">Go to bottom</a>
4665            <a id="bottom"></a> You're at the bottom!
4666          </div>
4667        </file>
4668        <file name="script.js">
4669          angular.module('anchorScrollExample', [])
4670            .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4671              function ($scope, $location, $anchorScroll) {
4672                $scope.gotoBottom = function() {
4673                  // set the location.hash to the id of
4674                  // the element you wish to scroll to.
4675                  $location.hash('bottom');
4676
4677                  // call $anchorScroll()
4678                  $anchorScroll();
4679                };
4680              }]);
4681        </file>
4682        <file name="style.css">
4683          #scrollArea {
4684            height: 280px;
4685            overflow: auto;
4686          }
4687
4688          #bottom {
4689            display: block;
4690            margin-top: 2000px;
4691          }
4692        </file>
4693      </example>
4694    *
4695    * <hr />
4696    * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4697    * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4698    *
4699    * @example
4700      <example module="anchorScrollOffsetExample">
4701        <file name="index.html">
4702          <div class="fixed-header" ng-controller="headerCtrl">
4703            <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4704              Go to anchor {{x}}
4705            </a>
4706          </div>
4707          <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4708            Anchor {{x}} of 5
4709          </div>
4710        </file>
4711        <file name="script.js">
4712          angular.module('anchorScrollOffsetExample', [])
4713            .run(['$anchorScroll', function($anchorScroll) {
4714              $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4715            }])
4716            .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4717              function ($anchorScroll, $location, $scope) {
4718                $scope.gotoAnchor = function(x) {
4719                  var newHash = 'anchor' + x;
4720                  if ($location.hash() !== newHash) {
4721                    // set the $location.hash to `newHash` and
4722                    // $anchorScroll will automatically scroll to it
4723                    $location.hash('anchor' + x);
4724                  } else {
4725                    // call $anchorScroll() explicitly,
4726                    // since $location.hash hasn't changed
4727                    $anchorScroll();
4728                  }
4729                };
4730              }
4731            ]);
4732        </file>
4733        <file name="style.css">
4734          body {
4735            padding-top: 50px;
4736          }
4737
4738          .anchor {
4739            border: 2px dashed DarkOrchid;
4740            padding: 10px 10px 200px 10px;
4741          }
4742
4743          .fixed-header {
4744            background-color: rgba(0, 0, 0, 0.2);
4745            height: 50px;
4746            position: fixed;
4747            top: 0; left: 0; right: 0;
4748          }
4749
4750          .fixed-header > a {
4751            display: inline-block;
4752            margin: 5px 15px;
4753          }
4754        </file>
4755      </example>
4756    */
4757   this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4758     var document = $window.document;
4759
4760     // Helper function to get first anchor from a NodeList
4761     // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4762     //  and working in all supported browsers.)
4763     function getFirstAnchor(list) {
4764       var result = null;
4765       Array.prototype.some.call(list, function(element) {
4766         if (nodeName_(element) === 'a') {
4767           result = element;
4768           return true;
4769         }
4770       });
4771       return result;
4772     }
4773
4774     function getYOffset() {
4775
4776       var offset = scroll.yOffset;
4777
4778       if (isFunction(offset)) {
4779         offset = offset();
4780       } else if (isElement(offset)) {
4781         var elem = offset[0];
4782         var style = $window.getComputedStyle(elem);
4783         if (style.position !== 'fixed') {
4784           offset = 0;
4785         } else {
4786           offset = elem.getBoundingClientRect().bottom;
4787         }
4788       } else if (!isNumber(offset)) {
4789         offset = 0;
4790       }
4791
4792       return offset;
4793     }
4794
4795     function scrollTo(elem) {
4796       if (elem) {
4797         elem.scrollIntoView();
4798
4799         var offset = getYOffset();
4800
4801         if (offset) {
4802           // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4803           // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4804           // top of the viewport.
4805           //
4806           // IF the number of pixels from the top of `elem` to the end of the page's content is less
4807           // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4808           // way down the page.
4809           //
4810           // This is often the case for elements near the bottom of the page.
4811           //
4812           // In such cases we do not need to scroll the whole `offset` up, just the difference between
4813           // the top of the element and the offset, which is enough to align the top of `elem` at the
4814           // desired position.
4815           var elemTop = elem.getBoundingClientRect().top;
4816           $window.scrollBy(0, elemTop - offset);
4817         }
4818       } else {
4819         $window.scrollTo(0, 0);
4820       }
4821     }
4822
4823     function scroll(hash) {
4824       hash = isString(hash) ? hash : $location.hash();
4825       var elm;
4826
4827       // empty hash, scroll to the top of the page
4828       if (!hash) scrollTo(null);
4829
4830       // element with given id
4831       else if ((elm = document.getElementById(hash))) scrollTo(elm);
4832
4833       // first anchor with given name :-D
4834       else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4835
4836       // no element and hash == 'top', scroll to the top of the page
4837       else if (hash === 'top') scrollTo(null);
4838     }
4839
4840     // does not scroll when user clicks on anchor link that is currently on
4841     // (no url change, no $location.hash() change), browser native does scroll
4842     if (autoScrollingEnabled) {
4843       $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4844         function autoScrollWatchAction(newVal, oldVal) {
4845           // skip the initial scroll if $location.hash is empty
4846           if (newVal === oldVal && newVal === '') return;
4847
4848           jqLiteDocumentLoaded(function() {
4849             $rootScope.$evalAsync(scroll);
4850           });
4851         });
4852     }
4853
4854     return scroll;
4855   }];
4856 }
4857
4858 var $animateMinErr = minErr('$animate');
4859 var ELEMENT_NODE = 1;
4860 var NG_ANIMATE_CLASSNAME = 'ng-animate';
4861
4862 function mergeClasses(a,b) {
4863   if (!a && !b) return '';
4864   if (!a) return b;
4865   if (!b) return a;
4866   if (isArray(a)) a = a.join(' ');
4867   if (isArray(b)) b = b.join(' ');
4868   return a + ' ' + b;
4869 }
4870
4871 function extractElementNode(element) {
4872   for (var i = 0; i < element.length; i++) {
4873     var elm = element[i];
4874     if (elm.nodeType === ELEMENT_NODE) {
4875       return elm;
4876     }
4877   }
4878 }
4879
4880 function splitClasses(classes) {
4881   if (isString(classes)) {
4882     classes = classes.split(' ');
4883   }
4884
4885   // Use createMap() to prevent class assumptions involving property names in
4886   // Object.prototype
4887   var obj = createMap();
4888   forEach(classes, function(klass) {
4889     // sometimes the split leaves empty string values
4890     // incase extra spaces were applied to the options
4891     if (klass.length) {
4892       obj[klass] = true;
4893     }
4894   });
4895   return obj;
4896 }
4897
4898 // if any other type of options value besides an Object value is
4899 // passed into the $animate.method() animation then this helper code
4900 // will be run which will ignore it. While this patch is not the
4901 // greatest solution to this, a lot of existing plugins depend on
4902 // $animate to either call the callback (< 1.2) or return a promise
4903 // that can be changed. This helper function ensures that the options
4904 // are wiped clean incase a callback function is provided.
4905 function prepareAnimateOptions(options) {
4906   return isObject(options)
4907       ? options
4908       : {};
4909 }
4910
4911 var $$CoreAnimateJsProvider = function() {
4912   this.$get = function() {};
4913 };
4914
4915 // this is prefixed with Core since it conflicts with
4916 // the animateQueueProvider defined in ngAnimate/animateQueue.js
4917 var $$CoreAnimateQueueProvider = function() {
4918   var postDigestQueue = new HashMap();
4919   var postDigestElements = [];
4920
4921   this.$get = ['$$AnimateRunner', '$rootScope',
4922        function($$AnimateRunner,   $rootScope) {
4923     return {
4924       enabled: noop,
4925       on: noop,
4926       off: noop,
4927       pin: noop,
4928
4929       push: function(element, event, options, domOperation) {
4930         domOperation        && domOperation();
4931
4932         options = options || {};
4933         options.from        && element.css(options.from);
4934         options.to          && element.css(options.to);
4935
4936         if (options.addClass || options.removeClass) {
4937           addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
4938         }
4939
4940         var runner = new $$AnimateRunner(); // jshint ignore:line
4941
4942         // since there are no animations to run the runner needs to be
4943         // notified that the animation call is complete.
4944         runner.complete();
4945         return runner;
4946       }
4947     };
4948
4949
4950     function updateData(data, classes, value) {
4951       var changed = false;
4952       if (classes) {
4953         classes = isString(classes) ? classes.split(' ') :
4954                   isArray(classes) ? classes : [];
4955         forEach(classes, function(className) {
4956           if (className) {
4957             changed = true;
4958             data[className] = value;
4959           }
4960         });
4961       }
4962       return changed;
4963     }
4964
4965     function handleCSSClassChanges() {
4966       forEach(postDigestElements, function(element) {
4967         var data = postDigestQueue.get(element);
4968         if (data) {
4969           var existing = splitClasses(element.attr('class'));
4970           var toAdd = '';
4971           var toRemove = '';
4972           forEach(data, function(status, className) {
4973             var hasClass = !!existing[className];
4974             if (status !== hasClass) {
4975               if (status) {
4976                 toAdd += (toAdd.length ? ' ' : '') + className;
4977               } else {
4978                 toRemove += (toRemove.length ? ' ' : '') + className;
4979               }
4980             }
4981           });
4982
4983           forEach(element, function(elm) {
4984             toAdd    && jqLiteAddClass(elm, toAdd);
4985             toRemove && jqLiteRemoveClass(elm, toRemove);
4986           });
4987           postDigestQueue.remove(element);
4988         }
4989       });
4990       postDigestElements.length = 0;
4991     }
4992
4993
4994     function addRemoveClassesPostDigest(element, add, remove) {
4995       var data = postDigestQueue.get(element) || {};
4996
4997       var classesAdded = updateData(data, add, true);
4998       var classesRemoved = updateData(data, remove, false);
4999
5000       if (classesAdded || classesRemoved) {
5001
5002         postDigestQueue.put(element, data);
5003         postDigestElements.push(element);
5004
5005         if (postDigestElements.length === 1) {
5006           $rootScope.$$postDigest(handleCSSClassChanges);
5007         }
5008       }
5009     }
5010   }];
5011 };
5012
5013 /**
5014  * @ngdoc provider
5015  * @name $animateProvider
5016  *
5017  * @description
5018  * Default implementation of $animate that doesn't perform any animations, instead just
5019  * synchronously performs DOM updates and resolves the returned runner promise.
5020  *
5021  * In order to enable animations the `ngAnimate` module has to be loaded.
5022  *
5023  * To see the functional implementation check out `src/ngAnimate/animate.js`.
5024  */
5025 var $AnimateProvider = ['$provide', function($provide) {
5026   var provider = this;
5027
5028   this.$$registeredAnimations = Object.create(null);
5029
5030    /**
5031    * @ngdoc method
5032    * @name $animateProvider#register
5033    *
5034    * @description
5035    * Registers a new injectable animation factory function. The factory function produces the
5036    * animation object which contains callback functions for each event that is expected to be
5037    * animated.
5038    *
5039    *   * `eventFn`: `function(element, ... , doneFunction, options)`
5040    *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
5041    *   on the type of animation additional arguments will be injected into the animation function. The
5042    *   list below explains the function signatures for the different animation methods:
5043    *
5044    *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5045    *   - addClass: function(element, addedClasses, doneFunction, options)
5046    *   - removeClass: function(element, removedClasses, doneFunction, options)
5047    *   - enter, leave, move: function(element, doneFunction, options)
5048    *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
5049    *
5050    *   Make sure to trigger the `doneFunction` once the animation is fully complete.
5051    *
5052    * ```js
5053    *   return {
5054    *     //enter, leave, move signature
5055    *     eventFn : function(element, done, options) {
5056    *       //code to run the animation
5057    *       //once complete, then run done()
5058    *       return function endFunction(wasCancelled) {
5059    *         //code to cancel the animation
5060    *       }
5061    *     }
5062    *   }
5063    * ```
5064    *
5065    * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5066    * @param {Function} factory The factory function that will be executed to return the animation
5067    *                           object.
5068    */
5069   this.register = function(name, factory) {
5070     if (name && name.charAt(0) !== '.') {
5071       throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5072     }
5073
5074     var key = name + '-animation';
5075     provider.$$registeredAnimations[name.substr(1)] = key;
5076     $provide.factory(key, factory);
5077   };
5078
5079   /**
5080    * @ngdoc method
5081    * @name $animateProvider#classNameFilter
5082    *
5083    * @description
5084    * Sets and/or returns the CSS class regular expression that is checked when performing
5085    * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5086    * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5087    * When setting the `classNameFilter` value, animations will only be performed on elements
5088    * that successfully match the filter expression. This in turn can boost performance
5089    * for low-powered devices as well as applications containing a lot of structural operations.
5090    * @param {RegExp=} expression The className expression which will be checked against all animations
5091    * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5092    */
5093   this.classNameFilter = function(expression) {
5094     if (arguments.length === 1) {
5095       this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5096       if (this.$$classNameFilter) {
5097         var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5098         if (reservedRegex.test(this.$$classNameFilter.toString())) {
5099           throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5100
5101         }
5102       }
5103     }
5104     return this.$$classNameFilter;
5105   };
5106
5107   this.$get = ['$$animateQueue', function($$animateQueue) {
5108     function domInsert(element, parentElement, afterElement) {
5109       // if for some reason the previous element was removed
5110       // from the dom sometime before this code runs then let's
5111       // just stick to using the parent element as the anchor
5112       if (afterElement) {
5113         var afterNode = extractElementNode(afterElement);
5114         if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5115           afterElement = null;
5116         }
5117       }
5118       afterElement ? afterElement.after(element) : parentElement.prepend(element);
5119     }
5120
5121     /**
5122      * @ngdoc service
5123      * @name $animate
5124      * @description The $animate service exposes a series of DOM utility methods that provide support
5125      * for animation hooks. The default behavior is the application of DOM operations, however,
5126      * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5127      * to ensure that animation runs with the triggered DOM operation.
5128      *
5129      * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5130      * included and only when it is active then the animation hooks that `$animate` triggers will be
5131      * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5132      * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5133      * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5134      *
5135      * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5136      *
5137      * To learn more about enabling animation support, click here to visit the
5138      * {@link ngAnimate ngAnimate module page}.
5139      */
5140     return {
5141       // we don't call it directly since non-existant arguments may
5142       // be interpreted as null within the sub enabled function
5143
5144       /**
5145        *
5146        * @ngdoc method
5147        * @name $animate#on
5148        * @kind function
5149        * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5150        *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5151        *    is fired with the following params:
5152        *
5153        * ```js
5154        * $animate.on('enter', container,
5155        *    function callback(element, phase) {
5156        *      // cool we detected an enter animation within the container
5157        *    }
5158        * );
5159        * ```
5160        *
5161        * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5162        * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5163        *     as well as among its children
5164        * @param {Function} callback the callback function that will be fired when the listener is triggered
5165        *
5166        * The arguments present in the callback function are:
5167        * * `element` - The captured DOM element that the animation was fired on.
5168        * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5169        */
5170       on: $$animateQueue.on,
5171
5172       /**
5173        *
5174        * @ngdoc method
5175        * @name $animate#off
5176        * @kind function
5177        * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5178        * can be used in three different ways depending on the arguments:
5179        *
5180        * ```js
5181        * // remove all the animation event listeners listening for `enter`
5182        * $animate.off('enter');
5183        *
5184        * // remove all the animation event listeners listening for `enter` on the given element and its children
5185        * $animate.off('enter', container);
5186        *
5187        * // remove the event listener function provided by `listenerFn` that is set
5188        * // to listen for `enter` on the given `element` as well as its children
5189        * $animate.off('enter', container, callback);
5190        * ```
5191        *
5192        * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5193        * @param {DOMElement=} container the container element the event listener was placed on
5194        * @param {Function=} callback the callback function that was registered as the listener
5195        */
5196       off: $$animateQueue.off,
5197
5198       /**
5199        * @ngdoc method
5200        * @name $animate#pin
5201        * @kind function
5202        * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5203        *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5204        *    element despite being outside the realm of the application or within another application. Say for example if the application
5205        *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5206        *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5207        *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5208        *
5209        *    Note that this feature is only active when the `ngAnimate` module is used.
5210        *
5211        * @param {DOMElement} element the external element that will be pinned
5212        * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5213        */
5214       pin: $$animateQueue.pin,
5215
5216       /**
5217        *
5218        * @ngdoc method
5219        * @name $animate#enabled
5220        * @kind function
5221        * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5222        * function can be called in four ways:
5223        *
5224        * ```js
5225        * // returns true or false
5226        * $animate.enabled();
5227        *
5228        * // changes the enabled state for all animations
5229        * $animate.enabled(false);
5230        * $animate.enabled(true);
5231        *
5232        * // returns true or false if animations are enabled for an element
5233        * $animate.enabled(element);
5234        *
5235        * // changes the enabled state for an element and its children
5236        * $animate.enabled(element, true);
5237        * $animate.enabled(element, false);
5238        * ```
5239        *
5240        * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5241        * @param {boolean=} enabled whether or not the animations will be enabled for the element
5242        *
5243        * @return {boolean} whether or not animations are enabled
5244        */
5245       enabled: $$animateQueue.enabled,
5246
5247       /**
5248        * @ngdoc method
5249        * @name $animate#cancel
5250        * @kind function
5251        * @description Cancels the provided animation.
5252        *
5253        * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5254        */
5255       cancel: function(runner) {
5256         runner.end && runner.end();
5257       },
5258
5259       /**
5260        *
5261        * @ngdoc method
5262        * @name $animate#enter
5263        * @kind function
5264        * @description Inserts the element into the DOM either after the `after` element (if provided) or
5265        *   as the first child within the `parent` element and then triggers an animation.
5266        *   A promise is returned that will be resolved during the next digest once the animation
5267        *   has completed.
5268        *
5269        * @param {DOMElement} element the element which will be inserted into the DOM
5270        * @param {DOMElement} parent the parent element which will append the element as
5271        *   a child (so long as the after element is not present)
5272        * @param {DOMElement=} after the sibling element after which the element will be appended
5273        * @param {object=} options an optional collection of options/styles that will be applied to the element
5274        *
5275        * @return {Promise} the animation callback promise
5276        */
5277       enter: function(element, parent, after, options) {
5278         parent = parent && jqLite(parent);
5279         after = after && jqLite(after);
5280         parent = parent || after.parent();
5281         domInsert(element, parent, after);
5282         return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5283       },
5284
5285       /**
5286        *
5287        * @ngdoc method
5288        * @name $animate#move
5289        * @kind function
5290        * @description Inserts (moves) the element into its new position in the DOM either after
5291        *   the `after` element (if provided) or as the first child within the `parent` element
5292        *   and then triggers an animation. A promise is returned that will be resolved
5293        *   during the next digest once the animation has completed.
5294        *
5295        * @param {DOMElement} element the element which will be moved into the new DOM position
5296        * @param {DOMElement} parent the parent element which will append the element as
5297        *   a child (so long as the after element is not present)
5298        * @param {DOMElement=} after the sibling element after which the element will be appended
5299        * @param {object=} options an optional collection of options/styles that will be applied to the element
5300        *
5301        * @return {Promise} the animation callback promise
5302        */
5303       move: function(element, parent, after, options) {
5304         parent = parent && jqLite(parent);
5305         after = after && jqLite(after);
5306         parent = parent || after.parent();
5307         domInsert(element, parent, after);
5308         return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5309       },
5310
5311       /**
5312        * @ngdoc method
5313        * @name $animate#leave
5314        * @kind function
5315        * @description Triggers an animation and then removes the element from the DOM.
5316        * When the function is called a promise is returned that will be resolved during the next
5317        * digest once the animation has completed.
5318        *
5319        * @param {DOMElement} element the element which will be removed from the DOM
5320        * @param {object=} options an optional collection of options/styles that will be applied to the element
5321        *
5322        * @return {Promise} the animation callback promise
5323        */
5324       leave: function(element, options) {
5325         return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5326           element.remove();
5327         });
5328       },
5329
5330       /**
5331        * @ngdoc method
5332        * @name $animate#addClass
5333        * @kind function
5334        *
5335        * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5336        *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5337        *   animation if element already contains the CSS class or if the class is removed at a later step.
5338        *   Note that class-based animations are treated differently compared to structural animations
5339        *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5340        *   depending if CSS or JavaScript animations are used.
5341        *
5342        * @param {DOMElement} element the element which the CSS classes will be applied to
5343        * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5344        * @param {object=} options an optional collection of options/styles that will be applied to the element
5345        *
5346        * @return {Promise} the animation callback promise
5347        */
5348       addClass: function(element, className, options) {
5349         options = prepareAnimateOptions(options);
5350         options.addClass = mergeClasses(options.addclass, className);
5351         return $$animateQueue.push(element, 'addClass', options);
5352       },
5353
5354       /**
5355        * @ngdoc method
5356        * @name $animate#removeClass
5357        * @kind function
5358        *
5359        * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5360        *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5361        *   animation if element does not contain the CSS class or if the class is added at a later step.
5362        *   Note that class-based animations are treated differently compared to structural animations
5363        *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5364        *   depending if CSS or JavaScript animations are used.
5365        *
5366        * @param {DOMElement} element the element which the CSS classes will be applied to
5367        * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5368        * @param {object=} options an optional collection of options/styles that will be applied to the element
5369        *
5370        * @return {Promise} the animation callback promise
5371        */
5372       removeClass: function(element, className, options) {
5373         options = prepareAnimateOptions(options);
5374         options.removeClass = mergeClasses(options.removeClass, className);
5375         return $$animateQueue.push(element, 'removeClass', options);
5376       },
5377
5378       /**
5379        * @ngdoc method
5380        * @name $animate#setClass
5381        * @kind function
5382        *
5383        * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5384        *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5385        *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5386        *    passed. Note that class-based animations are treated differently compared to structural animations
5387        *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5388        *    depending if CSS or JavaScript animations are used.
5389        *
5390        * @param {DOMElement} element the element which the CSS classes will be applied to
5391        * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5392        * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5393        * @param {object=} options an optional collection of options/styles that will be applied to the element
5394        *
5395        * @return {Promise} the animation callback promise
5396        */
5397       setClass: function(element, add, remove, options) {
5398         options = prepareAnimateOptions(options);
5399         options.addClass = mergeClasses(options.addClass, add);
5400         options.removeClass = mergeClasses(options.removeClass, remove);
5401         return $$animateQueue.push(element, 'setClass', options);
5402       },
5403
5404       /**
5405        * @ngdoc method
5406        * @name $animate#animate
5407        * @kind function
5408        *
5409        * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5410        * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
5411        * on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and
5412        * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
5413        * style in `to`, the style in `from` is applied immediately, and no animation is run.
5414        * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
5415        * method (or as part of the `options` parameter):
5416        *
5417        * ```js
5418        * ngModule.animation('.my-inline-animation', function() {
5419        *   return {
5420        *     animate : function(element, from, to, done, options) {
5421        *       //animation
5422        *       done();
5423        *     }
5424        *   }
5425        * });
5426        * ```
5427        *
5428        * @param {DOMElement} element the element which the CSS styles will be applied to
5429        * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5430        * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5431        * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5432        *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5433        *    (Note that if no animation is detected then this value will not be appplied to the element.)
5434        * @param {object=} options an optional collection of options/styles that will be applied to the element
5435        *
5436        * @return {Promise} the animation callback promise
5437        */
5438       animate: function(element, from, to, className, options) {
5439         options = prepareAnimateOptions(options);
5440         options.from = options.from ? extend(options.from, from) : from;
5441         options.to   = options.to   ? extend(options.to, to)     : to;
5442
5443         className = className || 'ng-inline-animate';
5444         options.tempClasses = mergeClasses(options.tempClasses, className);
5445         return $$animateQueue.push(element, 'animate', options);
5446       }
5447     };
5448   }];
5449 }];
5450
5451 var $$AnimateAsyncRunFactoryProvider = function() {
5452   this.$get = ['$$rAF', function($$rAF) {
5453     var waitQueue = [];
5454
5455     function waitForTick(fn) {
5456       waitQueue.push(fn);
5457       if (waitQueue.length > 1) return;
5458       $$rAF(function() {
5459         for (var i = 0; i < waitQueue.length; i++) {
5460           waitQueue[i]();
5461         }
5462         waitQueue = [];
5463       });
5464     }
5465
5466     return function() {
5467       var passed = false;
5468       waitForTick(function() {
5469         passed = true;
5470       });
5471       return function(callback) {
5472         passed ? callback() : waitForTick(callback);
5473       };
5474     };
5475   }];
5476 };
5477
5478 var $$AnimateRunnerFactoryProvider = function() {
5479   this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
5480        function($q,   $sniffer,   $$animateAsyncRun,   $document,   $timeout) {
5481
5482     var INITIAL_STATE = 0;
5483     var DONE_PENDING_STATE = 1;
5484     var DONE_COMPLETE_STATE = 2;
5485
5486     AnimateRunner.chain = function(chain, callback) {
5487       var index = 0;
5488
5489       next();
5490       function next() {
5491         if (index === chain.length) {
5492           callback(true);
5493           return;
5494         }
5495
5496         chain[index](function(response) {
5497           if (response === false) {
5498             callback(false);
5499             return;
5500           }
5501           index++;
5502           next();
5503         });
5504       }
5505     };
5506
5507     AnimateRunner.all = function(runners, callback) {
5508       var count = 0;
5509       var status = true;
5510       forEach(runners, function(runner) {
5511         runner.done(onProgress);
5512       });
5513
5514       function onProgress(response) {
5515         status = status && response;
5516         if (++count === runners.length) {
5517           callback(status);
5518         }
5519       }
5520     };
5521
5522     function AnimateRunner(host) {
5523       this.setHost(host);
5524
5525       var rafTick = $$animateAsyncRun();
5526       var timeoutTick = function(fn) {
5527         $timeout(fn, 0, false);
5528       };
5529
5530       this._doneCallbacks = [];
5531       this._tick = function(fn) {
5532         var doc = $document[0];
5533
5534         // the document may not be ready or attached
5535         // to the module for some internal tests
5536         if (doc && doc.hidden) {
5537           timeoutTick(fn);
5538         } else {
5539           rafTick(fn);
5540         }
5541       };
5542       this._state = 0;
5543     }
5544
5545     AnimateRunner.prototype = {
5546       setHost: function(host) {
5547         this.host = host || {};
5548       },
5549
5550       done: function(fn) {
5551         if (this._state === DONE_COMPLETE_STATE) {
5552           fn();
5553         } else {
5554           this._doneCallbacks.push(fn);
5555         }
5556       },
5557
5558       progress: noop,
5559
5560       getPromise: function() {
5561         if (!this.promise) {
5562           var self = this;
5563           this.promise = $q(function(resolve, reject) {
5564             self.done(function(status) {
5565               status === false ? reject() : resolve();
5566             });
5567           });
5568         }
5569         return this.promise;
5570       },
5571
5572       then: function(resolveHandler, rejectHandler) {
5573         return this.getPromise().then(resolveHandler, rejectHandler);
5574       },
5575
5576       'catch': function(handler) {
5577         return this.getPromise()['catch'](handler);
5578       },
5579
5580       'finally': function(handler) {
5581         return this.getPromise()['finally'](handler);
5582       },
5583
5584       pause: function() {
5585         if (this.host.pause) {
5586           this.host.pause();
5587         }
5588       },
5589
5590       resume: function() {
5591         if (this.host.resume) {
5592           this.host.resume();
5593         }
5594       },
5595
5596       end: function() {
5597         if (this.host.end) {
5598           this.host.end();
5599         }
5600         this._resolve(true);
5601       },
5602
5603       cancel: function() {
5604         if (this.host.cancel) {
5605           this.host.cancel();
5606         }
5607         this._resolve(false);
5608       },
5609
5610       complete: function(response) {
5611         var self = this;
5612         if (self._state === INITIAL_STATE) {
5613           self._state = DONE_PENDING_STATE;
5614           self._tick(function() {
5615             self._resolve(response);
5616           });
5617         }
5618       },
5619
5620       _resolve: function(response) {
5621         if (this._state !== DONE_COMPLETE_STATE) {
5622           forEach(this._doneCallbacks, function(fn) {
5623             fn(response);
5624           });
5625           this._doneCallbacks.length = 0;
5626           this._state = DONE_COMPLETE_STATE;
5627         }
5628       }
5629     };
5630
5631     return AnimateRunner;
5632   }];
5633 };
5634
5635 /**
5636  * @ngdoc service
5637  * @name $animateCss
5638  * @kind object
5639  *
5640  * @description
5641  * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5642  * then the `$animateCss` service will actually perform animations.
5643  *
5644  * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5645  */
5646 var $CoreAnimateCssProvider = function() {
5647   this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
5648
5649     return function(element, initialOptions) {
5650       // all of the animation functions should create
5651       // a copy of the options data, however, if a
5652       // parent service has already created a copy then
5653       // we should stick to using that
5654       var options = initialOptions || {};
5655       if (!options.$$prepared) {
5656         options = copy(options);
5657       }
5658
5659       // there is no point in applying the styles since
5660       // there is no animation that goes on at all in
5661       // this version of $animateCss.
5662       if (options.cleanupStyles) {
5663         options.from = options.to = null;
5664       }
5665
5666       if (options.from) {
5667         element.css(options.from);
5668         options.from = null;
5669       }
5670
5671       /* jshint newcap: false*/
5672       var closed, runner = new $$AnimateRunner();
5673       return {
5674         start: run,
5675         end: run
5676       };
5677
5678       function run() {
5679         $$rAF(function() {
5680           applyAnimationContents();
5681           if (!closed) {
5682             runner.complete();
5683           }
5684           closed = true;
5685         });
5686         return runner;
5687       }
5688
5689       function applyAnimationContents() {
5690         if (options.addClass) {
5691           element.addClass(options.addClass);
5692           options.addClass = null;
5693         }
5694         if (options.removeClass) {
5695           element.removeClass(options.removeClass);
5696           options.removeClass = null;
5697         }
5698         if (options.to) {
5699           element.css(options.to);
5700           options.to = null;
5701         }
5702       }
5703     };
5704   }];
5705 };
5706
5707 /* global stripHash: true */
5708
5709 /**
5710  * ! This is a private undocumented service !
5711  *
5712  * @name $browser
5713  * @requires $log
5714  * @description
5715  * This object has two goals:
5716  *
5717  * - hide all the global state in the browser caused by the window object
5718  * - abstract away all the browser specific features and inconsistencies
5719  *
5720  * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5721  * service, which can be used for convenient testing of the application without the interaction with
5722  * the real browser apis.
5723  */
5724 /**
5725  * @param {object} window The global window object.
5726  * @param {object} document jQuery wrapped document.
5727  * @param {object} $log window.console or an object with the same interface.
5728  * @param {object} $sniffer $sniffer service
5729  */
5730 function Browser(window, document, $log, $sniffer) {
5731   var self = this,
5732       rawDocument = document[0],
5733       location = window.location,
5734       history = window.history,
5735       setTimeout = window.setTimeout,
5736       clearTimeout = window.clearTimeout,
5737       pendingDeferIds = {};
5738
5739   self.isMock = false;
5740
5741   var outstandingRequestCount = 0;
5742   var outstandingRequestCallbacks = [];
5743
5744   // TODO(vojta): remove this temporary api
5745   self.$$completeOutstandingRequest = completeOutstandingRequest;
5746   self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5747
5748   /**
5749    * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5750    * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5751    */
5752   function completeOutstandingRequest(fn) {
5753     try {
5754       fn.apply(null, sliceArgs(arguments, 1));
5755     } finally {
5756       outstandingRequestCount--;
5757       if (outstandingRequestCount === 0) {
5758         while (outstandingRequestCallbacks.length) {
5759           try {
5760             outstandingRequestCallbacks.pop()();
5761           } catch (e) {
5762             $log.error(e);
5763           }
5764         }
5765       }
5766     }
5767   }
5768
5769   function getHash(url) {
5770     var index = url.indexOf('#');
5771     return index === -1 ? '' : url.substr(index);
5772   }
5773
5774   /**
5775    * @private
5776    * Note: this method is used only by scenario runner
5777    * TODO(vojta): prefix this method with $$ ?
5778    * @param {function()} callback Function that will be called when no outstanding request
5779    */
5780   self.notifyWhenNoOutstandingRequests = function(callback) {
5781     if (outstandingRequestCount === 0) {
5782       callback();
5783     } else {
5784       outstandingRequestCallbacks.push(callback);
5785     }
5786   };
5787
5788   //////////////////////////////////////////////////////////////
5789   // URL API
5790   //////////////////////////////////////////////////////////////
5791
5792   var cachedState, lastHistoryState,
5793       lastBrowserUrl = location.href,
5794       baseElement = document.find('base'),
5795       pendingLocation = null;
5796
5797   cacheState();
5798   lastHistoryState = cachedState;
5799
5800   /**
5801    * @name $browser#url
5802    *
5803    * @description
5804    * GETTER:
5805    * Without any argument, this method just returns current value of location.href.
5806    *
5807    * SETTER:
5808    * With at least one argument, this method sets url to new value.
5809    * If html5 history api supported, pushState/replaceState is used, otherwise
5810    * location.href/location.replace is used.
5811    * Returns its own instance to allow chaining
5812    *
5813    * NOTE: this api is intended for use only by the $location service. Please use the
5814    * {@link ng.$location $location service} to change url.
5815    *
5816    * @param {string} url New url (when used as setter)
5817    * @param {boolean=} replace Should new url replace current history record?
5818    * @param {object=} state object to use with pushState/replaceState
5819    */
5820   self.url = function(url, replace, state) {
5821     // In modern browsers `history.state` is `null` by default; treating it separately
5822     // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5823     // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5824     if (isUndefined(state)) {
5825       state = null;
5826     }
5827
5828     // Android Browser BFCache causes location, history reference to become stale.
5829     if (location !== window.location) location = window.location;
5830     if (history !== window.history) history = window.history;
5831
5832     // setter
5833     if (url) {
5834       var sameState = lastHistoryState === state;
5835
5836       // Don't change anything if previous and current URLs and states match. This also prevents
5837       // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5838       // See https://github.com/angular/angular.js/commit/ffb2701
5839       if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5840         return self;
5841       }
5842       var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5843       lastBrowserUrl = url;
5844       lastHistoryState = state;
5845       // Don't use history API if only the hash changed
5846       // due to a bug in IE10/IE11 which leads
5847       // to not firing a `hashchange` nor `popstate` event
5848       // in some cases (see #9143).
5849       if ($sniffer.history && (!sameBase || !sameState)) {
5850         history[replace ? 'replaceState' : 'pushState'](state, '', url);
5851         cacheState();
5852         // Do the assignment again so that those two variables are referentially identical.
5853         lastHistoryState = cachedState;
5854       } else {
5855         if (!sameBase || pendingLocation) {
5856           pendingLocation = url;
5857         }
5858         if (replace) {
5859           location.replace(url);
5860         } else if (!sameBase) {
5861           location.href = url;
5862         } else {
5863           location.hash = getHash(url);
5864         }
5865         if (location.href !== url) {
5866           pendingLocation = url;
5867         }
5868       }
5869       return self;
5870     // getter
5871     } else {
5872       // - pendingLocation is needed as browsers don't allow to read out
5873       //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5874       //   https://openradar.appspot.com/22186109).
5875       // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5876       return pendingLocation || location.href.replace(/%27/g,"'");
5877     }
5878   };
5879
5880   /**
5881    * @name $browser#state
5882    *
5883    * @description
5884    * This method is a getter.
5885    *
5886    * Return history.state or null if history.state is undefined.
5887    *
5888    * @returns {object} state
5889    */
5890   self.state = function() {
5891     return cachedState;
5892   };
5893
5894   var urlChangeListeners = [],
5895       urlChangeInit = false;
5896
5897   function cacheStateAndFireUrlChange() {
5898     pendingLocation = null;
5899     cacheState();
5900     fireUrlChange();
5901   }
5902
5903   function getCurrentState() {
5904     try {
5905       return history.state;
5906     } catch (e) {
5907       // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5908     }
5909   }
5910
5911   // This variable should be used *only* inside the cacheState function.
5912   var lastCachedState = null;
5913   function cacheState() {
5914     // This should be the only place in $browser where `history.state` is read.
5915     cachedState = getCurrentState();
5916     cachedState = isUndefined(cachedState) ? null : cachedState;
5917
5918     // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5919     if (equals(cachedState, lastCachedState)) {
5920       cachedState = lastCachedState;
5921     }
5922     lastCachedState = cachedState;
5923   }
5924
5925   function fireUrlChange() {
5926     if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5927       return;
5928     }
5929
5930     lastBrowserUrl = self.url();
5931     lastHistoryState = cachedState;
5932     forEach(urlChangeListeners, function(listener) {
5933       listener(self.url(), cachedState);
5934     });
5935   }
5936
5937   /**
5938    * @name $browser#onUrlChange
5939    *
5940    * @description
5941    * Register callback function that will be called, when url changes.
5942    *
5943    * It's only called when the url is changed from outside of angular:
5944    * - user types different url into address bar
5945    * - user clicks on history (forward/back) button
5946    * - user clicks on a link
5947    *
5948    * It's not called when url is changed by $browser.url() method
5949    *
5950    * The listener gets called with new url as parameter.
5951    *
5952    * NOTE: this api is intended for use only by the $location service. Please use the
5953    * {@link ng.$location $location service} to monitor url changes in angular apps.
5954    *
5955    * @param {function(string)} listener Listener function to be called when url changes.
5956    * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5957    */
5958   self.onUrlChange = function(callback) {
5959     // TODO(vojta): refactor to use node's syntax for events
5960     if (!urlChangeInit) {
5961       // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5962       // don't fire popstate when user change the address bar and don't fire hashchange when url
5963       // changed by push/replaceState
5964
5965       // html5 history api - popstate event
5966       if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5967       // hashchange event
5968       jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5969
5970       urlChangeInit = true;
5971     }
5972
5973     urlChangeListeners.push(callback);
5974     return callback;
5975   };
5976
5977   /**
5978    * @private
5979    * Remove popstate and hashchange handler from window.
5980    *
5981    * NOTE: this api is intended for use only by $rootScope.
5982    */
5983   self.$$applicationDestroyed = function() {
5984     jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5985   };
5986
5987   /**
5988    * Checks whether the url has changed outside of Angular.
5989    * Needs to be exported to be able to check for changes that have been done in sync,
5990    * as hashchange/popstate events fire in async.
5991    */
5992   self.$$checkUrlChange = fireUrlChange;
5993
5994   //////////////////////////////////////////////////////////////
5995   // Misc API
5996   //////////////////////////////////////////////////////////////
5997
5998   /**
5999    * @name $browser#baseHref
6000    *
6001    * @description
6002    * Returns current <base href>
6003    * (always relative - without domain)
6004    *
6005    * @returns {string} The current base href
6006    */
6007   self.baseHref = function() {
6008     var href = baseElement.attr('href');
6009     return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
6010   };
6011
6012   /**
6013    * @name $browser#defer
6014    * @param {function()} fn A function, who's execution should be deferred.
6015    * @param {number=} [delay=0] of milliseconds to defer the function execution.
6016    * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
6017    *
6018    * @description
6019    * Executes a fn asynchronously via `setTimeout(fn, delay)`.
6020    *
6021    * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
6022    * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
6023    * via `$browser.defer.flush()`.
6024    *
6025    */
6026   self.defer = function(fn, delay) {
6027     var timeoutId;
6028     outstandingRequestCount++;
6029     timeoutId = setTimeout(function() {
6030       delete pendingDeferIds[timeoutId];
6031       completeOutstandingRequest(fn);
6032     }, delay || 0);
6033     pendingDeferIds[timeoutId] = true;
6034     return timeoutId;
6035   };
6036
6037
6038   /**
6039    * @name $browser#defer.cancel
6040    *
6041    * @description
6042    * Cancels a deferred task identified with `deferId`.
6043    *
6044    * @param {*} deferId Token returned by the `$browser.defer` function.
6045    * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
6046    *                    canceled.
6047    */
6048   self.defer.cancel = function(deferId) {
6049     if (pendingDeferIds[deferId]) {
6050       delete pendingDeferIds[deferId];
6051       clearTimeout(deferId);
6052       completeOutstandingRequest(noop);
6053       return true;
6054     }
6055     return false;
6056   };
6057
6058 }
6059
6060 function $BrowserProvider() {
6061   this.$get = ['$window', '$log', '$sniffer', '$document',
6062       function($window, $log, $sniffer, $document) {
6063         return new Browser($window, $document, $log, $sniffer);
6064       }];
6065 }
6066
6067 /**
6068  * @ngdoc service
6069  * @name $cacheFactory
6070  *
6071  * @description
6072  * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
6073  * them.
6074  *
6075  * ```js
6076  *
6077  *  var cache = $cacheFactory('cacheId');
6078  *  expect($cacheFactory.get('cacheId')).toBe(cache);
6079  *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
6080  *
6081  *  cache.put("key", "value");
6082  *  cache.put("another key", "another value");
6083  *
6084  *  // We've specified no options on creation
6085  *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
6086  *
6087  * ```
6088  *
6089  *
6090  * @param {string} cacheId Name or id of the newly created cache.
6091  * @param {object=} options Options object that specifies the cache behavior. Properties:
6092  *
6093  *   - `{number=}` `capacity` — turns the cache into LRU cache.
6094  *
6095  * @returns {object} Newly created cache object with the following set of methods:
6096  *
6097  * - `{object}` `info()` — Returns id, size, and options of cache.
6098  * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
6099  *   it.
6100  * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
6101  * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
6102  * - `{void}` `removeAll()` — Removes all cached values.
6103  * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
6104  *
6105  * @example
6106    <example module="cacheExampleApp">
6107      <file name="index.html">
6108        <div ng-controller="CacheController">
6109          <input ng-model="newCacheKey" placeholder="Key">
6110          <input ng-model="newCacheValue" placeholder="Value">
6111          <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
6112
6113          <p ng-if="keys.length">Cached Values</p>
6114          <div ng-repeat="key in keys">
6115            <span ng-bind="key"></span>
6116            <span>: </span>
6117            <b ng-bind="cache.get(key)"></b>
6118          </div>
6119
6120          <p>Cache Info</p>
6121          <div ng-repeat="(key, value) in cache.info()">
6122            <span ng-bind="key"></span>
6123            <span>: </span>
6124            <b ng-bind="value"></b>
6125          </div>
6126        </div>
6127      </file>
6128      <file name="script.js">
6129        angular.module('cacheExampleApp', []).
6130          controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6131            $scope.keys = [];
6132            $scope.cache = $cacheFactory('cacheId');
6133            $scope.put = function(key, value) {
6134              if (angular.isUndefined($scope.cache.get(key))) {
6135                $scope.keys.push(key);
6136              }
6137              $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6138            };
6139          }]);
6140      </file>
6141      <file name="style.css">
6142        p {
6143          margin: 10px 0 3px;
6144        }
6145      </file>
6146    </example>
6147  */
6148 function $CacheFactoryProvider() {
6149
6150   this.$get = function() {
6151     var caches = {};
6152
6153     function cacheFactory(cacheId, options) {
6154       if (cacheId in caches) {
6155         throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6156       }
6157
6158       var size = 0,
6159           stats = extend({}, options, {id: cacheId}),
6160           data = createMap(),
6161           capacity = (options && options.capacity) || Number.MAX_VALUE,
6162           lruHash = createMap(),
6163           freshEnd = null,
6164           staleEnd = null;
6165
6166       /**
6167        * @ngdoc type
6168        * @name $cacheFactory.Cache
6169        *
6170        * @description
6171        * A cache object used to store and retrieve data, primarily used by
6172        * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6173        * templates and other data.
6174        *
6175        * ```js
6176        *  angular.module('superCache')
6177        *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6178        *      return $cacheFactory('super-cache');
6179        *    }]);
6180        * ```
6181        *
6182        * Example test:
6183        *
6184        * ```js
6185        *  it('should behave like a cache', inject(function(superCache) {
6186        *    superCache.put('key', 'value');
6187        *    superCache.put('another key', 'another value');
6188        *
6189        *    expect(superCache.info()).toEqual({
6190        *      id: 'super-cache',
6191        *      size: 2
6192        *    });
6193        *
6194        *    superCache.remove('another key');
6195        *    expect(superCache.get('another key')).toBeUndefined();
6196        *
6197        *    superCache.removeAll();
6198        *    expect(superCache.info()).toEqual({
6199        *      id: 'super-cache',
6200        *      size: 0
6201        *    });
6202        *  }));
6203        * ```
6204        */
6205       return caches[cacheId] = {
6206
6207         /**
6208          * @ngdoc method
6209          * @name $cacheFactory.Cache#put
6210          * @kind function
6211          *
6212          * @description
6213          * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6214          * retrieved later, and incrementing the size of the cache if the key was not already
6215          * present in the cache. If behaving like an LRU cache, it will also remove stale
6216          * entries from the set.
6217          *
6218          * It will not insert undefined values into the cache.
6219          *
6220          * @param {string} key the key under which the cached data is stored.
6221          * @param {*} value the value to store alongside the key. If it is undefined, the key
6222          *    will not be stored.
6223          * @returns {*} the value stored.
6224          */
6225         put: function(key, value) {
6226           if (isUndefined(value)) return;
6227           if (capacity < Number.MAX_VALUE) {
6228             var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6229
6230             refresh(lruEntry);
6231           }
6232
6233           if (!(key in data)) size++;
6234           data[key] = value;
6235
6236           if (size > capacity) {
6237             this.remove(staleEnd.key);
6238           }
6239
6240           return value;
6241         },
6242
6243         /**
6244          * @ngdoc method
6245          * @name $cacheFactory.Cache#get
6246          * @kind function
6247          *
6248          * @description
6249          * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6250          *
6251          * @param {string} key the key of the data to be retrieved
6252          * @returns {*} the value stored.
6253          */
6254         get: function(key) {
6255           if (capacity < Number.MAX_VALUE) {
6256             var lruEntry = lruHash[key];
6257
6258             if (!lruEntry) return;
6259
6260             refresh(lruEntry);
6261           }
6262
6263           return data[key];
6264         },
6265
6266
6267         /**
6268          * @ngdoc method
6269          * @name $cacheFactory.Cache#remove
6270          * @kind function
6271          *
6272          * @description
6273          * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6274          *
6275          * @param {string} key the key of the entry to be removed
6276          */
6277         remove: function(key) {
6278           if (capacity < Number.MAX_VALUE) {
6279             var lruEntry = lruHash[key];
6280
6281             if (!lruEntry) return;
6282
6283             if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6284             if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6285             link(lruEntry.n,lruEntry.p);
6286
6287             delete lruHash[key];
6288           }
6289
6290           if (!(key in data)) return;
6291
6292           delete data[key];
6293           size--;
6294         },
6295
6296
6297         /**
6298          * @ngdoc method
6299          * @name $cacheFactory.Cache#removeAll
6300          * @kind function
6301          *
6302          * @description
6303          * Clears the cache object of any entries.
6304          */
6305         removeAll: function() {
6306           data = createMap();
6307           size = 0;
6308           lruHash = createMap();
6309           freshEnd = staleEnd = null;
6310         },
6311
6312
6313         /**
6314          * @ngdoc method
6315          * @name $cacheFactory.Cache#destroy
6316          * @kind function
6317          *
6318          * @description
6319          * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6320          * removing it from the {@link $cacheFactory $cacheFactory} set.
6321          */
6322         destroy: function() {
6323           data = null;
6324           stats = null;
6325           lruHash = null;
6326           delete caches[cacheId];
6327         },
6328
6329
6330         /**
6331          * @ngdoc method
6332          * @name $cacheFactory.Cache#info
6333          * @kind function
6334          *
6335          * @description
6336          * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6337          *
6338          * @returns {object} an object with the following properties:
6339          *   <ul>
6340          *     <li>**id**: the id of the cache instance</li>
6341          *     <li>**size**: the number of entries kept in the cache instance</li>
6342          *     <li>**...**: any additional properties from the options object when creating the
6343          *       cache.</li>
6344          *   </ul>
6345          */
6346         info: function() {
6347           return extend({}, stats, {size: size});
6348         }
6349       };
6350
6351
6352       /**
6353        * makes the `entry` the freshEnd of the LRU linked list
6354        */
6355       function refresh(entry) {
6356         if (entry != freshEnd) {
6357           if (!staleEnd) {
6358             staleEnd = entry;
6359           } else if (staleEnd == entry) {
6360             staleEnd = entry.n;
6361           }
6362
6363           link(entry.n, entry.p);
6364           link(entry, freshEnd);
6365           freshEnd = entry;
6366           freshEnd.n = null;
6367         }
6368       }
6369
6370
6371       /**
6372        * bidirectionally links two entries of the LRU linked list
6373        */
6374       function link(nextEntry, prevEntry) {
6375         if (nextEntry != prevEntry) {
6376           if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6377           if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6378         }
6379       }
6380     }
6381
6382
6383   /**
6384    * @ngdoc method
6385    * @name $cacheFactory#info
6386    *
6387    * @description
6388    * Get information about all the caches that have been created
6389    *
6390    * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6391    */
6392     cacheFactory.info = function() {
6393       var info = {};
6394       forEach(caches, function(cache, cacheId) {
6395         info[cacheId] = cache.info();
6396       });
6397       return info;
6398     };
6399
6400
6401   /**
6402    * @ngdoc method
6403    * @name $cacheFactory#get
6404    *
6405    * @description
6406    * Get access to a cache object by the `cacheId` used when it was created.
6407    *
6408    * @param {string} cacheId Name or id of a cache to access.
6409    * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6410    */
6411     cacheFactory.get = function(cacheId) {
6412       return caches[cacheId];
6413     };
6414
6415
6416     return cacheFactory;
6417   };
6418 }
6419
6420 /**
6421  * @ngdoc service
6422  * @name $templateCache
6423  *
6424  * @description
6425  * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6426  * can load templates directly into the cache in a `script` tag, or by consuming the
6427  * `$templateCache` service directly.
6428  *
6429  * Adding via the `script` tag:
6430  *
6431  * ```html
6432  *   <script type="text/ng-template" id="templateId.html">
6433  *     <p>This is the content of the template</p>
6434  *   </script>
6435  * ```
6436  *
6437  * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6438  * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6439  * element with ng-app attribute), otherwise the template will be ignored.
6440  *
6441  * Adding via the `$templateCache` service:
6442  *
6443  * ```js
6444  * var myApp = angular.module('myApp', []);
6445  * myApp.run(function($templateCache) {
6446  *   $templateCache.put('templateId.html', 'This is the content of the template');
6447  * });
6448  * ```
6449  *
6450  * To retrieve the template later, simply use it in your HTML:
6451  * ```html
6452  * <div ng-include=" 'templateId.html' "></div>
6453  * ```
6454  *
6455  * or get it via Javascript:
6456  * ```js
6457  * $templateCache.get('templateId.html')
6458  * ```
6459  *
6460  * See {@link ng.$cacheFactory $cacheFactory}.
6461  *
6462  */
6463 function $TemplateCacheProvider() {
6464   this.$get = ['$cacheFactory', function($cacheFactory) {
6465     return $cacheFactory('templates');
6466   }];
6467 }
6468
6469 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6470  *     Any commits to this file should be reviewed with security in mind.  *
6471  *   Changes to this file can potentially create security vulnerabilities. *
6472  *          An approval from 2 Core members with history of modifying      *
6473  *                         this file is required.                          *
6474  *                                                                         *
6475  *  Does the change somehow allow for arbitrary javascript to be executed? *
6476  *    Or allows for someone to change the prototype of built-in objects?   *
6477  *     Or gives undesired access to variables likes document or window?    *
6478  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6479
6480 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6481  *
6482  * DOM-related variables:
6483  *
6484  * - "node" - DOM Node
6485  * - "element" - DOM Element or Node
6486  * - "$node" or "$element" - jqLite-wrapped node or element
6487  *
6488  *
6489  * Compiler related stuff:
6490  *
6491  * - "linkFn" - linking fn of a single directive
6492  * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6493  * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
6494  * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6495  */
6496
6497
6498 /**
6499  * @ngdoc service
6500  * @name $compile
6501  * @kind function
6502  *
6503  * @description
6504  * Compiles an HTML string or DOM into a template and produces a template function, which
6505  * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6506  *
6507  * The compilation is a process of walking the DOM tree and matching DOM elements to
6508  * {@link ng.$compileProvider#directive directives}.
6509  *
6510  * <div class="alert alert-warning">
6511  * **Note:** This document is an in-depth reference of all directive options.
6512  * For a gentle introduction to directives with examples of common use cases,
6513  * see the {@link guide/directive directive guide}.
6514  * </div>
6515  *
6516  * ## Comprehensive Directive API
6517  *
6518  * There are many different options for a directive.
6519  *
6520  * The difference resides in the return value of the factory function.
6521  * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6522  * or just the `postLink` function (all other properties will have the default values).
6523  *
6524  * <div class="alert alert-success">
6525  * **Best Practice:** It's recommended to use the "directive definition object" form.
6526  * </div>
6527  *
6528  * Here's an example directive declared with a Directive Definition Object:
6529  *
6530  * ```js
6531  *   var myModule = angular.module(...);
6532  *
6533  *   myModule.directive('directiveName', function factory(injectables) {
6534  *     var directiveDefinitionObject = {
6535  *       priority: 0,
6536  *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },
6537  *       // or
6538  *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6539  *       transclude: false,
6540  *       restrict: 'A',
6541  *       templateNamespace: 'html',
6542  *       scope: false,
6543  *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6544  *       controllerAs: 'stringIdentifier',
6545  *       bindToController: false,
6546  *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6547  *       compile: function compile(tElement, tAttrs, transclude) {
6548  *         return {
6549  *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6550  *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
6551  *         }
6552  *         // or
6553  *         // return function postLink( ... ) { ... }
6554  *       },
6555  *       // or
6556  *       // link: {
6557  *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6558  *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
6559  *       // }
6560  *       // or
6561  *       // link: function postLink( ... ) { ... }
6562  *     };
6563  *     return directiveDefinitionObject;
6564  *   });
6565  * ```
6566  *
6567  * <div class="alert alert-warning">
6568  * **Note:** Any unspecified options will use the default value. You can see the default values below.
6569  * </div>
6570  *
6571  * Therefore the above can be simplified as:
6572  *
6573  * ```js
6574  *   var myModule = angular.module(...);
6575  *
6576  *   myModule.directive('directiveName', function factory(injectables) {
6577  *     var directiveDefinitionObject = {
6578  *       link: function postLink(scope, iElement, iAttrs) { ... }
6579  *     };
6580  *     return directiveDefinitionObject;
6581  *     // or
6582  *     // return function postLink(scope, iElement, iAttrs) { ... }
6583  *   });
6584  * ```
6585  *
6586  *
6587  *
6588  * ### Directive Definition Object
6589  *
6590  * The directive definition object provides instructions to the {@link ng.$compile
6591  * compiler}. The attributes are:
6592  *
6593  * #### `multiElement`
6594  * When this property is set to true, the HTML compiler will collect DOM nodes between
6595  * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6596  * together as the directive elements. It is recommended that this feature be used on directives
6597  * which are not strictly behavioural (such as {@link ngClick}), and which
6598  * do not manipulate or replace child nodes (such as {@link ngInclude}).
6599  *
6600  * #### `priority`
6601  * When there are multiple directives defined on a single DOM element, sometimes it
6602  * is necessary to specify the order in which the directives are applied. The `priority` is used
6603  * to sort the directives before their `compile` functions get called. Priority is defined as a
6604  * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6605  * are also run in priority order, but post-link functions are run in reverse order. The order
6606  * of directives with the same priority is undefined. The default priority is `0`.
6607  *
6608  * #### `terminal`
6609  * If set to true then the current `priority` will be the last set of directives
6610  * which will execute (any directives at the current priority will still execute
6611  * as the order of execution on same `priority` is undefined). Note that expressions
6612  * and other directives used in the directive's template will also be excluded from execution.
6613  *
6614  * #### `scope`
6615  * The scope property can be `true`, an object or a falsy value:
6616  *
6617  * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6618  *
6619  * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6620  * the directive's element. If multiple directives on the same element request a new scope,
6621  * only one new scope is created. The new scope rule does not apply for the root of the template
6622  * since the root of the template always gets a new scope.
6623  *
6624  * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6625  * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6626  * scope. This is useful when creating reusable components, which should not accidentally read or modify
6627  * data in the parent scope.
6628  *
6629  * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6630  * directive's element. These local properties are useful for aliasing values for templates. The keys in
6631  * the object hash map to the name of the property on the isolate scope; the values define how the property
6632  * is bound to the parent scope, via matching attributes on the directive's element:
6633  *
6634  * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6635  *   always a string since DOM attributes are strings. If no `attr` name is specified then the
6636  *   attribute name is assumed to be the same as the local name. Given `<my-component
6637  *   my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
6638  *   the directive's scope property `localName` will reflect the interpolated value of `hello
6639  *   {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
6640  *   scope. The `name` is read from the parent scope (not the directive's scope).
6641  *
6642  * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
6643  *   passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
6644  *   If no `attr` name is specified then the attribute name is assumed to be the same as the local
6645  *   name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
6646  *   localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
6647  *   value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
6648  *   `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
6649  *   `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
6650  *   optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
6651  *   will be thrown upon discovering changes to the local value, since it will be impossible to sync
6652  *   them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
6653  *   method is used for tracking changes, and the equality check is based on object identity.
6654  *   However, if an object literal or an array literal is passed as the binding expression, the
6655  *   equality check is done by value (using the {@link angular.equals} function). It's also possible
6656  *   to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
6657  *   `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
6658  *
6659  * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
6660  *   no `attr` name is specified then the attribute name is assumed to be the same as the local name.
6661  *   Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
6662  *   localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
6663  *   the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
6664  *   via an expression to the parent scope. This can be done by passing a map of local variable names
6665  *   and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
6666  *   then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
6667  *
6668  * In general it's possible to apply more than one directive to one element, but there might be limitations
6669  * depending on the type of scope required by the directives. The following points will help explain these limitations.
6670  * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6671  *
6672  * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6673  * * **child scope** + **no scope** =>  Both directives will share one single child scope
6674  * * **child scope** + **child scope** =>  Both directives will share one single child scope
6675  * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
6676  * its parent's scope
6677  * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6678  * be applied to the same element.
6679  * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
6680  * cannot be applied to the same element.
6681  *
6682  *
6683  * #### `bindToController`
6684  * This property is used to bind scope properties directly to the controller. It can be either
6685  * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
6686  * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
6687  * definition: `controller: 'myCtrl as myAlias'`.
6688  *
6689  * When an isolate scope is used for a directive (see above), `bindToController: true` will
6690  * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6691  * is instantiated, the initial values of the isolate scope bindings are already available.
6692  *
6693  * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
6694  * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
6695  * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
6696  * scope (useful for component directives).
6697  *
6698  * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
6699  *
6700  *
6701  * #### `controller`
6702  * Controller constructor function. The controller is instantiated before the
6703  * pre-linking phase and can be accessed by other directives (see
6704  * `require` attribute). This allows the directives to communicate with each other and augment
6705  * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6706  *
6707  * * `$scope` - Current scope associated with the element
6708  * * `$element` - Current element
6709  * * `$attrs` - Current attributes object for the element
6710  * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6711  *   `function([scope], cloneLinkingFn, futureParentElement)`.
6712  *    * `scope`: optional argument to override the scope.
6713  *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6714  *    * `futureParentElement`:
6715  *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6716  *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6717  *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6718  *          and when the `cloneLinkinFn` is passed,
6719  *          as those elements need to created and cloned in a special way when they are defined outside their
6720  *          usual containers (e.g. like `<svg>`).
6721  *        * See also the `directive.templateNamespace` property.
6722  *
6723  *
6724  * #### `require`
6725  * Require another directive and inject its controller as the fourth argument to the linking function. The
6726  * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6727  * injected argument will be an array in corresponding order. If no such directive can be
6728  * found, or if the directive does not have a controller, then an error is raised (unless no link function
6729  * is specified, in which case error checking is skipped). The name can be prefixed with:
6730  *
6731  * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6732  * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6733  * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6734  * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6735  * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6736  *   `null` to the `link` fn if not found.
6737  * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6738  *   `null` to the `link` fn if not found.
6739  *
6740  *
6741  * #### `controllerAs`
6742  * Identifier name for a reference to the controller in the directive's scope.
6743  * This allows the controller to be referenced from the directive template. This is especially
6744  * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6745  * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6746  * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6747  *
6748  *
6749  * #### `restrict`
6750  * String of subset of `EACM` which restricts the directive to a specific directive
6751  * declaration style. If omitted, the defaults (elements and attributes) are used.
6752  *
6753  * * `E` - Element name (default): `<my-directive></my-directive>`
6754  * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6755  * * `C` - Class: `<div class="my-directive: exp;"></div>`
6756  * * `M` - Comment: `<!-- directive: my-directive exp -->`
6757  *
6758  *
6759  * #### `templateNamespace`
6760  * String representing the document type used by the markup in the template.
6761  * AngularJS needs this information as those elements need to be created and cloned
6762  * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6763  *
6764  * * `html` - All root nodes in the template are HTML. Root nodes may also be
6765  *   top-level elements such as `<svg>` or `<math>`.
6766  * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6767  * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6768  *
6769  * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6770  *
6771  * #### `template`
6772  * HTML markup that may:
6773  * * Replace the contents of the directive's element (default).
6774  * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6775  * * Wrap the contents of the directive's element (if `transclude` is true).
6776  *
6777  * Value may be:
6778  *
6779  * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6780  * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6781  *   function api below) and returns a string value.
6782  *
6783  *
6784  * #### `templateUrl`
6785  * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6786  *
6787  * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6788  * for later when the template has been resolved.  In the meantime it will continue to compile and link
6789  * sibling and parent elements as though this element had not contained any directives.
6790  *
6791  * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6792  * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6793  * case when only one deeply nested directive has `templateUrl`.
6794  *
6795  * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6796  *
6797  * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6798  * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6799  * a string value representing the url.  In either case, the template URL is passed through {@link
6800  * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6801  *
6802  *
6803  * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6804  * specify what the template should replace. Defaults to `false`.
6805  *
6806  * * `true` - the template will replace the directive's element.
6807  * * `false` - the template will replace the contents of the directive's element.
6808  *
6809  * The replacement process migrates all of the attributes / classes from the old element to the new
6810  * one. See the {@link guide/directive#template-expanding-directive
6811  * Directives Guide} for an example.
6812  *
6813  * There are very few scenarios where element replacement is required for the application function,
6814  * the main one being reusable custom components that are used within SVG contexts
6815  * (because SVG doesn't work with custom elements in the DOM tree).
6816  *
6817  * #### `transclude`
6818  * Extract the contents of the element where the directive appears and make it available to the directive.
6819  * The contents are compiled and provided to the directive as a **transclusion function**. See the
6820  * {@link $compile#transclusion Transclusion} section below.
6821  *
6822  * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6823  * directive's element or the entire element:
6824  *
6825  * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6826  * * `'element'` - transclude the whole of the directive's element including any directives on this
6827  *   element that defined at a lower priority than this directive. When used, the `template`
6828  *   property is ignored.
6829  *
6830  *
6831  * #### `compile`
6832  *
6833  * ```js
6834  *   function compile(tElement, tAttrs, transclude) { ... }
6835  * ```
6836  *
6837  * The compile function deals with transforming the template DOM. Since most directives do not do
6838  * template transformation, it is not used often. The compile function takes the following arguments:
6839  *
6840  *   * `tElement` - template element - The element where the directive has been declared. It is
6841  *     safe to do template transformation on the element and child elements only.
6842  *
6843  *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6844  *     between all directive compile functions.
6845  *
6846  *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6847  *
6848  * <div class="alert alert-warning">
6849  * **Note:** The template instance and the link instance may be different objects if the template has
6850  * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6851  * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6852  * should be done in a linking function rather than in a compile function.
6853  * </div>
6854
6855  * <div class="alert alert-warning">
6856  * **Note:** The compile function cannot handle directives that recursively use themselves in their
6857  * own templates or compile functions. Compiling these directives results in an infinite loop and
6858  * stack overflow errors.
6859  *
6860  * This can be avoided by manually using $compile in the postLink function to imperatively compile
6861  * a directive's template instead of relying on automatic template compilation via `template` or
6862  * `templateUrl` declaration or manual compilation inside the compile function.
6863  * </div>
6864  *
6865  * <div class="alert alert-danger">
6866  * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6867  *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6868  *   to the link function instead.
6869  * </div>
6870
6871  * A compile function can have a return value which can be either a function or an object.
6872  *
6873  * * returning a (post-link) function - is equivalent to registering the linking function via the
6874  *   `link` property of the config object when the compile function is empty.
6875  *
6876  * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6877  *   control when a linking function should be called during the linking phase. See info about
6878  *   pre-linking and post-linking functions below.
6879  *
6880  *
6881  * #### `link`
6882  * This property is used only if the `compile` property is not defined.
6883  *
6884  * ```js
6885  *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6886  * ```
6887  *
6888  * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6889  * executed after the template has been cloned. This is where most of the directive logic will be
6890  * put.
6891  *
6892  *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6893  *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6894  *
6895  *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6896  *     manipulate the children of the element only in `postLink` function since the children have
6897  *     already been linked.
6898  *
6899  *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6900  *     between all directive linking functions.
6901  *
6902  *   * `controller` - the directive's required controller instance(s) - Instances are shared
6903  *     among all directives, which allows the directives to use the controllers as a communication
6904  *     channel. The exact value depends on the directive's `require` property:
6905  *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6906  *       * `string`: the controller instance
6907  *       * `array`: array of controller instances
6908  *
6909  *     If a required controller cannot be found, and it is optional, the instance is `null`,
6910  *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6911  *
6912  *     Note that you can also require the directive's own controller - it will be made available like
6913  *     any other controller.
6914  *
6915  *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6916  *     This is the same as the `$transclude`
6917  *     parameter of directive controllers, see there for details.
6918  *     `function([scope], cloneLinkingFn, futureParentElement)`.
6919  *
6920  * #### Pre-linking function
6921  *
6922  * Executed before the child elements are linked. Not safe to do DOM transformation since the
6923  * compiler linking function will fail to locate the correct elements for linking.
6924  *
6925  * #### Post-linking function
6926  *
6927  * Executed after the child elements are linked.
6928  *
6929  * Note that child elements that contain `templateUrl` directives will not have been compiled
6930  * and linked since they are waiting for their template to load asynchronously and their own
6931  * compilation and linking has been suspended until that occurs.
6932  *
6933  * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6934  * for their async templates to be resolved.
6935  *
6936  *
6937  * ### Transclusion
6938  *
6939  * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6940  * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6941  * scope from where they were taken.
6942  *
6943  * Transclusion is used (often with {@link ngTransclude}) to insert the
6944  * original contents of a directive's element into a specified place in the template of the directive.
6945  * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6946  * content has access to the properties on the scope from which it was taken, even if the directive
6947  * has isolated scope.
6948  * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6949  *
6950  * This makes it possible for the widget to have private state for its template, while the transcluded
6951  * content has access to its originating scope.
6952  *
6953  * <div class="alert alert-warning">
6954  * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6955  * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6956  * Testing Transclusion Directives}.
6957  * </div>
6958  *
6959  * #### Transclusion Functions
6960  *
6961  * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6962  * function** to the directive's `link` function and `controller`. This transclusion function is a special
6963  * **linking function** that will return the compiled contents linked to a new transclusion scope.
6964  *
6965  * <div class="alert alert-info">
6966  * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6967  * ngTransclude will deal with it for us.
6968  * </div>
6969  *
6970  * If you want to manually control the insertion and removal of the transcluded content in your directive
6971  * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6972  * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6973  *
6974  * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6975  * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6976  * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6977  *
6978  * <div class="alert alert-info">
6979  * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6980  * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6981  * </div>
6982  *
6983  * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6984  * attach function**:
6985  *
6986  * ```js
6987  * var transcludedContent, transclusionScope;
6988  *
6989  * $transclude(function(clone, scope) {
6990  *   element.append(clone);
6991  *   transcludedContent = clone;
6992  *   transclusionScope = scope;
6993  * });
6994  * ```
6995  *
6996  * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6997  * associated transclusion scope:
6998  *
6999  * ```js
7000  * transcludedContent.remove();
7001  * transclusionScope.$destroy();
7002  * ```
7003  *
7004  * <div class="alert alert-info">
7005  * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
7006  * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
7007  * then you are also responsible for calling `$destroy` on the transclusion scope.
7008  * </div>
7009  *
7010  * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
7011  * automatically destroy their transluded clones as necessary so you do not need to worry about this if
7012  * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
7013  *
7014  *
7015  * #### Transclusion Scopes
7016  *
7017  * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
7018  * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
7019  * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
7020  * was taken.
7021  *
7022  * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
7023  * like this:
7024  *
7025  * ```html
7026  * <div ng-app>
7027  *   <div isolate>
7028  *     <div transclusion>
7029  *     </div>
7030  *   </div>
7031  * </div>
7032  * ```
7033  *
7034  * The `$parent` scope hierarchy will look like this:
7035  *
7036    ```
7037    - $rootScope
7038      - isolate
7039        - transclusion
7040    ```
7041  *
7042  * but the scopes will inherit prototypically from different scopes to their `$parent`.
7043  *
7044    ```
7045    - $rootScope
7046      - transclusion
7047    - isolate
7048    ```
7049  *
7050  *
7051  * ### Attributes
7052  *
7053  * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
7054  * `link()` or `compile()` functions. It has a variety of uses.
7055  *
7056  * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
7057  *   'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
7058  *   to the attributes.
7059  *
7060  * * *Directive inter-communication:* All directives share the same instance of the attributes
7061  *   object which allows the directives to use the attributes object as inter directive
7062  *   communication.
7063  *
7064  * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
7065  *   allowing other directives to read the interpolated value.
7066  *
7067  * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
7068  *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
7069  *   the only way to easily get the actual value because during the linking phase the interpolation
7070  *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
7071  *
7072  * ```js
7073  * function linkingFn(scope, elm, attrs, ctrl) {
7074  *   // get the attribute value
7075  *   console.log(attrs.ngModel);
7076  *
7077  *   // change the attribute
7078  *   attrs.$set('ngModel', 'new value');
7079  *
7080  *   // observe changes to interpolated attribute
7081  *   attrs.$observe('ngModel', function(value) {
7082  *     console.log('ngModel has changed value to ' + value);
7083  *   });
7084  * }
7085  * ```
7086  *
7087  * ## Example
7088  *
7089  * <div class="alert alert-warning">
7090  * **Note**: Typically directives are registered with `module.directive`. The example below is
7091  * to illustrate how `$compile` works.
7092  * </div>
7093  *
7094  <example module="compileExample">
7095    <file name="index.html">
7096     <script>
7097       angular.module('compileExample', [], function($compileProvider) {
7098         // configure new 'compile' directive by passing a directive
7099         // factory function. The factory function injects the '$compile'
7100         $compileProvider.directive('compile', function($compile) {
7101           // directive factory creates a link function
7102           return function(scope, element, attrs) {
7103             scope.$watch(
7104               function(scope) {
7105                  // watch the 'compile' expression for changes
7106                 return scope.$eval(attrs.compile);
7107               },
7108               function(value) {
7109                 // when the 'compile' expression changes
7110                 // assign it into the current DOM
7111                 element.html(value);
7112
7113                 // compile the new DOM and link it to the current
7114                 // scope.
7115                 // NOTE: we only compile .childNodes so that
7116                 // we don't get into infinite loop compiling ourselves
7117                 $compile(element.contents())(scope);
7118               }
7119             );
7120           };
7121         });
7122       })
7123       .controller('GreeterController', ['$scope', function($scope) {
7124         $scope.name = 'Angular';
7125         $scope.html = 'Hello {{name}}';
7126       }]);
7127     </script>
7128     <div ng-controller="GreeterController">
7129       <input ng-model="name"> <br/>
7130       <textarea ng-model="html"></textarea> <br/>
7131       <div compile="html"></div>
7132     </div>
7133    </file>
7134    <file name="protractor.js" type="protractor">
7135      it('should auto compile', function() {
7136        var textarea = $('textarea');
7137        var output = $('div[compile]');
7138        // The initial state reads 'Hello Angular'.
7139        expect(output.getText()).toBe('Hello Angular');
7140        textarea.clear();
7141        textarea.sendKeys('{{name}}!');
7142        expect(output.getText()).toBe('Angular!');
7143      });
7144    </file>
7145  </example>
7146
7147  *
7148  *
7149  * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7150  * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7151  *
7152  * <div class="alert alert-danger">
7153  * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7154  *   e.g. will not use the right outer scope. Please pass the transclude function as a
7155  *   `parentBoundTranscludeFn` to the link function instead.
7156  * </div>
7157  *
7158  * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7159  *                 root element(s), not their children)
7160  * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7161  * (a DOM element/tree) to a scope. Where:
7162  *
7163  *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7164  *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7165  *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
7166  *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7167  *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7168  *
7169  *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
7170  *      * `scope` - is the current scope with which the linking function is working with.
7171  *
7172  *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
7173  *  keys may be used to control linking behavior:
7174  *
7175  *      * `parentBoundTranscludeFn` - the transclude function made available to
7176  *        directives; if given, it will be passed through to the link functions of
7177  *        directives found in `element` during compilation.
7178  *      * `transcludeControllers` - an object hash with keys that map controller names
7179  *        to a hash with the key `instance`, which maps to the controller instance;
7180  *        if given, it will make the controllers available to directives on the compileNode:
7181  *        ```
7182  *        {
7183  *          parent: {
7184  *            instance: parentControllerInstance
7185  *          }
7186  *        }
7187  *        ```
7188  *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7189  *        the cloned elements; only needed for transcludes that are allowed to contain non html
7190  *        elements (e.g. SVG elements). See also the directive.controller property.
7191  *
7192  * Calling the linking function returns the element of the template. It is either the original
7193  * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7194  *
7195  * After linking the view is not updated until after a call to $digest which typically is done by
7196  * Angular automatically.
7197  *
7198  * If you need access to the bound view, there are two ways to do it:
7199  *
7200  * - If you are not asking the linking function to clone the template, create the DOM element(s)
7201  *   before you send them to the compiler and keep this reference around.
7202  *   ```js
7203  *     var element = $compile('<p>{{total}}</p>')(scope);
7204  *   ```
7205  *
7206  * - if on the other hand, you need the element to be cloned, the view reference from the original
7207  *   example would not point to the clone, but rather to the original template that was cloned. In
7208  *   this case, you can access the clone via the cloneAttachFn:
7209  *   ```js
7210  *     var templateElement = angular.element('<p>{{total}}</p>'),
7211  *         scope = ....;
7212  *
7213  *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7214  *       //attach the clone to DOM document at the right place
7215  *     });
7216  *
7217  *     //now we have reference to the cloned DOM via `clonedElement`
7218  *   ```
7219  *
7220  *
7221  * For information on how the compiler works, see the
7222  * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7223  */
7224
7225 var $compileMinErr = minErr('$compile');
7226
7227 /**
7228  * @ngdoc provider
7229  * @name $compileProvider
7230  *
7231  * @description
7232  */
7233 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7234 function $CompileProvider($provide, $$sanitizeUriProvider) {
7235   var hasDirectives = {},
7236       Suffix = 'Directive',
7237       COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7238       CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7239       ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7240       REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7241
7242   // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7243   // The assumption is that future DOM event attribute names will begin with
7244   // 'on' and be composed of only English letters.
7245   var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7246   var bindingCache = createMap();
7247
7248   function parseIsolateBindings(scope, directiveName, isController) {
7249     var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
7250
7251     var bindings = {};
7252
7253     forEach(scope, function(definition, scopeName) {
7254       if (definition in bindingCache) {
7255         bindings[scopeName] = bindingCache[definition];
7256         return;
7257       }
7258       var match = definition.match(LOCAL_REGEXP);
7259
7260       if (!match) {
7261         throw $compileMinErr('iscp',
7262             "Invalid {3} for directive '{0}'." +
7263             " Definition: {... {1}: '{2}' ...}",
7264             directiveName, scopeName, definition,
7265             (isController ? "controller bindings definition" :
7266             "isolate scope definition"));
7267       }
7268
7269       bindings[scopeName] = {
7270         mode: match[1][0],
7271         collection: match[2] === '*',
7272         optional: match[3] === '?',
7273         attrName: match[4] || scopeName
7274       };
7275       if (match[4]) {
7276         bindingCache[definition] = bindings[scopeName];
7277       }
7278     });
7279
7280     return bindings;
7281   }
7282
7283   function parseDirectiveBindings(directive, directiveName) {
7284     var bindings = {
7285       isolateScope: null,
7286       bindToController: null
7287     };
7288     if (isObject(directive.scope)) {
7289       if (directive.bindToController === true) {
7290         bindings.bindToController = parseIsolateBindings(directive.scope,
7291                                                          directiveName, true);
7292         bindings.isolateScope = {};
7293       } else {
7294         bindings.isolateScope = parseIsolateBindings(directive.scope,
7295                                                      directiveName, false);
7296       }
7297     }
7298     if (isObject(directive.bindToController)) {
7299       bindings.bindToController =
7300           parseIsolateBindings(directive.bindToController, directiveName, true);
7301     }
7302     if (isObject(bindings.bindToController)) {
7303       var controller = directive.controller;
7304       var controllerAs = directive.controllerAs;
7305       if (!controller) {
7306         // There is no controller, there may or may not be a controllerAs property
7307         throw $compileMinErr('noctrl',
7308               "Cannot bind to controller without directive '{0}'s controller.",
7309               directiveName);
7310       } else if (!identifierForController(controller, controllerAs)) {
7311         // There is a controller, but no identifier or controllerAs property
7312         throw $compileMinErr('noident',
7313               "Cannot bind to controller without identifier for directive '{0}'.",
7314               directiveName);
7315       }
7316     }
7317     return bindings;
7318   }
7319
7320   function assertValidDirectiveName(name) {
7321     var letter = name.charAt(0);
7322     if (!letter || letter !== lowercase(letter)) {
7323       throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
7324     }
7325     if (name !== name.trim()) {
7326       throw $compileMinErr('baddir',
7327             "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7328             name);
7329     }
7330   }
7331
7332   /**
7333    * @ngdoc method
7334    * @name $compileProvider#directive
7335    * @kind function
7336    *
7337    * @description
7338    * Register a new directive with the compiler.
7339    *
7340    * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7341    *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7342    *    names and the values are the factories.
7343    * @param {Function|Array} directiveFactory An injectable directive factory function. See
7344    *    {@link guide/directive} for more info.
7345    * @returns {ng.$compileProvider} Self for chaining.
7346    */
7347    this.directive = function registerDirective(name, directiveFactory) {
7348     assertNotHasOwnProperty(name, 'directive');
7349     if (isString(name)) {
7350       assertValidDirectiveName(name);
7351       assertArg(directiveFactory, 'directiveFactory');
7352       if (!hasDirectives.hasOwnProperty(name)) {
7353         hasDirectives[name] = [];
7354         $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7355           function($injector, $exceptionHandler) {
7356             var directives = [];
7357             forEach(hasDirectives[name], function(directiveFactory, index) {
7358               try {
7359                 var directive = $injector.invoke(directiveFactory);
7360                 if (isFunction(directive)) {
7361                   directive = { compile: valueFn(directive) };
7362                 } else if (!directive.compile && directive.link) {
7363                   directive.compile = valueFn(directive.link);
7364                 }
7365                 directive.priority = directive.priority || 0;
7366                 directive.index = index;
7367                 directive.name = directive.name || name;
7368                 directive.require = directive.require || (directive.controller && directive.name);
7369                 directive.restrict = directive.restrict || 'EA';
7370                 directive.$$moduleName = directiveFactory.$$moduleName;
7371                 directives.push(directive);
7372               } catch (e) {
7373                 $exceptionHandler(e);
7374               }
7375             });
7376             return directives;
7377           }]);
7378       }
7379       hasDirectives[name].push(directiveFactory);
7380     } else {
7381       forEach(name, reverseParams(registerDirective));
7382     }
7383     return this;
7384   };
7385
7386
7387   /**
7388    * @ngdoc method
7389    * @name $compileProvider#aHrefSanitizationWhitelist
7390    * @kind function
7391    *
7392    * @description
7393    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7394    * urls during a[href] sanitization.
7395    *
7396    * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7397    *
7398    * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7399    * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7400    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7401    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7402    *
7403    * @param {RegExp=} regexp New regexp to whitelist urls with.
7404    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7405    *    chaining otherwise.
7406    */
7407   this.aHrefSanitizationWhitelist = function(regexp) {
7408     if (isDefined(regexp)) {
7409       $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7410       return this;
7411     } else {
7412       return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7413     }
7414   };
7415
7416
7417   /**
7418    * @ngdoc method
7419    * @name $compileProvider#imgSrcSanitizationWhitelist
7420    * @kind function
7421    *
7422    * @description
7423    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7424    * urls during img[src] sanitization.
7425    *
7426    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7427    *
7428    * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7429    * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7430    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7431    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7432    *
7433    * @param {RegExp=} regexp New regexp to whitelist urls with.
7434    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7435    *    chaining otherwise.
7436    */
7437   this.imgSrcSanitizationWhitelist = function(regexp) {
7438     if (isDefined(regexp)) {
7439       $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7440       return this;
7441     } else {
7442       return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7443     }
7444   };
7445
7446   /**
7447    * @ngdoc method
7448    * @name  $compileProvider#debugInfoEnabled
7449    *
7450    * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7451    * current debugInfoEnabled state
7452    * @returns {*} current value if used as getter or itself (chaining) if used as setter
7453    *
7454    * @kind function
7455    *
7456    * @description
7457    * Call this method to enable/disable various debug runtime information in the compiler such as adding
7458    * binding information and a reference to the current scope on to DOM elements.
7459    * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7460    * * `ng-binding` CSS class
7461    * * `$binding` data property containing an array of the binding expressions
7462    *
7463    * You may want to disable this in production for a significant performance boost. See
7464    * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7465    *
7466    * The default value is true.
7467    */
7468   var debugInfoEnabled = true;
7469   this.debugInfoEnabled = function(enabled) {
7470     if (isDefined(enabled)) {
7471       debugInfoEnabled = enabled;
7472       return this;
7473     }
7474     return debugInfoEnabled;
7475   };
7476
7477   this.$get = [
7478             '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7479             '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
7480     function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
7481              $controller,   $rootScope,   $sce,   $animate,   $$sanitizeUri) {
7482
7483     var Attributes = function(element, attributesToCopy) {
7484       if (attributesToCopy) {
7485         var keys = Object.keys(attributesToCopy);
7486         var i, l, key;
7487
7488         for (i = 0, l = keys.length; i < l; i++) {
7489           key = keys[i];
7490           this[key] = attributesToCopy[key];
7491         }
7492       } else {
7493         this.$attr = {};
7494       }
7495
7496       this.$$element = element;
7497     };
7498
7499     Attributes.prototype = {
7500       /**
7501        * @ngdoc method
7502        * @name $compile.directive.Attributes#$normalize
7503        * @kind function
7504        *
7505        * @description
7506        * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7507        * `data-`) to its normalized, camelCase form.
7508        *
7509        * Also there is special case for Moz prefix starting with upper case letter.
7510        *
7511        * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7512        *
7513        * @param {string} name Name to normalize
7514        */
7515       $normalize: directiveNormalize,
7516
7517
7518       /**
7519        * @ngdoc method
7520        * @name $compile.directive.Attributes#$addClass
7521        * @kind function
7522        *
7523        * @description
7524        * Adds the CSS class value specified by the classVal parameter to the element. If animations
7525        * are enabled then an animation will be triggered for the class addition.
7526        *
7527        * @param {string} classVal The className value that will be added to the element
7528        */
7529       $addClass: function(classVal) {
7530         if (classVal && classVal.length > 0) {
7531           $animate.addClass(this.$$element, classVal);
7532         }
7533       },
7534
7535       /**
7536        * @ngdoc method
7537        * @name $compile.directive.Attributes#$removeClass
7538        * @kind function
7539        *
7540        * @description
7541        * Removes the CSS class value specified by the classVal parameter from the element. If
7542        * animations are enabled then an animation will be triggered for the class removal.
7543        *
7544        * @param {string} classVal The className value that will be removed from the element
7545        */
7546       $removeClass: function(classVal) {
7547         if (classVal && classVal.length > 0) {
7548           $animate.removeClass(this.$$element, classVal);
7549         }
7550       },
7551
7552       /**
7553        * @ngdoc method
7554        * @name $compile.directive.Attributes#$updateClass
7555        * @kind function
7556        *
7557        * @description
7558        * Adds and removes the appropriate CSS class values to the element based on the difference
7559        * between the new and old CSS class values (specified as newClasses and oldClasses).
7560        *
7561        * @param {string} newClasses The current CSS className value
7562        * @param {string} oldClasses The former CSS className value
7563        */
7564       $updateClass: function(newClasses, oldClasses) {
7565         var toAdd = tokenDifference(newClasses, oldClasses);
7566         if (toAdd && toAdd.length) {
7567           $animate.addClass(this.$$element, toAdd);
7568         }
7569
7570         var toRemove = tokenDifference(oldClasses, newClasses);
7571         if (toRemove && toRemove.length) {
7572           $animate.removeClass(this.$$element, toRemove);
7573         }
7574       },
7575
7576       /**
7577        * Set a normalized attribute on the element in a way such that all directives
7578        * can share the attribute. This function properly handles boolean attributes.
7579        * @param {string} key Normalized key. (ie ngAttribute)
7580        * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7581        * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7582        *     Defaults to true.
7583        * @param {string=} attrName Optional none normalized name. Defaults to key.
7584        */
7585       $set: function(key, value, writeAttr, attrName) {
7586         // TODO: decide whether or not to throw an error if "class"
7587         //is set through this function since it may cause $updateClass to
7588         //become unstable.
7589
7590         var node = this.$$element[0],
7591             booleanKey = getBooleanAttrName(node, key),
7592             aliasedKey = getAliasedAttrName(key),
7593             observer = key,
7594             nodeName;
7595
7596         if (booleanKey) {
7597           this.$$element.prop(key, value);
7598           attrName = booleanKey;
7599         } else if (aliasedKey) {
7600           this[aliasedKey] = value;
7601           observer = aliasedKey;
7602         }
7603
7604         this[key] = value;
7605
7606         // translate normalized key to actual key
7607         if (attrName) {
7608           this.$attr[key] = attrName;
7609         } else {
7610           attrName = this.$attr[key];
7611           if (!attrName) {
7612             this.$attr[key] = attrName = snake_case(key, '-');
7613           }
7614         }
7615
7616         nodeName = nodeName_(this.$$element);
7617
7618         if ((nodeName === 'a' && key === 'href') ||
7619             (nodeName === 'img' && key === 'src')) {
7620           // sanitize a[href] and img[src] values
7621           this[key] = value = $$sanitizeUri(value, key === 'src');
7622         } else if (nodeName === 'img' && key === 'srcset') {
7623           // sanitize img[srcset] values
7624           var result = "";
7625
7626           // first check if there are spaces because it's not the same pattern
7627           var trimmedSrcset = trim(value);
7628           //                (   999x   ,|   999w   ,|   ,|,   )
7629           var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7630           var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7631
7632           // split srcset into tuple of uri and descriptor except for the last item
7633           var rawUris = trimmedSrcset.split(pattern);
7634
7635           // for each tuples
7636           var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7637           for (var i = 0; i < nbrUrisWith2parts; i++) {
7638             var innerIdx = i * 2;
7639             // sanitize the uri
7640             result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7641             // add the descriptor
7642             result += (" " + trim(rawUris[innerIdx + 1]));
7643           }
7644
7645           // split the last item into uri and descriptor
7646           var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7647
7648           // sanitize the last uri
7649           result += $$sanitizeUri(trim(lastTuple[0]), true);
7650
7651           // and add the last descriptor if any
7652           if (lastTuple.length === 2) {
7653             result += (" " + trim(lastTuple[1]));
7654           }
7655           this[key] = value = result;
7656         }
7657
7658         if (writeAttr !== false) {
7659           if (value === null || isUndefined(value)) {
7660             this.$$element.removeAttr(attrName);
7661           } else {
7662             this.$$element.attr(attrName, value);
7663           }
7664         }
7665
7666         // fire observers
7667         var $$observers = this.$$observers;
7668         $$observers && forEach($$observers[observer], function(fn) {
7669           try {
7670             fn(value);
7671           } catch (e) {
7672             $exceptionHandler(e);
7673           }
7674         });
7675       },
7676
7677
7678       /**
7679        * @ngdoc method
7680        * @name $compile.directive.Attributes#$observe
7681        * @kind function
7682        *
7683        * @description
7684        * Observes an interpolated attribute.
7685        *
7686        * The observer function will be invoked once during the next `$digest` following
7687        * compilation. The observer is then invoked whenever the interpolated value
7688        * changes.
7689        *
7690        * @param {string} key Normalized key. (ie ngAttribute) .
7691        * @param {function(interpolatedValue)} fn Function that will be called whenever
7692                 the interpolated value of the attribute changes.
7693        *        See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
7694        *        guide} for more info.
7695        * @returns {function()} Returns a deregistration function for this observer.
7696        */
7697       $observe: function(key, fn) {
7698         var attrs = this,
7699             $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7700             listeners = ($$observers[key] || ($$observers[key] = []));
7701
7702         listeners.push(fn);
7703         $rootScope.$evalAsync(function() {
7704           if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7705             // no one registered attribute interpolation function, so lets call it manually
7706             fn(attrs[key]);
7707           }
7708         });
7709
7710         return function() {
7711           arrayRemove(listeners, fn);
7712         };
7713       }
7714     };
7715
7716
7717     function safeAddClass($element, className) {
7718       try {
7719         $element.addClass(className);
7720       } catch (e) {
7721         // ignore, since it means that we are trying to set class on
7722         // SVG element, where class name is read-only.
7723       }
7724     }
7725
7726
7727     var startSymbol = $interpolate.startSymbol(),
7728         endSymbol = $interpolate.endSymbol(),
7729         denormalizeTemplate = (startSymbol == '{{' && endSymbol  == '}}')
7730             ? identity
7731             : function denormalizeTemplate(template) {
7732               return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7733         },
7734         NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7735     var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
7736
7737     compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7738       var bindings = $element.data('$binding') || [];
7739
7740       if (isArray(binding)) {
7741         bindings = bindings.concat(binding);
7742       } else {
7743         bindings.push(binding);
7744       }
7745
7746       $element.data('$binding', bindings);
7747     } : noop;
7748
7749     compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7750       safeAddClass($element, 'ng-binding');
7751     } : noop;
7752
7753     compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7754       var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7755       $element.data(dataName, scope);
7756     } : noop;
7757
7758     compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7759       safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7760     } : noop;
7761
7762     return compile;
7763
7764     //================================
7765
7766     function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7767                         previousCompileContext) {
7768       if (!($compileNodes instanceof jqLite)) {
7769         // jquery always rewraps, whereas we need to preserve the original selector so that we can
7770         // modify it.
7771         $compileNodes = jqLite($compileNodes);
7772       }
7773
7774       var NOT_EMPTY = /\S+/;
7775
7776       // We can not compile top level text elements since text nodes can be merged and we will
7777       // not be able to attach scope data to them, so we will wrap them in <span>
7778       for (var i = 0, len = $compileNodes.length; i < len; i++) {
7779         var domNode = $compileNodes[i];
7780
7781         if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
7782           jqLiteWrapNode(domNode, $compileNodes[i] = document.createElement('span'));
7783         }
7784       }
7785
7786       var compositeLinkFn =
7787               compileNodes($compileNodes, transcludeFn, $compileNodes,
7788                            maxPriority, ignoreDirective, previousCompileContext);
7789       compile.$$addScopeClass($compileNodes);
7790       var namespace = null;
7791       return function publicLinkFn(scope, cloneConnectFn, options) {
7792         assertArg(scope, 'scope');
7793
7794         if (previousCompileContext && previousCompileContext.needsNewScope) {
7795           // A parent directive did a replace and a directive on this element asked
7796           // for transclusion, which caused us to lose a layer of element on which
7797           // we could hold the new transclusion scope, so we will create it manually
7798           // here.
7799           scope = scope.$parent.$new();
7800         }
7801
7802         options = options || {};
7803         var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7804           transcludeControllers = options.transcludeControllers,
7805           futureParentElement = options.futureParentElement;
7806
7807         // When `parentBoundTranscludeFn` is passed, it is a
7808         // `controllersBoundTransclude` function (it was previously passed
7809         // as `transclude` to directive.link) so we must unwrap it to get
7810         // its `boundTranscludeFn`
7811         if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7812           parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7813         }
7814
7815         if (!namespace) {
7816           namespace = detectNamespaceForChildElements(futureParentElement);
7817         }
7818         var $linkNode;
7819         if (namespace !== 'html') {
7820           // When using a directive with replace:true and templateUrl the $compileNodes
7821           // (or a child element inside of them)
7822           // might change, so we need to recreate the namespace adapted compileNodes
7823           // for call to the link function.
7824           // Note: This will already clone the nodes...
7825           $linkNode = jqLite(
7826             wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7827           );
7828         } else if (cloneConnectFn) {
7829           // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7830           // and sometimes changes the structure of the DOM.
7831           $linkNode = JQLitePrototype.clone.call($compileNodes);
7832         } else {
7833           $linkNode = $compileNodes;
7834         }
7835
7836         if (transcludeControllers) {
7837           for (var controllerName in transcludeControllers) {
7838             $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7839           }
7840         }
7841
7842         compile.$$addScopeInfo($linkNode, scope);
7843
7844         if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7845         if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7846         return $linkNode;
7847       };
7848     }
7849
7850     function detectNamespaceForChildElements(parentElement) {
7851       // TODO: Make this detect MathML as well...
7852       var node = parentElement && parentElement[0];
7853       if (!node) {
7854         return 'html';
7855       } else {
7856         return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7857       }
7858     }
7859
7860     /**
7861      * Compile function matches each node in nodeList against the directives. Once all directives
7862      * for a particular node are collected their compile functions are executed. The compile
7863      * functions return values - the linking functions - are combined into a composite linking
7864      * function, which is the a linking function for the node.
7865      *
7866      * @param {NodeList} nodeList an array of nodes or NodeList to compile
7867      * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7868      *        scope argument is auto-generated to the new child of the transcluded parent scope.
7869      * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7870      *        the rootElement must be set the jqLite collection of the compile root. This is
7871      *        needed so that the jqLite collection items can be replaced with widgets.
7872      * @param {number=} maxPriority Max directive priority.
7873      * @returns {Function} A composite linking function of all of the matched directives or null.
7874      */
7875     function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7876                             previousCompileContext) {
7877       var linkFns = [],
7878           attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7879
7880       for (var i = 0; i < nodeList.length; i++) {
7881         attrs = new Attributes();
7882
7883         // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7884         directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7885                                         ignoreDirective);
7886
7887         nodeLinkFn = (directives.length)
7888             ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7889                                       null, [], [], previousCompileContext)
7890             : null;
7891
7892         if (nodeLinkFn && nodeLinkFn.scope) {
7893           compile.$$addScopeClass(attrs.$$element);
7894         }
7895
7896         childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7897                       !(childNodes = nodeList[i].childNodes) ||
7898                       !childNodes.length)
7899             ? null
7900             : compileNodes(childNodes,
7901                  nodeLinkFn ? (
7902                   (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7903                      && nodeLinkFn.transclude) : transcludeFn);
7904
7905         if (nodeLinkFn || childLinkFn) {
7906           linkFns.push(i, nodeLinkFn, childLinkFn);
7907           linkFnFound = true;
7908           nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7909         }
7910
7911         //use the previous context only for the first element in the virtual group
7912         previousCompileContext = null;
7913       }
7914
7915       // return a linking function if we have found anything, null otherwise
7916       return linkFnFound ? compositeLinkFn : null;
7917
7918       function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7919         var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7920         var stableNodeList;
7921
7922
7923         if (nodeLinkFnFound) {
7924           // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7925           // offsets don't get screwed up
7926           var nodeListLength = nodeList.length;
7927           stableNodeList = new Array(nodeListLength);
7928
7929           // create a sparse array by only copying the elements which have a linkFn
7930           for (i = 0; i < linkFns.length; i+=3) {
7931             idx = linkFns[i];
7932             stableNodeList[idx] = nodeList[idx];
7933           }
7934         } else {
7935           stableNodeList = nodeList;
7936         }
7937
7938         for (i = 0, ii = linkFns.length; i < ii;) {
7939           node = stableNodeList[linkFns[i++]];
7940           nodeLinkFn = linkFns[i++];
7941           childLinkFn = linkFns[i++];
7942
7943           if (nodeLinkFn) {
7944             if (nodeLinkFn.scope) {
7945               childScope = scope.$new();
7946               compile.$$addScopeInfo(jqLite(node), childScope);
7947             } else {
7948               childScope = scope;
7949             }
7950
7951             if (nodeLinkFn.transcludeOnThisElement) {
7952               childBoundTranscludeFn = createBoundTranscludeFn(
7953                   scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7954
7955             } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7956               childBoundTranscludeFn = parentBoundTranscludeFn;
7957
7958             } else if (!parentBoundTranscludeFn && transcludeFn) {
7959               childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7960
7961             } else {
7962               childBoundTranscludeFn = null;
7963             }
7964
7965             nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7966
7967           } else if (childLinkFn) {
7968             childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7969           }
7970         }
7971       }
7972     }
7973
7974     function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7975
7976       var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7977
7978         if (!transcludedScope) {
7979           transcludedScope = scope.$new(false, containingScope);
7980           transcludedScope.$$transcluded = true;
7981         }
7982
7983         return transcludeFn(transcludedScope, cloneFn, {
7984           parentBoundTranscludeFn: previousBoundTranscludeFn,
7985           transcludeControllers: controllers,
7986           futureParentElement: futureParentElement
7987         });
7988       };
7989
7990       return boundTranscludeFn;
7991     }
7992
7993     /**
7994      * Looks for directives on the given node and adds them to the directive collection which is
7995      * sorted.
7996      *
7997      * @param node Node to search.
7998      * @param directives An array to which the directives are added to. This array is sorted before
7999      *        the function returns.
8000      * @param attrs The shared attrs object which is used to populate the normalized attributes.
8001      * @param {number=} maxPriority Max directive priority.
8002      */
8003     function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
8004       var nodeType = node.nodeType,
8005           attrsMap = attrs.$attr,
8006           match,
8007           className;
8008
8009       switch (nodeType) {
8010         case NODE_TYPE_ELEMENT: /* Element */
8011           // use the node name: <directive>
8012           addDirective(directives,
8013               directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
8014
8015           // iterate over the attributes
8016           for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
8017                    j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
8018             var attrStartName = false;
8019             var attrEndName = false;
8020
8021             attr = nAttrs[j];
8022             name = attr.name;
8023             value = trim(attr.value);
8024
8025             // support ngAttr attribute binding
8026             ngAttrName = directiveNormalize(name);
8027             if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
8028               name = name.replace(PREFIX_REGEXP, '')
8029                 .substr(8).replace(/_(.)/g, function(match, letter) {
8030                   return letter.toUpperCase();
8031                 });
8032             }
8033
8034             var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
8035             if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
8036               attrStartName = name;
8037               attrEndName = name.substr(0, name.length - 5) + 'end';
8038               name = name.substr(0, name.length - 6);
8039             }
8040
8041             nName = directiveNormalize(name.toLowerCase());
8042             attrsMap[nName] = name;
8043             if (isNgAttr || !attrs.hasOwnProperty(nName)) {
8044                 attrs[nName] = value;
8045                 if (getBooleanAttrName(node, nName)) {
8046                   attrs[nName] = true; // presence means true
8047                 }
8048             }
8049             addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
8050             addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
8051                           attrEndName);
8052           }
8053
8054           // use class as directive
8055           className = node.className;
8056           if (isObject(className)) {
8057               // Maybe SVGAnimatedString
8058               className = className.animVal;
8059           }
8060           if (isString(className) && className !== '') {
8061             while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
8062               nName = directiveNormalize(match[2]);
8063               if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
8064                 attrs[nName] = trim(match[3]);
8065               }
8066               className = className.substr(match.index + match[0].length);
8067             }
8068           }
8069           break;
8070         case NODE_TYPE_TEXT: /* Text Node */
8071           if (msie === 11) {
8072             // Workaround for #11781
8073             while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
8074               node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
8075               node.parentNode.removeChild(node.nextSibling);
8076             }
8077           }
8078           addTextInterpolateDirective(directives, node.nodeValue);
8079           break;
8080         case NODE_TYPE_COMMENT: /* Comment */
8081           try {
8082             match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
8083             if (match) {
8084               nName = directiveNormalize(match[1]);
8085               if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
8086                 attrs[nName] = trim(match[2]);
8087               }
8088             }
8089           } catch (e) {
8090             // turns out that under some circumstances IE9 throws errors when one attempts to read
8091             // comment's node value.
8092             // Just ignore it and continue. (Can't seem to reproduce in test case.)
8093           }
8094           break;
8095       }
8096
8097       directives.sort(byPriority);
8098       return directives;
8099     }
8100
8101     /**
8102      * Given a node with an directive-start it collects all of the siblings until it finds
8103      * directive-end.
8104      * @param node
8105      * @param attrStart
8106      * @param attrEnd
8107      * @returns {*}
8108      */
8109     function groupScan(node, attrStart, attrEnd) {
8110       var nodes = [];
8111       var depth = 0;
8112       if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
8113         do {
8114           if (!node) {
8115             throw $compileMinErr('uterdir',
8116                       "Unterminated attribute, found '{0}' but no matching '{1}' found.",
8117                       attrStart, attrEnd);
8118           }
8119           if (node.nodeType == NODE_TYPE_ELEMENT) {
8120             if (node.hasAttribute(attrStart)) depth++;
8121             if (node.hasAttribute(attrEnd)) depth--;
8122           }
8123           nodes.push(node);
8124           node = node.nextSibling;
8125         } while (depth > 0);
8126       } else {
8127         nodes.push(node);
8128       }
8129
8130       return jqLite(nodes);
8131     }
8132
8133     /**
8134      * Wrapper for linking function which converts normal linking function into a grouped
8135      * linking function.
8136      * @param linkFn
8137      * @param attrStart
8138      * @param attrEnd
8139      * @returns {Function}
8140      */
8141     function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
8142       return function(scope, element, attrs, controllers, transcludeFn) {
8143         element = groupScan(element[0], attrStart, attrEnd);
8144         return linkFn(scope, element, attrs, controllers, transcludeFn);
8145       };
8146     }
8147
8148     /**
8149      * Once the directives have been collected, their compile functions are executed. This method
8150      * is responsible for inlining directive templates as well as terminating the application
8151      * of the directives if the terminal directive has been reached.
8152      *
8153      * @param {Array} directives Array of collected directives to execute their compile function.
8154      *        this needs to be pre-sorted by priority order.
8155      * @param {Node} compileNode The raw DOM node to apply the compile functions to
8156      * @param {Object} templateAttrs The shared attribute function
8157      * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8158      *                                                  scope argument is auto-generated to the new
8159      *                                                  child of the transcluded parent scope.
8160      * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8161      *                              argument has the root jqLite array so that we can replace nodes
8162      *                              on it.
8163      * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8164      *                                           compiling the transclusion.
8165      * @param {Array.<Function>} preLinkFns
8166      * @param {Array.<Function>} postLinkFns
8167      * @param {Object} previousCompileContext Context used for previous compilation of the current
8168      *                                        node
8169      * @returns {Function} linkFn
8170      */
8171     function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8172                                    jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8173                                    previousCompileContext) {
8174       previousCompileContext = previousCompileContext || {};
8175
8176       var terminalPriority = -Number.MAX_VALUE,
8177           newScopeDirective = previousCompileContext.newScopeDirective,
8178           controllerDirectives = previousCompileContext.controllerDirectives,
8179           newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8180           templateDirective = previousCompileContext.templateDirective,
8181           nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8182           hasTranscludeDirective = false,
8183           hasTemplate = false,
8184           hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8185           $compileNode = templateAttrs.$$element = jqLite(compileNode),
8186           directive,
8187           directiveName,
8188           $template,
8189           replaceDirective = originalReplaceDirective,
8190           childTranscludeFn = transcludeFn,
8191           linkFn,
8192           directiveValue;
8193
8194       // executes all directives on the current element
8195       for (var i = 0, ii = directives.length; i < ii; i++) {
8196         directive = directives[i];
8197         var attrStart = directive.$$start;
8198         var attrEnd = directive.$$end;
8199
8200         // collect multiblock sections
8201         if (attrStart) {
8202           $compileNode = groupScan(compileNode, attrStart, attrEnd);
8203         }
8204         $template = undefined;
8205
8206         if (terminalPriority > directive.priority) {
8207           break; // prevent further processing of directives
8208         }
8209
8210         if (directiveValue = directive.scope) {
8211
8212           // skip the check for directives with async templates, we'll check the derived sync
8213           // directive when the template arrives
8214           if (!directive.templateUrl) {
8215             if (isObject(directiveValue)) {
8216               // This directive is trying to add an isolated scope.
8217               // Check that there is no scope of any kind already
8218               assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8219                                 directive, $compileNode);
8220               newIsolateScopeDirective = directive;
8221             } else {
8222               // This directive is trying to add a child scope.
8223               // Check that there is no isolated scope already
8224               assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8225                                 $compileNode);
8226             }
8227           }
8228
8229           newScopeDirective = newScopeDirective || directive;
8230         }
8231
8232         directiveName = directive.name;
8233
8234         if (!directive.templateUrl && directive.controller) {
8235           directiveValue = directive.controller;
8236           controllerDirectives = controllerDirectives || createMap();
8237           assertNoDuplicate("'" + directiveName + "' controller",
8238               controllerDirectives[directiveName], directive, $compileNode);
8239           controllerDirectives[directiveName] = directive;
8240         }
8241
8242         if (directiveValue = directive.transclude) {
8243           hasTranscludeDirective = true;
8244
8245           // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8246           // This option should only be used by directives that know how to safely handle element transclusion,
8247           // where the transcluded nodes are added or replaced after linking.
8248           if (!directive.$$tlb) {
8249             assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8250             nonTlbTranscludeDirective = directive;
8251           }
8252
8253           if (directiveValue == 'element') {
8254             hasElementTranscludeDirective = true;
8255             terminalPriority = directive.priority;
8256             $template = $compileNode;
8257             $compileNode = templateAttrs.$$element =
8258                 jqLite(document.createComment(' ' + directiveName + ': ' +
8259                                               templateAttrs[directiveName] + ' '));
8260             compileNode = $compileNode[0];
8261             replaceWith(jqCollection, sliceArgs($template), compileNode);
8262
8263             childTranscludeFn = compile($template, transcludeFn, terminalPriority,
8264                                         replaceDirective && replaceDirective.name, {
8265                                           // Don't pass in:
8266                                           // - controllerDirectives - otherwise we'll create duplicates controllers
8267                                           // - newIsolateScopeDirective or templateDirective - combining templates with
8268                                           //   element transclusion doesn't make sense.
8269                                           //
8270                                           // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8271                                           // on the same element more than once.
8272                                           nonTlbTranscludeDirective: nonTlbTranscludeDirective
8273                                         });
8274           } else {
8275             $template = jqLite(jqLiteClone(compileNode)).contents();
8276             $compileNode.empty(); // clear contents
8277             childTranscludeFn = compile($template, transcludeFn, undefined,
8278                 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8279           }
8280         }
8281
8282         if (directive.template) {
8283           hasTemplate = true;
8284           assertNoDuplicate('template', templateDirective, directive, $compileNode);
8285           templateDirective = directive;
8286
8287           directiveValue = (isFunction(directive.template))
8288               ? directive.template($compileNode, templateAttrs)
8289               : directive.template;
8290
8291           directiveValue = denormalizeTemplate(directiveValue);
8292
8293           if (directive.replace) {
8294             replaceDirective = directive;
8295             if (jqLiteIsTextNode(directiveValue)) {
8296               $template = [];
8297             } else {
8298               $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8299             }
8300             compileNode = $template[0];
8301
8302             if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8303               throw $compileMinErr('tplrt',
8304                   "Template for directive '{0}' must have exactly one root element. {1}",
8305                   directiveName, '');
8306             }
8307
8308             replaceWith(jqCollection, $compileNode, compileNode);
8309
8310             var newTemplateAttrs = {$attr: {}};
8311
8312             // combine directives from the original node and from the template:
8313             // - take the array of directives for this element
8314             // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8315             // - collect directives from the template and sort them by priority
8316             // - combine directives as: processed + template + unprocessed
8317             var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8318             var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8319
8320             if (newIsolateScopeDirective || newScopeDirective) {
8321               // The original directive caused the current element to be replaced but this element
8322               // also needs to have a new scope, so we need to tell the template directives
8323               // that they would need to get their scope from further up, if they require transclusion
8324               markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8325             }
8326             directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8327             mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8328
8329             ii = directives.length;
8330           } else {
8331             $compileNode.html(directiveValue);
8332           }
8333         }
8334
8335         if (directive.templateUrl) {
8336           hasTemplate = true;
8337           assertNoDuplicate('template', templateDirective, directive, $compileNode);
8338           templateDirective = directive;
8339
8340           if (directive.replace) {
8341             replaceDirective = directive;
8342           }
8343
8344           nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8345               templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8346                 controllerDirectives: controllerDirectives,
8347                 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8348                 newIsolateScopeDirective: newIsolateScopeDirective,
8349                 templateDirective: templateDirective,
8350                 nonTlbTranscludeDirective: nonTlbTranscludeDirective
8351               });
8352           ii = directives.length;
8353         } else if (directive.compile) {
8354           try {
8355             linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8356             if (isFunction(linkFn)) {
8357               addLinkFns(null, linkFn, attrStart, attrEnd);
8358             } else if (linkFn) {
8359               addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8360             }
8361           } catch (e) {
8362             $exceptionHandler(e, startingTag($compileNode));
8363           }
8364         }
8365
8366         if (directive.terminal) {
8367           nodeLinkFn.terminal = true;
8368           terminalPriority = Math.max(terminalPriority, directive.priority);
8369         }
8370
8371       }
8372
8373       nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8374       nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8375       nodeLinkFn.templateOnThisElement = hasTemplate;
8376       nodeLinkFn.transclude = childTranscludeFn;
8377
8378       previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8379
8380       // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8381       return nodeLinkFn;
8382
8383       ////////////////////
8384
8385       function addLinkFns(pre, post, attrStart, attrEnd) {
8386         if (pre) {
8387           if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8388           pre.require = directive.require;
8389           pre.directiveName = directiveName;
8390           if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8391             pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8392           }
8393           preLinkFns.push(pre);
8394         }
8395         if (post) {
8396           if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8397           post.require = directive.require;
8398           post.directiveName = directiveName;
8399           if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8400             post = cloneAndAnnotateFn(post, {isolateScope: true});
8401           }
8402           postLinkFns.push(post);
8403         }
8404       }
8405
8406
8407       function getControllers(directiveName, require, $element, elementControllers) {
8408         var value;
8409
8410         if (isString(require)) {
8411           var match = require.match(REQUIRE_PREFIX_REGEXP);
8412           var name = require.substring(match[0].length);
8413           var inheritType = match[1] || match[3];
8414           var optional = match[2] === '?';
8415
8416           //If only parents then start at the parent element
8417           if (inheritType === '^^') {
8418             $element = $element.parent();
8419           //Otherwise attempt getting the controller from elementControllers in case
8420           //the element is transcluded (and has no data) and to avoid .data if possible
8421           } else {
8422             value = elementControllers && elementControllers[name];
8423             value = value && value.instance;
8424           }
8425
8426           if (!value) {
8427             var dataName = '$' + name + 'Controller';
8428             value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8429           }
8430
8431           if (!value && !optional) {
8432             throw $compileMinErr('ctreq',
8433                 "Controller '{0}', required by directive '{1}', can't be found!",
8434                 name, directiveName);
8435           }
8436         } else if (isArray(require)) {
8437           value = [];
8438           for (var i = 0, ii = require.length; i < ii; i++) {
8439             value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8440           }
8441         }
8442
8443         return value || null;
8444       }
8445
8446       function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8447         var elementControllers = createMap();
8448         for (var controllerKey in controllerDirectives) {
8449           var directive = controllerDirectives[controllerKey];
8450           var locals = {
8451             $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8452             $element: $element,
8453             $attrs: attrs,
8454             $transclude: transcludeFn
8455           };
8456
8457           var controller = directive.controller;
8458           if (controller == '@') {
8459             controller = attrs[directive.name];
8460           }
8461
8462           var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8463
8464           // For directives with element transclusion the element is a comment.
8465           // In this case .data will not attach any data.
8466           // Instead, we save the controllers for the element in a local hash and attach to .data
8467           // later, once we have the actual element.
8468           elementControllers[directive.name] = controllerInstance;
8469           $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8470         }
8471         return elementControllers;
8472       }
8473
8474       function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8475         var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8476             attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8477
8478         if (compileNode === linkNode) {
8479           attrs = templateAttrs;
8480           $element = templateAttrs.$$element;
8481         } else {
8482           $element = jqLite(linkNode);
8483           attrs = new Attributes($element, templateAttrs);
8484         }
8485
8486         controllerScope = scope;
8487         if (newIsolateScopeDirective) {
8488           isolateScope = scope.$new(true);
8489         } else if (newScopeDirective) {
8490           controllerScope = scope.$parent;
8491         }
8492
8493         if (boundTranscludeFn) {
8494           // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8495           // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8496           transcludeFn = controllersBoundTransclude;
8497           transcludeFn.$$boundTransclude = boundTranscludeFn;
8498         }
8499
8500         if (controllerDirectives) {
8501           elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8502         }
8503
8504         if (newIsolateScopeDirective) {
8505           // Initialize isolate scope bindings for new isolate scope directive.
8506           compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8507               templateDirective === newIsolateScopeDirective.$$originalDirective)));
8508           compile.$$addScopeClass($element, true);
8509           isolateScope.$$isolateBindings =
8510               newIsolateScopeDirective.$$isolateBindings;
8511           removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8512                                         isolateScope.$$isolateBindings,
8513                                         newIsolateScopeDirective);
8514           if (removeScopeBindingWatches) {
8515             isolateScope.$on('$destroy', removeScopeBindingWatches);
8516           }
8517         }
8518
8519         // Initialize bindToController bindings
8520         for (var name in elementControllers) {
8521           var controllerDirective = controllerDirectives[name];
8522           var controller = elementControllers[name];
8523           var bindings = controllerDirective.$$bindings.bindToController;
8524
8525           if (controller.identifier && bindings) {
8526             removeControllerBindingWatches =
8527               initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8528           }
8529
8530           var controllerResult = controller();
8531           if (controllerResult !== controller.instance) {
8532             // If the controller constructor has a return value, overwrite the instance
8533             // from setupControllers
8534             controller.instance = controllerResult;
8535             $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8536             removeControllerBindingWatches && removeControllerBindingWatches();
8537             removeControllerBindingWatches =
8538               initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8539           }
8540         }
8541
8542         // PRELINKING
8543         for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8544           linkFn = preLinkFns[i];
8545           invokeLinkFn(linkFn,
8546               linkFn.isolateScope ? isolateScope : scope,
8547               $element,
8548               attrs,
8549               linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8550               transcludeFn
8551           );
8552         }
8553
8554         // RECURSION
8555         // We only pass the isolate scope, if the isolate directive has a template,
8556         // otherwise the child elements do not belong to the isolate directive.
8557         var scopeToChild = scope;
8558         if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8559           scopeToChild = isolateScope;
8560         }
8561         childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8562
8563         // POSTLINKING
8564         for (i = postLinkFns.length - 1; i >= 0; i--) {
8565           linkFn = postLinkFns[i];
8566           invokeLinkFn(linkFn,
8567               linkFn.isolateScope ? isolateScope : scope,
8568               $element,
8569               attrs,
8570               linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8571               transcludeFn
8572           );
8573         }
8574
8575         // This is the function that is injected as `$transclude`.
8576         // Note: all arguments are optional!
8577         function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8578           var transcludeControllers;
8579
8580           // No scope passed in:
8581           if (!isScope(scope)) {
8582             futureParentElement = cloneAttachFn;
8583             cloneAttachFn = scope;
8584             scope = undefined;
8585           }
8586
8587           if (hasElementTranscludeDirective) {
8588             transcludeControllers = elementControllers;
8589           }
8590           if (!futureParentElement) {
8591             futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8592           }
8593           return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8594         }
8595       }
8596     }
8597
8598     // Depending upon the context in which a directive finds itself it might need to have a new isolated
8599     // or child scope created. For instance:
8600     // * if the directive has been pulled into a template because another directive with a higher priority
8601     // asked for element transclusion
8602     // * if the directive itself asks for transclusion but it is at the root of a template and the original
8603     // element was replaced. See https://github.com/angular/angular.js/issues/12936
8604     function markDirectiveScope(directives, isolateScope, newScope) {
8605       for (var j = 0, jj = directives.length; j < jj; j++) {
8606         directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
8607       }
8608     }
8609
8610     /**
8611      * looks up the directive and decorates it with exception handling and proper parameters. We
8612      * call this the boundDirective.
8613      *
8614      * @param {string} name name of the directive to look up.
8615      * @param {string} location The directive must be found in specific format.
8616      *   String containing any of theses characters:
8617      *
8618      *   * `E`: element name
8619      *   * `A': attribute
8620      *   * `C`: class
8621      *   * `M`: comment
8622      * @returns {boolean} true if directive was added.
8623      */
8624     function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8625                           endAttrName) {
8626       if (name === ignoreDirective) return null;
8627       var match = null;
8628       if (hasDirectives.hasOwnProperty(name)) {
8629         for (var directive, directives = $injector.get(name + Suffix),
8630             i = 0, ii = directives.length; i < ii; i++) {
8631           try {
8632             directive = directives[i];
8633             if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8634                  directive.restrict.indexOf(location) != -1) {
8635               if (startAttrName) {
8636                 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8637               }
8638               if (!directive.$$bindings) {
8639                 var bindings = directive.$$bindings =
8640                     parseDirectiveBindings(directive, directive.name);
8641                 if (isObject(bindings.isolateScope)) {
8642                   directive.$$isolateBindings = bindings.isolateScope;
8643                 }
8644               }
8645               tDirectives.push(directive);
8646               match = directive;
8647             }
8648           } catch (e) { $exceptionHandler(e); }
8649         }
8650       }
8651       return match;
8652     }
8653
8654
8655     /**
8656      * looks up the directive and returns true if it is a multi-element directive,
8657      * and therefore requires DOM nodes between -start and -end markers to be grouped
8658      * together.
8659      *
8660      * @param {string} name name of the directive to look up.
8661      * @returns true if directive was registered as multi-element.
8662      */
8663     function directiveIsMultiElement(name) {
8664       if (hasDirectives.hasOwnProperty(name)) {
8665         for (var directive, directives = $injector.get(name + Suffix),
8666             i = 0, ii = directives.length; i < ii; i++) {
8667           directive = directives[i];
8668           if (directive.multiElement) {
8669             return true;
8670           }
8671         }
8672       }
8673       return false;
8674     }
8675
8676     /**
8677      * When the element is replaced with HTML template then the new attributes
8678      * on the template need to be merged with the existing attributes in the DOM.
8679      * The desired effect is to have both of the attributes present.
8680      *
8681      * @param {object} dst destination attributes (original DOM)
8682      * @param {object} src source attributes (from the directive template)
8683      */
8684     function mergeTemplateAttributes(dst, src) {
8685       var srcAttr = src.$attr,
8686           dstAttr = dst.$attr,
8687           $element = dst.$$element;
8688
8689       // reapply the old attributes to the new element
8690       forEach(dst, function(value, key) {
8691         if (key.charAt(0) != '$') {
8692           if (src[key] && src[key] !== value) {
8693             value += (key === 'style' ? ';' : ' ') + src[key];
8694           }
8695           dst.$set(key, value, true, srcAttr[key]);
8696         }
8697       });
8698
8699       // copy the new attributes on the old attrs object
8700       forEach(src, function(value, key) {
8701         if (key == 'class') {
8702           safeAddClass($element, value);
8703           dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8704         } else if (key == 'style') {
8705           $element.attr('style', $element.attr('style') + ';' + value);
8706           dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8707           // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8708           // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8709           // have an attribute like "has-own-property" or "data-has-own-property", etc.
8710         } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8711           dst[key] = value;
8712           dstAttr[key] = srcAttr[key];
8713         }
8714       });
8715     }
8716
8717
8718     function compileTemplateUrl(directives, $compileNode, tAttrs,
8719         $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8720       var linkQueue = [],
8721           afterTemplateNodeLinkFn,
8722           afterTemplateChildLinkFn,
8723           beforeTemplateCompileNode = $compileNode[0],
8724           origAsyncDirective = directives.shift(),
8725           derivedSyncDirective = inherit(origAsyncDirective, {
8726             templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8727           }),
8728           templateUrl = (isFunction(origAsyncDirective.templateUrl))
8729               ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8730               : origAsyncDirective.templateUrl,
8731           templateNamespace = origAsyncDirective.templateNamespace;
8732
8733       $compileNode.empty();
8734
8735       $templateRequest(templateUrl)
8736         .then(function(content) {
8737           var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8738
8739           content = denormalizeTemplate(content);
8740
8741           if (origAsyncDirective.replace) {
8742             if (jqLiteIsTextNode(content)) {
8743               $template = [];
8744             } else {
8745               $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8746             }
8747             compileNode = $template[0];
8748
8749             if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8750               throw $compileMinErr('tplrt',
8751                   "Template for directive '{0}' must have exactly one root element. {1}",
8752                   origAsyncDirective.name, templateUrl);
8753             }
8754
8755             tempTemplateAttrs = {$attr: {}};
8756             replaceWith($rootElement, $compileNode, compileNode);
8757             var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8758
8759             if (isObject(origAsyncDirective.scope)) {
8760               // the original directive that caused the template to be loaded async required
8761               // an isolate scope
8762               markDirectiveScope(templateDirectives, true);
8763             }
8764             directives = templateDirectives.concat(directives);
8765             mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8766           } else {
8767             compileNode = beforeTemplateCompileNode;
8768             $compileNode.html(content);
8769           }
8770
8771           directives.unshift(derivedSyncDirective);
8772
8773           afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8774               childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8775               previousCompileContext);
8776           forEach($rootElement, function(node, i) {
8777             if (node == compileNode) {
8778               $rootElement[i] = $compileNode[0];
8779             }
8780           });
8781           afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8782
8783           while (linkQueue.length) {
8784             var scope = linkQueue.shift(),
8785                 beforeTemplateLinkNode = linkQueue.shift(),
8786                 linkRootElement = linkQueue.shift(),
8787                 boundTranscludeFn = linkQueue.shift(),
8788                 linkNode = $compileNode[0];
8789
8790             if (scope.$$destroyed) continue;
8791
8792             if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8793               var oldClasses = beforeTemplateLinkNode.className;
8794
8795               if (!(previousCompileContext.hasElementTranscludeDirective &&
8796                   origAsyncDirective.replace)) {
8797                 // it was cloned therefore we have to clone as well.
8798                 linkNode = jqLiteClone(compileNode);
8799               }
8800               replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8801
8802               // Copy in CSS classes from original node
8803               safeAddClass(jqLite(linkNode), oldClasses);
8804             }
8805             if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8806               childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8807             } else {
8808               childBoundTranscludeFn = boundTranscludeFn;
8809             }
8810             afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8811               childBoundTranscludeFn);
8812           }
8813           linkQueue = null;
8814         });
8815
8816       return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8817         var childBoundTranscludeFn = boundTranscludeFn;
8818         if (scope.$$destroyed) return;
8819         if (linkQueue) {
8820           linkQueue.push(scope,
8821                          node,
8822                          rootElement,
8823                          childBoundTranscludeFn);
8824         } else {
8825           if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8826             childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8827           }
8828           afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8829         }
8830       };
8831     }
8832
8833
8834     /**
8835      * Sorting function for bound directives.
8836      */
8837     function byPriority(a, b) {
8838       var diff = b.priority - a.priority;
8839       if (diff !== 0) return diff;
8840       if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8841       return a.index - b.index;
8842     }
8843
8844     function assertNoDuplicate(what, previousDirective, directive, element) {
8845
8846       function wrapModuleNameIfDefined(moduleName) {
8847         return moduleName ?
8848           (' (module: ' + moduleName + ')') :
8849           '';
8850       }
8851
8852       if (previousDirective) {
8853         throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8854             previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8855             directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8856       }
8857     }
8858
8859
8860     function addTextInterpolateDirective(directives, text) {
8861       var interpolateFn = $interpolate(text, true);
8862       if (interpolateFn) {
8863         directives.push({
8864           priority: 0,
8865           compile: function textInterpolateCompileFn(templateNode) {
8866             var templateNodeParent = templateNode.parent(),
8867                 hasCompileParent = !!templateNodeParent.length;
8868
8869             // When transcluding a template that has bindings in the root
8870             // we don't have a parent and thus need to add the class during linking fn.
8871             if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8872
8873             return function textInterpolateLinkFn(scope, node) {
8874               var parent = node.parent();
8875               if (!hasCompileParent) compile.$$addBindingClass(parent);
8876               compile.$$addBindingInfo(parent, interpolateFn.expressions);
8877               scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8878                 node[0].nodeValue = value;
8879               });
8880             };
8881           }
8882         });
8883       }
8884     }
8885
8886
8887     function wrapTemplate(type, template) {
8888       type = lowercase(type || 'html');
8889       switch (type) {
8890       case 'svg':
8891       case 'math':
8892         var wrapper = document.createElement('div');
8893         wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8894         return wrapper.childNodes[0].childNodes;
8895       default:
8896         return template;
8897       }
8898     }
8899
8900
8901     function getTrustedContext(node, attrNormalizedName) {
8902       if (attrNormalizedName == "srcdoc") {
8903         return $sce.HTML;
8904       }
8905       var tag = nodeName_(node);
8906       // maction[xlink:href] can source SVG.  It's not limited to <maction>.
8907       if (attrNormalizedName == "xlinkHref" ||
8908           (tag == "form" && attrNormalizedName == "action") ||
8909           (tag != "img" && (attrNormalizedName == "src" ||
8910                             attrNormalizedName == "ngSrc"))) {
8911         return $sce.RESOURCE_URL;
8912       }
8913     }
8914
8915
8916     function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8917       var trustedContext = getTrustedContext(node, name);
8918       allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8919
8920       var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8921
8922       // no interpolation found -> ignore
8923       if (!interpolateFn) return;
8924
8925
8926       if (name === "multiple" && nodeName_(node) === "select") {
8927         throw $compileMinErr("selmulti",
8928             "Binding to the 'multiple' attribute is not supported. Element: {0}",
8929             startingTag(node));
8930       }
8931
8932       directives.push({
8933         priority: 100,
8934         compile: function() {
8935             return {
8936               pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8937                 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
8938
8939                 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8940                   throw $compileMinErr('nodomevents',
8941                       "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
8942                           "ng- versions (such as ng-click instead of onclick) instead.");
8943                 }
8944
8945                 // If the attribute has changed since last $interpolate()ed
8946                 var newValue = attr[name];
8947                 if (newValue !== value) {
8948                   // we need to interpolate again since the attribute value has been updated
8949                   // (e.g. by another directive's compile function)
8950                   // ensure unset/empty values make interpolateFn falsy
8951                   interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8952                   value = newValue;
8953                 }
8954
8955                 // if attribute was updated so that there is no interpolation going on we don't want to
8956                 // register any observers
8957                 if (!interpolateFn) return;
8958
8959                 // initialize attr object so that it's ready in case we need the value for isolate
8960                 // scope initialization, otherwise the value would not be available from isolate
8961                 // directive's linking fn during linking phase
8962                 attr[name] = interpolateFn(scope);
8963
8964                 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8965                 (attr.$$observers && attr.$$observers[name].$$scope || scope).
8966                   $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8967                     //special case for class attribute addition + removal
8968                     //so that class changes can tap into the animation
8969                     //hooks provided by the $animate service. Be sure to
8970                     //skip animations when the first digest occurs (when
8971                     //both the new and the old values are the same) since
8972                     //the CSS classes are the non-interpolated values
8973                     if (name === 'class' && newValue != oldValue) {
8974                       attr.$updateClass(newValue, oldValue);
8975                     } else {
8976                       attr.$set(name, newValue);
8977                     }
8978                   });
8979               }
8980             };
8981           }
8982       });
8983     }
8984
8985
8986     /**
8987      * This is a special jqLite.replaceWith, which can replace items which
8988      * have no parents, provided that the containing jqLite collection is provided.
8989      *
8990      * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8991      *                               in the root of the tree.
8992      * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8993      *                                  the shell, but replace its DOM node reference.
8994      * @param {Node} newNode The new DOM node.
8995      */
8996     function replaceWith($rootElement, elementsToRemove, newNode) {
8997       var firstElementToRemove = elementsToRemove[0],
8998           removeCount = elementsToRemove.length,
8999           parent = firstElementToRemove.parentNode,
9000           i, ii;
9001
9002       if ($rootElement) {
9003         for (i = 0, ii = $rootElement.length; i < ii; i++) {
9004           if ($rootElement[i] == firstElementToRemove) {
9005             $rootElement[i++] = newNode;
9006             for (var j = i, j2 = j + removeCount - 1,
9007                      jj = $rootElement.length;
9008                  j < jj; j++, j2++) {
9009               if (j2 < jj) {
9010                 $rootElement[j] = $rootElement[j2];
9011               } else {
9012                 delete $rootElement[j];
9013               }
9014             }
9015             $rootElement.length -= removeCount - 1;
9016
9017             // If the replaced element is also the jQuery .context then replace it
9018             // .context is a deprecated jQuery api, so we should set it only when jQuery set it
9019             // http://api.jquery.com/context/
9020             if ($rootElement.context === firstElementToRemove) {
9021               $rootElement.context = newNode;
9022             }
9023             break;
9024           }
9025         }
9026       }
9027
9028       if (parent) {
9029         parent.replaceChild(newNode, firstElementToRemove);
9030       }
9031
9032       // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
9033       var fragment = document.createDocumentFragment();
9034       fragment.appendChild(firstElementToRemove);
9035
9036       if (jqLite.hasData(firstElementToRemove)) {
9037         // Copy over user data (that includes Angular's $scope etc.). Don't copy private
9038         // data here because there's no public interface in jQuery to do that and copying over
9039         // event listeners (which is the main use of private data) wouldn't work anyway.
9040         jqLite.data(newNode, jqLite.data(firstElementToRemove));
9041
9042         // Remove data of the replaced element. We cannot just call .remove()
9043         // on the element it since that would deallocate scope that is needed
9044         // for the new node. Instead, remove the data "manually".
9045         if (!jQuery) {
9046           delete jqLite.cache[firstElementToRemove[jqLite.expando]];
9047         } else {
9048           // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
9049           // the replaced element. The cleanData version monkey-patched by Angular would cause
9050           // the scope to be trashed and we do need the very same scope to work with the new
9051           // element. However, we cannot just cache the non-patched version and use it here as
9052           // that would break if another library patches the method after Angular does (one
9053           // example is jQuery UI). Instead, set a flag indicating scope destroying should be
9054           // skipped this one time.
9055           skipDestroyOnNextJQueryCleanData = true;
9056           jQuery.cleanData([firstElementToRemove]);
9057         }
9058       }
9059
9060       for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
9061         var element = elementsToRemove[k];
9062         jqLite(element).remove(); // must do this way to clean up expando
9063         fragment.appendChild(element);
9064         delete elementsToRemove[k];
9065       }
9066
9067       elementsToRemove[0] = newNode;
9068       elementsToRemove.length = 1;
9069     }
9070
9071
9072     function cloneAndAnnotateFn(fn, annotation) {
9073       return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
9074     }
9075
9076
9077     function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
9078       try {
9079         linkFn(scope, $element, attrs, controllers, transcludeFn);
9080       } catch (e) {
9081         $exceptionHandler(e, startingTag($element));
9082       }
9083     }
9084
9085
9086     // Set up $watches for isolate scope and controller bindings. This process
9087     // only occurs for isolate scopes and new scopes with controllerAs.
9088     function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
9089       var removeWatchCollection = [];
9090       forEach(bindings, function(definition, scopeName) {
9091         var attrName = definition.attrName,
9092         optional = definition.optional,
9093         mode = definition.mode, // @, =, or &
9094         lastValue,
9095         parentGet, parentSet, compare;
9096
9097         switch (mode) {
9098
9099           case '@':
9100             if (!optional && !hasOwnProperty.call(attrs, attrName)) {
9101               destination[scopeName] = attrs[attrName] = void 0;
9102             }
9103             attrs.$observe(attrName, function(value) {
9104               if (isString(value)) {
9105                 destination[scopeName] = value;
9106               }
9107             });
9108             attrs.$$observers[attrName].$$scope = scope;
9109             lastValue = attrs[attrName];
9110             if (isString(lastValue)) {
9111               // If the attribute has been provided then we trigger an interpolation to ensure
9112               // the value is there for use in the link fn
9113               destination[scopeName] = $interpolate(lastValue)(scope);
9114             } else if (isBoolean(lastValue)) {
9115               // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
9116               // the value to boolean rather than a string, so we special case this situation
9117               destination[scopeName] = lastValue;
9118             }
9119             break;
9120
9121           case '=':
9122             if (!hasOwnProperty.call(attrs, attrName)) {
9123               if (optional) break;
9124               attrs[attrName] = void 0;
9125             }
9126             if (optional && !attrs[attrName]) break;
9127
9128             parentGet = $parse(attrs[attrName]);
9129             if (parentGet.literal) {
9130               compare = equals;
9131             } else {
9132               compare = function(a, b) { return a === b || (a !== a && b !== b); };
9133             }
9134             parentSet = parentGet.assign || function() {
9135               // reset the change, or we will throw this exception on every $digest
9136               lastValue = destination[scopeName] = parentGet(scope);
9137               throw $compileMinErr('nonassign',
9138                   "Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!",
9139                   attrs[attrName], attrName, directive.name);
9140             };
9141             lastValue = destination[scopeName] = parentGet(scope);
9142             var parentValueWatch = function parentValueWatch(parentValue) {
9143               if (!compare(parentValue, destination[scopeName])) {
9144                 // we are out of sync and need to copy
9145                 if (!compare(parentValue, lastValue)) {
9146                   // parent changed and it has precedence
9147                   destination[scopeName] = parentValue;
9148                 } else {
9149                   // if the parent can be assigned then do so
9150                   parentSet(scope, parentValue = destination[scopeName]);
9151                 }
9152               }
9153               return lastValue = parentValue;
9154             };
9155             parentValueWatch.$stateful = true;
9156             var removeWatch;
9157             if (definition.collection) {
9158               removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
9159             } else {
9160               removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
9161             }
9162             removeWatchCollection.push(removeWatch);
9163             break;
9164
9165           case '&':
9166             // Don't assign Object.prototype method to scope
9167             parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
9168
9169             // Don't assign noop to destination if expression is not valid
9170             if (parentGet === noop && optional) break;
9171
9172             destination[scopeName] = function(locals) {
9173               return parentGet(scope, locals);
9174             };
9175             break;
9176         }
9177       });
9178
9179       return removeWatchCollection.length && function removeWatches() {
9180         for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
9181           removeWatchCollection[i]();
9182         }
9183       };
9184     }
9185   }];
9186 }
9187
9188 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9189 /**
9190  * Converts all accepted directives format into proper directive name.
9191  * @param name Name to normalize
9192  */
9193 function directiveNormalize(name) {
9194   return camelCase(name.replace(PREFIX_REGEXP, ''));
9195 }
9196
9197 /**
9198  * @ngdoc type
9199  * @name $compile.directive.Attributes
9200  *
9201  * @description
9202  * A shared object between directive compile / linking functions which contains normalized DOM
9203  * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9204  * needed since all of these are treated as equivalent in Angular:
9205  *
9206  * ```
9207  *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9208  * ```
9209  */
9210
9211 /**
9212  * @ngdoc property
9213  * @name $compile.directive.Attributes#$attr
9214  *
9215  * @description
9216  * A map of DOM element attribute names to the normalized name. This is
9217  * needed to do reverse lookup from normalized name back to actual name.
9218  */
9219
9220
9221 /**
9222  * @ngdoc method
9223  * @name $compile.directive.Attributes#$set
9224  * @kind function
9225  *
9226  * @description
9227  * Set DOM element attribute value.
9228  *
9229  *
9230  * @param {string} name Normalized element attribute name of the property to modify. The name is
9231  *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9232  *          property to the original name.
9233  * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9234  */
9235
9236
9237
9238 /**
9239  * Closure compiler type information
9240  */
9241
9242 function nodesetLinkingFn(
9243   /* angular.Scope */ scope,
9244   /* NodeList */ nodeList,
9245   /* Element */ rootElement,
9246   /* function(Function) */ boundTranscludeFn
9247 ) {}
9248
9249 function directiveLinkingFn(
9250   /* nodesetLinkingFn */ nodesetLinkingFn,
9251   /* angular.Scope */ scope,
9252   /* Node */ node,
9253   /* Element */ rootElement,
9254   /* function(Function) */ boundTranscludeFn
9255 ) {}
9256
9257 function tokenDifference(str1, str2) {
9258   var values = '',
9259       tokens1 = str1.split(/\s+/),
9260       tokens2 = str2.split(/\s+/);
9261
9262   outer:
9263   for (var i = 0; i < tokens1.length; i++) {
9264     var token = tokens1[i];
9265     for (var j = 0; j < tokens2.length; j++) {
9266       if (token == tokens2[j]) continue outer;
9267     }
9268     values += (values.length > 0 ? ' ' : '') + token;
9269   }
9270   return values;
9271 }
9272
9273 function removeComments(jqNodes) {
9274   jqNodes = jqLite(jqNodes);
9275   var i = jqNodes.length;
9276
9277   if (i <= 1) {
9278     return jqNodes;
9279   }
9280
9281   while (i--) {
9282     var node = jqNodes[i];
9283     if (node.nodeType === NODE_TYPE_COMMENT) {
9284       splice.call(jqNodes, i, 1);
9285     }
9286   }
9287   return jqNodes;
9288 }
9289
9290 var $controllerMinErr = minErr('$controller');
9291
9292
9293 var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
9294 function identifierForController(controller, ident) {
9295   if (ident && isString(ident)) return ident;
9296   if (isString(controller)) {
9297     var match = CNTRL_REG.exec(controller);
9298     if (match) return match[3];
9299   }
9300 }
9301
9302
9303 /**
9304  * @ngdoc provider
9305  * @name $controllerProvider
9306  * @description
9307  * The {@link ng.$controller $controller service} is used by Angular to create new
9308  * controllers.
9309  *
9310  * This provider allows controller registration via the
9311  * {@link ng.$controllerProvider#register register} method.
9312  */
9313 function $ControllerProvider() {
9314   var controllers = {},
9315       globals = false;
9316
9317   /**
9318    * @ngdoc method
9319    * @name $controllerProvider#register
9320    * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9321    *    the names and the values are the constructors.
9322    * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9323    *    annotations in the array notation).
9324    */
9325   this.register = function(name, constructor) {
9326     assertNotHasOwnProperty(name, 'controller');
9327     if (isObject(name)) {
9328       extend(controllers, name);
9329     } else {
9330       controllers[name] = constructor;
9331     }
9332   };
9333
9334   /**
9335    * @ngdoc method
9336    * @name $controllerProvider#allowGlobals
9337    * @description If called, allows `$controller` to find controller constructors on `window`
9338    */
9339   this.allowGlobals = function() {
9340     globals = true;
9341   };
9342
9343
9344   this.$get = ['$injector', '$window', function($injector, $window) {
9345
9346     /**
9347      * @ngdoc service
9348      * @name $controller
9349      * @requires $injector
9350      *
9351      * @param {Function|string} constructor If called with a function then it's considered to be the
9352      *    controller constructor function. Otherwise it's considered to be a string which is used
9353      *    to retrieve the controller constructor using the following steps:
9354      *
9355      *    * check if a controller with given name is registered via `$controllerProvider`
9356      *    * check if evaluating the string on the current scope returns a constructor
9357      *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9358      *      `window` object (not recommended)
9359      *
9360      *    The string can use the `controller as property` syntax, where the controller instance is published
9361      *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9362      *    to work correctly.
9363      *
9364      * @param {Object} locals Injection locals for Controller.
9365      * @return {Object} Instance of given controller.
9366      *
9367      * @description
9368      * `$controller` service is responsible for instantiating controllers.
9369      *
9370      * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9371      * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9372      */
9373     return function(expression, locals, later, ident) {
9374       // PRIVATE API:
9375       //   param `later` --- indicates that the controller's constructor is invoked at a later time.
9376       //                     If true, $controller will allocate the object with the correct
9377       //                     prototype chain, but will not invoke the controller until a returned
9378       //                     callback is invoked.
9379       //   param `ident` --- An optional label which overrides the label parsed from the controller
9380       //                     expression, if any.
9381       var instance, match, constructor, identifier;
9382       later = later === true;
9383       if (ident && isString(ident)) {
9384         identifier = ident;
9385       }
9386
9387       if (isString(expression)) {
9388         match = expression.match(CNTRL_REG);
9389         if (!match) {
9390           throw $controllerMinErr('ctrlfmt',
9391             "Badly formed controller string '{0}'. " +
9392             "Must match `__name__ as __id__` or `__name__`.", expression);
9393         }
9394         constructor = match[1],
9395         identifier = identifier || match[3];
9396         expression = controllers.hasOwnProperty(constructor)
9397             ? controllers[constructor]
9398             : getter(locals.$scope, constructor, true) ||
9399                 (globals ? getter($window, constructor, true) : undefined);
9400
9401         assertArgFn(expression, constructor, true);
9402       }
9403
9404       if (later) {
9405         // Instantiate controller later:
9406         // This machinery is used to create an instance of the object before calling the
9407         // controller's constructor itself.
9408         //
9409         // This allows properties to be added to the controller before the constructor is
9410         // invoked. Primarily, this is used for isolate scope bindings in $compile.
9411         //
9412         // This feature is not intended for use by applications, and is thus not documented
9413         // publicly.
9414         // Object creation: http://jsperf.com/create-constructor/2
9415         var controllerPrototype = (isArray(expression) ?
9416           expression[expression.length - 1] : expression).prototype;
9417         instance = Object.create(controllerPrototype || null);
9418
9419         if (identifier) {
9420           addIdentifier(locals, identifier, instance, constructor || expression.name);
9421         }
9422
9423         var instantiate;
9424         return instantiate = extend(function() {
9425           var result = $injector.invoke(expression, instance, locals, constructor);
9426           if (result !== instance && (isObject(result) || isFunction(result))) {
9427             instance = result;
9428             if (identifier) {
9429               // If result changed, re-assign controllerAs value to scope.
9430               addIdentifier(locals, identifier, instance, constructor || expression.name);
9431             }
9432           }
9433           return instance;
9434         }, {
9435           instance: instance,
9436           identifier: identifier
9437         });
9438       }
9439
9440       instance = $injector.instantiate(expression, locals, constructor);
9441
9442       if (identifier) {
9443         addIdentifier(locals, identifier, instance, constructor || expression.name);
9444       }
9445
9446       return instance;
9447     };
9448
9449     function addIdentifier(locals, identifier, instance, name) {
9450       if (!(locals && isObject(locals.$scope))) {
9451         throw minErr('$controller')('noscp',
9452           "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9453           name, identifier);
9454       }
9455
9456       locals.$scope[identifier] = instance;
9457     }
9458   }];
9459 }
9460
9461 /**
9462  * @ngdoc service
9463  * @name $document
9464  * @requires $window
9465  *
9466  * @description
9467  * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9468  *
9469  * @example
9470    <example module="documentExample">
9471      <file name="index.html">
9472        <div ng-controller="ExampleController">
9473          <p>$document title: <b ng-bind="title"></b></p>
9474          <p>window.document title: <b ng-bind="windowTitle"></b></p>
9475        </div>
9476      </file>
9477      <file name="script.js">
9478        angular.module('documentExample', [])
9479          .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9480            $scope.title = $document[0].title;
9481            $scope.windowTitle = angular.element(window.document)[0].title;
9482          }]);
9483      </file>
9484    </example>
9485  */
9486 function $DocumentProvider() {
9487   this.$get = ['$window', function(window) {
9488     return jqLite(window.document);
9489   }];
9490 }
9491
9492 /**
9493  * @ngdoc service
9494  * @name $exceptionHandler
9495  * @requires ng.$log
9496  *
9497  * @description
9498  * Any uncaught exception in angular expressions is delegated to this service.
9499  * The default implementation simply delegates to `$log.error` which logs it into
9500  * the browser console.
9501  *
9502  * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9503  * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9504  *
9505  * ## Example:
9506  *
9507  * ```js
9508  *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9509  *     return function(exception, cause) {
9510  *       exception.message += ' (caused by "' + cause + '")';
9511  *       throw exception;
9512  *     };
9513  *   });
9514  * ```
9515  *
9516  * This example will override the normal action of `$exceptionHandler`, to make angular
9517  * exceptions fail hard when they happen, instead of just logging to the console.
9518  *
9519  * <hr />
9520  * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9521  * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9522  * (unless executed during a digest).
9523  *
9524  * If you wish, you can manually delegate exceptions, e.g.
9525  * `try { ... } catch(e) { $exceptionHandler(e); }`
9526  *
9527  * @param {Error} exception Exception associated with the error.
9528  * @param {string=} cause optional information about the context in which
9529  *       the error was thrown.
9530  *
9531  */
9532 function $ExceptionHandlerProvider() {
9533   this.$get = ['$log', function($log) {
9534     return function(exception, cause) {
9535       $log.error.apply($log, arguments);
9536     };
9537   }];
9538 }
9539
9540 var $$ForceReflowProvider = function() {
9541   this.$get = ['$document', function($document) {
9542     return function(domNode) {
9543       //the line below will force the browser to perform a repaint so
9544       //that all the animated elements within the animation frame will
9545       //be properly updated and drawn on screen. This is required to
9546       //ensure that the preparation animation is properly flushed so that
9547       //the active state picks up from there. DO NOT REMOVE THIS LINE.
9548       //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9549       //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9550       //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9551       if (domNode) {
9552         if (!domNode.nodeType && domNode instanceof jqLite) {
9553           domNode = domNode[0];
9554         }
9555       } else {
9556         domNode = $document[0].body;
9557       }
9558       return domNode.offsetWidth + 1;
9559     };
9560   }];
9561 };
9562
9563 var APPLICATION_JSON = 'application/json';
9564 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9565 var JSON_START = /^\[|^\{(?!\{)/;
9566 var JSON_ENDS = {
9567   '[': /]$/,
9568   '{': /}$/
9569 };
9570 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9571 var $httpMinErr = minErr('$http');
9572 var $httpMinErrLegacyFn = function(method) {
9573   return function() {
9574     throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9575   };
9576 };
9577
9578 function serializeValue(v) {
9579   if (isObject(v)) {
9580     return isDate(v) ? v.toISOString() : toJson(v);
9581   }
9582   return v;
9583 }
9584
9585
9586 function $HttpParamSerializerProvider() {
9587   /**
9588    * @ngdoc service
9589    * @name $httpParamSerializer
9590    * @description
9591    *
9592    * Default {@link $http `$http`} params serializer that converts objects to strings
9593    * according to the following rules:
9594    *
9595    * * `{'foo': 'bar'}` results in `foo=bar`
9596    * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9597    * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9598    * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9599    *
9600    * Note that serializer will sort the request parameters alphabetically.
9601    * */
9602
9603   this.$get = function() {
9604     return function ngParamSerializer(params) {
9605       if (!params) return '';
9606       var parts = [];
9607       forEachSorted(params, function(value, key) {
9608         if (value === null || isUndefined(value)) return;
9609         if (isArray(value)) {
9610           forEach(value, function(v, k) {
9611             parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
9612           });
9613         } else {
9614           parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9615         }
9616       });
9617
9618       return parts.join('&');
9619     };
9620   };
9621 }
9622
9623 function $HttpParamSerializerJQLikeProvider() {
9624   /**
9625    * @ngdoc service
9626    * @name $httpParamSerializerJQLike
9627    * @description
9628    *
9629    * Alternative {@link $http `$http`} params serializer that follows
9630    * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9631    * The serializer will also sort the params alphabetically.
9632    *
9633    * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9634    *
9635    * ```js
9636    * $http({
9637    *   url: myUrl,
9638    *   method: 'GET',
9639    *   params: myParams,
9640    *   paramSerializer: '$httpParamSerializerJQLike'
9641    * });
9642    * ```
9643    *
9644    * It is also possible to set it as the default `paramSerializer` in the
9645    * {@link $httpProvider#defaults `$httpProvider`}.
9646    *
9647    * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9648    * form data for submission:
9649    *
9650    * ```js
9651    * .controller(function($http, $httpParamSerializerJQLike) {
9652    *   //...
9653    *
9654    *   $http({
9655    *     url: myUrl,
9656    *     method: 'POST',
9657    *     data: $httpParamSerializerJQLike(myData),
9658    *     headers: {
9659    *       'Content-Type': 'application/x-www-form-urlencoded'
9660    *     }
9661    *   });
9662    *
9663    * });
9664    * ```
9665    *
9666    * */
9667   this.$get = function() {
9668     return function jQueryLikeParamSerializer(params) {
9669       if (!params) return '';
9670       var parts = [];
9671       serialize(params, '', true);
9672       return parts.join('&');
9673
9674       function serialize(toSerialize, prefix, topLevel) {
9675         if (toSerialize === null || isUndefined(toSerialize)) return;
9676         if (isArray(toSerialize)) {
9677           forEach(toSerialize, function(value, index) {
9678             serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
9679           });
9680         } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9681           forEachSorted(toSerialize, function(value, key) {
9682             serialize(value, prefix +
9683                 (topLevel ? '' : '[') +
9684                 key +
9685                 (topLevel ? '' : ']'));
9686           });
9687         } else {
9688           parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9689         }
9690       }
9691     };
9692   };
9693 }
9694
9695 function defaultHttpResponseTransform(data, headers) {
9696   if (isString(data)) {
9697     // Strip json vulnerability protection prefix and trim whitespace
9698     var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9699
9700     if (tempData) {
9701       var contentType = headers('Content-Type');
9702       if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9703         data = fromJson(tempData);
9704       }
9705     }
9706   }
9707
9708   return data;
9709 }
9710
9711 function isJsonLike(str) {
9712     var jsonStart = str.match(JSON_START);
9713     return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9714 }
9715
9716 /**
9717  * Parse headers into key value object
9718  *
9719  * @param {string} headers Raw headers as a string
9720  * @returns {Object} Parsed headers as key value object
9721  */
9722 function parseHeaders(headers) {
9723   var parsed = createMap(), i;
9724
9725   function fillInParsed(key, val) {
9726     if (key) {
9727       parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9728     }
9729   }
9730
9731   if (isString(headers)) {
9732     forEach(headers.split('\n'), function(line) {
9733       i = line.indexOf(':');
9734       fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9735     });
9736   } else if (isObject(headers)) {
9737     forEach(headers, function(headerVal, headerKey) {
9738       fillInParsed(lowercase(headerKey), trim(headerVal));
9739     });
9740   }
9741
9742   return parsed;
9743 }
9744
9745
9746 /**
9747  * Returns a function that provides access to parsed headers.
9748  *
9749  * Headers are lazy parsed when first requested.
9750  * @see parseHeaders
9751  *
9752  * @param {(string|Object)} headers Headers to provide access to.
9753  * @returns {function(string=)} Returns a getter function which if called with:
9754  *
9755  *   - if called with single an argument returns a single header value or null
9756  *   - if called with no arguments returns an object containing all headers.
9757  */
9758 function headersGetter(headers) {
9759   var headersObj;
9760
9761   return function(name) {
9762     if (!headersObj) headersObj =  parseHeaders(headers);
9763
9764     if (name) {
9765       var value = headersObj[lowercase(name)];
9766       if (value === void 0) {
9767         value = null;
9768       }
9769       return value;
9770     }
9771
9772     return headersObj;
9773   };
9774 }
9775
9776
9777 /**
9778  * Chain all given functions
9779  *
9780  * This function is used for both request and response transforming
9781  *
9782  * @param {*} data Data to transform.
9783  * @param {function(string=)} headers HTTP headers getter fn.
9784  * @param {number} status HTTP status code of the response.
9785  * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9786  * @returns {*} Transformed data.
9787  */
9788 function transformData(data, headers, status, fns) {
9789   if (isFunction(fns)) {
9790     return fns(data, headers, status);
9791   }
9792
9793   forEach(fns, function(fn) {
9794     data = fn(data, headers, status);
9795   });
9796
9797   return data;
9798 }
9799
9800
9801 function isSuccess(status) {
9802   return 200 <= status && status < 300;
9803 }
9804
9805
9806 /**
9807  * @ngdoc provider
9808  * @name $httpProvider
9809  * @description
9810  * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9811  * */
9812 function $HttpProvider() {
9813   /**
9814    * @ngdoc property
9815    * @name $httpProvider#defaults
9816    * @description
9817    *
9818    * Object containing default values for all {@link ng.$http $http} requests.
9819    *
9820    * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
9821    * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
9822    * by default. See {@link $http#caching $http Caching} for more information.
9823    *
9824    * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9825    * Defaults value is `'XSRF-TOKEN'`.
9826    *
9827    * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9828    * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9829    *
9830    * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9831    * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9832    * setting default headers.
9833    *     - **`defaults.headers.common`**
9834    *     - **`defaults.headers.post`**
9835    *     - **`defaults.headers.put`**
9836    *     - **`defaults.headers.patch`**
9837    *
9838    *
9839    * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9840    *  used to the prepare string representation of request parameters (specified as an object).
9841    *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9842    *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9843    *
9844    **/
9845   var defaults = this.defaults = {
9846     // transform incoming response data
9847     transformResponse: [defaultHttpResponseTransform],
9848
9849     // transform outgoing request data
9850     transformRequest: [function(d) {
9851       return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9852     }],
9853
9854     // default headers
9855     headers: {
9856       common: {
9857         'Accept': 'application/json, text/plain, */*'
9858       },
9859       post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9860       put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9861       patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9862     },
9863
9864     xsrfCookieName: 'XSRF-TOKEN',
9865     xsrfHeaderName: 'X-XSRF-TOKEN',
9866
9867     paramSerializer: '$httpParamSerializer'
9868   };
9869
9870   var useApplyAsync = false;
9871   /**
9872    * @ngdoc method
9873    * @name $httpProvider#useApplyAsync
9874    * @description
9875    *
9876    * Configure $http service to combine processing of multiple http responses received at around
9877    * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9878    * significant performance improvement for bigger applications that make many HTTP requests
9879    * concurrently (common during application bootstrap).
9880    *
9881    * Defaults to false. If no value is specified, returns the current configured value.
9882    *
9883    * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9884    *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9885    *    to load and share the same digest cycle.
9886    *
9887    * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9888    *    otherwise, returns the current configured value.
9889    **/
9890   this.useApplyAsync = function(value) {
9891     if (isDefined(value)) {
9892       useApplyAsync = !!value;
9893       return this;
9894     }
9895     return useApplyAsync;
9896   };
9897
9898   var useLegacyPromise = true;
9899   /**
9900    * @ngdoc method
9901    * @name $httpProvider#useLegacyPromiseExtensions
9902    * @description
9903    *
9904    * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9905    * This should be used to make sure that applications work without these methods.
9906    *
9907    * Defaults to true. If no value is specified, returns the current configured value.
9908    *
9909    * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
9910    *
9911    * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9912    *    otherwise, returns the current configured value.
9913    **/
9914   this.useLegacyPromiseExtensions = function(value) {
9915     if (isDefined(value)) {
9916       useLegacyPromise = !!value;
9917       return this;
9918     }
9919     return useLegacyPromise;
9920   };
9921
9922   /**
9923    * @ngdoc property
9924    * @name $httpProvider#interceptors
9925    * @description
9926    *
9927    * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9928    * pre-processing of request or postprocessing of responses.
9929    *
9930    * These service factories are ordered by request, i.e. they are applied in the same order as the
9931    * array, on request, but reverse order, on response.
9932    *
9933    * {@link ng.$http#interceptors Interceptors detailed info}
9934    **/
9935   var interceptorFactories = this.interceptors = [];
9936
9937   this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9938       function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9939
9940     var defaultCache = $cacheFactory('$http');
9941
9942     /**
9943      * Make sure that default param serializer is exposed as a function
9944      */
9945     defaults.paramSerializer = isString(defaults.paramSerializer) ?
9946       $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9947
9948     /**
9949      * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9950      * The reversal is needed so that we can build up the interception chain around the
9951      * server request.
9952      */
9953     var reversedInterceptors = [];
9954
9955     forEach(interceptorFactories, function(interceptorFactory) {
9956       reversedInterceptors.unshift(isString(interceptorFactory)
9957           ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9958     });
9959
9960     /**
9961      * @ngdoc service
9962      * @kind function
9963      * @name $http
9964      * @requires ng.$httpBackend
9965      * @requires $cacheFactory
9966      * @requires $rootScope
9967      * @requires $q
9968      * @requires $injector
9969      *
9970      * @description
9971      * The `$http` service is a core Angular service that facilitates communication with the remote
9972      * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9973      * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9974      *
9975      * For unit testing applications that use `$http` service, see
9976      * {@link ngMock.$httpBackend $httpBackend mock}.
9977      *
9978      * For a higher level of abstraction, please check out the {@link ngResource.$resource
9979      * $resource} service.
9980      *
9981      * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
9982      * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9983      * it is important to familiarize yourself with these APIs and the guarantees they provide.
9984      *
9985      *
9986      * ## General usage
9987      * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
9988      * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
9989      *
9990      * ```js
9991      *   // Simple GET request example:
9992      *   $http({
9993      *     method: 'GET',
9994      *     url: '/someUrl'
9995      *   }).then(function successCallback(response) {
9996      *       // this callback will be called asynchronously
9997      *       // when the response is available
9998      *     }, function errorCallback(response) {
9999      *       // called asynchronously if an error occurs
10000      *       // or server returns response with an error status.
10001      *     });
10002      * ```
10003      *
10004      * The response object has these properties:
10005      *
10006      *   - **data** – `{string|Object}` – The response body transformed with the transform
10007      *     functions.
10008      *   - **status** – `{number}` – HTTP status code of the response.
10009      *   - **headers** – `{function([headerName])}` – Header getter function.
10010      *   - **config** – `{Object}` – The configuration object that was used to generate the request.
10011      *   - **statusText** – `{string}` – HTTP status text of the response.
10012      *
10013      * A response status code between 200 and 299 is considered a success status and
10014      * will result in the success callback being called. Note that if the response is a redirect,
10015      * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
10016      * called for such responses.
10017      *
10018      *
10019      * ## Shortcut methods
10020      *
10021      * Shortcut methods are also available. All shortcut methods require passing in the URL, and
10022      * request data must be passed in for POST/PUT requests. An optional config can be passed as the
10023      * last argument.
10024      *
10025      * ```js
10026      *   $http.get('/someUrl', config).then(successCallback, errorCallback);
10027      *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
10028      * ```
10029      *
10030      * Complete list of shortcut methods:
10031      *
10032      * - {@link ng.$http#get $http.get}
10033      * - {@link ng.$http#head $http.head}
10034      * - {@link ng.$http#post $http.post}
10035      * - {@link ng.$http#put $http.put}
10036      * - {@link ng.$http#delete $http.delete}
10037      * - {@link ng.$http#jsonp $http.jsonp}
10038      * - {@link ng.$http#patch $http.patch}
10039      *
10040      *
10041      * ## Writing Unit Tests that use $http
10042      * When unit testing (using {@link ngMock ngMock}), it is necessary to call
10043      * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
10044      * request using trained responses.
10045      *
10046      * ```
10047      * $httpBackend.expectGET(...);
10048      * $http.get(...);
10049      * $httpBackend.flush();
10050      * ```
10051      *
10052      * ## Deprecation Notice
10053      * <div class="alert alert-danger">
10054      *   The `$http` legacy promise methods `success` and `error` have been deprecated.
10055      *   Use the standard `then` method instead.
10056      *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
10057      *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
10058      * </div>
10059      *
10060      * ## Setting HTTP Headers
10061      *
10062      * The $http service will automatically add certain HTTP headers to all requests. These defaults
10063      * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
10064      * object, which currently contains this default configuration:
10065      *
10066      * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
10067      *   - `Accept: application/json, text/plain, * / *`
10068      * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
10069      *   - `Content-Type: application/json`
10070      * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
10071      *   - `Content-Type: application/json`
10072      *
10073      * To add or overwrite these defaults, simply add or remove a property from these configuration
10074      * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
10075      * with the lowercased HTTP method name as the key, e.g.
10076      * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
10077      *
10078      * The defaults can also be set at runtime via the `$http.defaults` object in the same
10079      * fashion. For example:
10080      *
10081      * ```
10082      * module.run(function($http) {
10083      *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
10084      * });
10085      * ```
10086      *
10087      * In addition, you can supply a `headers` property in the config object passed when
10088      * calling `$http(config)`, which overrides the defaults without changing them globally.
10089      *
10090      * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
10091      * Use the `headers` property, setting the desired header to `undefined`. For example:
10092      *
10093      * ```js
10094      * var req = {
10095      *  method: 'POST',
10096      *  url: 'http://example.com',
10097      *  headers: {
10098      *    'Content-Type': undefined
10099      *  },
10100      *  data: { test: 'test' }
10101      * }
10102      *
10103      * $http(req).then(function(){...}, function(){...});
10104      * ```
10105      *
10106      * ## Transforming Requests and Responses
10107      *
10108      * Both requests and responses can be transformed using transformation functions: `transformRequest`
10109      * and `transformResponse`. These properties can be a single function that returns
10110      * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
10111      * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
10112      *
10113      * <div class="alert alert-warning">
10114      * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
10115      * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
10116      * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
10117      * function will be reflected on the scope and in any templates where the object is data-bound.
10118      * To prevent his, transform functions should have no side-effects.
10119      * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
10120      * </div>
10121      *
10122      * ### Default Transformations
10123      *
10124      * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
10125      * `defaults.transformResponse` properties. If a request does not provide its own transformations
10126      * then these will be applied.
10127      *
10128      * You can augment or replace the default transformations by modifying these properties by adding to or
10129      * replacing the array.
10130      *
10131      * Angular provides the following default transformations:
10132      *
10133      * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
10134      *
10135      * - If the `data` property of the request configuration object contains an object, serialize it
10136      *   into JSON format.
10137      *
10138      * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
10139      *
10140      *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
10141      *  - If JSON response is detected, deserialize it using a JSON parser.
10142      *
10143      *
10144      * ### Overriding the Default Transformations Per Request
10145      *
10146      * If you wish override the request/response transformations only for a single request then provide
10147      * `transformRequest` and/or `transformResponse` properties on the configuration object passed
10148      * into `$http`.
10149      *
10150      * Note that if you provide these properties on the config object the default transformations will be
10151      * overwritten. If you wish to augment the default transformations then you must include them in your
10152      * local transformation array.
10153      *
10154      * The following code demonstrates adding a new response transformation to be run after the default response
10155      * transformations have been run.
10156      *
10157      * ```js
10158      * function appendTransform(defaults, transform) {
10159      *
10160      *   // We can't guarantee that the default transformation is an array
10161      *   defaults = angular.isArray(defaults) ? defaults : [defaults];
10162      *
10163      *   // Append the new transformation to the defaults
10164      *   return defaults.concat(transform);
10165      * }
10166      *
10167      * $http({
10168      *   url: '...',
10169      *   method: 'GET',
10170      *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
10171      *     return doTransform(value);
10172      *   })
10173      * });
10174      * ```
10175      *
10176      *
10177      * ## Caching
10178      *
10179      * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
10180      * set the config.cache value or the default cache value to TRUE or to a cache object (created
10181      * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
10182      * precedence over the default cache value.
10183      *
10184      * In order to:
10185      *   * cache all responses - set the default cache value to TRUE or to a cache object
10186      *   * cache a specific response - set config.cache value to TRUE or to a cache object
10187      *
10188      * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
10189      * then the default `$cacheFactory($http)` object is used.
10190      *
10191      * The default cache value can be set by updating the
10192      * {@link ng.$http#defaults `$http.defaults.cache`} property or the
10193      * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
10194      *
10195      * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
10196      * the relevant cache object. The next time the same request is made, the response is returned
10197      * from the cache without sending a request to the server.
10198      *
10199      * Take note that:
10200      *
10201      *   * Only GET and JSONP requests are cached.
10202      *   * The cache key is the request URL including search parameters; headers are not considered.
10203      *   * Cached responses are returned asynchronously, in the same way as responses from the server.
10204      *   * If multiple identical requests are made using the same cache, which is not yet populated,
10205      *     one request will be made to the server and remaining requests will return the same response.
10206      *   * A cache-control header on the response does not affect if or how responses are cached.
10207      *
10208      *
10209      * ## Interceptors
10210      *
10211      * Before you start creating interceptors, be sure to understand the
10212      * {@link ng.$q $q and deferred/promise APIs}.
10213      *
10214      * For purposes of global error handling, authentication, or any kind of synchronous or
10215      * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10216      * able to intercept requests before they are handed to the server and
10217      * responses before they are handed over to the application code that
10218      * initiated these requests. The interceptors leverage the {@link ng.$q
10219      * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10220      *
10221      * The interceptors are service factories that are registered with the `$httpProvider` by
10222      * adding them to the `$httpProvider.interceptors` array. The factory is called and
10223      * injected with dependencies (if specified) and returns the interceptor.
10224      *
10225      * There are two kinds of interceptors (and two kinds of rejection interceptors):
10226      *
10227      *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10228      *     modify the `config` object or create a new one. The function needs to return the `config`
10229      *     object directly, or a promise containing the `config` or a new `config` object.
10230      *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
10231      *     resolved with a rejection.
10232      *   * `response`: interceptors get called with http `response` object. The function is free to
10233      *     modify the `response` object or create a new one. The function needs to return the `response`
10234      *     object directly, or as a promise containing the `response` or a new `response` object.
10235      *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
10236      *     resolved with a rejection.
10237      *
10238      *
10239      * ```js
10240      *   // register the interceptor as a service
10241      *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10242      *     return {
10243      *       // optional method
10244      *       'request': function(config) {
10245      *         // do something on success
10246      *         return config;
10247      *       },
10248      *
10249      *       // optional method
10250      *      'requestError': function(rejection) {
10251      *         // do something on error
10252      *         if (canRecover(rejection)) {
10253      *           return responseOrNewPromise
10254      *         }
10255      *         return $q.reject(rejection);
10256      *       },
10257      *
10258      *
10259      *
10260      *       // optional method
10261      *       'response': function(response) {
10262      *         // do something on success
10263      *         return response;
10264      *       },
10265      *
10266      *       // optional method
10267      *      'responseError': function(rejection) {
10268      *         // do something on error
10269      *         if (canRecover(rejection)) {
10270      *           return responseOrNewPromise
10271      *         }
10272      *         return $q.reject(rejection);
10273      *       }
10274      *     };
10275      *   });
10276      *
10277      *   $httpProvider.interceptors.push('myHttpInterceptor');
10278      *
10279      *
10280      *   // alternatively, register the interceptor via an anonymous factory
10281      *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10282      *     return {
10283      *      'request': function(config) {
10284      *          // same as above
10285      *       },
10286      *
10287      *       'response': function(response) {
10288      *          // same as above
10289      *       }
10290      *     };
10291      *   });
10292      * ```
10293      *
10294      * ## Security Considerations
10295      *
10296      * When designing web applications, consider security threats from:
10297      *
10298      * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10299      * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10300      *
10301      * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10302      * pre-configured with strategies that address these issues, but for this to work backend server
10303      * cooperation is required.
10304      *
10305      * ### JSON Vulnerability Protection
10306      *
10307      * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10308      * allows third party website to turn your JSON resource URL into
10309      * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10310      * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10311      * Angular will automatically strip the prefix before processing it as JSON.
10312      *
10313      * For example if your server needs to return:
10314      * ```js
10315      * ['one','two']
10316      * ```
10317      *
10318      * which is vulnerable to attack, your server can return:
10319      * ```js
10320      * )]}',
10321      * ['one','two']
10322      * ```
10323      *
10324      * Angular will strip the prefix, before processing the JSON.
10325      *
10326      *
10327      * ### Cross Site Request Forgery (XSRF) Protection
10328      *
10329      * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
10330      * which the attacker can trick an authenticated user into unknowingly executing actions on your
10331      * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
10332      * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
10333      * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
10334      * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
10335      * The header will not be set for cross-domain requests.
10336      *
10337      * To take advantage of this, your server needs to set a token in a JavaScript readable session
10338      * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10339      * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10340      * that only JavaScript running on your domain could have sent the request. The token must be
10341      * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10342      * making up its own tokens). We recommend that the token is a digest of your site's
10343      * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
10344      * for added security.
10345      *
10346      * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10347      * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10348      * or the per-request config object.
10349      *
10350      * In order to prevent collisions in environments where multiple Angular apps share the
10351      * same domain or subdomain, we recommend that each application uses unique cookie name.
10352      *
10353      * @param {object} config Object describing the request to be made and how it should be
10354      *    processed. The object has following properties:
10355      *
10356      *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10357      *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10358      *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10359      *      with the `paramSerializer` and appended as GET parameters.
10360      *    - **data** – `{string|Object}` – Data to be sent as the request message data.
10361      *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
10362      *      HTTP headers to send to the server. If the return value of a function is null, the
10363      *      header will not be sent. Functions accept a config object as an argument.
10364      *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10365      *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10366      *    - **transformRequest** –
10367      *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
10368      *      transform function or an array of such functions. The transform function takes the http
10369      *      request body and headers and returns its transformed (typically serialized) version.
10370      *      See {@link ng.$http#overriding-the-default-transformations-per-request
10371      *      Overriding the Default Transformations}
10372      *    - **transformResponse** –
10373      *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10374      *      transform function or an array of such functions. The transform function takes the http
10375      *      response body, headers and status and returns its transformed (typically deserialized) version.
10376      *      See {@link ng.$http#overriding-the-default-transformations-per-request
10377      *      Overriding the Default Transformations}
10378      *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10379      *      prepare the string representation of request parameters (specified as an object).
10380      *      If specified as string, it is interpreted as function registered with the
10381      *      {@link $injector $injector}, which means you can create your own serializer
10382      *      by registering it as a {@link auto.$provide#service service}.
10383      *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10384      *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10385      *    - **cache** – `{boolean|Object}` – A boolean value or object created with
10386      *      {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
10387      *      See {@link $http#caching $http Caching} for more information.
10388      *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10389      *      that should abort the request when resolved.
10390      *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10391      *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10392      *      for more information.
10393      *    - **responseType** - `{string}` - see
10394      *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10395      *
10396      * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10397      *                        when the request succeeds or fails.
10398      *
10399      *
10400      * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10401      *   requests. This is primarily meant to be used for debugging purposes.
10402      *
10403      *
10404      * @example
10405 <example module="httpExample">
10406 <file name="index.html">
10407   <div ng-controller="FetchController">
10408     <select ng-model="method" aria-label="Request method">
10409       <option>GET</option>
10410       <option>JSONP</option>
10411     </select>
10412     <input type="text" ng-model="url" size="80" aria-label="URL" />
10413     <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10414     <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10415     <button id="samplejsonpbtn"
10416       ng-click="updateModel('JSONP',
10417                     'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10418       Sample JSONP
10419     </button>
10420     <button id="invalidjsonpbtn"
10421       ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10422         Invalid JSONP
10423       </button>
10424     <pre>http status code: {{status}}</pre>
10425     <pre>http response data: {{data}}</pre>
10426   </div>
10427 </file>
10428 <file name="script.js">
10429   angular.module('httpExample', [])
10430     .controller('FetchController', ['$scope', '$http', '$templateCache',
10431       function($scope, $http, $templateCache) {
10432         $scope.method = 'GET';
10433         $scope.url = 'http-hello.html';
10434
10435         $scope.fetch = function() {
10436           $scope.code = null;
10437           $scope.response = null;
10438
10439           $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10440             then(function(response) {
10441               $scope.status = response.status;
10442               $scope.data = response.data;
10443             }, function(response) {
10444               $scope.data = response.data || "Request failed";
10445               $scope.status = response.status;
10446           });
10447         };
10448
10449         $scope.updateModel = function(method, url) {
10450           $scope.method = method;
10451           $scope.url = url;
10452         };
10453       }]);
10454 </file>
10455 <file name="http-hello.html">
10456   Hello, $http!
10457 </file>
10458 <file name="protractor.js" type="protractor">
10459   var status = element(by.binding('status'));
10460   var data = element(by.binding('data'));
10461   var fetchBtn = element(by.id('fetchbtn'));
10462   var sampleGetBtn = element(by.id('samplegetbtn'));
10463   var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10464   var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10465
10466   it('should make an xhr GET request', function() {
10467     sampleGetBtn.click();
10468     fetchBtn.click();
10469     expect(status.getText()).toMatch('200');
10470     expect(data.getText()).toMatch(/Hello, \$http!/);
10471   });
10472
10473 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10474 // it('should make a JSONP request to angularjs.org', function() {
10475 //   sampleJsonpBtn.click();
10476 //   fetchBtn.click();
10477 //   expect(status.getText()).toMatch('200');
10478 //   expect(data.getText()).toMatch(/Super Hero!/);
10479 // });
10480
10481   it('should make JSONP request to invalid URL and invoke the error handler',
10482       function() {
10483     invalidJsonpBtn.click();
10484     fetchBtn.click();
10485     expect(status.getText()).toMatch('0');
10486     expect(data.getText()).toMatch('Request failed');
10487   });
10488 </file>
10489 </example>
10490      */
10491     function $http(requestConfig) {
10492
10493       if (!angular.isObject(requestConfig)) {
10494         throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
10495       }
10496
10497       if (!isString(requestConfig.url)) {
10498         throw minErr('$http')('badreq', 'Http request configuration url must be a string.  Received: {0}', requestConfig.url);
10499       }
10500
10501       var config = extend({
10502         method: 'get',
10503         transformRequest: defaults.transformRequest,
10504         transformResponse: defaults.transformResponse,
10505         paramSerializer: defaults.paramSerializer
10506       }, requestConfig);
10507
10508       config.headers = mergeHeaders(requestConfig);
10509       config.method = uppercase(config.method);
10510       config.paramSerializer = isString(config.paramSerializer) ?
10511         $injector.get(config.paramSerializer) : config.paramSerializer;
10512
10513       var serverRequest = function(config) {
10514         var headers = config.headers;
10515         var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10516
10517         // strip content-type if data is undefined
10518         if (isUndefined(reqData)) {
10519           forEach(headers, function(value, header) {
10520             if (lowercase(header) === 'content-type') {
10521                 delete headers[header];
10522             }
10523           });
10524         }
10525
10526         if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10527           config.withCredentials = defaults.withCredentials;
10528         }
10529
10530         // send request
10531         return sendReq(config, reqData).then(transformResponse, transformResponse);
10532       };
10533
10534       var chain = [serverRequest, undefined];
10535       var promise = $q.when(config);
10536
10537       // apply interceptors
10538       forEach(reversedInterceptors, function(interceptor) {
10539         if (interceptor.request || interceptor.requestError) {
10540           chain.unshift(interceptor.request, interceptor.requestError);
10541         }
10542         if (interceptor.response || interceptor.responseError) {
10543           chain.push(interceptor.response, interceptor.responseError);
10544         }
10545       });
10546
10547       while (chain.length) {
10548         var thenFn = chain.shift();
10549         var rejectFn = chain.shift();
10550
10551         promise = promise.then(thenFn, rejectFn);
10552       }
10553
10554       if (useLegacyPromise) {
10555         promise.success = function(fn) {
10556           assertArgFn(fn, 'fn');
10557
10558           promise.then(function(response) {
10559             fn(response.data, response.status, response.headers, config);
10560           });
10561           return promise;
10562         };
10563
10564         promise.error = function(fn) {
10565           assertArgFn(fn, 'fn');
10566
10567           promise.then(null, function(response) {
10568             fn(response.data, response.status, response.headers, config);
10569           });
10570           return promise;
10571         };
10572       } else {
10573         promise.success = $httpMinErrLegacyFn('success');
10574         promise.error = $httpMinErrLegacyFn('error');
10575       }
10576
10577       return promise;
10578
10579       function transformResponse(response) {
10580         // make a copy since the response must be cacheable
10581         var resp = extend({}, response);
10582         resp.data = transformData(response.data, response.headers, response.status,
10583                                   config.transformResponse);
10584         return (isSuccess(response.status))
10585           ? resp
10586           : $q.reject(resp);
10587       }
10588
10589       function executeHeaderFns(headers, config) {
10590         var headerContent, processedHeaders = {};
10591
10592         forEach(headers, function(headerFn, header) {
10593           if (isFunction(headerFn)) {
10594             headerContent = headerFn(config);
10595             if (headerContent != null) {
10596               processedHeaders[header] = headerContent;
10597             }
10598           } else {
10599             processedHeaders[header] = headerFn;
10600           }
10601         });
10602
10603         return processedHeaders;
10604       }
10605
10606       function mergeHeaders(config) {
10607         var defHeaders = defaults.headers,
10608             reqHeaders = extend({}, config.headers),
10609             defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10610
10611         defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10612
10613         // using for-in instead of forEach to avoid unecessary iteration after header has been found
10614         defaultHeadersIteration:
10615         for (defHeaderName in defHeaders) {
10616           lowercaseDefHeaderName = lowercase(defHeaderName);
10617
10618           for (reqHeaderName in reqHeaders) {
10619             if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10620               continue defaultHeadersIteration;
10621             }
10622           }
10623
10624           reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10625         }
10626
10627         // execute if header value is a function for merged headers
10628         return executeHeaderFns(reqHeaders, shallowCopy(config));
10629       }
10630     }
10631
10632     $http.pendingRequests = [];
10633
10634     /**
10635      * @ngdoc method
10636      * @name $http#get
10637      *
10638      * @description
10639      * Shortcut method to perform `GET` request.
10640      *
10641      * @param {string} url Relative or absolute URL specifying the destination of the request
10642      * @param {Object=} config Optional configuration object
10643      * @returns {HttpPromise} Future object
10644      */
10645
10646     /**
10647      * @ngdoc method
10648      * @name $http#delete
10649      *
10650      * @description
10651      * Shortcut method to perform `DELETE` request.
10652      *
10653      * @param {string} url Relative or absolute URL specifying the destination of the request
10654      * @param {Object=} config Optional configuration object
10655      * @returns {HttpPromise} Future object
10656      */
10657
10658     /**
10659      * @ngdoc method
10660      * @name $http#head
10661      *
10662      * @description
10663      * Shortcut method to perform `HEAD` request.
10664      *
10665      * @param {string} url Relative or absolute URL specifying the destination of the request
10666      * @param {Object=} config Optional configuration object
10667      * @returns {HttpPromise} Future object
10668      */
10669
10670     /**
10671      * @ngdoc method
10672      * @name $http#jsonp
10673      *
10674      * @description
10675      * Shortcut method to perform `JSONP` request.
10676      *
10677      * @param {string} url Relative or absolute URL specifying the destination of the request.
10678      *                     The name of the callback should be the string `JSON_CALLBACK`.
10679      * @param {Object=} config Optional configuration object
10680      * @returns {HttpPromise} Future object
10681      */
10682     createShortMethods('get', 'delete', 'head', 'jsonp');
10683
10684     /**
10685      * @ngdoc method
10686      * @name $http#post
10687      *
10688      * @description
10689      * Shortcut method to perform `POST` request.
10690      *
10691      * @param {string} url Relative or absolute URL specifying the destination of the request
10692      * @param {*} data Request content
10693      * @param {Object=} config Optional configuration object
10694      * @returns {HttpPromise} Future object
10695      */
10696
10697     /**
10698      * @ngdoc method
10699      * @name $http#put
10700      *
10701      * @description
10702      * Shortcut method to perform `PUT` request.
10703      *
10704      * @param {string} url Relative or absolute URL specifying the destination of the request
10705      * @param {*} data Request content
10706      * @param {Object=} config Optional configuration object
10707      * @returns {HttpPromise} Future object
10708      */
10709
10710      /**
10711       * @ngdoc method
10712       * @name $http#patch
10713       *
10714       * @description
10715       * Shortcut method to perform `PATCH` request.
10716       *
10717       * @param {string} url Relative or absolute URL specifying the destination of the request
10718       * @param {*} data Request content
10719       * @param {Object=} config Optional configuration object
10720       * @returns {HttpPromise} Future object
10721       */
10722     createShortMethodsWithData('post', 'put', 'patch');
10723
10724         /**
10725          * @ngdoc property
10726          * @name $http#defaults
10727          *
10728          * @description
10729          * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10730          * default headers, withCredentials as well as request and response transformations.
10731          *
10732          * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10733          */
10734     $http.defaults = defaults;
10735
10736
10737     return $http;
10738
10739
10740     function createShortMethods(names) {
10741       forEach(arguments, function(name) {
10742         $http[name] = function(url, config) {
10743           return $http(extend({}, config || {}, {
10744             method: name,
10745             url: url
10746           }));
10747         };
10748       });
10749     }
10750
10751
10752     function createShortMethodsWithData(name) {
10753       forEach(arguments, function(name) {
10754         $http[name] = function(url, data, config) {
10755           return $http(extend({}, config || {}, {
10756             method: name,
10757             url: url,
10758             data: data
10759           }));
10760         };
10761       });
10762     }
10763
10764
10765     /**
10766      * Makes the request.
10767      *
10768      * !!! ACCESSES CLOSURE VARS:
10769      * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10770      */
10771     function sendReq(config, reqData) {
10772       var deferred = $q.defer(),
10773           promise = deferred.promise,
10774           cache,
10775           cachedResp,
10776           reqHeaders = config.headers,
10777           url = buildUrl(config.url, config.paramSerializer(config.params));
10778
10779       $http.pendingRequests.push(config);
10780       promise.then(removePendingReq, removePendingReq);
10781
10782
10783       if ((config.cache || defaults.cache) && config.cache !== false &&
10784           (config.method === 'GET' || config.method === 'JSONP')) {
10785         cache = isObject(config.cache) ? config.cache
10786               : isObject(defaults.cache) ? defaults.cache
10787               : defaultCache;
10788       }
10789
10790       if (cache) {
10791         cachedResp = cache.get(url);
10792         if (isDefined(cachedResp)) {
10793           if (isPromiseLike(cachedResp)) {
10794             // cached request has already been sent, but there is no response yet
10795             cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10796           } else {
10797             // serving from cache
10798             if (isArray(cachedResp)) {
10799               resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10800             } else {
10801               resolvePromise(cachedResp, 200, {}, 'OK');
10802             }
10803           }
10804         } else {
10805           // put the promise for the non-transformed response into cache as a placeholder
10806           cache.put(url, promise);
10807         }
10808       }
10809
10810
10811       // if we won't have the response in cache, set the xsrf headers and
10812       // send the request to the backend
10813       if (isUndefined(cachedResp)) {
10814         var xsrfValue = urlIsSameOrigin(config.url)
10815             ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10816             : undefined;
10817         if (xsrfValue) {
10818           reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10819         }
10820
10821         $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10822             config.withCredentials, config.responseType);
10823       }
10824
10825       return promise;
10826
10827
10828       /**
10829        * Callback registered to $httpBackend():
10830        *  - caches the response if desired
10831        *  - resolves the raw $http promise
10832        *  - calls $apply
10833        */
10834       function done(status, response, headersString, statusText) {
10835         if (cache) {
10836           if (isSuccess(status)) {
10837             cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10838           } else {
10839             // remove promise from the cache
10840             cache.remove(url);
10841           }
10842         }
10843
10844         function resolveHttpPromise() {
10845           resolvePromise(response, status, headersString, statusText);
10846         }
10847
10848         if (useApplyAsync) {
10849           $rootScope.$applyAsync(resolveHttpPromise);
10850         } else {
10851           resolveHttpPromise();
10852           if (!$rootScope.$$phase) $rootScope.$apply();
10853         }
10854       }
10855
10856
10857       /**
10858        * Resolves the raw $http promise.
10859        */
10860       function resolvePromise(response, status, headers, statusText) {
10861         //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10862         status = status >= -1 ? status : 0;
10863
10864         (isSuccess(status) ? deferred.resolve : deferred.reject)({
10865           data: response,
10866           status: status,
10867           headers: headersGetter(headers),
10868           config: config,
10869           statusText: statusText
10870         });
10871       }
10872
10873       function resolvePromiseWithResult(result) {
10874         resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10875       }
10876
10877       function removePendingReq() {
10878         var idx = $http.pendingRequests.indexOf(config);
10879         if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10880       }
10881     }
10882
10883
10884     function buildUrl(url, serializedParams) {
10885       if (serializedParams.length > 0) {
10886         url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10887       }
10888       return url;
10889     }
10890   }];
10891 }
10892
10893 /**
10894  * @ngdoc service
10895  * @name $xhrFactory
10896  *
10897  * @description
10898  * Factory function used to create XMLHttpRequest objects.
10899  *
10900  * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10901  *
10902  * ```
10903  * angular.module('myApp', [])
10904  * .factory('$xhrFactory', function() {
10905  *   return function createXhr(method, url) {
10906  *     return new window.XMLHttpRequest({mozSystem: true});
10907  *   };
10908  * });
10909  * ```
10910  *
10911  * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10912  * @param {string} url URL of the request.
10913  */
10914 function $xhrFactoryProvider() {
10915   this.$get = function() {
10916     return function createXhr() {
10917       return new window.XMLHttpRequest();
10918     };
10919   };
10920 }
10921
10922 /**
10923  * @ngdoc service
10924  * @name $httpBackend
10925  * @requires $window
10926  * @requires $document
10927  * @requires $xhrFactory
10928  *
10929  * @description
10930  * HTTP backend used by the {@link ng.$http service} that delegates to
10931  * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10932  *
10933  * You should never need to use this service directly, instead use the higher-level abstractions:
10934  * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10935  *
10936  * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10937  * $httpBackend} which can be trained with responses.
10938  */
10939 function $HttpBackendProvider() {
10940   this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10941     return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10942   }];
10943 }
10944
10945 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10946   // TODO(vojta): fix the signature
10947   return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10948     $browser.$$incOutstandingRequestCount();
10949     url = url || $browser.url();
10950
10951     if (lowercase(method) == 'jsonp') {
10952       var callbackId = '_' + (callbacks.counter++).toString(36);
10953       callbacks[callbackId] = function(data) {
10954         callbacks[callbackId].data = data;
10955         callbacks[callbackId].called = true;
10956       };
10957
10958       var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10959           callbackId, function(status, text) {
10960         completeRequest(callback, status, callbacks[callbackId].data, "", text);
10961         callbacks[callbackId] = noop;
10962       });
10963     } else {
10964
10965       var xhr = createXhr(method, url);
10966
10967       xhr.open(method, url, true);
10968       forEach(headers, function(value, key) {
10969         if (isDefined(value)) {
10970             xhr.setRequestHeader(key, value);
10971         }
10972       });
10973
10974       xhr.onload = function requestLoaded() {
10975         var statusText = xhr.statusText || '';
10976
10977         // responseText is the old-school way of retrieving response (supported by IE9)
10978         // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10979         var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10980
10981         // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10982         var status = xhr.status === 1223 ? 204 : xhr.status;
10983
10984         // fix status code when it is 0 (0 status is undocumented).
10985         // Occurs when accessing file resources or on Android 4.1 stock browser
10986         // while retrieving files from application cache.
10987         if (status === 0) {
10988           status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10989         }
10990
10991         completeRequest(callback,
10992             status,
10993             response,
10994             xhr.getAllResponseHeaders(),
10995             statusText);
10996       };
10997
10998       var requestError = function() {
10999         // The response is always empty
11000         // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
11001         completeRequest(callback, -1, null, null, '');
11002       };
11003
11004       xhr.onerror = requestError;
11005       xhr.onabort = requestError;
11006
11007       if (withCredentials) {
11008         xhr.withCredentials = true;
11009       }
11010
11011       if (responseType) {
11012         try {
11013           xhr.responseType = responseType;
11014         } catch (e) {
11015           // WebKit added support for the json responseType value on 09/03/2013
11016           // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
11017           // known to throw when setting the value "json" as the response type. Other older
11018           // browsers implementing the responseType
11019           //
11020           // The json response type can be ignored if not supported, because JSON payloads are
11021           // parsed on the client-side regardless.
11022           if (responseType !== 'json') {
11023             throw e;
11024           }
11025         }
11026       }
11027
11028       xhr.send(isUndefined(post) ? null : post);
11029     }
11030
11031     if (timeout > 0) {
11032       var timeoutId = $browserDefer(timeoutRequest, timeout);
11033     } else if (isPromiseLike(timeout)) {
11034       timeout.then(timeoutRequest);
11035     }
11036
11037
11038     function timeoutRequest() {
11039       jsonpDone && jsonpDone();
11040       xhr && xhr.abort();
11041     }
11042
11043     function completeRequest(callback, status, response, headersString, statusText) {
11044       // cancel timeout and subsequent timeout promise resolution
11045       if (isDefined(timeoutId)) {
11046         $browserDefer.cancel(timeoutId);
11047       }
11048       jsonpDone = xhr = null;
11049
11050       callback(status, response, headersString, statusText);
11051       $browser.$$completeOutstandingRequest(noop);
11052     }
11053   };
11054
11055   function jsonpReq(url, callbackId, done) {
11056     // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
11057     // - fetches local scripts via XHR and evals them
11058     // - adds and immediately removes script elements from the document
11059     var script = rawDocument.createElement('script'), callback = null;
11060     script.type = "text/javascript";
11061     script.src = url;
11062     script.async = true;
11063
11064     callback = function(event) {
11065       removeEventListenerFn(script, "load", callback);
11066       removeEventListenerFn(script, "error", callback);
11067       rawDocument.body.removeChild(script);
11068       script = null;
11069       var status = -1;
11070       var text = "unknown";
11071
11072       if (event) {
11073         if (event.type === "load" && !callbacks[callbackId].called) {
11074           event = { type: "error" };
11075         }
11076         text = event.type;
11077         status = event.type === "error" ? 404 : 200;
11078       }
11079
11080       if (done) {
11081         done(status, text);
11082       }
11083     };
11084
11085     addEventListenerFn(script, "load", callback);
11086     addEventListenerFn(script, "error", callback);
11087     rawDocument.body.appendChild(script);
11088     return callback;
11089   }
11090 }
11091
11092 var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
11093 $interpolateMinErr.throwNoconcat = function(text) {
11094   throw $interpolateMinErr('noconcat',
11095       "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
11096       "interpolations that concatenate multiple expressions when a trusted value is " +
11097       "required.  See http://docs.angularjs.org/api/ng.$sce", text);
11098 };
11099
11100 $interpolateMinErr.interr = function(text, err) {
11101   return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
11102 };
11103
11104 /**
11105  * @ngdoc provider
11106  * @name $interpolateProvider
11107  *
11108  * @description
11109  *
11110  * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
11111  *
11112  * @example
11113 <example name="custom-interpolation-markup" module="customInterpolationApp">
11114 <file name="index.html">
11115 <script>
11116   var customInterpolationApp = angular.module('customInterpolationApp', []);
11117
11118   customInterpolationApp.config(function($interpolateProvider) {
11119     $interpolateProvider.startSymbol('//');
11120     $interpolateProvider.endSymbol('//');
11121   });
11122
11123
11124   customInterpolationApp.controller('DemoController', function() {
11125       this.label = "This binding is brought you by // interpolation symbols.";
11126   });
11127 </script>
11128 <div ng-controller="DemoController as demo">
11129     //demo.label//
11130 </div>
11131 </file>
11132 <file name="protractor.js" type="protractor">
11133   it('should interpolate binding with custom symbols', function() {
11134     expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
11135   });
11136 </file>
11137 </example>
11138  */
11139 function $InterpolateProvider() {
11140   var startSymbol = '{{';
11141   var endSymbol = '}}';
11142
11143   /**
11144    * @ngdoc method
11145    * @name $interpolateProvider#startSymbol
11146    * @description
11147    * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
11148    *
11149    * @param {string=} value new value to set the starting symbol to.
11150    * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11151    */
11152   this.startSymbol = function(value) {
11153     if (value) {
11154       startSymbol = value;
11155       return this;
11156     } else {
11157       return startSymbol;
11158     }
11159   };
11160
11161   /**
11162    * @ngdoc method
11163    * @name $interpolateProvider#endSymbol
11164    * @description
11165    * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11166    *
11167    * @param {string=} value new value to set the ending symbol to.
11168    * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11169    */
11170   this.endSymbol = function(value) {
11171     if (value) {
11172       endSymbol = value;
11173       return this;
11174     } else {
11175       return endSymbol;
11176     }
11177   };
11178
11179
11180   this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
11181     var startSymbolLength = startSymbol.length,
11182         endSymbolLength = endSymbol.length,
11183         escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
11184         escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
11185
11186     function escape(ch) {
11187       return '\\\\\\' + ch;
11188     }
11189
11190     function unescapeText(text) {
11191       return text.replace(escapedStartRegexp, startSymbol).
11192         replace(escapedEndRegexp, endSymbol);
11193     }
11194
11195     function stringify(value) {
11196       if (value == null) { // null || undefined
11197         return '';
11198       }
11199       switch (typeof value) {
11200         case 'string':
11201           break;
11202         case 'number':
11203           value = '' + value;
11204           break;
11205         default:
11206           value = toJson(value);
11207       }
11208
11209       return value;
11210     }
11211
11212     /**
11213      * @ngdoc service
11214      * @name $interpolate
11215      * @kind function
11216      *
11217      * @requires $parse
11218      * @requires $sce
11219      *
11220      * @description
11221      *
11222      * Compiles a string with markup into an interpolation function. This service is used by the
11223      * HTML {@link ng.$compile $compile} service for data binding. See
11224      * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11225      * interpolation markup.
11226      *
11227      *
11228      * ```js
11229      *   var $interpolate = ...; // injected
11230      *   var exp = $interpolate('Hello {{name | uppercase}}!');
11231      *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11232      * ```
11233      *
11234      * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11235      * `true`, the interpolation function will return `undefined` unless all embedded expressions
11236      * evaluate to a value other than `undefined`.
11237      *
11238      * ```js
11239      *   var $interpolate = ...; // injected
11240      *   var context = {greeting: 'Hello', name: undefined };
11241      *
11242      *   // default "forgiving" mode
11243      *   var exp = $interpolate('{{greeting}} {{name}}!');
11244      *   expect(exp(context)).toEqual('Hello !');
11245      *
11246      *   // "allOrNothing" mode
11247      *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11248      *   expect(exp(context)).toBeUndefined();
11249      *   context.name = 'Angular';
11250      *   expect(exp(context)).toEqual('Hello Angular!');
11251      * ```
11252      *
11253      * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
11254      *
11255      * ####Escaped Interpolation
11256      * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
11257      * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
11258      * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
11259      * or binding.
11260      *
11261      * This enables web-servers to prevent script injection attacks and defacing attacks, to some
11262      * degree, while also enabling code examples to work without relying on the
11263      * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11264      *
11265      * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11266      * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
11267      * interpolation start/end markers with their escaped counterparts.**
11268      *
11269      * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11270      * output when the $interpolate service processes the text. So, for HTML elements interpolated
11271      * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
11272      * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11273      * this is typically useful only when user-data is used in rendering a template from the server, or
11274      * when otherwise untrusted data is used by a directive.
11275      *
11276      * <example>
11277      *  <file name="index.html">
11278      *    <div ng-init="username='A user'">
11279      *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11280      *        </p>
11281      *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
11282      *        application, but fails to accomplish their task, because the server has correctly
11283      *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11284      *        characters.</p>
11285      *      <p>Instead, the result of the attempted script injection is visible, and can be removed
11286      *        from the database by an administrator.</p>
11287      *    </div>
11288      *  </file>
11289      * </example>
11290      *
11291      * @param {string} text The text with markup to interpolate.
11292      * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11293      *    embedded expression in order to return an interpolation function. Strings with no
11294      *    embedded expression will return null for the interpolation function.
11295      * @param {string=} trustedContext when provided, the returned function passes the interpolated
11296      *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11297      *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
11298      *    provides Strict Contextual Escaping for details.
11299      * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11300      *    unless all embedded expressions evaluate to a value other than `undefined`.
11301      * @returns {function(context)} an interpolation function which is used to compute the
11302      *    interpolated string. The function has these parameters:
11303      *
11304      * - `context`: evaluation context for all expressions embedded in the interpolated text
11305      */
11306     function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11307       allOrNothing = !!allOrNothing;
11308       var startIndex,
11309           endIndex,
11310           index = 0,
11311           expressions = [],
11312           parseFns = [],
11313           textLength = text.length,
11314           exp,
11315           concat = [],
11316           expressionPositions = [];
11317
11318       while (index < textLength) {
11319         if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11320              ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11321           if (index !== startIndex) {
11322             concat.push(unescapeText(text.substring(index, startIndex)));
11323           }
11324           exp = text.substring(startIndex + startSymbolLength, endIndex);
11325           expressions.push(exp);
11326           parseFns.push($parse(exp, parseStringifyInterceptor));
11327           index = endIndex + endSymbolLength;
11328           expressionPositions.push(concat.length);
11329           concat.push('');
11330         } else {
11331           // we did not find an interpolation, so we have to add the remainder to the separators array
11332           if (index !== textLength) {
11333             concat.push(unescapeText(text.substring(index)));
11334           }
11335           break;
11336         }
11337       }
11338
11339       // Concatenating expressions makes it hard to reason about whether some combination of
11340       // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
11341       // single expression be used for iframe[src], object[src], etc., we ensure that the value
11342       // that's used is assigned or constructed by some JS code somewhere that is more testable or
11343       // make it obvious that you bound the value to some user controlled value.  This helps reduce
11344       // the load when auditing for XSS issues.
11345       if (trustedContext && concat.length > 1) {
11346           $interpolateMinErr.throwNoconcat(text);
11347       }
11348
11349       if (!mustHaveExpression || expressions.length) {
11350         var compute = function(values) {
11351           for (var i = 0, ii = expressions.length; i < ii; i++) {
11352             if (allOrNothing && isUndefined(values[i])) return;
11353             concat[expressionPositions[i]] = values[i];
11354           }
11355           return concat.join('');
11356         };
11357
11358         var getValue = function(value) {
11359           return trustedContext ?
11360             $sce.getTrusted(trustedContext, value) :
11361             $sce.valueOf(value);
11362         };
11363
11364         return extend(function interpolationFn(context) {
11365             var i = 0;
11366             var ii = expressions.length;
11367             var values = new Array(ii);
11368
11369             try {
11370               for (; i < ii; i++) {
11371                 values[i] = parseFns[i](context);
11372               }
11373
11374               return compute(values);
11375             } catch (err) {
11376               $exceptionHandler($interpolateMinErr.interr(text, err));
11377             }
11378
11379           }, {
11380           // all of these properties are undocumented for now
11381           exp: text, //just for compatibility with regular watchers created via $watch
11382           expressions: expressions,
11383           $$watchDelegate: function(scope, listener) {
11384             var lastValue;
11385             return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11386               var currValue = compute(values);
11387               if (isFunction(listener)) {
11388                 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11389               }
11390               lastValue = currValue;
11391             });
11392           }
11393         });
11394       }
11395
11396       function parseStringifyInterceptor(value) {
11397         try {
11398           value = getValue(value);
11399           return allOrNothing && !isDefined(value) ? value : stringify(value);
11400         } catch (err) {
11401           $exceptionHandler($interpolateMinErr.interr(text, err));
11402         }
11403       }
11404     }
11405
11406
11407     /**
11408      * @ngdoc method
11409      * @name $interpolate#startSymbol
11410      * @description
11411      * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11412      *
11413      * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11414      * the symbol.
11415      *
11416      * @returns {string} start symbol.
11417      */
11418     $interpolate.startSymbol = function() {
11419       return startSymbol;
11420     };
11421
11422
11423     /**
11424      * @ngdoc method
11425      * @name $interpolate#endSymbol
11426      * @description
11427      * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11428      *
11429      * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11430      * the symbol.
11431      *
11432      * @returns {string} end symbol.
11433      */
11434     $interpolate.endSymbol = function() {
11435       return endSymbol;
11436     };
11437
11438     return $interpolate;
11439   }];
11440 }
11441
11442 function $IntervalProvider() {
11443   this.$get = ['$rootScope', '$window', '$q', '$$q',
11444        function($rootScope,   $window,   $q,   $$q) {
11445     var intervals = {};
11446
11447
11448      /**
11449       * @ngdoc service
11450       * @name $interval
11451       *
11452       * @description
11453       * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11454       * milliseconds.
11455       *
11456       * The return value of registering an interval function is a promise. This promise will be
11457       * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11458       * run indefinitely if `count` is not defined. The value of the notification will be the
11459       * number of iterations that have run.
11460       * To cancel an interval, call `$interval.cancel(promise)`.
11461       *
11462       * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11463       * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11464       * time.
11465       *
11466       * <div class="alert alert-warning">
11467       * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11468       * with them.  In particular they are not automatically destroyed when a controller's scope or a
11469       * directive's element are destroyed.
11470       * You should take this into consideration and make sure to always cancel the interval at the
11471       * appropriate moment.  See the example below for more details on how and when to do this.
11472       * </div>
11473       *
11474       * @param {function()} fn A function that should be called repeatedly.
11475       * @param {number} delay Number of milliseconds between each function call.
11476       * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11477       *   indefinitely.
11478       * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11479       *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11480       * @param {...*=} Pass additional parameters to the executed function.
11481       * @returns {promise} A promise which will be notified on each iteration.
11482       *
11483       * @example
11484       * <example module="intervalExample">
11485       * <file name="index.html">
11486       *   <script>
11487       *     angular.module('intervalExample', [])
11488       *       .controller('ExampleController', ['$scope', '$interval',
11489       *         function($scope, $interval) {
11490       *           $scope.format = 'M/d/yy h:mm:ss a';
11491       *           $scope.blood_1 = 100;
11492       *           $scope.blood_2 = 120;
11493       *
11494       *           var stop;
11495       *           $scope.fight = function() {
11496       *             // Don't start a new fight if we are already fighting
11497       *             if ( angular.isDefined(stop) ) return;
11498       *
11499       *             stop = $interval(function() {
11500       *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11501       *                 $scope.blood_1 = $scope.blood_1 - 3;
11502       *                 $scope.blood_2 = $scope.blood_2 - 4;
11503       *               } else {
11504       *                 $scope.stopFight();
11505       *               }
11506       *             }, 100);
11507       *           };
11508       *
11509       *           $scope.stopFight = function() {
11510       *             if (angular.isDefined(stop)) {
11511       *               $interval.cancel(stop);
11512       *               stop = undefined;
11513       *             }
11514       *           };
11515       *
11516       *           $scope.resetFight = function() {
11517       *             $scope.blood_1 = 100;
11518       *             $scope.blood_2 = 120;
11519       *           };
11520       *
11521       *           $scope.$on('$destroy', function() {
11522       *             // Make sure that the interval is destroyed too
11523       *             $scope.stopFight();
11524       *           });
11525       *         }])
11526       *       // Register the 'myCurrentTime' directive factory method.
11527       *       // We inject $interval and dateFilter service since the factory method is DI.
11528       *       .directive('myCurrentTime', ['$interval', 'dateFilter',
11529       *         function($interval, dateFilter) {
11530       *           // return the directive link function. (compile function not needed)
11531       *           return function(scope, element, attrs) {
11532       *             var format,  // date format
11533       *                 stopTime; // so that we can cancel the time updates
11534       *
11535       *             // used to update the UI
11536       *             function updateTime() {
11537       *               element.text(dateFilter(new Date(), format));
11538       *             }
11539       *
11540       *             // watch the expression, and update the UI on change.
11541       *             scope.$watch(attrs.myCurrentTime, function(value) {
11542       *               format = value;
11543       *               updateTime();
11544       *             });
11545       *
11546       *             stopTime = $interval(updateTime, 1000);
11547       *
11548       *             // listen on DOM destroy (removal) event, and cancel the next UI update
11549       *             // to prevent updating time after the DOM element was removed.
11550       *             element.on('$destroy', function() {
11551       *               $interval.cancel(stopTime);
11552       *             });
11553       *           }
11554       *         }]);
11555       *   </script>
11556       *
11557       *   <div>
11558       *     <div ng-controller="ExampleController">
11559       *       <label>Date format: <input ng-model="format"></label> <hr/>
11560       *       Current time is: <span my-current-time="format"></span>
11561       *       <hr/>
11562       *       Blood 1 : <font color='red'>{{blood_1}}</font>
11563       *       Blood 2 : <font color='red'>{{blood_2}}</font>
11564       *       <button type="button" data-ng-click="fight()">Fight</button>
11565       *       <button type="button" data-ng-click="stopFight()">StopFight</button>
11566       *       <button type="button" data-ng-click="resetFight()">resetFight</button>
11567       *     </div>
11568       *   </div>
11569       *
11570       * </file>
11571       * </example>
11572       */
11573     function interval(fn, delay, count, invokeApply) {
11574       var hasParams = arguments.length > 4,
11575           args = hasParams ? sliceArgs(arguments, 4) : [],
11576           setInterval = $window.setInterval,
11577           clearInterval = $window.clearInterval,
11578           iteration = 0,
11579           skipApply = (isDefined(invokeApply) && !invokeApply),
11580           deferred = (skipApply ? $$q : $q).defer(),
11581           promise = deferred.promise;
11582
11583       count = isDefined(count) ? count : 0;
11584
11585       promise.then(null, null, (!hasParams) ? fn : function() {
11586         fn.apply(null, args);
11587       });
11588
11589       promise.$$intervalId = setInterval(function tick() {
11590         deferred.notify(iteration++);
11591
11592         if (count > 0 && iteration >= count) {
11593           deferred.resolve(iteration);
11594           clearInterval(promise.$$intervalId);
11595           delete intervals[promise.$$intervalId];
11596         }
11597
11598         if (!skipApply) $rootScope.$apply();
11599
11600       }, delay);
11601
11602       intervals[promise.$$intervalId] = deferred;
11603
11604       return promise;
11605     }
11606
11607
11608      /**
11609       * @ngdoc method
11610       * @name $interval#cancel
11611       *
11612       * @description
11613       * Cancels a task associated with the `promise`.
11614       *
11615       * @param {Promise=} promise returned by the `$interval` function.
11616       * @returns {boolean} Returns `true` if the task was successfully canceled.
11617       */
11618     interval.cancel = function(promise) {
11619       if (promise && promise.$$intervalId in intervals) {
11620         intervals[promise.$$intervalId].reject('canceled');
11621         $window.clearInterval(promise.$$intervalId);
11622         delete intervals[promise.$$intervalId];
11623         return true;
11624       }
11625       return false;
11626     };
11627
11628     return interval;
11629   }];
11630 }
11631
11632 /**
11633  * @ngdoc service
11634  * @name $locale
11635  *
11636  * @description
11637  * $locale service provides localization rules for various Angular components. As of right now the
11638  * only public api is:
11639  *
11640  * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11641  */
11642
11643 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11644     DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11645 var $locationMinErr = minErr('$location');
11646
11647
11648 /**
11649  * Encode path using encodeUriSegment, ignoring forward slashes
11650  *
11651  * @param {string} path Path to encode
11652  * @returns {string}
11653  */
11654 function encodePath(path) {
11655   var segments = path.split('/'),
11656       i = segments.length;
11657
11658   while (i--) {
11659     segments[i] = encodeUriSegment(segments[i]);
11660   }
11661
11662   return segments.join('/');
11663 }
11664
11665 function parseAbsoluteUrl(absoluteUrl, locationObj) {
11666   var parsedUrl = urlResolve(absoluteUrl);
11667
11668   locationObj.$$protocol = parsedUrl.protocol;
11669   locationObj.$$host = parsedUrl.hostname;
11670   locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11671 }
11672
11673
11674 function parseAppUrl(relativeUrl, locationObj) {
11675   var prefixed = (relativeUrl.charAt(0) !== '/');
11676   if (prefixed) {
11677     relativeUrl = '/' + relativeUrl;
11678   }
11679   var match = urlResolve(relativeUrl);
11680   locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11681       match.pathname.substring(1) : match.pathname);
11682   locationObj.$$search = parseKeyValue(match.search);
11683   locationObj.$$hash = decodeURIComponent(match.hash);
11684
11685   // make sure path starts with '/';
11686   if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11687     locationObj.$$path = '/' + locationObj.$$path;
11688   }
11689 }
11690
11691
11692 /**
11693  *
11694  * @param {string} begin
11695  * @param {string} whole
11696  * @returns {string} returns text from whole after begin or undefined if it does not begin with
11697  *                   expected string.
11698  */
11699 function beginsWith(begin, whole) {
11700   if (whole.indexOf(begin) === 0) {
11701     return whole.substr(begin.length);
11702   }
11703 }
11704
11705
11706 function stripHash(url) {
11707   var index = url.indexOf('#');
11708   return index == -1 ? url : url.substr(0, index);
11709 }
11710
11711 function trimEmptyHash(url) {
11712   return url.replace(/(#.+)|#$/, '$1');
11713 }
11714
11715
11716 function stripFile(url) {
11717   return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11718 }
11719
11720 /* return the server only (scheme://host:port) */
11721 function serverBase(url) {
11722   return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11723 }
11724
11725
11726 /**
11727  * LocationHtml5Url represents an url
11728  * This object is exposed as $location service when HTML5 mode is enabled and supported
11729  *
11730  * @constructor
11731  * @param {string} appBase application base URL
11732  * @param {string} appBaseNoFile application base URL stripped of any filename
11733  * @param {string} basePrefix url path prefix
11734  */
11735 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11736   this.$$html5 = true;
11737   basePrefix = basePrefix || '';
11738   parseAbsoluteUrl(appBase, this);
11739
11740
11741   /**
11742    * Parse given html5 (regular) url string into properties
11743    * @param {string} url HTML5 url
11744    * @private
11745    */
11746   this.$$parse = function(url) {
11747     var pathUrl = beginsWith(appBaseNoFile, url);
11748     if (!isString(pathUrl)) {
11749       throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11750           appBaseNoFile);
11751     }
11752
11753     parseAppUrl(pathUrl, this);
11754
11755     if (!this.$$path) {
11756       this.$$path = '/';
11757     }
11758
11759     this.$$compose();
11760   };
11761
11762   /**
11763    * Compose url and update `absUrl` property
11764    * @private
11765    */
11766   this.$$compose = function() {
11767     var search = toKeyValue(this.$$search),
11768         hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11769
11770     this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11771     this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11772   };
11773
11774   this.$$parseLinkUrl = function(url, relHref) {
11775     if (relHref && relHref[0] === '#') {
11776       // special case for links to hash fragments:
11777       // keep the old url and only replace the hash fragment
11778       this.hash(relHref.slice(1));
11779       return true;
11780     }
11781     var appUrl, prevAppUrl;
11782     var rewrittenUrl;
11783
11784     if (isDefined(appUrl = beginsWith(appBase, url))) {
11785       prevAppUrl = appUrl;
11786       if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11787         rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11788       } else {
11789         rewrittenUrl = appBase + prevAppUrl;
11790       }
11791     } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11792       rewrittenUrl = appBaseNoFile + appUrl;
11793     } else if (appBaseNoFile == url + '/') {
11794       rewrittenUrl = appBaseNoFile;
11795     }
11796     if (rewrittenUrl) {
11797       this.$$parse(rewrittenUrl);
11798     }
11799     return !!rewrittenUrl;
11800   };
11801 }
11802
11803
11804 /**
11805  * LocationHashbangUrl represents url
11806  * This object is exposed as $location service when developer doesn't opt into html5 mode.
11807  * It also serves as the base class for html5 mode fallback on legacy browsers.
11808  *
11809  * @constructor
11810  * @param {string} appBase application base URL
11811  * @param {string} appBaseNoFile application base URL stripped of any filename
11812  * @param {string} hashPrefix hashbang prefix
11813  */
11814 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
11815
11816   parseAbsoluteUrl(appBase, this);
11817
11818
11819   /**
11820    * Parse given hashbang url into properties
11821    * @param {string} url Hashbang url
11822    * @private
11823    */
11824   this.$$parse = function(url) {
11825     var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11826     var withoutHashUrl;
11827
11828     if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
11829
11830       // The rest of the url starts with a hash so we have
11831       // got either a hashbang path or a plain hash fragment
11832       withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11833       if (isUndefined(withoutHashUrl)) {
11834         // There was no hashbang prefix so we just have a hash fragment
11835         withoutHashUrl = withoutBaseUrl;
11836       }
11837
11838     } else {
11839       // There was no hashbang path nor hash fragment:
11840       // If we are in HTML5 mode we use what is left as the path;
11841       // Otherwise we ignore what is left
11842       if (this.$$html5) {
11843         withoutHashUrl = withoutBaseUrl;
11844       } else {
11845         withoutHashUrl = '';
11846         if (isUndefined(withoutBaseUrl)) {
11847           appBase = url;
11848           this.replace();
11849         }
11850       }
11851     }
11852
11853     parseAppUrl(withoutHashUrl, this);
11854
11855     this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11856
11857     this.$$compose();
11858
11859     /*
11860      * In Windows, on an anchor node on documents loaded from
11861      * the filesystem, the browser will return a pathname
11862      * prefixed with the drive name ('/C:/path') when a
11863      * pathname without a drive is set:
11864      *  * a.setAttribute('href', '/foo')
11865      *   * a.pathname === '/C:/foo' //true
11866      *
11867      * Inside of Angular, we're always using pathnames that
11868      * do not include drive names for routing.
11869      */
11870     function removeWindowsDriveName(path, url, base) {
11871       /*
11872       Matches paths for file protocol on windows,
11873       such as /C:/foo/bar, and captures only /foo/bar.
11874       */
11875       var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11876
11877       var firstPathSegmentMatch;
11878
11879       //Get the relative path from the input URL.
11880       if (url.indexOf(base) === 0) {
11881         url = url.replace(base, '');
11882       }
11883
11884       // The input URL intentionally contains a first path segment that ends with a colon.
11885       if (windowsFilePathExp.exec(url)) {
11886         return path;
11887       }
11888
11889       firstPathSegmentMatch = windowsFilePathExp.exec(path);
11890       return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11891     }
11892   };
11893
11894   /**
11895    * Compose hashbang url and update `absUrl` property
11896    * @private
11897    */
11898   this.$$compose = function() {
11899     var search = toKeyValue(this.$$search),
11900         hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11901
11902     this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11903     this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11904   };
11905
11906   this.$$parseLinkUrl = function(url, relHref) {
11907     if (stripHash(appBase) == stripHash(url)) {
11908       this.$$parse(url);
11909       return true;
11910     }
11911     return false;
11912   };
11913 }
11914
11915
11916 /**
11917  * LocationHashbangUrl represents url
11918  * This object is exposed as $location service when html5 history api is enabled but the browser
11919  * does not support it.
11920  *
11921  * @constructor
11922  * @param {string} appBase application base URL
11923  * @param {string} appBaseNoFile application base URL stripped of any filename
11924  * @param {string} hashPrefix hashbang prefix
11925  */
11926 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
11927   this.$$html5 = true;
11928   LocationHashbangUrl.apply(this, arguments);
11929
11930   this.$$parseLinkUrl = function(url, relHref) {
11931     if (relHref && relHref[0] === '#') {
11932       // special case for links to hash fragments:
11933       // keep the old url and only replace the hash fragment
11934       this.hash(relHref.slice(1));
11935       return true;
11936     }
11937
11938     var rewrittenUrl;
11939     var appUrl;
11940
11941     if (appBase == stripHash(url)) {
11942       rewrittenUrl = url;
11943     } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11944       rewrittenUrl = appBase + hashPrefix + appUrl;
11945     } else if (appBaseNoFile === url + '/') {
11946       rewrittenUrl = appBaseNoFile;
11947     }
11948     if (rewrittenUrl) {
11949       this.$$parse(rewrittenUrl);
11950     }
11951     return !!rewrittenUrl;
11952   };
11953
11954   this.$$compose = function() {
11955     var search = toKeyValue(this.$$search),
11956         hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11957
11958     this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11959     // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
11960     this.$$absUrl = appBase + hashPrefix + this.$$url;
11961   };
11962
11963 }
11964
11965
11966 var locationPrototype = {
11967
11968   /**
11969    * Are we in html5 mode?
11970    * @private
11971    */
11972   $$html5: false,
11973
11974   /**
11975    * Has any change been replacing?
11976    * @private
11977    */
11978   $$replace: false,
11979
11980   /**
11981    * @ngdoc method
11982    * @name $location#absUrl
11983    *
11984    * @description
11985    * This method is getter only.
11986    *
11987    * Return full url representation with all segments encoded according to rules specified in
11988    * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11989    *
11990    *
11991    * ```js
11992    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11993    * var absUrl = $location.absUrl();
11994    * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11995    * ```
11996    *
11997    * @return {string} full url
11998    */
11999   absUrl: locationGetter('$$absUrl'),
12000
12001   /**
12002    * @ngdoc method
12003    * @name $location#url
12004    *
12005    * @description
12006    * This method is getter / setter.
12007    *
12008    * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
12009    *
12010    * Change path, search and hash, when called with parameter and return `$location`.
12011    *
12012    *
12013    * ```js
12014    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12015    * var url = $location.url();
12016    * // => "/some/path?foo=bar&baz=xoxo"
12017    * ```
12018    *
12019    * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
12020    * @return {string} url
12021    */
12022   url: function(url) {
12023     if (isUndefined(url)) {
12024       return this.$$url;
12025     }
12026
12027     var match = PATH_MATCH.exec(url);
12028     if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
12029     if (match[2] || match[1] || url === '') this.search(match[3] || '');
12030     this.hash(match[5] || '');
12031
12032     return this;
12033   },
12034
12035   /**
12036    * @ngdoc method
12037    * @name $location#protocol
12038    *
12039    * @description
12040    * This method is getter only.
12041    *
12042    * Return protocol of current url.
12043    *
12044    *
12045    * ```js
12046    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12047    * var protocol = $location.protocol();
12048    * // => "http"
12049    * ```
12050    *
12051    * @return {string} protocol of current url
12052    */
12053   protocol: locationGetter('$$protocol'),
12054
12055   /**
12056    * @ngdoc method
12057    * @name $location#host
12058    *
12059    * @description
12060    * This method is getter only.
12061    *
12062    * Return host of current url.
12063    *
12064    * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
12065    *
12066    *
12067    * ```js
12068    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12069    * var host = $location.host();
12070    * // => "example.com"
12071    *
12072    * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
12073    * host = $location.host();
12074    * // => "example.com"
12075    * host = location.host;
12076    * // => "example.com:8080"
12077    * ```
12078    *
12079    * @return {string} host of current url.
12080    */
12081   host: locationGetter('$$host'),
12082
12083   /**
12084    * @ngdoc method
12085    * @name $location#port
12086    *
12087    * @description
12088    * This method is getter only.
12089    *
12090    * Return port of current url.
12091    *
12092    *
12093    * ```js
12094    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12095    * var port = $location.port();
12096    * // => 80
12097    * ```
12098    *
12099    * @return {Number} port
12100    */
12101   port: locationGetter('$$port'),
12102
12103   /**
12104    * @ngdoc method
12105    * @name $location#path
12106    *
12107    * @description
12108    * This method is getter / setter.
12109    *
12110    * Return path of current url when called without any parameter.
12111    *
12112    * Change path when called with parameter and return `$location`.
12113    *
12114    * Note: Path should always begin with forward slash (/), this method will add the forward slash
12115    * if it is missing.
12116    *
12117    *
12118    * ```js
12119    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12120    * var path = $location.path();
12121    * // => "/some/path"
12122    * ```
12123    *
12124    * @param {(string|number)=} path New path
12125    * @return {string} path
12126    */
12127   path: locationGetterSetter('$$path', function(path) {
12128     path = path !== null ? path.toString() : '';
12129     return path.charAt(0) == '/' ? path : '/' + path;
12130   }),
12131
12132   /**
12133    * @ngdoc method
12134    * @name $location#search
12135    *
12136    * @description
12137    * This method is getter / setter.
12138    *
12139    * Return search part (as object) of current url when called without any parameter.
12140    *
12141    * Change search part when called with parameter and return `$location`.
12142    *
12143    *
12144    * ```js
12145    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12146    * var searchObject = $location.search();
12147    * // => {foo: 'bar', baz: 'xoxo'}
12148    *
12149    * // set foo to 'yipee'
12150    * $location.search('foo', 'yipee');
12151    * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
12152    * ```
12153    *
12154    * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
12155    * hash object.
12156    *
12157    * When called with a single argument the method acts as a setter, setting the `search` component
12158    * of `$location` to the specified value.
12159    *
12160    * If the argument is a hash object containing an array of values, these values will be encoded
12161    * as duplicate search parameters in the url.
12162    *
12163    * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
12164    * will override only a single search property.
12165    *
12166    * If `paramValue` is an array, it will override the property of the `search` component of
12167    * `$location` specified via the first argument.
12168    *
12169    * If `paramValue` is `null`, the property specified via the first argument will be deleted.
12170    *
12171    * If `paramValue` is `true`, the property specified via the first argument will be added with no
12172    * value nor trailing equal sign.
12173    *
12174    * @return {Object} If called with no arguments returns the parsed `search` object. If called with
12175    * one or more arguments returns `$location` object itself.
12176    */
12177   search: function(search, paramValue) {
12178     switch (arguments.length) {
12179       case 0:
12180         return this.$$search;
12181       case 1:
12182         if (isString(search) || isNumber(search)) {
12183           search = search.toString();
12184           this.$$search = parseKeyValue(search);
12185         } else if (isObject(search)) {
12186           search = copy(search, {});
12187           // remove object undefined or null properties
12188           forEach(search, function(value, key) {
12189             if (value == null) delete search[key];
12190           });
12191
12192           this.$$search = search;
12193         } else {
12194           throw $locationMinErr('isrcharg',
12195               'The first argument of the `$location#search()` call must be a string or an object.');
12196         }
12197         break;
12198       default:
12199         if (isUndefined(paramValue) || paramValue === null) {
12200           delete this.$$search[search];
12201         } else {
12202           this.$$search[search] = paramValue;
12203         }
12204     }
12205
12206     this.$$compose();
12207     return this;
12208   },
12209
12210   /**
12211    * @ngdoc method
12212    * @name $location#hash
12213    *
12214    * @description
12215    * This method is getter / setter.
12216    *
12217    * Returns the hash fragment when called without any parameters.
12218    *
12219    * Changes the hash fragment when called with a parameter and returns `$location`.
12220    *
12221    *
12222    * ```js
12223    * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12224    * var hash = $location.hash();
12225    * // => "hashValue"
12226    * ```
12227    *
12228    * @param {(string|number)=} hash New hash fragment
12229    * @return {string} hash
12230    */
12231   hash: locationGetterSetter('$$hash', function(hash) {
12232     return hash !== null ? hash.toString() : '';
12233   }),
12234
12235   /**
12236    * @ngdoc method
12237    * @name $location#replace
12238    *
12239    * @description
12240    * If called, all changes to $location during the current `$digest` will replace the current history
12241    * record, instead of adding a new one.
12242    */
12243   replace: function() {
12244     this.$$replace = true;
12245     return this;
12246   }
12247 };
12248
12249 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
12250   Location.prototype = Object.create(locationPrototype);
12251
12252   /**
12253    * @ngdoc method
12254    * @name $location#state
12255    *
12256    * @description
12257    * This method is getter / setter.
12258    *
12259    * Return the history state object when called without any parameter.
12260    *
12261    * Change the history state object when called with one parameter and return `$location`.
12262    * The state object is later passed to `pushState` or `replaceState`.
12263    *
12264    * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12265    * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12266    * older browsers (like IE9 or Android < 4.0), don't use this method.
12267    *
12268    * @param {object=} state State object for pushState or replaceState
12269    * @return {object} state
12270    */
12271   Location.prototype.state = function(state) {
12272     if (!arguments.length) {
12273       return this.$$state;
12274     }
12275
12276     if (Location !== LocationHtml5Url || !this.$$html5) {
12277       throw $locationMinErr('nostate', 'History API state support is available only ' +
12278         'in HTML5 mode and only in browsers supporting HTML5 History API');
12279     }
12280     // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12281     // but we're changing the $$state reference to $browser.state() during the $digest
12282     // so the modification window is narrow.
12283     this.$$state = isUndefined(state) ? null : state;
12284
12285     return this;
12286   };
12287 });
12288
12289
12290 function locationGetter(property) {
12291   return function() {
12292     return this[property];
12293   };
12294 }
12295
12296
12297 function locationGetterSetter(property, preprocess) {
12298   return function(value) {
12299     if (isUndefined(value)) {
12300       return this[property];
12301     }
12302
12303     this[property] = preprocess(value);
12304     this.$$compose();
12305
12306     return this;
12307   };
12308 }
12309
12310
12311 /**
12312  * @ngdoc service
12313  * @name $location
12314  *
12315  * @requires $rootElement
12316  *
12317  * @description
12318  * The $location service parses the URL in the browser address bar (based on the
12319  * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12320  * available to your application. Changes to the URL in the address bar are reflected into
12321  * $location service and changes to $location are reflected into the browser address bar.
12322  *
12323  * **The $location service:**
12324  *
12325  * - Exposes the current URL in the browser address bar, so you can
12326  *   - Watch and observe the URL.
12327  *   - Change the URL.
12328  * - Synchronizes the URL with the browser when the user
12329  *   - Changes the address bar.
12330  *   - Clicks the back or forward button (or clicks a History link).
12331  *   - Clicks on a link.
12332  * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12333  *
12334  * For more information see {@link guide/$location Developer Guide: Using $location}
12335  */
12336
12337 /**
12338  * @ngdoc provider
12339  * @name $locationProvider
12340  * @description
12341  * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12342  */
12343 function $LocationProvider() {
12344   var hashPrefix = '',
12345       html5Mode = {
12346         enabled: false,
12347         requireBase: true,
12348         rewriteLinks: true
12349       };
12350
12351   /**
12352    * @ngdoc method
12353    * @name $locationProvider#hashPrefix
12354    * @description
12355    * @param {string=} prefix Prefix for hash part (containing path and search)
12356    * @returns {*} current value if used as getter or itself (chaining) if used as setter
12357    */
12358   this.hashPrefix = function(prefix) {
12359     if (isDefined(prefix)) {
12360       hashPrefix = prefix;
12361       return this;
12362     } else {
12363       return hashPrefix;
12364     }
12365   };
12366
12367   /**
12368    * @ngdoc method
12369    * @name $locationProvider#html5Mode
12370    * @description
12371    * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12372    *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12373    *   properties:
12374    *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12375    *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12376    *     support `pushState`.
12377    *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12378    *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12379    *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
12380    *     See the {@link guide/$location $location guide for more information}
12381    *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12382    *     enables/disables url rewriting for relative links.
12383    *
12384    * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12385    */
12386   this.html5Mode = function(mode) {
12387     if (isBoolean(mode)) {
12388       html5Mode.enabled = mode;
12389       return this;
12390     } else if (isObject(mode)) {
12391
12392       if (isBoolean(mode.enabled)) {
12393         html5Mode.enabled = mode.enabled;
12394       }
12395
12396       if (isBoolean(mode.requireBase)) {
12397         html5Mode.requireBase = mode.requireBase;
12398       }
12399
12400       if (isBoolean(mode.rewriteLinks)) {
12401         html5Mode.rewriteLinks = mode.rewriteLinks;
12402       }
12403
12404       return this;
12405     } else {
12406       return html5Mode;
12407     }
12408   };
12409
12410   /**
12411    * @ngdoc event
12412    * @name $location#$locationChangeStart
12413    * @eventType broadcast on root scope
12414    * @description
12415    * Broadcasted before a URL will change.
12416    *
12417    * This change can be prevented by calling
12418    * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12419    * details about event object. Upon successful change
12420    * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12421    *
12422    * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12423    * the browser supports the HTML5 History API.
12424    *
12425    * @param {Object} angularEvent Synthetic event object.
12426    * @param {string} newUrl New URL
12427    * @param {string=} oldUrl URL that was before it was changed.
12428    * @param {string=} newState New history state object
12429    * @param {string=} oldState History state object that was before it was changed.
12430    */
12431
12432   /**
12433    * @ngdoc event
12434    * @name $location#$locationChangeSuccess
12435    * @eventType broadcast on root scope
12436    * @description
12437    * Broadcasted after a URL was changed.
12438    *
12439    * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12440    * the browser supports the HTML5 History API.
12441    *
12442    * @param {Object} angularEvent Synthetic event object.
12443    * @param {string} newUrl New URL
12444    * @param {string=} oldUrl URL that was before it was changed.
12445    * @param {string=} newState New history state object
12446    * @param {string=} oldState History state object that was before it was changed.
12447    */
12448
12449   this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12450       function($rootScope, $browser, $sniffer, $rootElement, $window) {
12451     var $location,
12452         LocationMode,
12453         baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12454         initialUrl = $browser.url(),
12455         appBase;
12456
12457     if (html5Mode.enabled) {
12458       if (!baseHref && html5Mode.requireBase) {
12459         throw $locationMinErr('nobase',
12460           "$location in HTML5 mode requires a <base> tag to be present!");
12461       }
12462       appBase = serverBase(initialUrl) + (baseHref || '/');
12463       LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12464     } else {
12465       appBase = stripHash(initialUrl);
12466       LocationMode = LocationHashbangUrl;
12467     }
12468     var appBaseNoFile = stripFile(appBase);
12469
12470     $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12471     $location.$$parseLinkUrl(initialUrl, initialUrl);
12472
12473     $location.$$state = $browser.state();
12474
12475     var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12476
12477     function setBrowserUrlWithFallback(url, replace, state) {
12478       var oldUrl = $location.url();
12479       var oldState = $location.$$state;
12480       try {
12481         $browser.url(url, replace, state);
12482
12483         // Make sure $location.state() returns referentially identical (not just deeply equal)
12484         // state object; this makes possible quick checking if the state changed in the digest
12485         // loop. Checking deep equality would be too expensive.
12486         $location.$$state = $browser.state();
12487       } catch (e) {
12488         // Restore old values if pushState fails
12489         $location.url(oldUrl);
12490         $location.$$state = oldState;
12491
12492         throw e;
12493       }
12494     }
12495
12496     $rootElement.on('click', function(event) {
12497       // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12498       // currently we open nice url link and redirect then
12499
12500       if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12501
12502       var elm = jqLite(event.target);
12503
12504       // traverse the DOM up to find first A tag
12505       while (nodeName_(elm[0]) !== 'a') {
12506         // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12507         if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12508       }
12509
12510       var absHref = elm.prop('href');
12511       // get the actual href attribute - see
12512       // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12513       var relHref = elm.attr('href') || elm.attr('xlink:href');
12514
12515       if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12516         // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12517         // an animation.
12518         absHref = urlResolve(absHref.animVal).href;
12519       }
12520
12521       // Ignore when url is started with javascript: or mailto:
12522       if (IGNORE_URI_REGEXP.test(absHref)) return;
12523
12524       if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12525         if ($location.$$parseLinkUrl(absHref, relHref)) {
12526           // We do a preventDefault for all urls that are part of the angular application,
12527           // in html5mode and also without, so that we are able to abort navigation without
12528           // getting double entries in the location history.
12529           event.preventDefault();
12530           // update location manually
12531           if ($location.absUrl() != $browser.url()) {
12532             $rootScope.$apply();
12533             // hack to work around FF6 bug 684208 when scenario runner clicks on links
12534             $window.angular['ff-684208-preventDefault'] = true;
12535           }
12536         }
12537       }
12538     });
12539
12540
12541     // rewrite hashbang url <> html5 url
12542     if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12543       $browser.url($location.absUrl(), true);
12544     }
12545
12546     var initializing = true;
12547
12548     // update $location when $browser url changes
12549     $browser.onUrlChange(function(newUrl, newState) {
12550
12551       if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12552         // If we are navigating outside of the app then force a reload
12553         $window.location.href = newUrl;
12554         return;
12555       }
12556
12557       $rootScope.$evalAsync(function() {
12558         var oldUrl = $location.absUrl();
12559         var oldState = $location.$$state;
12560         var defaultPrevented;
12561         newUrl = trimEmptyHash(newUrl);
12562         $location.$$parse(newUrl);
12563         $location.$$state = newState;
12564
12565         defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12566             newState, oldState).defaultPrevented;
12567
12568         // if the location was changed by a `$locationChangeStart` handler then stop
12569         // processing this location change
12570         if ($location.absUrl() !== newUrl) return;
12571
12572         if (defaultPrevented) {
12573           $location.$$parse(oldUrl);
12574           $location.$$state = oldState;
12575           setBrowserUrlWithFallback(oldUrl, false, oldState);
12576         } else {
12577           initializing = false;
12578           afterLocationChange(oldUrl, oldState);
12579         }
12580       });
12581       if (!$rootScope.$$phase) $rootScope.$digest();
12582     });
12583
12584     // update browser
12585     $rootScope.$watch(function $locationWatch() {
12586       var oldUrl = trimEmptyHash($browser.url());
12587       var newUrl = trimEmptyHash($location.absUrl());
12588       var oldState = $browser.state();
12589       var currentReplace = $location.$$replace;
12590       var urlOrStateChanged = oldUrl !== newUrl ||
12591         ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12592
12593       if (initializing || urlOrStateChanged) {
12594         initializing = false;
12595
12596         $rootScope.$evalAsync(function() {
12597           var newUrl = $location.absUrl();
12598           var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12599               $location.$$state, oldState).defaultPrevented;
12600
12601           // if the location was changed by a `$locationChangeStart` handler then stop
12602           // processing this location change
12603           if ($location.absUrl() !== newUrl) return;
12604
12605           if (defaultPrevented) {
12606             $location.$$parse(oldUrl);
12607             $location.$$state = oldState;
12608           } else {
12609             if (urlOrStateChanged) {
12610               setBrowserUrlWithFallback(newUrl, currentReplace,
12611                                         oldState === $location.$$state ? null : $location.$$state);
12612             }
12613             afterLocationChange(oldUrl, oldState);
12614           }
12615         });
12616       }
12617
12618       $location.$$replace = false;
12619
12620       // we don't need to return anything because $evalAsync will make the digest loop dirty when
12621       // there is a change
12622     });
12623
12624     return $location;
12625
12626     function afterLocationChange(oldUrl, oldState) {
12627       $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12628         $location.$$state, oldState);
12629     }
12630 }];
12631 }
12632
12633 /**
12634  * @ngdoc service
12635  * @name $log
12636  * @requires $window
12637  *
12638  * @description
12639  * Simple service for logging. Default implementation safely writes the message
12640  * into the browser's console (if present).
12641  *
12642  * The main purpose of this service is to simplify debugging and troubleshooting.
12643  *
12644  * The default is to log `debug` messages. You can use
12645  * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12646  *
12647  * @example
12648    <example module="logExample">
12649      <file name="script.js">
12650        angular.module('logExample', [])
12651          .controller('LogController', ['$scope', '$log', function($scope, $log) {
12652            $scope.$log = $log;
12653            $scope.message = 'Hello World!';
12654          }]);
12655      </file>
12656      <file name="index.html">
12657        <div ng-controller="LogController">
12658          <p>Reload this page with open console, enter text and hit the log button...</p>
12659          <label>Message:
12660          <input type="text" ng-model="message" /></label>
12661          <button ng-click="$log.log(message)">log</button>
12662          <button ng-click="$log.warn(message)">warn</button>
12663          <button ng-click="$log.info(message)">info</button>
12664          <button ng-click="$log.error(message)">error</button>
12665          <button ng-click="$log.debug(message)">debug</button>
12666        </div>
12667      </file>
12668    </example>
12669  */
12670
12671 /**
12672  * @ngdoc provider
12673  * @name $logProvider
12674  * @description
12675  * Use the `$logProvider` to configure how the application logs messages
12676  */
12677 function $LogProvider() {
12678   var debug = true,
12679       self = this;
12680
12681   /**
12682    * @ngdoc method
12683    * @name $logProvider#debugEnabled
12684    * @description
12685    * @param {boolean=} flag enable or disable debug level messages
12686    * @returns {*} current value if used as getter or itself (chaining) if used as setter
12687    */
12688   this.debugEnabled = function(flag) {
12689     if (isDefined(flag)) {
12690       debug = flag;
12691     return this;
12692     } else {
12693       return debug;
12694     }
12695   };
12696
12697   this.$get = ['$window', function($window) {
12698     return {
12699       /**
12700        * @ngdoc method
12701        * @name $log#log
12702        *
12703        * @description
12704        * Write a log message
12705        */
12706       log: consoleLog('log'),
12707
12708       /**
12709        * @ngdoc method
12710        * @name $log#info
12711        *
12712        * @description
12713        * Write an information message
12714        */
12715       info: consoleLog('info'),
12716
12717       /**
12718        * @ngdoc method
12719        * @name $log#warn
12720        *
12721        * @description
12722        * Write a warning message
12723        */
12724       warn: consoleLog('warn'),
12725
12726       /**
12727        * @ngdoc method
12728        * @name $log#error
12729        *
12730        * @description
12731        * Write an error message
12732        */
12733       error: consoleLog('error'),
12734
12735       /**
12736        * @ngdoc method
12737        * @name $log#debug
12738        *
12739        * @description
12740        * Write a debug message
12741        */
12742       debug: (function() {
12743         var fn = consoleLog('debug');
12744
12745         return function() {
12746           if (debug) {
12747             fn.apply(self, arguments);
12748           }
12749         };
12750       }())
12751     };
12752
12753     function formatError(arg) {
12754       if (arg instanceof Error) {
12755         if (arg.stack) {
12756           arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12757               ? 'Error: ' + arg.message + '\n' + arg.stack
12758               : arg.stack;
12759         } else if (arg.sourceURL) {
12760           arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12761         }
12762       }
12763       return arg;
12764     }
12765
12766     function consoleLog(type) {
12767       var console = $window.console || {},
12768           logFn = console[type] || console.log || noop,
12769           hasApply = false;
12770
12771       // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12772       // The reason behind this is that console.log has type "object" in IE8...
12773       try {
12774         hasApply = !!logFn.apply;
12775       } catch (e) {}
12776
12777       if (hasApply) {
12778         return function() {
12779           var args = [];
12780           forEach(arguments, function(arg) {
12781             args.push(formatError(arg));
12782           });
12783           return logFn.apply(console, args);
12784         };
12785       }
12786
12787       // we are IE which either doesn't have window.console => this is noop and we do nothing,
12788       // or we are IE where console.log doesn't have apply so we log at least first 2 args
12789       return function(arg1, arg2) {
12790         logFn(arg1, arg2 == null ? '' : arg2);
12791       };
12792     }
12793   }];
12794 }
12795
12796 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12797  *     Any commits to this file should be reviewed with security in mind.  *
12798  *   Changes to this file can potentially create security vulnerabilities. *
12799  *          An approval from 2 Core members with history of modifying      *
12800  *                         this file is required.                          *
12801  *                                                                         *
12802  *  Does the change somehow allow for arbitrary javascript to be executed? *
12803  *    Or allows for someone to change the prototype of built-in objects?   *
12804  *     Or gives undesired access to variables likes document or window?    *
12805  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12806
12807 var $parseMinErr = minErr('$parse');
12808
12809 // Sandboxing Angular Expressions
12810 // ------------------------------
12811 // Angular expressions are generally considered safe because these expressions only have direct
12812 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12813 // obtaining a reference to native JS functions such as the Function constructor.
12814 //
12815 // As an example, consider the following Angular expression:
12816 //
12817 //   {}.toString.constructor('alert("evil JS code")')
12818 //
12819 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12820 // against the expression language, but not to prevent exploits that were enabled by exposing
12821 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12822 // practice and therefore we are not even trying to protect against interaction with an object
12823 // explicitly exposed in this way.
12824 //
12825 // In general, it is not possible to access a Window object from an angular expression unless a
12826 // window or some DOM object that has a reference to window is published onto a Scope.
12827 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12828 // native objects.
12829 //
12830 // See https://docs.angularjs.org/guide/security
12831
12832
12833 function ensureSafeMemberName(name, fullExpression) {
12834   if (name === "__defineGetter__" || name === "__defineSetter__"
12835       || name === "__lookupGetter__" || name === "__lookupSetter__"
12836       || name === "__proto__") {
12837     throw $parseMinErr('isecfld',
12838         'Attempting to access a disallowed field in Angular expressions! '
12839         + 'Expression: {0}', fullExpression);
12840   }
12841   return name;
12842 }
12843
12844 function getStringValue(name, fullExpression) {
12845   // From the JavaScript docs:
12846   // Property names must be strings. This means that non-string objects cannot be used
12847   // as keys in an object. Any non-string object, including a number, is typecasted
12848   // into a string via the toString method.
12849   //
12850   // So, to ensure that we are checking the same `name` that JavaScript would use,
12851   // we cast it to a string, if possible.
12852   // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12853   // this is, this will handle objects that misbehave.
12854   name = name + '';
12855   if (!isString(name)) {
12856     throw $parseMinErr('iseccst',
12857         'Cannot convert object to primitive value! '
12858         + 'Expression: {0}', fullExpression);
12859   }
12860   return name;
12861 }
12862
12863 function ensureSafeObject(obj, fullExpression) {
12864   // nifty check if obj is Function that is fast and works across iframes and other contexts
12865   if (obj) {
12866     if (obj.constructor === obj) {
12867       throw $parseMinErr('isecfn',
12868           'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12869           fullExpression);
12870     } else if (// isWindow(obj)
12871         obj.window === obj) {
12872       throw $parseMinErr('isecwindow',
12873           'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12874           fullExpression);
12875     } else if (// isElement(obj)
12876         obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12877       throw $parseMinErr('isecdom',
12878           'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12879           fullExpression);
12880     } else if (// block Object so that we can't get hold of dangerous Object.* methods
12881         obj === Object) {
12882       throw $parseMinErr('isecobj',
12883           'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12884           fullExpression);
12885     }
12886   }
12887   return obj;
12888 }
12889
12890 var CALL = Function.prototype.call;
12891 var APPLY = Function.prototype.apply;
12892 var BIND = Function.prototype.bind;
12893
12894 function ensureSafeFunction(obj, fullExpression) {
12895   if (obj) {
12896     if (obj.constructor === obj) {
12897       throw $parseMinErr('isecfn',
12898         'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12899         fullExpression);
12900     } else if (obj === CALL || obj === APPLY || obj === BIND) {
12901       throw $parseMinErr('isecff',
12902         'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12903         fullExpression);
12904     }
12905   }
12906 }
12907
12908 function ensureSafeAssignContext(obj, fullExpression) {
12909   if (obj) {
12910     if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12911         obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12912       throw $parseMinErr('isecaf',
12913         'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12914     }
12915   }
12916 }
12917
12918 var OPERATORS = createMap();
12919 forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12920 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12921
12922
12923 /////////////////////////////////////////
12924
12925
12926 /**
12927  * @constructor
12928  */
12929 var Lexer = function(options) {
12930   this.options = options;
12931 };
12932
12933 Lexer.prototype = {
12934   constructor: Lexer,
12935
12936   lex: function(text) {
12937     this.text = text;
12938     this.index = 0;
12939     this.tokens = [];
12940
12941     while (this.index < this.text.length) {
12942       var ch = this.text.charAt(this.index);
12943       if (ch === '"' || ch === "'") {
12944         this.readString(ch);
12945       } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12946         this.readNumber();
12947       } else if (this.isIdent(ch)) {
12948         this.readIdent();
12949       } else if (this.is(ch, '(){}[].,;:?')) {
12950         this.tokens.push({index: this.index, text: ch});
12951         this.index++;
12952       } else if (this.isWhitespace(ch)) {
12953         this.index++;
12954       } else {
12955         var ch2 = ch + this.peek();
12956         var ch3 = ch2 + this.peek(2);
12957         var op1 = OPERATORS[ch];
12958         var op2 = OPERATORS[ch2];
12959         var op3 = OPERATORS[ch3];
12960         if (op1 || op2 || op3) {
12961           var token = op3 ? ch3 : (op2 ? ch2 : ch);
12962           this.tokens.push({index: this.index, text: token, operator: true});
12963           this.index += token.length;
12964         } else {
12965           this.throwError('Unexpected next character ', this.index, this.index + 1);
12966         }
12967       }
12968     }
12969     return this.tokens;
12970   },
12971
12972   is: function(ch, chars) {
12973     return chars.indexOf(ch) !== -1;
12974   },
12975
12976   peek: function(i) {
12977     var num = i || 1;
12978     return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12979   },
12980
12981   isNumber: function(ch) {
12982     return ('0' <= ch && ch <= '9') && typeof ch === "string";
12983   },
12984
12985   isWhitespace: function(ch) {
12986     // IE treats non-breaking space as \u00A0
12987     return (ch === ' ' || ch === '\r' || ch === '\t' ||
12988             ch === '\n' || ch === '\v' || ch === '\u00A0');
12989   },
12990
12991   isIdent: function(ch) {
12992     return ('a' <= ch && ch <= 'z' ||
12993             'A' <= ch && ch <= 'Z' ||
12994             '_' === ch || ch === '$');
12995   },
12996
12997   isExpOperator: function(ch) {
12998     return (ch === '-' || ch === '+' || this.isNumber(ch));
12999   },
13000
13001   throwError: function(error, start, end) {
13002     end = end || this.index;
13003     var colStr = (isDefined(start)
13004             ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
13005             : ' ' + end);
13006     throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
13007         error, colStr, this.text);
13008   },
13009
13010   readNumber: function() {
13011     var number = '';
13012     var start = this.index;
13013     while (this.index < this.text.length) {
13014       var ch = lowercase(this.text.charAt(this.index));
13015       if (ch == '.' || this.isNumber(ch)) {
13016         number += ch;
13017       } else {
13018         var peekCh = this.peek();
13019         if (ch == 'e' && this.isExpOperator(peekCh)) {
13020           number += ch;
13021         } else if (this.isExpOperator(ch) &&
13022             peekCh && this.isNumber(peekCh) &&
13023             number.charAt(number.length - 1) == 'e') {
13024           number += ch;
13025         } else if (this.isExpOperator(ch) &&
13026             (!peekCh || !this.isNumber(peekCh)) &&
13027             number.charAt(number.length - 1) == 'e') {
13028           this.throwError('Invalid exponent');
13029         } else {
13030           break;
13031         }
13032       }
13033       this.index++;
13034     }
13035     this.tokens.push({
13036       index: start,
13037       text: number,
13038       constant: true,
13039       value: Number(number)
13040     });
13041   },
13042
13043   readIdent: function() {
13044     var start = this.index;
13045     while (this.index < this.text.length) {
13046       var ch = this.text.charAt(this.index);
13047       if (!(this.isIdent(ch) || this.isNumber(ch))) {
13048         break;
13049       }
13050       this.index++;
13051     }
13052     this.tokens.push({
13053       index: start,
13054       text: this.text.slice(start, this.index),
13055       identifier: true
13056     });
13057   },
13058
13059   readString: function(quote) {
13060     var start = this.index;
13061     this.index++;
13062     var string = '';
13063     var rawString = quote;
13064     var escape = false;
13065     while (this.index < this.text.length) {
13066       var ch = this.text.charAt(this.index);
13067       rawString += ch;
13068       if (escape) {
13069         if (ch === 'u') {
13070           var hex = this.text.substring(this.index + 1, this.index + 5);
13071           if (!hex.match(/[\da-f]{4}/i)) {
13072             this.throwError('Invalid unicode escape [\\u' + hex + ']');
13073           }
13074           this.index += 4;
13075           string += String.fromCharCode(parseInt(hex, 16));
13076         } else {
13077           var rep = ESCAPE[ch];
13078           string = string + (rep || ch);
13079         }
13080         escape = false;
13081       } else if (ch === '\\') {
13082         escape = true;
13083       } else if (ch === quote) {
13084         this.index++;
13085         this.tokens.push({
13086           index: start,
13087           text: rawString,
13088           constant: true,
13089           value: string
13090         });
13091         return;
13092       } else {
13093         string += ch;
13094       }
13095       this.index++;
13096     }
13097     this.throwError('Unterminated quote', start);
13098   }
13099 };
13100
13101 var AST = function(lexer, options) {
13102   this.lexer = lexer;
13103   this.options = options;
13104 };
13105
13106 AST.Program = 'Program';
13107 AST.ExpressionStatement = 'ExpressionStatement';
13108 AST.AssignmentExpression = 'AssignmentExpression';
13109 AST.ConditionalExpression = 'ConditionalExpression';
13110 AST.LogicalExpression = 'LogicalExpression';
13111 AST.BinaryExpression = 'BinaryExpression';
13112 AST.UnaryExpression = 'UnaryExpression';
13113 AST.CallExpression = 'CallExpression';
13114 AST.MemberExpression = 'MemberExpression';
13115 AST.Identifier = 'Identifier';
13116 AST.Literal = 'Literal';
13117 AST.ArrayExpression = 'ArrayExpression';
13118 AST.Property = 'Property';
13119 AST.ObjectExpression = 'ObjectExpression';
13120 AST.ThisExpression = 'ThisExpression';
13121
13122 // Internal use only
13123 AST.NGValueParameter = 'NGValueParameter';
13124
13125 AST.prototype = {
13126   ast: function(text) {
13127     this.text = text;
13128     this.tokens = this.lexer.lex(text);
13129
13130     var value = this.program();
13131
13132     if (this.tokens.length !== 0) {
13133       this.throwError('is an unexpected token', this.tokens[0]);
13134     }
13135
13136     return value;
13137   },
13138
13139   program: function() {
13140     var body = [];
13141     while (true) {
13142       if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
13143         body.push(this.expressionStatement());
13144       if (!this.expect(';')) {
13145         return { type: AST.Program, body: body};
13146       }
13147     }
13148   },
13149
13150   expressionStatement: function() {
13151     return { type: AST.ExpressionStatement, expression: this.filterChain() };
13152   },
13153
13154   filterChain: function() {
13155     var left = this.expression();
13156     var token;
13157     while ((token = this.expect('|'))) {
13158       left = this.filter(left);
13159     }
13160     return left;
13161   },
13162
13163   expression: function() {
13164     return this.assignment();
13165   },
13166
13167   assignment: function() {
13168     var result = this.ternary();
13169     if (this.expect('=')) {
13170       result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
13171     }
13172     return result;
13173   },
13174
13175   ternary: function() {
13176     var test = this.logicalOR();
13177     var alternate;
13178     var consequent;
13179     if (this.expect('?')) {
13180       alternate = this.expression();
13181       if (this.consume(':')) {
13182         consequent = this.expression();
13183         return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
13184       }
13185     }
13186     return test;
13187   },
13188
13189   logicalOR: function() {
13190     var left = this.logicalAND();
13191     while (this.expect('||')) {
13192       left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
13193     }
13194     return left;
13195   },
13196
13197   logicalAND: function() {
13198     var left = this.equality();
13199     while (this.expect('&&')) {
13200       left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
13201     }
13202     return left;
13203   },
13204
13205   equality: function() {
13206     var left = this.relational();
13207     var token;
13208     while ((token = this.expect('==','!=','===','!=='))) {
13209       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
13210     }
13211     return left;
13212   },
13213
13214   relational: function() {
13215     var left = this.additive();
13216     var token;
13217     while ((token = this.expect('<', '>', '<=', '>='))) {
13218       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
13219     }
13220     return left;
13221   },
13222
13223   additive: function() {
13224     var left = this.multiplicative();
13225     var token;
13226     while ((token = this.expect('+','-'))) {
13227       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
13228     }
13229     return left;
13230   },
13231
13232   multiplicative: function() {
13233     var left = this.unary();
13234     var token;
13235     while ((token = this.expect('*','/','%'))) {
13236       left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
13237     }
13238     return left;
13239   },
13240
13241   unary: function() {
13242     var token;
13243     if ((token = this.expect('+', '-', '!'))) {
13244       return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
13245     } else {
13246       return this.primary();
13247     }
13248   },
13249
13250   primary: function() {
13251     var primary;
13252     if (this.expect('(')) {
13253       primary = this.filterChain();
13254       this.consume(')');
13255     } else if (this.expect('[')) {
13256       primary = this.arrayDeclaration();
13257     } else if (this.expect('{')) {
13258       primary = this.object();
13259     } else if (this.constants.hasOwnProperty(this.peek().text)) {
13260       primary = copy(this.constants[this.consume().text]);
13261     } else if (this.peek().identifier) {
13262       primary = this.identifier();
13263     } else if (this.peek().constant) {
13264       primary = this.constant();
13265     } else {
13266       this.throwError('not a primary expression', this.peek());
13267     }
13268
13269     var next;
13270     while ((next = this.expect('(', '[', '.'))) {
13271       if (next.text === '(') {
13272         primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13273         this.consume(')');
13274       } else if (next.text === '[') {
13275         primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13276         this.consume(']');
13277       } else if (next.text === '.') {
13278         primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13279       } else {
13280         this.throwError('IMPOSSIBLE');
13281       }
13282     }
13283     return primary;
13284   },
13285
13286   filter: function(baseExpression) {
13287     var args = [baseExpression];
13288     var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13289
13290     while (this.expect(':')) {
13291       args.push(this.expression());
13292     }
13293
13294     return result;
13295   },
13296
13297   parseArguments: function() {
13298     var args = [];
13299     if (this.peekToken().text !== ')') {
13300       do {
13301         args.push(this.expression());
13302       } while (this.expect(','));
13303     }
13304     return args;
13305   },
13306
13307   identifier: function() {
13308     var token = this.consume();
13309     if (!token.identifier) {
13310       this.throwError('is not a valid identifier', token);
13311     }
13312     return { type: AST.Identifier, name: token.text };
13313   },
13314
13315   constant: function() {
13316     // TODO check that it is a constant
13317     return { type: AST.Literal, value: this.consume().value };
13318   },
13319
13320   arrayDeclaration: function() {
13321     var elements = [];
13322     if (this.peekToken().text !== ']') {
13323       do {
13324         if (this.peek(']')) {
13325           // Support trailing commas per ES5.1.
13326           break;
13327         }
13328         elements.push(this.expression());
13329       } while (this.expect(','));
13330     }
13331     this.consume(']');
13332
13333     return { type: AST.ArrayExpression, elements: elements };
13334   },
13335
13336   object: function() {
13337     var properties = [], property;
13338     if (this.peekToken().text !== '}') {
13339       do {
13340         if (this.peek('}')) {
13341           // Support trailing commas per ES5.1.
13342           break;
13343         }
13344         property = {type: AST.Property, kind: 'init'};
13345         if (this.peek().constant) {
13346           property.key = this.constant();
13347         } else if (this.peek().identifier) {
13348           property.key = this.identifier();
13349         } else {
13350           this.throwError("invalid key", this.peek());
13351         }
13352         this.consume(':');
13353         property.value = this.expression();
13354         properties.push(property);
13355       } while (this.expect(','));
13356     }
13357     this.consume('}');
13358
13359     return {type: AST.ObjectExpression, properties: properties };
13360   },
13361
13362   throwError: function(msg, token) {
13363     throw $parseMinErr('syntax',
13364         'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13365           token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13366   },
13367
13368   consume: function(e1) {
13369     if (this.tokens.length === 0) {
13370       throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13371     }
13372
13373     var token = this.expect(e1);
13374     if (!token) {
13375       this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13376     }
13377     return token;
13378   },
13379
13380   peekToken: function() {
13381     if (this.tokens.length === 0) {
13382       throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13383     }
13384     return this.tokens[0];
13385   },
13386
13387   peek: function(e1, e2, e3, e4) {
13388     return this.peekAhead(0, e1, e2, e3, e4);
13389   },
13390
13391   peekAhead: function(i, e1, e2, e3, e4) {
13392     if (this.tokens.length > i) {
13393       var token = this.tokens[i];
13394       var t = token.text;
13395       if (t === e1 || t === e2 || t === e3 || t === e4 ||
13396           (!e1 && !e2 && !e3 && !e4)) {
13397         return token;
13398       }
13399     }
13400     return false;
13401   },
13402
13403   expect: function(e1, e2, e3, e4) {
13404     var token = this.peek(e1, e2, e3, e4);
13405     if (token) {
13406       this.tokens.shift();
13407       return token;
13408     }
13409     return false;
13410   },
13411
13412
13413   /* `undefined` is not a constant, it is an identifier,
13414    * but using it as an identifier is not supported
13415    */
13416   constants: {
13417     'true': { type: AST.Literal, value: true },
13418     'false': { type: AST.Literal, value: false },
13419     'null': { type: AST.Literal, value: null },
13420     'undefined': {type: AST.Literal, value: undefined },
13421     'this': {type: AST.ThisExpression }
13422   }
13423 };
13424
13425 function ifDefined(v, d) {
13426   return typeof v !== 'undefined' ? v : d;
13427 }
13428
13429 function plusFn(l, r) {
13430   if (typeof l === 'undefined') return r;
13431   if (typeof r === 'undefined') return l;
13432   return l + r;
13433 }
13434
13435 function isStateless($filter, filterName) {
13436   var fn = $filter(filterName);
13437   return !fn.$stateful;
13438 }
13439
13440 function findConstantAndWatchExpressions(ast, $filter) {
13441   var allConstants;
13442   var argsToWatch;
13443   switch (ast.type) {
13444   case AST.Program:
13445     allConstants = true;
13446     forEach(ast.body, function(expr) {
13447       findConstantAndWatchExpressions(expr.expression, $filter);
13448       allConstants = allConstants && expr.expression.constant;
13449     });
13450     ast.constant = allConstants;
13451     break;
13452   case AST.Literal:
13453     ast.constant = true;
13454     ast.toWatch = [];
13455     break;
13456   case AST.UnaryExpression:
13457     findConstantAndWatchExpressions(ast.argument, $filter);
13458     ast.constant = ast.argument.constant;
13459     ast.toWatch = ast.argument.toWatch;
13460     break;
13461   case AST.BinaryExpression:
13462     findConstantAndWatchExpressions(ast.left, $filter);
13463     findConstantAndWatchExpressions(ast.right, $filter);
13464     ast.constant = ast.left.constant && ast.right.constant;
13465     ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13466     break;
13467   case AST.LogicalExpression:
13468     findConstantAndWatchExpressions(ast.left, $filter);
13469     findConstantAndWatchExpressions(ast.right, $filter);
13470     ast.constant = ast.left.constant && ast.right.constant;
13471     ast.toWatch = ast.constant ? [] : [ast];
13472     break;
13473   case AST.ConditionalExpression:
13474     findConstantAndWatchExpressions(ast.test, $filter);
13475     findConstantAndWatchExpressions(ast.alternate, $filter);
13476     findConstantAndWatchExpressions(ast.consequent, $filter);
13477     ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13478     ast.toWatch = ast.constant ? [] : [ast];
13479     break;
13480   case AST.Identifier:
13481     ast.constant = false;
13482     ast.toWatch = [ast];
13483     break;
13484   case AST.MemberExpression:
13485     findConstantAndWatchExpressions(ast.object, $filter);
13486     if (ast.computed) {
13487       findConstantAndWatchExpressions(ast.property, $filter);
13488     }
13489     ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13490     ast.toWatch = [ast];
13491     break;
13492   case AST.CallExpression:
13493     allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13494     argsToWatch = [];
13495     forEach(ast.arguments, function(expr) {
13496       findConstantAndWatchExpressions(expr, $filter);
13497       allConstants = allConstants && expr.constant;
13498       if (!expr.constant) {
13499         argsToWatch.push.apply(argsToWatch, expr.toWatch);
13500       }
13501     });
13502     ast.constant = allConstants;
13503     ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13504     break;
13505   case AST.AssignmentExpression:
13506     findConstantAndWatchExpressions(ast.left, $filter);
13507     findConstantAndWatchExpressions(ast.right, $filter);
13508     ast.constant = ast.left.constant && ast.right.constant;
13509     ast.toWatch = [ast];
13510     break;
13511   case AST.ArrayExpression:
13512     allConstants = true;
13513     argsToWatch = [];
13514     forEach(ast.elements, function(expr) {
13515       findConstantAndWatchExpressions(expr, $filter);
13516       allConstants = allConstants && expr.constant;
13517       if (!expr.constant) {
13518         argsToWatch.push.apply(argsToWatch, expr.toWatch);
13519       }
13520     });
13521     ast.constant = allConstants;
13522     ast.toWatch = argsToWatch;
13523     break;
13524   case AST.ObjectExpression:
13525     allConstants = true;
13526     argsToWatch = [];
13527     forEach(ast.properties, function(property) {
13528       findConstantAndWatchExpressions(property.value, $filter);
13529       allConstants = allConstants && property.value.constant;
13530       if (!property.value.constant) {
13531         argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13532       }
13533     });
13534     ast.constant = allConstants;
13535     ast.toWatch = argsToWatch;
13536     break;
13537   case AST.ThisExpression:
13538     ast.constant = false;
13539     ast.toWatch = [];
13540     break;
13541   }
13542 }
13543
13544 function getInputs(body) {
13545   if (body.length != 1) return;
13546   var lastExpression = body[0].expression;
13547   var candidate = lastExpression.toWatch;
13548   if (candidate.length !== 1) return candidate;
13549   return candidate[0] !== lastExpression ? candidate : undefined;
13550 }
13551
13552 function isAssignable(ast) {
13553   return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13554 }
13555
13556 function assignableAST(ast) {
13557   if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13558     return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13559   }
13560 }
13561
13562 function isLiteral(ast) {
13563   return ast.body.length === 0 ||
13564       ast.body.length === 1 && (
13565       ast.body[0].expression.type === AST.Literal ||
13566       ast.body[0].expression.type === AST.ArrayExpression ||
13567       ast.body[0].expression.type === AST.ObjectExpression);
13568 }
13569
13570 function isConstant(ast) {
13571   return ast.constant;
13572 }
13573
13574 function ASTCompiler(astBuilder, $filter) {
13575   this.astBuilder = astBuilder;
13576   this.$filter = $filter;
13577 }
13578
13579 ASTCompiler.prototype = {
13580   compile: function(expression, expensiveChecks) {
13581     var self = this;
13582     var ast = this.astBuilder.ast(expression);
13583     this.state = {
13584       nextId: 0,
13585       filters: {},
13586       expensiveChecks: expensiveChecks,
13587       fn: {vars: [], body: [], own: {}},
13588       assign: {vars: [], body: [], own: {}},
13589       inputs: []
13590     };
13591     findConstantAndWatchExpressions(ast, self.$filter);
13592     var extra = '';
13593     var assignable;
13594     this.stage = 'assign';
13595     if ((assignable = assignableAST(ast))) {
13596       this.state.computing = 'assign';
13597       var result = this.nextId();
13598       this.recurse(assignable, result);
13599       this.return_(result);
13600       extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13601     }
13602     var toWatch = getInputs(ast.body);
13603     self.stage = 'inputs';
13604     forEach(toWatch, function(watch, key) {
13605       var fnKey = 'fn' + key;
13606       self.state[fnKey] = {vars: [], body: [], own: {}};
13607       self.state.computing = fnKey;
13608       var intoId = self.nextId();
13609       self.recurse(watch, intoId);
13610       self.return_(intoId);
13611       self.state.inputs.push(fnKey);
13612       watch.watchId = key;
13613     });
13614     this.state.computing = 'fn';
13615     this.stage = 'main';
13616     this.recurse(ast);
13617     var fnString =
13618       // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13619       // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13620       '"' + this.USE + ' ' + this.STRICT + '";\n' +
13621       this.filterPrefix() +
13622       'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13623       extra +
13624       this.watchFns() +
13625       'return fn;';
13626
13627     /* jshint -W054 */
13628     var fn = (new Function('$filter',
13629         'ensureSafeMemberName',
13630         'ensureSafeObject',
13631         'ensureSafeFunction',
13632         'getStringValue',
13633         'ensureSafeAssignContext',
13634         'ifDefined',
13635         'plus',
13636         'text',
13637         fnString))(
13638           this.$filter,
13639           ensureSafeMemberName,
13640           ensureSafeObject,
13641           ensureSafeFunction,
13642           getStringValue,
13643           ensureSafeAssignContext,
13644           ifDefined,
13645           plusFn,
13646           expression);
13647     /* jshint +W054 */
13648     this.state = this.stage = undefined;
13649     fn.literal = isLiteral(ast);
13650     fn.constant = isConstant(ast);
13651     return fn;
13652   },
13653
13654   USE: 'use',
13655
13656   STRICT: 'strict',
13657
13658   watchFns: function() {
13659     var result = [];
13660     var fns = this.state.inputs;
13661     var self = this;
13662     forEach(fns, function(name) {
13663       result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13664     });
13665     if (fns.length) {
13666       result.push('fn.inputs=[' + fns.join(',') + '];');
13667     }
13668     return result.join('');
13669   },
13670
13671   generateFunction: function(name, params) {
13672     return 'function(' + params + '){' +
13673         this.varsPrefix(name) +
13674         this.body(name) +
13675         '};';
13676   },
13677
13678   filterPrefix: function() {
13679     var parts = [];
13680     var self = this;
13681     forEach(this.state.filters, function(id, filter) {
13682       parts.push(id + '=$filter(' + self.escape(filter) + ')');
13683     });
13684     if (parts.length) return 'var ' + parts.join(',') + ';';
13685     return '';
13686   },
13687
13688   varsPrefix: function(section) {
13689     return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13690   },
13691
13692   body: function(section) {
13693     return this.state[section].body.join('');
13694   },
13695
13696   recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13697     var left, right, self = this, args, expression;
13698     recursionFn = recursionFn || noop;
13699     if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13700       intoId = intoId || this.nextId();
13701       this.if_('i',
13702         this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13703         this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13704       );
13705       return;
13706     }
13707     switch (ast.type) {
13708     case AST.Program:
13709       forEach(ast.body, function(expression, pos) {
13710         self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13711         if (pos !== ast.body.length - 1) {
13712           self.current().body.push(right, ';');
13713         } else {
13714           self.return_(right);
13715         }
13716       });
13717       break;
13718     case AST.Literal:
13719       expression = this.escape(ast.value);
13720       this.assign(intoId, expression);
13721       recursionFn(expression);
13722       break;
13723     case AST.UnaryExpression:
13724       this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13725       expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13726       this.assign(intoId, expression);
13727       recursionFn(expression);
13728       break;
13729     case AST.BinaryExpression:
13730       this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13731       this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13732       if (ast.operator === '+') {
13733         expression = this.plus(left, right);
13734       } else if (ast.operator === '-') {
13735         expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13736       } else {
13737         expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13738       }
13739       this.assign(intoId, expression);
13740       recursionFn(expression);
13741       break;
13742     case AST.LogicalExpression:
13743       intoId = intoId || this.nextId();
13744       self.recurse(ast.left, intoId);
13745       self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13746       recursionFn(intoId);
13747       break;
13748     case AST.ConditionalExpression:
13749       intoId = intoId || this.nextId();
13750       self.recurse(ast.test, intoId);
13751       self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13752       recursionFn(intoId);
13753       break;
13754     case AST.Identifier:
13755       intoId = intoId || this.nextId();
13756       if (nameId) {
13757         nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13758         nameId.computed = false;
13759         nameId.name = ast.name;
13760       }
13761       ensureSafeMemberName(ast.name);
13762       self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13763         function() {
13764           self.if_(self.stage === 'inputs' || 's', function() {
13765             if (create && create !== 1) {
13766               self.if_(
13767                 self.not(self.nonComputedMember('s', ast.name)),
13768                 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13769             }
13770             self.assign(intoId, self.nonComputedMember('s', ast.name));
13771           });
13772         }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13773         );
13774       if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13775         self.addEnsureSafeObject(intoId);
13776       }
13777       recursionFn(intoId);
13778       break;
13779     case AST.MemberExpression:
13780       left = nameId && (nameId.context = this.nextId()) || this.nextId();
13781       intoId = intoId || this.nextId();
13782       self.recurse(ast.object, left, undefined, function() {
13783         self.if_(self.notNull(left), function() {
13784           if (create && create !== 1) {
13785             self.addEnsureSafeAssignContext(left);
13786           }
13787           if (ast.computed) {
13788             right = self.nextId();
13789             self.recurse(ast.property, right);
13790             self.getStringValue(right);
13791             self.addEnsureSafeMemberName(right);
13792             if (create && create !== 1) {
13793               self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13794             }
13795             expression = self.ensureSafeObject(self.computedMember(left, right));
13796             self.assign(intoId, expression);
13797             if (nameId) {
13798               nameId.computed = true;
13799               nameId.name = right;
13800             }
13801           } else {
13802             ensureSafeMemberName(ast.property.name);
13803             if (create && create !== 1) {
13804               self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13805             }
13806             expression = self.nonComputedMember(left, ast.property.name);
13807             if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13808               expression = self.ensureSafeObject(expression);
13809             }
13810             self.assign(intoId, expression);
13811             if (nameId) {
13812               nameId.computed = false;
13813               nameId.name = ast.property.name;
13814             }
13815           }
13816         }, function() {
13817           self.assign(intoId, 'undefined');
13818         });
13819         recursionFn(intoId);
13820       }, !!create);
13821       break;
13822     case AST.CallExpression:
13823       intoId = intoId || this.nextId();
13824       if (ast.filter) {
13825         right = self.filter(ast.callee.name);
13826         args = [];
13827         forEach(ast.arguments, function(expr) {
13828           var argument = self.nextId();
13829           self.recurse(expr, argument);
13830           args.push(argument);
13831         });
13832         expression = right + '(' + args.join(',') + ')';
13833         self.assign(intoId, expression);
13834         recursionFn(intoId);
13835       } else {
13836         right = self.nextId();
13837         left = {};
13838         args = [];
13839         self.recurse(ast.callee, right, left, function() {
13840           self.if_(self.notNull(right), function() {
13841             self.addEnsureSafeFunction(right);
13842             forEach(ast.arguments, function(expr) {
13843               self.recurse(expr, self.nextId(), undefined, function(argument) {
13844                 args.push(self.ensureSafeObject(argument));
13845               });
13846             });
13847             if (left.name) {
13848               if (!self.state.expensiveChecks) {
13849                 self.addEnsureSafeObject(left.context);
13850               }
13851               expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13852             } else {
13853               expression = right + '(' + args.join(',') + ')';
13854             }
13855             expression = self.ensureSafeObject(expression);
13856             self.assign(intoId, expression);
13857           }, function() {
13858             self.assign(intoId, 'undefined');
13859           });
13860           recursionFn(intoId);
13861         });
13862       }
13863       break;
13864     case AST.AssignmentExpression:
13865       right = this.nextId();
13866       left = {};
13867       if (!isAssignable(ast.left)) {
13868         throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
13869       }
13870       this.recurse(ast.left, undefined, left, function() {
13871         self.if_(self.notNull(left.context), function() {
13872           self.recurse(ast.right, right);
13873           self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13874           self.addEnsureSafeAssignContext(left.context);
13875           expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13876           self.assign(intoId, expression);
13877           recursionFn(intoId || expression);
13878         });
13879       }, 1);
13880       break;
13881     case AST.ArrayExpression:
13882       args = [];
13883       forEach(ast.elements, function(expr) {
13884         self.recurse(expr, self.nextId(), undefined, function(argument) {
13885           args.push(argument);
13886         });
13887       });
13888       expression = '[' + args.join(',') + ']';
13889       this.assign(intoId, expression);
13890       recursionFn(expression);
13891       break;
13892     case AST.ObjectExpression:
13893       args = [];
13894       forEach(ast.properties, function(property) {
13895         self.recurse(property.value, self.nextId(), undefined, function(expr) {
13896           args.push(self.escape(
13897               property.key.type === AST.Identifier ? property.key.name :
13898                 ('' + property.key.value)) +
13899               ':' + expr);
13900         });
13901       });
13902       expression = '{' + args.join(',') + '}';
13903       this.assign(intoId, expression);
13904       recursionFn(expression);
13905       break;
13906     case AST.ThisExpression:
13907       this.assign(intoId, 's');
13908       recursionFn('s');
13909       break;
13910     case AST.NGValueParameter:
13911       this.assign(intoId, 'v');
13912       recursionFn('v');
13913       break;
13914     }
13915   },
13916
13917   getHasOwnProperty: function(element, property) {
13918     var key = element + '.' + property;
13919     var own = this.current().own;
13920     if (!own.hasOwnProperty(key)) {
13921       own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13922     }
13923     return own[key];
13924   },
13925
13926   assign: function(id, value) {
13927     if (!id) return;
13928     this.current().body.push(id, '=', value, ';');
13929     return id;
13930   },
13931
13932   filter: function(filterName) {
13933     if (!this.state.filters.hasOwnProperty(filterName)) {
13934       this.state.filters[filterName] = this.nextId(true);
13935     }
13936     return this.state.filters[filterName];
13937   },
13938
13939   ifDefined: function(id, defaultValue) {
13940     return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13941   },
13942
13943   plus: function(left, right) {
13944     return 'plus(' + left + ',' + right + ')';
13945   },
13946
13947   return_: function(id) {
13948     this.current().body.push('return ', id, ';');
13949   },
13950
13951   if_: function(test, alternate, consequent) {
13952     if (test === true) {
13953       alternate();
13954     } else {
13955       var body = this.current().body;
13956       body.push('if(', test, '){');
13957       alternate();
13958       body.push('}');
13959       if (consequent) {
13960         body.push('else{');
13961         consequent();
13962         body.push('}');
13963       }
13964     }
13965   },
13966
13967   not: function(expression) {
13968     return '!(' + expression + ')';
13969   },
13970
13971   notNull: function(expression) {
13972     return expression + '!=null';
13973   },
13974
13975   nonComputedMember: function(left, right) {
13976     return left + '.' + right;
13977   },
13978
13979   computedMember: function(left, right) {
13980     return left + '[' + right + ']';
13981   },
13982
13983   member: function(left, right, computed) {
13984     if (computed) return this.computedMember(left, right);
13985     return this.nonComputedMember(left, right);
13986   },
13987
13988   addEnsureSafeObject: function(item) {
13989     this.current().body.push(this.ensureSafeObject(item), ';');
13990   },
13991
13992   addEnsureSafeMemberName: function(item) {
13993     this.current().body.push(this.ensureSafeMemberName(item), ';');
13994   },
13995
13996   addEnsureSafeFunction: function(item) {
13997     this.current().body.push(this.ensureSafeFunction(item), ';');
13998   },
13999
14000   addEnsureSafeAssignContext: function(item) {
14001     this.current().body.push(this.ensureSafeAssignContext(item), ';');
14002   },
14003
14004   ensureSafeObject: function(item) {
14005     return 'ensureSafeObject(' + item + ',text)';
14006   },
14007
14008   ensureSafeMemberName: function(item) {
14009     return 'ensureSafeMemberName(' + item + ',text)';
14010   },
14011
14012   ensureSafeFunction: function(item) {
14013     return 'ensureSafeFunction(' + item + ',text)';
14014   },
14015
14016   getStringValue: function(item) {
14017     this.assign(item, 'getStringValue(' + item + ',text)');
14018   },
14019
14020   ensureSafeAssignContext: function(item) {
14021     return 'ensureSafeAssignContext(' + item + ',text)';
14022   },
14023
14024   lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
14025     var self = this;
14026     return function() {
14027       self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
14028     };
14029   },
14030
14031   lazyAssign: function(id, value) {
14032     var self = this;
14033     return function() {
14034       self.assign(id, value);
14035     };
14036   },
14037
14038   stringEscapeRegex: /[^ a-zA-Z0-9]/g,
14039
14040   stringEscapeFn: function(c) {
14041     return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
14042   },
14043
14044   escape: function(value) {
14045     if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
14046     if (isNumber(value)) return value.toString();
14047     if (value === true) return 'true';
14048     if (value === false) return 'false';
14049     if (value === null) return 'null';
14050     if (typeof value === 'undefined') return 'undefined';
14051
14052     throw $parseMinErr('esc', 'IMPOSSIBLE');
14053   },
14054
14055   nextId: function(skip, init) {
14056     var id = 'v' + (this.state.nextId++);
14057     if (!skip) {
14058       this.current().vars.push(id + (init ? '=' + init : ''));
14059     }
14060     return id;
14061   },
14062
14063   current: function() {
14064     return this.state[this.state.computing];
14065   }
14066 };
14067
14068
14069 function ASTInterpreter(astBuilder, $filter) {
14070   this.astBuilder = astBuilder;
14071   this.$filter = $filter;
14072 }
14073
14074 ASTInterpreter.prototype = {
14075   compile: function(expression, expensiveChecks) {
14076     var self = this;
14077     var ast = this.astBuilder.ast(expression);
14078     this.expression = expression;
14079     this.expensiveChecks = expensiveChecks;
14080     findConstantAndWatchExpressions(ast, self.$filter);
14081     var assignable;
14082     var assign;
14083     if ((assignable = assignableAST(ast))) {
14084       assign = this.recurse(assignable);
14085     }
14086     var toWatch = getInputs(ast.body);
14087     var inputs;
14088     if (toWatch) {
14089       inputs = [];
14090       forEach(toWatch, function(watch, key) {
14091         var input = self.recurse(watch);
14092         watch.input = input;
14093         inputs.push(input);
14094         watch.watchId = key;
14095       });
14096     }
14097     var expressions = [];
14098     forEach(ast.body, function(expression) {
14099       expressions.push(self.recurse(expression.expression));
14100     });
14101     var fn = ast.body.length === 0 ? function() {} :
14102              ast.body.length === 1 ? expressions[0] :
14103              function(scope, locals) {
14104                var lastValue;
14105                forEach(expressions, function(exp) {
14106                  lastValue = exp(scope, locals);
14107                });
14108                return lastValue;
14109              };
14110     if (assign) {
14111       fn.assign = function(scope, value, locals) {
14112         return assign(scope, locals, value);
14113       };
14114     }
14115     if (inputs) {
14116       fn.inputs = inputs;
14117     }
14118     fn.literal = isLiteral(ast);
14119     fn.constant = isConstant(ast);
14120     return fn;
14121   },
14122
14123   recurse: function(ast, context, create) {
14124     var left, right, self = this, args, expression;
14125     if (ast.input) {
14126       return this.inputs(ast.input, ast.watchId);
14127     }
14128     switch (ast.type) {
14129     case AST.Literal:
14130       return this.value(ast.value, context);
14131     case AST.UnaryExpression:
14132       right = this.recurse(ast.argument);
14133       return this['unary' + ast.operator](right, context);
14134     case AST.BinaryExpression:
14135       left = this.recurse(ast.left);
14136       right = this.recurse(ast.right);
14137       return this['binary' + ast.operator](left, right, context);
14138     case AST.LogicalExpression:
14139       left = this.recurse(ast.left);
14140       right = this.recurse(ast.right);
14141       return this['binary' + ast.operator](left, right, context);
14142     case AST.ConditionalExpression:
14143       return this['ternary?:'](
14144         this.recurse(ast.test),
14145         this.recurse(ast.alternate),
14146         this.recurse(ast.consequent),
14147         context
14148       );
14149     case AST.Identifier:
14150       ensureSafeMemberName(ast.name, self.expression);
14151       return self.identifier(ast.name,
14152                              self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
14153                              context, create, self.expression);
14154     case AST.MemberExpression:
14155       left = this.recurse(ast.object, false, !!create);
14156       if (!ast.computed) {
14157         ensureSafeMemberName(ast.property.name, self.expression);
14158         right = ast.property.name;
14159       }
14160       if (ast.computed) right = this.recurse(ast.property);
14161       return ast.computed ?
14162         this.computedMember(left, right, context, create, self.expression) :
14163         this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
14164     case AST.CallExpression:
14165       args = [];
14166       forEach(ast.arguments, function(expr) {
14167         args.push(self.recurse(expr));
14168       });
14169       if (ast.filter) right = this.$filter(ast.callee.name);
14170       if (!ast.filter) right = this.recurse(ast.callee, true);
14171       return ast.filter ?
14172         function(scope, locals, assign, inputs) {
14173           var values = [];
14174           for (var i = 0; i < args.length; ++i) {
14175             values.push(args[i](scope, locals, assign, inputs));
14176           }
14177           var value = right.apply(undefined, values, inputs);
14178           return context ? {context: undefined, name: undefined, value: value} : value;
14179         } :
14180         function(scope, locals, assign, inputs) {
14181           var rhs = right(scope, locals, assign, inputs);
14182           var value;
14183           if (rhs.value != null) {
14184             ensureSafeObject(rhs.context, self.expression);
14185             ensureSafeFunction(rhs.value, self.expression);
14186             var values = [];
14187             for (var i = 0; i < args.length; ++i) {
14188               values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
14189             }
14190             value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
14191           }
14192           return context ? {value: value} : value;
14193         };
14194     case AST.AssignmentExpression:
14195       left = this.recurse(ast.left, true, 1);
14196       right = this.recurse(ast.right);
14197       return function(scope, locals, assign, inputs) {
14198         var lhs = left(scope, locals, assign, inputs);
14199         var rhs = right(scope, locals, assign, inputs);
14200         ensureSafeObject(lhs.value, self.expression);
14201         ensureSafeAssignContext(lhs.context);
14202         lhs.context[lhs.name] = rhs;
14203         return context ? {value: rhs} : rhs;
14204       };
14205     case AST.ArrayExpression:
14206       args = [];
14207       forEach(ast.elements, function(expr) {
14208         args.push(self.recurse(expr));
14209       });
14210       return function(scope, locals, assign, inputs) {
14211         var value = [];
14212         for (var i = 0; i < args.length; ++i) {
14213           value.push(args[i](scope, locals, assign, inputs));
14214         }
14215         return context ? {value: value} : value;
14216       };
14217     case AST.ObjectExpression:
14218       args = [];
14219       forEach(ast.properties, function(property) {
14220         args.push({key: property.key.type === AST.Identifier ?
14221                         property.key.name :
14222                         ('' + property.key.value),
14223                    value: self.recurse(property.value)
14224         });
14225       });
14226       return function(scope, locals, assign, inputs) {
14227         var value = {};
14228         for (var i = 0; i < args.length; ++i) {
14229           value[args[i].key] = args[i].value(scope, locals, assign, inputs);
14230         }
14231         return context ? {value: value} : value;
14232       };
14233     case AST.ThisExpression:
14234       return function(scope) {
14235         return context ? {value: scope} : scope;
14236       };
14237     case AST.NGValueParameter:
14238       return function(scope, locals, assign, inputs) {
14239         return context ? {value: assign} : assign;
14240       };
14241     }
14242   },
14243
14244   'unary+': function(argument, context) {
14245     return function(scope, locals, assign, inputs) {
14246       var arg = argument(scope, locals, assign, inputs);
14247       if (isDefined(arg)) {
14248         arg = +arg;
14249       } else {
14250         arg = 0;
14251       }
14252       return context ? {value: arg} : arg;
14253     };
14254   },
14255   'unary-': function(argument, context) {
14256     return function(scope, locals, assign, inputs) {
14257       var arg = argument(scope, locals, assign, inputs);
14258       if (isDefined(arg)) {
14259         arg = -arg;
14260       } else {
14261         arg = 0;
14262       }
14263       return context ? {value: arg} : arg;
14264     };
14265   },
14266   'unary!': function(argument, context) {
14267     return function(scope, locals, assign, inputs) {
14268       var arg = !argument(scope, locals, assign, inputs);
14269       return context ? {value: arg} : arg;
14270     };
14271   },
14272   'binary+': function(left, right, context) {
14273     return function(scope, locals, assign, inputs) {
14274       var lhs = left(scope, locals, assign, inputs);
14275       var rhs = right(scope, locals, assign, inputs);
14276       var arg = plusFn(lhs, rhs);
14277       return context ? {value: arg} : arg;
14278     };
14279   },
14280   'binary-': function(left, right, context) {
14281     return function(scope, locals, assign, inputs) {
14282       var lhs = left(scope, locals, assign, inputs);
14283       var rhs = right(scope, locals, assign, inputs);
14284       var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14285       return context ? {value: arg} : arg;
14286     };
14287   },
14288   'binary*': function(left, right, context) {
14289     return function(scope, locals, assign, inputs) {
14290       var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14291       return context ? {value: arg} : arg;
14292     };
14293   },
14294   'binary/': function(left, right, context) {
14295     return function(scope, locals, assign, inputs) {
14296       var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14297       return context ? {value: arg} : arg;
14298     };
14299   },
14300   'binary%': function(left, right, context) {
14301     return function(scope, locals, assign, inputs) {
14302       var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14303       return context ? {value: arg} : arg;
14304     };
14305   },
14306   'binary===': function(left, right, context) {
14307     return function(scope, locals, assign, inputs) {
14308       var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14309       return context ? {value: arg} : arg;
14310     };
14311   },
14312   'binary!==': function(left, right, context) {
14313     return function(scope, locals, assign, inputs) {
14314       var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14315       return context ? {value: arg} : arg;
14316     };
14317   },
14318   'binary==': function(left, right, context) {
14319     return function(scope, locals, assign, inputs) {
14320       var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14321       return context ? {value: arg} : arg;
14322     };
14323   },
14324   'binary!=': function(left, right, context) {
14325     return function(scope, locals, assign, inputs) {
14326       var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14327       return context ? {value: arg} : arg;
14328     };
14329   },
14330   'binary<': function(left, right, context) {
14331     return function(scope, locals, assign, inputs) {
14332       var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14333       return context ? {value: arg} : arg;
14334     };
14335   },
14336   'binary>': function(left, right, context) {
14337     return function(scope, locals, assign, inputs) {
14338       var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14339       return context ? {value: arg} : arg;
14340     };
14341   },
14342   'binary<=': function(left, right, context) {
14343     return function(scope, locals, assign, inputs) {
14344       var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14345       return context ? {value: arg} : arg;
14346     };
14347   },
14348   'binary>=': function(left, right, context) {
14349     return function(scope, locals, assign, inputs) {
14350       var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14351       return context ? {value: arg} : arg;
14352     };
14353   },
14354   'binary&&': function(left, right, context) {
14355     return function(scope, locals, assign, inputs) {
14356       var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14357       return context ? {value: arg} : arg;
14358     };
14359   },
14360   'binary||': function(left, right, context) {
14361     return function(scope, locals, assign, inputs) {
14362       var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14363       return context ? {value: arg} : arg;
14364     };
14365   },
14366   'ternary?:': function(test, alternate, consequent, context) {
14367     return function(scope, locals, assign, inputs) {
14368       var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14369       return context ? {value: arg} : arg;
14370     };
14371   },
14372   value: function(value, context) {
14373     return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14374   },
14375   identifier: function(name, expensiveChecks, context, create, expression) {
14376     return function(scope, locals, assign, inputs) {
14377       var base = locals && (name in locals) ? locals : scope;
14378       if (create && create !== 1 && base && !(base[name])) {
14379         base[name] = {};
14380       }
14381       var value = base ? base[name] : undefined;
14382       if (expensiveChecks) {
14383         ensureSafeObject(value, expression);
14384       }
14385       if (context) {
14386         return {context: base, name: name, value: value};
14387       } else {
14388         return value;
14389       }
14390     };
14391   },
14392   computedMember: function(left, right, context, create, expression) {
14393     return function(scope, locals, assign, inputs) {
14394       var lhs = left(scope, locals, assign, inputs);
14395       var rhs;
14396       var value;
14397       if (lhs != null) {
14398         rhs = right(scope, locals, assign, inputs);
14399         rhs = getStringValue(rhs);
14400         ensureSafeMemberName(rhs, expression);
14401         if (create && create !== 1) {
14402           ensureSafeAssignContext(lhs);
14403           if (lhs && !(lhs[rhs])) {
14404             lhs[rhs] = {};
14405           }
14406         }
14407         value = lhs[rhs];
14408         ensureSafeObject(value, expression);
14409       }
14410       if (context) {
14411         return {context: lhs, name: rhs, value: value};
14412       } else {
14413         return value;
14414       }
14415     };
14416   },
14417   nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14418     return function(scope, locals, assign, inputs) {
14419       var lhs = left(scope, locals, assign, inputs);
14420       if (create && create !== 1) {
14421         ensureSafeAssignContext(lhs);
14422         if (lhs && !(lhs[right])) {
14423           lhs[right] = {};
14424         }
14425       }
14426       var value = lhs != null ? lhs[right] : undefined;
14427       if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14428         ensureSafeObject(value, expression);
14429       }
14430       if (context) {
14431         return {context: lhs, name: right, value: value};
14432       } else {
14433         return value;
14434       }
14435     };
14436   },
14437   inputs: function(input, watchId) {
14438     return function(scope, value, locals, inputs) {
14439       if (inputs) return inputs[watchId];
14440       return input(scope, value, locals);
14441     };
14442   }
14443 };
14444
14445 /**
14446  * @constructor
14447  */
14448 var Parser = function(lexer, $filter, options) {
14449   this.lexer = lexer;
14450   this.$filter = $filter;
14451   this.options = options;
14452   this.ast = new AST(this.lexer);
14453   this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14454                                    new ASTCompiler(this.ast, $filter);
14455 };
14456
14457 Parser.prototype = {
14458   constructor: Parser,
14459
14460   parse: function(text) {
14461     return this.astCompiler.compile(text, this.options.expensiveChecks);
14462   }
14463 };
14464
14465 function isPossiblyDangerousMemberName(name) {
14466   return name == 'constructor';
14467 }
14468
14469 var objectValueOf = Object.prototype.valueOf;
14470
14471 function getValueOf(value) {
14472   return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14473 }
14474
14475 ///////////////////////////////////
14476
14477 /**
14478  * @ngdoc service
14479  * @name $parse
14480  * @kind function
14481  *
14482  * @description
14483  *
14484  * Converts Angular {@link guide/expression expression} into a function.
14485  *
14486  * ```js
14487  *   var getter = $parse('user.name');
14488  *   var setter = getter.assign;
14489  *   var context = {user:{name:'angular'}};
14490  *   var locals = {user:{name:'local'}};
14491  *
14492  *   expect(getter(context)).toEqual('angular');
14493  *   setter(context, 'newValue');
14494  *   expect(context.user.name).toEqual('newValue');
14495  *   expect(getter(context, locals)).toEqual('local');
14496  * ```
14497  *
14498  *
14499  * @param {string} expression String expression to compile.
14500  * @returns {function(context, locals)} a function which represents the compiled expression:
14501  *
14502  *    * `context` – `{object}` – an object against which any expressions embedded in the strings
14503  *      are evaluated against (typically a scope object).
14504  *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
14505  *      `context`.
14506  *
14507  *    The returned function also has the following properties:
14508  *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14509  *        literal.
14510  *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14511  *        constant literals.
14512  *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14513  *        set to a function to change its value on the given context.
14514  *
14515  */
14516
14517
14518 /**
14519  * @ngdoc provider
14520  * @name $parseProvider
14521  *
14522  * @description
14523  * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14524  *  service.
14525  */
14526 function $ParseProvider() {
14527   var cacheDefault = createMap();
14528   var cacheExpensive = createMap();
14529
14530   this.$get = ['$filter', function($filter) {
14531     var noUnsafeEval = csp().noUnsafeEval;
14532     var $parseOptions = {
14533           csp: noUnsafeEval,
14534           expensiveChecks: false
14535         },
14536         $parseOptionsExpensive = {
14537           csp: noUnsafeEval,
14538           expensiveChecks: true
14539         };
14540     var runningChecksEnabled = false;
14541
14542     $parse.$$runningExpensiveChecks = function() {
14543       return runningChecksEnabled;
14544     };
14545
14546     return $parse;
14547
14548     function $parse(exp, interceptorFn, expensiveChecks) {
14549       var parsedExpression, oneTime, cacheKey;
14550
14551       expensiveChecks = expensiveChecks || runningChecksEnabled;
14552
14553       switch (typeof exp) {
14554         case 'string':
14555           exp = exp.trim();
14556           cacheKey = exp;
14557
14558           var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14559           parsedExpression = cache[cacheKey];
14560
14561           if (!parsedExpression) {
14562             if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14563               oneTime = true;
14564               exp = exp.substring(2);
14565             }
14566             var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14567             var lexer = new Lexer(parseOptions);
14568             var parser = new Parser(lexer, $filter, parseOptions);
14569             parsedExpression = parser.parse(exp);
14570             if (parsedExpression.constant) {
14571               parsedExpression.$$watchDelegate = constantWatchDelegate;
14572             } else if (oneTime) {
14573               parsedExpression.$$watchDelegate = parsedExpression.literal ?
14574                   oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14575             } else if (parsedExpression.inputs) {
14576               parsedExpression.$$watchDelegate = inputsWatchDelegate;
14577             }
14578             if (expensiveChecks) {
14579               parsedExpression = expensiveChecksInterceptor(parsedExpression);
14580             }
14581             cache[cacheKey] = parsedExpression;
14582           }
14583           return addInterceptor(parsedExpression, interceptorFn);
14584
14585         case 'function':
14586           return addInterceptor(exp, interceptorFn);
14587
14588         default:
14589           return addInterceptor(noop, interceptorFn);
14590       }
14591     }
14592
14593     function expensiveChecksInterceptor(fn) {
14594       if (!fn) return fn;
14595       expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
14596       expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
14597       expensiveCheckFn.constant = fn.constant;
14598       expensiveCheckFn.literal = fn.literal;
14599       for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
14600         fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
14601       }
14602       expensiveCheckFn.inputs = fn.inputs;
14603
14604       return expensiveCheckFn;
14605
14606       function expensiveCheckFn(scope, locals, assign, inputs) {
14607         var expensiveCheckOldValue = runningChecksEnabled;
14608         runningChecksEnabled = true;
14609         try {
14610           return fn(scope, locals, assign, inputs);
14611         } finally {
14612           runningChecksEnabled = expensiveCheckOldValue;
14613         }
14614       }
14615     }
14616
14617     function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14618
14619       if (newValue == null || oldValueOfValue == null) { // null/undefined
14620         return newValue === oldValueOfValue;
14621       }
14622
14623       if (typeof newValue === 'object') {
14624
14625         // attempt to convert the value to a primitive type
14626         // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14627         //             be cheaply dirty-checked
14628         newValue = getValueOf(newValue);
14629
14630         if (typeof newValue === 'object') {
14631           // objects/arrays are not supported - deep-watching them would be too expensive
14632           return false;
14633         }
14634
14635         // fall-through to the primitive equality check
14636       }
14637
14638       //Primitive or NaN
14639       return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14640     }
14641
14642     function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14643       var inputExpressions = parsedExpression.inputs;
14644       var lastResult;
14645
14646       if (inputExpressions.length === 1) {
14647         var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14648         inputExpressions = inputExpressions[0];
14649         return scope.$watch(function expressionInputWatch(scope) {
14650           var newInputValue = inputExpressions(scope);
14651           if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14652             lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14653             oldInputValueOf = newInputValue && getValueOf(newInputValue);
14654           }
14655           return lastResult;
14656         }, listener, objectEquality, prettyPrintExpression);
14657       }
14658
14659       var oldInputValueOfValues = [];
14660       var oldInputValues = [];
14661       for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14662         oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14663         oldInputValues[i] = null;
14664       }
14665
14666       return scope.$watch(function expressionInputsWatch(scope) {
14667         var changed = false;
14668
14669         for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14670           var newInputValue = inputExpressions[i](scope);
14671           if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14672             oldInputValues[i] = newInputValue;
14673             oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14674           }
14675         }
14676
14677         if (changed) {
14678           lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14679         }
14680
14681         return lastResult;
14682       }, listener, objectEquality, prettyPrintExpression);
14683     }
14684
14685     function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14686       var unwatch, lastValue;
14687       return unwatch = scope.$watch(function oneTimeWatch(scope) {
14688         return parsedExpression(scope);
14689       }, function oneTimeListener(value, old, scope) {
14690         lastValue = value;
14691         if (isFunction(listener)) {
14692           listener.apply(this, arguments);
14693         }
14694         if (isDefined(value)) {
14695           scope.$$postDigest(function() {
14696             if (isDefined(lastValue)) {
14697               unwatch();
14698             }
14699           });
14700         }
14701       }, objectEquality);
14702     }
14703
14704     function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14705       var unwatch, lastValue;
14706       return unwatch = scope.$watch(function oneTimeWatch(scope) {
14707         return parsedExpression(scope);
14708       }, function oneTimeListener(value, old, scope) {
14709         lastValue = value;
14710         if (isFunction(listener)) {
14711           listener.call(this, value, old, scope);
14712         }
14713         if (isAllDefined(value)) {
14714           scope.$$postDigest(function() {
14715             if (isAllDefined(lastValue)) unwatch();
14716           });
14717         }
14718       }, objectEquality);
14719
14720       function isAllDefined(value) {
14721         var allDefined = true;
14722         forEach(value, function(val) {
14723           if (!isDefined(val)) allDefined = false;
14724         });
14725         return allDefined;
14726       }
14727     }
14728
14729     function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14730       var unwatch;
14731       return unwatch = scope.$watch(function constantWatch(scope) {
14732         return parsedExpression(scope);
14733       }, function constantListener(value, old, scope) {
14734         if (isFunction(listener)) {
14735           listener.apply(this, arguments);
14736         }
14737         unwatch();
14738       }, objectEquality);
14739     }
14740
14741     function addInterceptor(parsedExpression, interceptorFn) {
14742       if (!interceptorFn) return parsedExpression;
14743       var watchDelegate = parsedExpression.$$watchDelegate;
14744       var useInputs = false;
14745
14746       var regularWatch =
14747           watchDelegate !== oneTimeLiteralWatchDelegate &&
14748           watchDelegate !== oneTimeWatchDelegate;
14749
14750       var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14751         var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
14752         return interceptorFn(value, scope, locals);
14753       } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14754         var value = parsedExpression(scope, locals, assign, inputs);
14755         var result = interceptorFn(value, scope, locals);
14756         // we only return the interceptor's result if the
14757         // initial value is defined (for bind-once)
14758         return isDefined(value) ? result : value;
14759       };
14760
14761       // Propagate $$watchDelegates other then inputsWatchDelegate
14762       if (parsedExpression.$$watchDelegate &&
14763           parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14764         fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14765       } else if (!interceptorFn.$stateful) {
14766         // If there is an interceptor, but no watchDelegate then treat the interceptor like
14767         // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14768         fn.$$watchDelegate = inputsWatchDelegate;
14769         useInputs = !parsedExpression.inputs;
14770         fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14771       }
14772
14773       return fn;
14774     }
14775   }];
14776 }
14777
14778 /**
14779  * @ngdoc service
14780  * @name $q
14781  * @requires $rootScope
14782  *
14783  * @description
14784  * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14785  * when they are done processing.
14786  *
14787  * This is an implementation of promises/deferred objects inspired by
14788  * [Kris Kowal's Q](https://github.com/kriskowal/q).
14789  *
14790  * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14791  * implementations, and the other which resembles ES6 promises to some degree.
14792  *
14793  * # $q constructor
14794  *
14795  * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14796  * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14797  * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14798  *
14799  * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14800  * available yet.
14801  *
14802  * It can be used like so:
14803  *
14804  * ```js
14805  *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14806  *   // are available in the current lexical scope (they could have been injected or passed in).
14807  *
14808  *   function asyncGreet(name) {
14809  *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
14810  *     return $q(function(resolve, reject) {
14811  *       setTimeout(function() {
14812  *         if (okToGreet(name)) {
14813  *           resolve('Hello, ' + name + '!');
14814  *         } else {
14815  *           reject('Greeting ' + name + ' is not allowed.');
14816  *         }
14817  *       }, 1000);
14818  *     });
14819  *   }
14820  *
14821  *   var promise = asyncGreet('Robin Hood');
14822  *   promise.then(function(greeting) {
14823  *     alert('Success: ' + greeting);
14824  *   }, function(reason) {
14825  *     alert('Failed: ' + reason);
14826  *   });
14827  * ```
14828  *
14829  * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14830  *
14831  * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
14832  *
14833  * However, the more traditional CommonJS-style usage is still available, and documented below.
14834  *
14835  * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14836  * interface for interacting with an object that represents the result of an action that is
14837  * performed asynchronously, and may or may not be finished at any given point in time.
14838  *
14839  * From the perspective of dealing with error handling, deferred and promise APIs are to
14840  * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14841  *
14842  * ```js
14843  *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14844  *   // are available in the current lexical scope (they could have been injected or passed in).
14845  *
14846  *   function asyncGreet(name) {
14847  *     var deferred = $q.defer();
14848  *
14849  *     setTimeout(function() {
14850  *       deferred.notify('About to greet ' + name + '.');
14851  *
14852  *       if (okToGreet(name)) {
14853  *         deferred.resolve('Hello, ' + name + '!');
14854  *       } else {
14855  *         deferred.reject('Greeting ' + name + ' is not allowed.');
14856  *       }
14857  *     }, 1000);
14858  *
14859  *     return deferred.promise;
14860  *   }
14861  *
14862  *   var promise = asyncGreet('Robin Hood');
14863  *   promise.then(function(greeting) {
14864  *     alert('Success: ' + greeting);
14865  *   }, function(reason) {
14866  *     alert('Failed: ' + reason);
14867  *   }, function(update) {
14868  *     alert('Got notification: ' + update);
14869  *   });
14870  * ```
14871  *
14872  * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14873  * comes in the way of guarantees that promise and deferred APIs make, see
14874  * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14875  *
14876  * Additionally the promise api allows for composition that is very hard to do with the
14877  * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14878  * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14879  * section on serial or parallel joining of promises.
14880  *
14881  * # The Deferred API
14882  *
14883  * A new instance of deferred is constructed by calling `$q.defer()`.
14884  *
14885  * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14886  * that can be used for signaling the successful or unsuccessful completion, as well as the status
14887  * of the task.
14888  *
14889  * **Methods**
14890  *
14891  * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14892  *   constructed via `$q.reject`, the promise will be rejected instead.
14893  * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14894  *   resolving it with a rejection constructed via `$q.reject`.
14895  * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14896  *   multiple times before the promise is either resolved or rejected.
14897  *
14898  * **Properties**
14899  *
14900  * - promise – `{Promise}` – promise object associated with this deferred.
14901  *
14902  *
14903  * # The Promise API
14904  *
14905  * A new promise instance is created when a deferred instance is created and can be retrieved by
14906  * calling `deferred.promise`.
14907  *
14908  * The purpose of the promise object is to allow for interested parties to get access to the result
14909  * of the deferred task when it completes.
14910  *
14911  * **Methods**
14912  *
14913  * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14914  *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14915  *   as soon as the result is available. The callbacks are called with a single argument: the result
14916  *   or rejection reason. Additionally, the notify callback may be called zero or more times to
14917  *   provide a progress indication, before the promise is resolved or rejected.
14918  *
14919  *   This method *returns a new promise* which is resolved or rejected via the return value of the
14920  *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14921  *   with the value which is resolved in that promise using
14922  *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14923  *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14924  *   resolved or rejected from the notifyCallback method.
14925  *
14926  * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14927  *
14928  * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14929  *   but to do so without modifying the final value. This is useful to release resources or do some
14930  *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14931  *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14932  *   more information.
14933  *
14934  * # Chaining promises
14935  *
14936  * Because calling the `then` method of a promise returns a new derived promise, it is easily
14937  * possible to create a chain of promises:
14938  *
14939  * ```js
14940  *   promiseB = promiseA.then(function(result) {
14941  *     return result + 1;
14942  *   });
14943  *
14944  *   // promiseB will be resolved immediately after promiseA is resolved and its value
14945  *   // will be the result of promiseA incremented by 1
14946  * ```
14947  *
14948  * It is possible to create chains of any length and since a promise can be resolved with another
14949  * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14950  * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14951  * $http's response interceptors.
14952  *
14953  *
14954  * # Differences between Kris Kowal's Q and $q
14955  *
14956  *  There are two main differences:
14957  *
14958  * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14959  *   mechanism in angular, which means faster propagation of resolution or rejection into your
14960  *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
14961  * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14962  *   all the important functionality needed for common async tasks.
14963  *
14964  *  # Testing
14965  *
14966  *  ```js
14967  *    it('should simulate promise', inject(function($q, $rootScope) {
14968  *      var deferred = $q.defer();
14969  *      var promise = deferred.promise;
14970  *      var resolvedValue;
14971  *
14972  *      promise.then(function(value) { resolvedValue = value; });
14973  *      expect(resolvedValue).toBeUndefined();
14974  *
14975  *      // Simulate resolving of promise
14976  *      deferred.resolve(123);
14977  *      // Note that the 'then' function does not get called synchronously.
14978  *      // This is because we want the promise API to always be async, whether or not
14979  *      // it got called synchronously or asynchronously.
14980  *      expect(resolvedValue).toBeUndefined();
14981  *
14982  *      // Propagate promise resolution to 'then' functions using $apply().
14983  *      $rootScope.$apply();
14984  *      expect(resolvedValue).toEqual(123);
14985  *    }));
14986  *  ```
14987  *
14988  * @param {function(function, function)} resolver Function which is responsible for resolving or
14989  *   rejecting the newly created promise. The first parameter is a function which resolves the
14990  *   promise, the second parameter is a function which rejects the promise.
14991  *
14992  * @returns {Promise} The newly created promise.
14993  */
14994 function $QProvider() {
14995
14996   this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14997     return qFactory(function(callback) {
14998       $rootScope.$evalAsync(callback);
14999     }, $exceptionHandler);
15000   }];
15001 }
15002
15003 function $$QProvider() {
15004   this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
15005     return qFactory(function(callback) {
15006       $browser.defer(callback);
15007     }, $exceptionHandler);
15008   }];
15009 }
15010
15011 /**
15012  * Constructs a promise manager.
15013  *
15014  * @param {function(function)} nextTick Function for executing functions in the next turn.
15015  * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
15016  *     debugging purposes.
15017  * @returns {object} Promise manager.
15018  */
15019 function qFactory(nextTick, exceptionHandler) {
15020   var $qMinErr = minErr('$q', TypeError);
15021   function callOnce(self, resolveFn, rejectFn) {
15022     var called = false;
15023     function wrap(fn) {
15024       return function(value) {
15025         if (called) return;
15026         called = true;
15027         fn.call(self, value);
15028       };
15029     }
15030
15031     return [wrap(resolveFn), wrap(rejectFn)];
15032   }
15033
15034   /**
15035    * @ngdoc method
15036    * @name ng.$q#defer
15037    * @kind function
15038    *
15039    * @description
15040    * Creates a `Deferred` object which represents a task which will finish in the future.
15041    *
15042    * @returns {Deferred} Returns a new instance of deferred.
15043    */
15044   var defer = function() {
15045     return new Deferred();
15046   };
15047
15048   function Promise() {
15049     this.$$state = { status: 0 };
15050   }
15051
15052   extend(Promise.prototype, {
15053     then: function(onFulfilled, onRejected, progressBack) {
15054       if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
15055         return this;
15056       }
15057       var result = new Deferred();
15058
15059       this.$$state.pending = this.$$state.pending || [];
15060       this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
15061       if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
15062
15063       return result.promise;
15064     },
15065
15066     "catch": function(callback) {
15067       return this.then(null, callback);
15068     },
15069
15070     "finally": function(callback, progressBack) {
15071       return this.then(function(value) {
15072         return handleCallback(value, true, callback);
15073       }, function(error) {
15074         return handleCallback(error, false, callback);
15075       }, progressBack);
15076     }
15077   });
15078
15079   //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
15080   function simpleBind(context, fn) {
15081     return function(value) {
15082       fn.call(context, value);
15083     };
15084   }
15085
15086   function processQueue(state) {
15087     var fn, deferred, pending;
15088
15089     pending = state.pending;
15090     state.processScheduled = false;
15091     state.pending = undefined;
15092     for (var i = 0, ii = pending.length; i < ii; ++i) {
15093       deferred = pending[i][0];
15094       fn = pending[i][state.status];
15095       try {
15096         if (isFunction(fn)) {
15097           deferred.resolve(fn(state.value));
15098         } else if (state.status === 1) {
15099           deferred.resolve(state.value);
15100         } else {
15101           deferred.reject(state.value);
15102         }
15103       } catch (e) {
15104         deferred.reject(e);
15105         exceptionHandler(e);
15106       }
15107     }
15108   }
15109
15110   function scheduleProcessQueue(state) {
15111     if (state.processScheduled || !state.pending) return;
15112     state.processScheduled = true;
15113     nextTick(function() { processQueue(state); });
15114   }
15115
15116   function Deferred() {
15117     this.promise = new Promise();
15118     //Necessary to support unbound execution :/
15119     this.resolve = simpleBind(this, this.resolve);
15120     this.reject = simpleBind(this, this.reject);
15121     this.notify = simpleBind(this, this.notify);
15122   }
15123
15124   extend(Deferred.prototype, {
15125     resolve: function(val) {
15126       if (this.promise.$$state.status) return;
15127       if (val === this.promise) {
15128         this.$$reject($qMinErr(
15129           'qcycle',
15130           "Expected promise to be resolved with value other than itself '{0}'",
15131           val));
15132       } else {
15133         this.$$resolve(val);
15134       }
15135
15136     },
15137
15138     $$resolve: function(val) {
15139       var then, fns;
15140
15141       fns = callOnce(this, this.$$resolve, this.$$reject);
15142       try {
15143         if ((isObject(val) || isFunction(val))) then = val && val.then;
15144         if (isFunction(then)) {
15145           this.promise.$$state.status = -1;
15146           then.call(val, fns[0], fns[1], this.notify);
15147         } else {
15148           this.promise.$$state.value = val;
15149           this.promise.$$state.status = 1;
15150           scheduleProcessQueue(this.promise.$$state);
15151         }
15152       } catch (e) {
15153         fns[1](e);
15154         exceptionHandler(e);
15155       }
15156     },
15157
15158     reject: function(reason) {
15159       if (this.promise.$$state.status) return;
15160       this.$$reject(reason);
15161     },
15162
15163     $$reject: function(reason) {
15164       this.promise.$$state.value = reason;
15165       this.promise.$$state.status = 2;
15166       scheduleProcessQueue(this.promise.$$state);
15167     },
15168
15169     notify: function(progress) {
15170       var callbacks = this.promise.$$state.pending;
15171
15172       if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
15173         nextTick(function() {
15174           var callback, result;
15175           for (var i = 0, ii = callbacks.length; i < ii; i++) {
15176             result = callbacks[i][0];
15177             callback = callbacks[i][3];
15178             try {
15179               result.notify(isFunction(callback) ? callback(progress) : progress);
15180             } catch (e) {
15181               exceptionHandler(e);
15182             }
15183           }
15184         });
15185       }
15186     }
15187   });
15188
15189   /**
15190    * @ngdoc method
15191    * @name $q#reject
15192    * @kind function
15193    *
15194    * @description
15195    * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
15196    * used to forward rejection in a chain of promises. If you are dealing with the last promise in
15197    * a promise chain, you don't need to worry about it.
15198    *
15199    * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
15200    * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
15201    * a promise error callback and you want to forward the error to the promise derived from the
15202    * current promise, you have to "rethrow" the error by returning a rejection constructed via
15203    * `reject`.
15204    *
15205    * ```js
15206    *   promiseB = promiseA.then(function(result) {
15207    *     // success: do something and resolve promiseB
15208    *     //          with the old or a new result
15209    *     return result;
15210    *   }, function(reason) {
15211    *     // error: handle the error if possible and
15212    *     //        resolve promiseB with newPromiseOrValue,
15213    *     //        otherwise forward the rejection to promiseB
15214    *     if (canHandle(reason)) {
15215    *      // handle the error and recover
15216    *      return newPromiseOrValue;
15217    *     }
15218    *     return $q.reject(reason);
15219    *   });
15220    * ```
15221    *
15222    * @param {*} reason Constant, message, exception or an object representing the rejection reason.
15223    * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
15224    */
15225   var reject = function(reason) {
15226     var result = new Deferred();
15227     result.reject(reason);
15228     return result.promise;
15229   };
15230
15231   var makePromise = function makePromise(value, resolved) {
15232     var result = new Deferred();
15233     if (resolved) {
15234       result.resolve(value);
15235     } else {
15236       result.reject(value);
15237     }
15238     return result.promise;
15239   };
15240
15241   var handleCallback = function handleCallback(value, isResolved, callback) {
15242     var callbackOutput = null;
15243     try {
15244       if (isFunction(callback)) callbackOutput = callback();
15245     } catch (e) {
15246       return makePromise(e, false);
15247     }
15248     if (isPromiseLike(callbackOutput)) {
15249       return callbackOutput.then(function() {
15250         return makePromise(value, isResolved);
15251       }, function(error) {
15252         return makePromise(error, false);
15253       });
15254     } else {
15255       return makePromise(value, isResolved);
15256     }
15257   };
15258
15259   /**
15260    * @ngdoc method
15261    * @name $q#when
15262    * @kind function
15263    *
15264    * @description
15265    * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
15266    * This is useful when you are dealing with an object that might or might not be a promise, or if
15267    * the promise comes from a source that can't be trusted.
15268    *
15269    * @param {*} value Value or a promise
15270    * @param {Function=} successCallback
15271    * @param {Function=} errorCallback
15272    * @param {Function=} progressCallback
15273    * @returns {Promise} Returns a promise of the passed value or promise
15274    */
15275
15276
15277   var when = function(value, callback, errback, progressBack) {
15278     var result = new Deferred();
15279     result.resolve(value);
15280     return result.promise.then(callback, errback, progressBack);
15281   };
15282
15283   /**
15284    * @ngdoc method
15285    * @name $q#resolve
15286    * @kind function
15287    *
15288    * @description
15289    * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
15290    *
15291    * @param {*} value Value or a promise
15292    * @param {Function=} successCallback
15293    * @param {Function=} errorCallback
15294    * @param {Function=} progressCallback
15295    * @returns {Promise} Returns a promise of the passed value or promise
15296    */
15297   var resolve = when;
15298
15299   /**
15300    * @ngdoc method
15301    * @name $q#all
15302    * @kind function
15303    *
15304    * @description
15305    * Combines multiple promises into a single promise that is resolved when all of the input
15306    * promises are resolved.
15307    *
15308    * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15309    * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15310    *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
15311    *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
15312    *   with the same rejection value.
15313    */
15314
15315   function all(promises) {
15316     var deferred = new Deferred(),
15317         counter = 0,
15318         results = isArray(promises) ? [] : {};
15319
15320     forEach(promises, function(promise, key) {
15321       counter++;
15322       when(promise).then(function(value) {
15323         if (results.hasOwnProperty(key)) return;
15324         results[key] = value;
15325         if (!(--counter)) deferred.resolve(results);
15326       }, function(reason) {
15327         if (results.hasOwnProperty(key)) return;
15328         deferred.reject(reason);
15329       });
15330     });
15331
15332     if (counter === 0) {
15333       deferred.resolve(results);
15334     }
15335
15336     return deferred.promise;
15337   }
15338
15339   var $Q = function Q(resolver) {
15340     if (!isFunction(resolver)) {
15341       throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
15342     }
15343
15344     if (!(this instanceof Q)) {
15345       // More useful when $Q is the Promise itself.
15346       return new Q(resolver);
15347     }
15348
15349     var deferred = new Deferred();
15350
15351     function resolveFn(value) {
15352       deferred.resolve(value);
15353     }
15354
15355     function rejectFn(reason) {
15356       deferred.reject(reason);
15357     }
15358
15359     resolver(resolveFn, rejectFn);
15360
15361     return deferred.promise;
15362   };
15363
15364   $Q.defer = defer;
15365   $Q.reject = reject;
15366   $Q.when = when;
15367   $Q.resolve = resolve;
15368   $Q.all = all;
15369
15370   return $Q;
15371 }
15372
15373 function $$RAFProvider() { //rAF
15374   this.$get = ['$window', '$timeout', function($window, $timeout) {
15375     var requestAnimationFrame = $window.requestAnimationFrame ||
15376                                 $window.webkitRequestAnimationFrame;
15377
15378     var cancelAnimationFrame = $window.cancelAnimationFrame ||
15379                                $window.webkitCancelAnimationFrame ||
15380                                $window.webkitCancelRequestAnimationFrame;
15381
15382     var rafSupported = !!requestAnimationFrame;
15383     var raf = rafSupported
15384       ? function(fn) {
15385           var id = requestAnimationFrame(fn);
15386           return function() {
15387             cancelAnimationFrame(id);
15388           };
15389         }
15390       : function(fn) {
15391           var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15392           return function() {
15393             $timeout.cancel(timer);
15394           };
15395         };
15396
15397     raf.supported = rafSupported;
15398
15399     return raf;
15400   }];
15401 }
15402
15403 /**
15404  * DESIGN NOTES
15405  *
15406  * The design decisions behind the scope are heavily favored for speed and memory consumption.
15407  *
15408  * The typical use of scope is to watch the expressions, which most of the time return the same
15409  * value as last time so we optimize the operation.
15410  *
15411  * Closures construction is expensive in terms of speed as well as memory:
15412  *   - No closures, instead use prototypical inheritance for API
15413  *   - Internal state needs to be stored on scope directly, which means that private state is
15414  *     exposed as $$____ properties
15415  *
15416  * Loop operations are optimized by using while(count--) { ... }
15417  *   - This means that in order to keep the same order of execution as addition we have to add
15418  *     items to the array at the beginning (unshift) instead of at the end (push)
15419  *
15420  * Child scopes are created and removed often
15421  *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
15422  *
15423  * There are fewer watches than observers. This is why you don't want the observer to be implemented
15424  * in the same way as watch. Watch requires return of the initialization function which is expensive
15425  * to construct.
15426  */
15427
15428
15429 /**
15430  * @ngdoc provider
15431  * @name $rootScopeProvider
15432  * @description
15433  *
15434  * Provider for the $rootScope service.
15435  */
15436
15437 /**
15438  * @ngdoc method
15439  * @name $rootScopeProvider#digestTtl
15440  * @description
15441  *
15442  * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15443  * assuming that the model is unstable.
15444  *
15445  * The current default is 10 iterations.
15446  *
15447  * In complex applications it's possible that the dependencies between `$watch`s will result in
15448  * several digest iterations. However if an application needs more than the default 10 digest
15449  * iterations for its model to stabilize then you should investigate what is causing the model to
15450  * continuously change during the digest.
15451  *
15452  * Increasing the TTL could have performance implications, so you should not change it without
15453  * proper justification.
15454  *
15455  * @param {number} limit The number of digest iterations.
15456  */
15457
15458
15459 /**
15460  * @ngdoc service
15461  * @name $rootScope
15462  * @description
15463  *
15464  * Every application has a single root {@link ng.$rootScope.Scope scope}.
15465  * All other scopes are descendant scopes of the root scope. Scopes provide separation
15466  * between the model and the view, via a mechanism for watching the model for changes.
15467  * They also provide event emission/broadcast and subscription facility. See the
15468  * {@link guide/scope developer guide on scopes}.
15469  */
15470 function $RootScopeProvider() {
15471   var TTL = 10;
15472   var $rootScopeMinErr = minErr('$rootScope');
15473   var lastDirtyWatch = null;
15474   var applyAsyncId = null;
15475
15476   this.digestTtl = function(value) {
15477     if (arguments.length) {
15478       TTL = value;
15479     }
15480     return TTL;
15481   };
15482
15483   function createChildScopeClass(parent) {
15484     function ChildScope() {
15485       this.$$watchers = this.$$nextSibling =
15486           this.$$childHead = this.$$childTail = null;
15487       this.$$listeners = {};
15488       this.$$listenerCount = {};
15489       this.$$watchersCount = 0;
15490       this.$id = nextUid();
15491       this.$$ChildScope = null;
15492     }
15493     ChildScope.prototype = parent;
15494     return ChildScope;
15495   }
15496
15497   this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
15498       function($injector, $exceptionHandler, $parse, $browser) {
15499
15500     function destroyChildScope($event) {
15501         $event.currentScope.$$destroyed = true;
15502     }
15503
15504     function cleanUpScope($scope) {
15505
15506       if (msie === 9) {
15507         // There is a memory leak in IE9 if all child scopes are not disconnected
15508         // completely when a scope is destroyed. So this code will recurse up through
15509         // all this scopes children
15510         //
15511         // See issue https://github.com/angular/angular.js/issues/10706
15512         $scope.$$childHead && cleanUpScope($scope.$$childHead);
15513         $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
15514       }
15515
15516       // The code below works around IE9 and V8's memory leaks
15517       //
15518       // See:
15519       // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15520       // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15521       // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15522
15523       $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
15524           $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
15525     }
15526
15527     /**
15528      * @ngdoc type
15529      * @name $rootScope.Scope
15530      *
15531      * @description
15532      * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15533      * {@link auto.$injector $injector}. Child scopes are created using the
15534      * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15535      * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15536      * an in-depth introduction and usage examples.
15537      *
15538      *
15539      * # Inheritance
15540      * A scope can inherit from a parent scope, as in this example:
15541      * ```js
15542          var parent = $rootScope;
15543          var child = parent.$new();
15544
15545          parent.salutation = "Hello";
15546          expect(child.salutation).toEqual('Hello');
15547
15548          child.salutation = "Welcome";
15549          expect(child.salutation).toEqual('Welcome');
15550          expect(parent.salutation).toEqual('Hello');
15551      * ```
15552      *
15553      * When interacting with `Scope` in tests, additional helper methods are available on the
15554      * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15555      * details.
15556      *
15557      *
15558      * @param {Object.<string, function()>=} providers Map of service factory which need to be
15559      *                                       provided for the current scope. Defaults to {@link ng}.
15560      * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15561      *                              append/override services provided by `providers`. This is handy
15562      *                              when unit-testing and having the need to override a default
15563      *                              service.
15564      * @returns {Object} Newly created scope.
15565      *
15566      */
15567     function Scope() {
15568       this.$id = nextUid();
15569       this.$$phase = this.$parent = this.$$watchers =
15570                      this.$$nextSibling = this.$$prevSibling =
15571                      this.$$childHead = this.$$childTail = null;
15572       this.$root = this;
15573       this.$$destroyed = false;
15574       this.$$listeners = {};
15575       this.$$listenerCount = {};
15576       this.$$watchersCount = 0;
15577       this.$$isolateBindings = null;
15578     }
15579
15580     /**
15581      * @ngdoc property
15582      * @name $rootScope.Scope#$id
15583      *
15584      * @description
15585      * Unique scope ID (monotonically increasing) useful for debugging.
15586      */
15587
15588      /**
15589       * @ngdoc property
15590       * @name $rootScope.Scope#$parent
15591       *
15592       * @description
15593       * Reference to the parent scope.
15594       */
15595
15596       /**
15597        * @ngdoc property
15598        * @name $rootScope.Scope#$root
15599        *
15600        * @description
15601        * Reference to the root scope.
15602        */
15603
15604     Scope.prototype = {
15605       constructor: Scope,
15606       /**
15607        * @ngdoc method
15608        * @name $rootScope.Scope#$new
15609        * @kind function
15610        *
15611        * @description
15612        * Creates a new child {@link ng.$rootScope.Scope scope}.
15613        *
15614        * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15615        * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15616        *
15617        * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15618        * desired for the scope and its child scopes to be permanently detached from the parent and
15619        * thus stop participating in model change detection and listener notification by invoking.
15620        *
15621        * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15622        *         parent scope. The scope is isolated, as it can not see parent scope properties.
15623        *         When creating widgets, it is useful for the widget to not accidentally read parent
15624        *         state.
15625        *
15626        * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15627        *                              of the newly created scope. Defaults to `this` scope if not provided.
15628        *                              This is used when creating a transclude scope to correctly place it
15629        *                              in the scope hierarchy while maintaining the correct prototypical
15630        *                              inheritance.
15631        *
15632        * @returns {Object} The newly created child scope.
15633        *
15634        */
15635       $new: function(isolate, parent) {
15636         var child;
15637
15638         parent = parent || this;
15639
15640         if (isolate) {
15641           child = new Scope();
15642           child.$root = this.$root;
15643         } else {
15644           // Only create a child scope class if somebody asks for one,
15645           // but cache it to allow the VM to optimize lookups.
15646           if (!this.$$ChildScope) {
15647             this.$$ChildScope = createChildScopeClass(this);
15648           }
15649           child = new this.$$ChildScope();
15650         }
15651         child.$parent = parent;
15652         child.$$prevSibling = parent.$$childTail;
15653         if (parent.$$childHead) {
15654           parent.$$childTail.$$nextSibling = child;
15655           parent.$$childTail = child;
15656         } else {
15657           parent.$$childHead = parent.$$childTail = child;
15658         }
15659
15660         // When the new scope is not isolated or we inherit from `this`, and
15661         // the parent scope is destroyed, the property `$$destroyed` is inherited
15662         // prototypically. In all other cases, this property needs to be set
15663         // when the parent scope is destroyed.
15664         // The listener needs to be added after the parent is set
15665         if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15666
15667         return child;
15668       },
15669
15670       /**
15671        * @ngdoc method
15672        * @name $rootScope.Scope#$watch
15673        * @kind function
15674        *
15675        * @description
15676        * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15677        *
15678        * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15679        *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
15680        *   its value when executed multiple times with the same input because it may be executed multiple
15681        *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15682        *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15683        * - The `listener` is called only when the value from the current `watchExpression` and the
15684        *   previous call to `watchExpression` are not equal (with the exception of the initial run,
15685        *   see below). Inequality is determined according to reference inequality,
15686        *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15687        *    via the `!==` Javascript operator, unless `objectEquality == true`
15688        *   (see next point)
15689        * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15690        *   according to the {@link angular.equals} function. To save the value of the object for
15691        *   later comparison, the {@link angular.copy} function is used. This therefore means that
15692        *   watching complex objects will have adverse memory and performance implications.
15693        * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15694        *   This is achieved by rerunning the watchers until no changes are detected. The rerun
15695        *   iteration limit is 10 to prevent an infinite loop deadlock.
15696        *
15697        *
15698        * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15699        * you can register a `watchExpression` function with no `listener`. (Be prepared for
15700        * multiple calls to your `watchExpression` because it will execute multiple times in a
15701        * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
15702        *
15703        * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15704        * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15705        * watcher. In rare cases, this is undesirable because the listener is called when the result
15706        * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15707        * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15708        * listener was called due to initialization.
15709        *
15710        *
15711        *
15712        * # Example
15713        * ```js
15714            // let's assume that scope was dependency injected as the $rootScope
15715            var scope = $rootScope;
15716            scope.name = 'misko';
15717            scope.counter = 0;
15718
15719            expect(scope.counter).toEqual(0);
15720            scope.$watch('name', function(newValue, oldValue) {
15721              scope.counter = scope.counter + 1;
15722            });
15723            expect(scope.counter).toEqual(0);
15724
15725            scope.$digest();
15726            // the listener is always called during the first $digest loop after it was registered
15727            expect(scope.counter).toEqual(1);
15728
15729            scope.$digest();
15730            // but now it will not be called unless the value changes
15731            expect(scope.counter).toEqual(1);
15732
15733            scope.name = 'adam';
15734            scope.$digest();
15735            expect(scope.counter).toEqual(2);
15736
15737
15738
15739            // Using a function as a watchExpression
15740            var food;
15741            scope.foodCounter = 0;
15742            expect(scope.foodCounter).toEqual(0);
15743            scope.$watch(
15744              // This function returns the value being watched. It is called for each turn of the $digest loop
15745              function() { return food; },
15746              // This is the change listener, called when the value returned from the above function changes
15747              function(newValue, oldValue) {
15748                if ( newValue !== oldValue ) {
15749                  // Only increment the counter if the value changed
15750                  scope.foodCounter = scope.foodCounter + 1;
15751                }
15752              }
15753            );
15754            // No digest has been run so the counter will be zero
15755            expect(scope.foodCounter).toEqual(0);
15756
15757            // Run the digest but since food has not changed count will still be zero
15758            scope.$digest();
15759            expect(scope.foodCounter).toEqual(0);
15760
15761            // Update food and run digest.  Now the counter will increment
15762            food = 'cheeseburger';
15763            scope.$digest();
15764            expect(scope.foodCounter).toEqual(1);
15765
15766        * ```
15767        *
15768        *
15769        *
15770        * @param {(function()|string)} watchExpression Expression that is evaluated on each
15771        *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15772        *    a call to the `listener`.
15773        *
15774        *    - `string`: Evaluated as {@link guide/expression expression}
15775        *    - `function(scope)`: called with current `scope` as a parameter.
15776        * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15777        *    of `watchExpression` changes.
15778        *
15779        *    - `newVal` contains the current value of the `watchExpression`
15780        *    - `oldVal` contains the previous value of the `watchExpression`
15781        *    - `scope` refers to the current scope
15782        * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15783        *     comparing for reference equality.
15784        * @returns {function()} Returns a deregistration function for this listener.
15785        */
15786       $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15787         var get = $parse(watchExp);
15788
15789         if (get.$$watchDelegate) {
15790           return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15791         }
15792         var scope = this,
15793             array = scope.$$watchers,
15794             watcher = {
15795               fn: listener,
15796               last: initWatchVal,
15797               get: get,
15798               exp: prettyPrintExpression || watchExp,
15799               eq: !!objectEquality
15800             };
15801
15802         lastDirtyWatch = null;
15803
15804         if (!isFunction(listener)) {
15805           watcher.fn = noop;
15806         }
15807
15808         if (!array) {
15809           array = scope.$$watchers = [];
15810         }
15811         // we use unshift since we use a while loop in $digest for speed.
15812         // the while loop reads in reverse order.
15813         array.unshift(watcher);
15814         incrementWatchersCount(this, 1);
15815
15816         return function deregisterWatch() {
15817           if (arrayRemove(array, watcher) >= 0) {
15818             incrementWatchersCount(scope, -1);
15819           }
15820           lastDirtyWatch = null;
15821         };
15822       },
15823
15824       /**
15825        * @ngdoc method
15826        * @name $rootScope.Scope#$watchGroup
15827        * @kind function
15828        *
15829        * @description
15830        * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15831        * If any one expression in the collection changes the `listener` is executed.
15832        *
15833        * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15834        *   call to $digest() to see if any items changes.
15835        * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15836        *
15837        * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15838        * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15839        *
15840        * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15841        *    expression in `watchExpressions` changes
15842        *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15843        *    those of `watchExpression`
15844        *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15845        *    those of `watchExpression`
15846        *    The `scope` refers to the current scope.
15847        * @returns {function()} Returns a de-registration function for all listeners.
15848        */
15849       $watchGroup: function(watchExpressions, listener) {
15850         var oldValues = new Array(watchExpressions.length);
15851         var newValues = new Array(watchExpressions.length);
15852         var deregisterFns = [];
15853         var self = this;
15854         var changeReactionScheduled = false;
15855         var firstRun = true;
15856
15857         if (!watchExpressions.length) {
15858           // No expressions means we call the listener ASAP
15859           var shouldCall = true;
15860           self.$evalAsync(function() {
15861             if (shouldCall) listener(newValues, newValues, self);
15862           });
15863           return function deregisterWatchGroup() {
15864             shouldCall = false;
15865           };
15866         }
15867
15868         if (watchExpressions.length === 1) {
15869           // Special case size of one
15870           return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15871             newValues[0] = value;
15872             oldValues[0] = oldValue;
15873             listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15874           });
15875         }
15876
15877         forEach(watchExpressions, function(expr, i) {
15878           var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15879             newValues[i] = value;
15880             oldValues[i] = oldValue;
15881             if (!changeReactionScheduled) {
15882               changeReactionScheduled = true;
15883               self.$evalAsync(watchGroupAction);
15884             }
15885           });
15886           deregisterFns.push(unwatchFn);
15887         });
15888
15889         function watchGroupAction() {
15890           changeReactionScheduled = false;
15891
15892           if (firstRun) {
15893             firstRun = false;
15894             listener(newValues, newValues, self);
15895           } else {
15896             listener(newValues, oldValues, self);
15897           }
15898         }
15899
15900         return function deregisterWatchGroup() {
15901           while (deregisterFns.length) {
15902             deregisterFns.shift()();
15903           }
15904         };
15905       },
15906
15907
15908       /**
15909        * @ngdoc method
15910        * @name $rootScope.Scope#$watchCollection
15911        * @kind function
15912        *
15913        * @description
15914        * Shallow watches the properties of an object and fires whenever any of the properties change
15915        * (for arrays, this implies watching the array items; for object maps, this implies watching
15916        * the properties). If a change is detected, the `listener` callback is fired.
15917        *
15918        * - The `obj` collection is observed via standard $watch operation and is examined on every
15919        *   call to $digest() to see if any items have been added, removed, or moved.
15920        * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15921        *   adding, removing, and moving items belonging to an object or array.
15922        *
15923        *
15924        * # Example
15925        * ```js
15926           $scope.names = ['igor', 'matias', 'misko', 'james'];
15927           $scope.dataCount = 4;
15928
15929           $scope.$watchCollection('names', function(newNames, oldNames) {
15930             $scope.dataCount = newNames.length;
15931           });
15932
15933           expect($scope.dataCount).toEqual(4);
15934           $scope.$digest();
15935
15936           //still at 4 ... no changes
15937           expect($scope.dataCount).toEqual(4);
15938
15939           $scope.names.pop();
15940           $scope.$digest();
15941
15942           //now there's been a change
15943           expect($scope.dataCount).toEqual(3);
15944        * ```
15945        *
15946        *
15947        * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15948        *    expression value should evaluate to an object or an array which is observed on each
15949        *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15950        *    collection will trigger a call to the `listener`.
15951        *
15952        * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15953        *    when a change is detected.
15954        *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
15955        *    - The `oldCollection` object is a copy of the former collection data.
15956        *      Due to performance considerations, the`oldCollection` value is computed only if the
15957        *      `listener` function declares two or more arguments.
15958        *    - The `scope` argument refers to the current scope.
15959        *
15960        * @returns {function()} Returns a de-registration function for this listener. When the
15961        *    de-registration function is executed, the internal watch operation is terminated.
15962        */
15963       $watchCollection: function(obj, listener) {
15964         $watchCollectionInterceptor.$stateful = true;
15965
15966         var self = this;
15967         // the current value, updated on each dirty-check run
15968         var newValue;
15969         // a shallow copy of the newValue from the last dirty-check run,
15970         // updated to match newValue during dirty-check run
15971         var oldValue;
15972         // a shallow copy of the newValue from when the last change happened
15973         var veryOldValue;
15974         // only track veryOldValue if the listener is asking for it
15975         var trackVeryOldValue = (listener.length > 1);
15976         var changeDetected = 0;
15977         var changeDetector = $parse(obj, $watchCollectionInterceptor);
15978         var internalArray = [];
15979         var internalObject = {};
15980         var initRun = true;
15981         var oldLength = 0;
15982
15983         function $watchCollectionInterceptor(_value) {
15984           newValue = _value;
15985           var newLength, key, bothNaN, newItem, oldItem;
15986
15987           // If the new value is undefined, then return undefined as the watch may be a one-time watch
15988           if (isUndefined(newValue)) return;
15989
15990           if (!isObject(newValue)) { // if primitive
15991             if (oldValue !== newValue) {
15992               oldValue = newValue;
15993               changeDetected++;
15994             }
15995           } else if (isArrayLike(newValue)) {
15996             if (oldValue !== internalArray) {
15997               // we are transitioning from something which was not an array into array.
15998               oldValue = internalArray;
15999               oldLength = oldValue.length = 0;
16000               changeDetected++;
16001             }
16002
16003             newLength = newValue.length;
16004
16005             if (oldLength !== newLength) {
16006               // if lengths do not match we need to trigger change notification
16007               changeDetected++;
16008               oldValue.length = oldLength = newLength;
16009             }
16010             // copy the items to oldValue and look for changes.
16011             for (var i = 0; i < newLength; i++) {
16012               oldItem = oldValue[i];
16013               newItem = newValue[i];
16014
16015               bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
16016               if (!bothNaN && (oldItem !== newItem)) {
16017                 changeDetected++;
16018                 oldValue[i] = newItem;
16019               }
16020             }
16021           } else {
16022             if (oldValue !== internalObject) {
16023               // we are transitioning from something which was not an object into object.
16024               oldValue = internalObject = {};
16025               oldLength = 0;
16026               changeDetected++;
16027             }
16028             // copy the items to oldValue and look for changes.
16029             newLength = 0;
16030             for (key in newValue) {
16031               if (hasOwnProperty.call(newValue, key)) {
16032                 newLength++;
16033                 newItem = newValue[key];
16034                 oldItem = oldValue[key];
16035
16036                 if (key in oldValue) {
16037                   bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
16038                   if (!bothNaN && (oldItem !== newItem)) {
16039                     changeDetected++;
16040                     oldValue[key] = newItem;
16041                   }
16042                 } else {
16043                   oldLength++;
16044                   oldValue[key] = newItem;
16045                   changeDetected++;
16046                 }
16047               }
16048             }
16049             if (oldLength > newLength) {
16050               // we used to have more keys, need to find them and destroy them.
16051               changeDetected++;
16052               for (key in oldValue) {
16053                 if (!hasOwnProperty.call(newValue, key)) {
16054                   oldLength--;
16055                   delete oldValue[key];
16056                 }
16057               }
16058             }
16059           }
16060           return changeDetected;
16061         }
16062
16063         function $watchCollectionAction() {
16064           if (initRun) {
16065             initRun = false;
16066             listener(newValue, newValue, self);
16067           } else {
16068             listener(newValue, veryOldValue, self);
16069           }
16070
16071           // make a copy for the next time a collection is changed
16072           if (trackVeryOldValue) {
16073             if (!isObject(newValue)) {
16074               //primitive
16075               veryOldValue = newValue;
16076             } else if (isArrayLike(newValue)) {
16077               veryOldValue = new Array(newValue.length);
16078               for (var i = 0; i < newValue.length; i++) {
16079                 veryOldValue[i] = newValue[i];
16080               }
16081             } else { // if object
16082               veryOldValue = {};
16083               for (var key in newValue) {
16084                 if (hasOwnProperty.call(newValue, key)) {
16085                   veryOldValue[key] = newValue[key];
16086                 }
16087               }
16088             }
16089           }
16090         }
16091
16092         return this.$watch(changeDetector, $watchCollectionAction);
16093       },
16094
16095       /**
16096        * @ngdoc method
16097        * @name $rootScope.Scope#$digest
16098        * @kind function
16099        *
16100        * @description
16101        * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
16102        * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
16103        * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
16104        * until no more listeners are firing. This means that it is possible to get into an infinite
16105        * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
16106        * iterations exceeds 10.
16107        *
16108        * Usually, you don't call `$digest()` directly in
16109        * {@link ng.directive:ngController controllers} or in
16110        * {@link ng.$compileProvider#directive directives}.
16111        * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
16112        * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
16113        *
16114        * If you want to be notified whenever `$digest()` is called,
16115        * you can register a `watchExpression` function with
16116        * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
16117        *
16118        * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
16119        *
16120        * # Example
16121        * ```js
16122            var scope = ...;
16123            scope.name = 'misko';
16124            scope.counter = 0;
16125
16126            expect(scope.counter).toEqual(0);
16127            scope.$watch('name', function(newValue, oldValue) {
16128              scope.counter = scope.counter + 1;
16129            });
16130            expect(scope.counter).toEqual(0);
16131
16132            scope.$digest();
16133            // the listener is always called during the first $digest loop after it was registered
16134            expect(scope.counter).toEqual(1);
16135
16136            scope.$digest();
16137            // but now it will not be called unless the value changes
16138            expect(scope.counter).toEqual(1);
16139
16140            scope.name = 'adam';
16141            scope.$digest();
16142            expect(scope.counter).toEqual(2);
16143        * ```
16144        *
16145        */
16146       $digest: function() {
16147         var watch, value, last, fn, get,
16148             watchers,
16149             length,
16150             dirty, ttl = TTL,
16151             next, current, target = this,
16152             watchLog = [],
16153             logIdx, logMsg, asyncTask;
16154
16155         beginPhase('$digest');
16156         // Check for changes to browser url that happened in sync before the call to $digest
16157         $browser.$$checkUrlChange();
16158
16159         if (this === $rootScope && applyAsyncId !== null) {
16160           // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
16161           // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
16162           $browser.defer.cancel(applyAsyncId);
16163           flushApplyAsync();
16164         }
16165
16166         lastDirtyWatch = null;
16167
16168         do { // "while dirty" loop
16169           dirty = false;
16170           current = target;
16171
16172           while (asyncQueue.length) {
16173             try {
16174               asyncTask = asyncQueue.shift();
16175               asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
16176             } catch (e) {
16177               $exceptionHandler(e);
16178             }
16179             lastDirtyWatch = null;
16180           }
16181
16182           traverseScopesLoop:
16183           do { // "traverse the scopes" loop
16184             if ((watchers = current.$$watchers)) {
16185               // process our watches
16186               length = watchers.length;
16187               while (length--) {
16188                 try {
16189                   watch = watchers[length];
16190                   // Most common watches are on primitives, in which case we can short
16191                   // circuit it with === operator, only when === fails do we use .equals
16192                   if (watch) {
16193                     get = watch.get;
16194                     if ((value = get(current)) !== (last = watch.last) &&
16195                         !(watch.eq
16196                             ? equals(value, last)
16197                             : (typeof value === 'number' && typeof last === 'number'
16198                                && isNaN(value) && isNaN(last)))) {
16199                       dirty = true;
16200                       lastDirtyWatch = watch;
16201                       watch.last = watch.eq ? copy(value, null) : value;
16202                       fn = watch.fn;
16203                       fn(value, ((last === initWatchVal) ? value : last), current);
16204                       if (ttl < 5) {
16205                         logIdx = 4 - ttl;
16206                         if (!watchLog[logIdx]) watchLog[logIdx] = [];
16207                         watchLog[logIdx].push({
16208                           msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
16209                           newVal: value,
16210                           oldVal: last
16211                         });
16212                       }
16213                     } else if (watch === lastDirtyWatch) {
16214                       // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
16215                       // have already been tested.
16216                       dirty = false;
16217                       break traverseScopesLoop;
16218                     }
16219                   }
16220                 } catch (e) {
16221                   $exceptionHandler(e);
16222                 }
16223               }
16224             }
16225
16226             // Insanity Warning: scope depth-first traversal
16227             // yes, this code is a bit crazy, but it works and we have tests to prove it!
16228             // this piece should be kept in sync with the traversal in $broadcast
16229             if (!(next = ((current.$$watchersCount && current.$$childHead) ||
16230                 (current !== target && current.$$nextSibling)))) {
16231               while (current !== target && !(next = current.$$nextSibling)) {
16232                 current = current.$parent;
16233               }
16234             }
16235           } while ((current = next));
16236
16237           // `break traverseScopesLoop;` takes us to here
16238
16239           if ((dirty || asyncQueue.length) && !(ttl--)) {
16240             clearPhase();
16241             throw $rootScopeMinErr('infdig',
16242                 '{0} $digest() iterations reached. Aborting!\n' +
16243                 'Watchers fired in the last 5 iterations: {1}',
16244                 TTL, watchLog);
16245           }
16246
16247         } while (dirty || asyncQueue.length);
16248
16249         clearPhase();
16250
16251         while (postDigestQueue.length) {
16252           try {
16253             postDigestQueue.shift()();
16254           } catch (e) {
16255             $exceptionHandler(e);
16256           }
16257         }
16258       },
16259
16260
16261       /**
16262        * @ngdoc event
16263        * @name $rootScope.Scope#$destroy
16264        * @eventType broadcast on scope being destroyed
16265        *
16266        * @description
16267        * Broadcasted when a scope and its children are being destroyed.
16268        *
16269        * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16270        * clean up DOM bindings before an element is removed from the DOM.
16271        */
16272
16273       /**
16274        * @ngdoc method
16275        * @name $rootScope.Scope#$destroy
16276        * @kind function
16277        *
16278        * @description
16279        * Removes the current scope (and all of its children) from the parent scope. Removal implies
16280        * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
16281        * propagate to the current scope and its children. Removal also implies that the current
16282        * scope is eligible for garbage collection.
16283        *
16284        * The `$destroy()` is usually used by directives such as
16285        * {@link ng.directive:ngRepeat ngRepeat} for managing the
16286        * unrolling of the loop.
16287        *
16288        * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
16289        * Application code can register a `$destroy` event handler that will give it a chance to
16290        * perform any necessary cleanup.
16291        *
16292        * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16293        * clean up DOM bindings before an element is removed from the DOM.
16294        */
16295       $destroy: function() {
16296         // We can't destroy a scope that has been already destroyed.
16297         if (this.$$destroyed) return;
16298         var parent = this.$parent;
16299
16300         this.$broadcast('$destroy');
16301         this.$$destroyed = true;
16302
16303         if (this === $rootScope) {
16304           //Remove handlers attached to window when $rootScope is removed
16305           $browser.$$applicationDestroyed();
16306         }
16307
16308         incrementWatchersCount(this, -this.$$watchersCount);
16309         for (var eventName in this.$$listenerCount) {
16310           decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16311         }
16312
16313         // sever all the references to parent scopes (after this cleanup, the current scope should
16314         // not be retained by any of our references and should be eligible for garbage collection)
16315         if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16316         if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16317         if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16318         if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16319
16320         // Disable listeners, watchers and apply/digest methods
16321         this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16322         this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16323         this.$$listeners = {};
16324
16325         // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16326         this.$$nextSibling = null;
16327         cleanUpScope(this);
16328       },
16329
16330       /**
16331        * @ngdoc method
16332        * @name $rootScope.Scope#$eval
16333        * @kind function
16334        *
16335        * @description
16336        * Executes the `expression` on the current scope and returns the result. Any exceptions in
16337        * the expression are propagated (uncaught). This is useful when evaluating Angular
16338        * expressions.
16339        *
16340        * # Example
16341        * ```js
16342            var scope = ng.$rootScope.Scope();
16343            scope.a = 1;
16344            scope.b = 2;
16345
16346            expect(scope.$eval('a+b')).toEqual(3);
16347            expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
16348        * ```
16349        *
16350        * @param {(string|function())=} expression An angular expression to be executed.
16351        *
16352        *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
16353        *    - `function(scope)`: execute the function with the current `scope` parameter.
16354        *
16355        * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16356        * @returns {*} The result of evaluating the expression.
16357        */
16358       $eval: function(expr, locals) {
16359         return $parse(expr)(this, locals);
16360       },
16361
16362       /**
16363        * @ngdoc method
16364        * @name $rootScope.Scope#$evalAsync
16365        * @kind function
16366        *
16367        * @description
16368        * Executes the expression on the current scope at a later point in time.
16369        *
16370        * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16371        * that:
16372        *
16373        *   - it will execute after the function that scheduled the evaluation (preferably before DOM
16374        *     rendering).
16375        *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16376        *     `expression` execution.
16377        *
16378        * Any exceptions from the execution of the expression are forwarded to the
16379        * {@link ng.$exceptionHandler $exceptionHandler} service.
16380        *
16381        * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16382        * will be scheduled. However, it is encouraged to always call code that changes the model
16383        * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16384        *
16385        * @param {(string|function())=} expression An angular expression to be executed.
16386        *
16387        *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16388        *    - `function(scope)`: execute the function with the current `scope` parameter.
16389        *
16390        * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16391        */
16392       $evalAsync: function(expr, locals) {
16393         // if we are outside of an $digest loop and this is the first time we are scheduling async
16394         // task also schedule async auto-flush
16395         if (!$rootScope.$$phase && !asyncQueue.length) {
16396           $browser.defer(function() {
16397             if (asyncQueue.length) {
16398               $rootScope.$digest();
16399             }
16400           });
16401         }
16402
16403         asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
16404       },
16405
16406       $$postDigest: function(fn) {
16407         postDigestQueue.push(fn);
16408       },
16409
16410       /**
16411        * @ngdoc method
16412        * @name $rootScope.Scope#$apply
16413        * @kind function
16414        *
16415        * @description
16416        * `$apply()` is used to execute an expression in angular from outside of the angular
16417        * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16418        * Because we are calling into the angular framework we need to perform proper scope life
16419        * cycle of {@link ng.$exceptionHandler exception handling},
16420        * {@link ng.$rootScope.Scope#$digest executing watches}.
16421        *
16422        * ## Life cycle
16423        *
16424        * # Pseudo-Code of `$apply()`
16425        * ```js
16426            function $apply(expr) {
16427              try {
16428                return $eval(expr);
16429              } catch (e) {
16430                $exceptionHandler(e);
16431              } finally {
16432                $root.$digest();
16433              }
16434            }
16435        * ```
16436        *
16437        *
16438        * Scope's `$apply()` method transitions through the following stages:
16439        *
16440        * 1. The {@link guide/expression expression} is executed using the
16441        *    {@link ng.$rootScope.Scope#$eval $eval()} method.
16442        * 2. Any exceptions from the execution of the expression are forwarded to the
16443        *    {@link ng.$exceptionHandler $exceptionHandler} service.
16444        * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16445        *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16446        *
16447        *
16448        * @param {(string|function())=} exp An angular expression to be executed.
16449        *
16450        *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16451        *    - `function(scope)`: execute the function with current `scope` parameter.
16452        *
16453        * @returns {*} The result of evaluating the expression.
16454        */
16455       $apply: function(expr) {
16456         try {
16457           beginPhase('$apply');
16458           try {
16459             return this.$eval(expr);
16460           } finally {
16461             clearPhase();
16462           }
16463         } catch (e) {
16464           $exceptionHandler(e);
16465         } finally {
16466           try {
16467             $rootScope.$digest();
16468           } catch (e) {
16469             $exceptionHandler(e);
16470             throw e;
16471           }
16472         }
16473       },
16474
16475       /**
16476        * @ngdoc method
16477        * @name $rootScope.Scope#$applyAsync
16478        * @kind function
16479        *
16480        * @description
16481        * Schedule the invocation of $apply to occur at a later time. The actual time difference
16482        * varies across browsers, but is typically around ~10 milliseconds.
16483        *
16484        * This can be used to queue up multiple expressions which need to be evaluated in the same
16485        * digest.
16486        *
16487        * @param {(string|function())=} exp An angular expression to be executed.
16488        *
16489        *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16490        *    - `function(scope)`: execute the function with current `scope` parameter.
16491        */
16492       $applyAsync: function(expr) {
16493         var scope = this;
16494         expr && applyAsyncQueue.push($applyAsyncExpression);
16495         expr = $parse(expr);
16496         scheduleApplyAsync();
16497
16498         function $applyAsyncExpression() {
16499           scope.$eval(expr);
16500         }
16501       },
16502
16503       /**
16504        * @ngdoc method
16505        * @name $rootScope.Scope#$on
16506        * @kind function
16507        *
16508        * @description
16509        * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16510        * discussion of event life cycle.
16511        *
16512        * The event listener function format is: `function(event, args...)`. The `event` object
16513        * passed into the listener has the following attributes:
16514        *
16515        *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16516        *     `$broadcast`-ed.
16517        *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16518        *     event propagates through the scope hierarchy, this property is set to null.
16519        *   - `name` - `{string}`: name of the event.
16520        *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16521        *     further event propagation (available only for events that were `$emit`-ed).
16522        *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16523        *     to true.
16524        *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16525        *
16526        * @param {string} name Event name to listen on.
16527        * @param {function(event, ...args)} listener Function to call when the event is emitted.
16528        * @returns {function()} Returns a deregistration function for this listener.
16529        */
16530       $on: function(name, listener) {
16531         var namedListeners = this.$$listeners[name];
16532         if (!namedListeners) {
16533           this.$$listeners[name] = namedListeners = [];
16534         }
16535         namedListeners.push(listener);
16536
16537         var current = this;
16538         do {
16539           if (!current.$$listenerCount[name]) {
16540             current.$$listenerCount[name] = 0;
16541           }
16542           current.$$listenerCount[name]++;
16543         } while ((current = current.$parent));
16544
16545         var self = this;
16546         return function() {
16547           var indexOfListener = namedListeners.indexOf(listener);
16548           if (indexOfListener !== -1) {
16549             namedListeners[indexOfListener] = null;
16550             decrementListenerCount(self, 1, name);
16551           }
16552         };
16553       },
16554
16555
16556       /**
16557        * @ngdoc method
16558        * @name $rootScope.Scope#$emit
16559        * @kind function
16560        *
16561        * @description
16562        * Dispatches an event `name` upwards through the scope hierarchy notifying the
16563        * registered {@link ng.$rootScope.Scope#$on} listeners.
16564        *
16565        * The event life cycle starts at the scope on which `$emit` was called. All
16566        * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16567        * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16568        * registered listeners along the way. The event will stop propagating if one of the listeners
16569        * cancels it.
16570        *
16571        * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16572        * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16573        *
16574        * @param {string} name Event name to emit.
16575        * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16576        * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16577        */
16578       $emit: function(name, args) {
16579         var empty = [],
16580             namedListeners,
16581             scope = this,
16582             stopPropagation = false,
16583             event = {
16584               name: name,
16585               targetScope: scope,
16586               stopPropagation: function() {stopPropagation = true;},
16587               preventDefault: function() {
16588                 event.defaultPrevented = true;
16589               },
16590               defaultPrevented: false
16591             },
16592             listenerArgs = concat([event], arguments, 1),
16593             i, length;
16594
16595         do {
16596           namedListeners = scope.$$listeners[name] || empty;
16597           event.currentScope = scope;
16598           for (i = 0, length = namedListeners.length; i < length; i++) {
16599
16600             // if listeners were deregistered, defragment the array
16601             if (!namedListeners[i]) {
16602               namedListeners.splice(i, 1);
16603               i--;
16604               length--;
16605               continue;
16606             }
16607             try {
16608               //allow all listeners attached to the current scope to run
16609               namedListeners[i].apply(null, listenerArgs);
16610             } catch (e) {
16611               $exceptionHandler(e);
16612             }
16613           }
16614           //if any listener on the current scope stops propagation, prevent bubbling
16615           if (stopPropagation) {
16616             event.currentScope = null;
16617             return event;
16618           }
16619           //traverse upwards
16620           scope = scope.$parent;
16621         } while (scope);
16622
16623         event.currentScope = null;
16624
16625         return event;
16626       },
16627
16628
16629       /**
16630        * @ngdoc method
16631        * @name $rootScope.Scope#$broadcast
16632        * @kind function
16633        *
16634        * @description
16635        * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16636        * registered {@link ng.$rootScope.Scope#$on} listeners.
16637        *
16638        * The event life cycle starts at the scope on which `$broadcast` was called. All
16639        * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16640        * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16641        * scope and calls all registered listeners along the way. The event cannot be canceled.
16642        *
16643        * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16644        * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16645        *
16646        * @param {string} name Event name to broadcast.
16647        * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16648        * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16649        */
16650       $broadcast: function(name, args) {
16651         var target = this,
16652             current = target,
16653             next = target,
16654             event = {
16655               name: name,
16656               targetScope: target,
16657               preventDefault: function() {
16658                 event.defaultPrevented = true;
16659               },
16660               defaultPrevented: false
16661             };
16662
16663         if (!target.$$listenerCount[name]) return event;
16664
16665         var listenerArgs = concat([event], arguments, 1),
16666             listeners, i, length;
16667
16668         //down while you can, then up and next sibling or up and next sibling until back at root
16669         while ((current = next)) {
16670           event.currentScope = current;
16671           listeners = current.$$listeners[name] || [];
16672           for (i = 0, length = listeners.length; i < length; i++) {
16673             // if listeners were deregistered, defragment the array
16674             if (!listeners[i]) {
16675               listeners.splice(i, 1);
16676               i--;
16677               length--;
16678               continue;
16679             }
16680
16681             try {
16682               listeners[i].apply(null, listenerArgs);
16683             } catch (e) {
16684               $exceptionHandler(e);
16685             }
16686           }
16687
16688           // Insanity Warning: scope depth-first traversal
16689           // yes, this code is a bit crazy, but it works and we have tests to prove it!
16690           // this piece should be kept in sync with the traversal in $digest
16691           // (though it differs due to having the extra check for $$listenerCount)
16692           if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16693               (current !== target && current.$$nextSibling)))) {
16694             while (current !== target && !(next = current.$$nextSibling)) {
16695               current = current.$parent;
16696             }
16697           }
16698         }
16699
16700         event.currentScope = null;
16701         return event;
16702       }
16703     };
16704
16705     var $rootScope = new Scope();
16706
16707     //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16708     var asyncQueue = $rootScope.$$asyncQueue = [];
16709     var postDigestQueue = $rootScope.$$postDigestQueue = [];
16710     var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16711
16712     return $rootScope;
16713
16714
16715     function beginPhase(phase) {
16716       if ($rootScope.$$phase) {
16717         throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16718       }
16719
16720       $rootScope.$$phase = phase;
16721     }
16722
16723     function clearPhase() {
16724       $rootScope.$$phase = null;
16725     }
16726
16727     function incrementWatchersCount(current, count) {
16728       do {
16729         current.$$watchersCount += count;
16730       } while ((current = current.$parent));
16731     }
16732
16733     function decrementListenerCount(current, count, name) {
16734       do {
16735         current.$$listenerCount[name] -= count;
16736
16737         if (current.$$listenerCount[name] === 0) {
16738           delete current.$$listenerCount[name];
16739         }
16740       } while ((current = current.$parent));
16741     }
16742
16743     /**
16744      * function used as an initial value for watchers.
16745      * because it's unique we can easily tell it apart from other values
16746      */
16747     function initWatchVal() {}
16748
16749     function flushApplyAsync() {
16750       while (applyAsyncQueue.length) {
16751         try {
16752           applyAsyncQueue.shift()();
16753         } catch (e) {
16754           $exceptionHandler(e);
16755         }
16756       }
16757       applyAsyncId = null;
16758     }
16759
16760     function scheduleApplyAsync() {
16761       if (applyAsyncId === null) {
16762         applyAsyncId = $browser.defer(function() {
16763           $rootScope.$apply(flushApplyAsync);
16764         });
16765       }
16766     }
16767   }];
16768 }
16769
16770 /**
16771  * @ngdoc service
16772  * @name $rootElement
16773  *
16774  * @description
16775  * The root element of Angular application. This is either the element where {@link
16776  * ng.directive:ngApp ngApp} was declared or the element passed into
16777  * {@link angular.bootstrap}. The element represents the root element of application. It is also the
16778  * location where the application's {@link auto.$injector $injector} service gets
16779  * published, and can be retrieved using `$rootElement.injector()`.
16780  */
16781
16782
16783 // the implementation is in angular.bootstrap
16784
16785 /**
16786  * @description
16787  * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16788  */
16789 function $$SanitizeUriProvider() {
16790   var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16791     imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16792
16793   /**
16794    * @description
16795    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16796    * urls during a[href] sanitization.
16797    *
16798    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16799    *
16800    * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16801    * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16802    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16803    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16804    *
16805    * @param {RegExp=} regexp New regexp to whitelist urls with.
16806    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16807    *    chaining otherwise.
16808    */
16809   this.aHrefSanitizationWhitelist = function(regexp) {
16810     if (isDefined(regexp)) {
16811       aHrefSanitizationWhitelist = regexp;
16812       return this;
16813     }
16814     return aHrefSanitizationWhitelist;
16815   };
16816
16817
16818   /**
16819    * @description
16820    * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16821    * urls during img[src] sanitization.
16822    *
16823    * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16824    *
16825    * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16826    * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16827    * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16828    * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16829    *
16830    * @param {RegExp=} regexp New regexp to whitelist urls with.
16831    * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16832    *    chaining otherwise.
16833    */
16834   this.imgSrcSanitizationWhitelist = function(regexp) {
16835     if (isDefined(regexp)) {
16836       imgSrcSanitizationWhitelist = regexp;
16837       return this;
16838     }
16839     return imgSrcSanitizationWhitelist;
16840   };
16841
16842   this.$get = function() {
16843     return function sanitizeUri(uri, isImage) {
16844       var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16845       var normalizedVal;
16846       normalizedVal = urlResolve(uri).href;
16847       if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16848         return 'unsafe:' + normalizedVal;
16849       }
16850       return uri;
16851     };
16852   };
16853 }
16854
16855 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16856  *     Any commits to this file should be reviewed with security in mind.  *
16857  *   Changes to this file can potentially create security vulnerabilities. *
16858  *          An approval from 2 Core members with history of modifying      *
16859  *                         this file is required.                          *
16860  *                                                                         *
16861  *  Does the change somehow allow for arbitrary javascript to be executed? *
16862  *    Or allows for someone to change the prototype of built-in objects?   *
16863  *     Or gives undesired access to variables likes document or window?    *
16864  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16865
16866 var $sceMinErr = minErr('$sce');
16867
16868 var SCE_CONTEXTS = {
16869   HTML: 'html',
16870   CSS: 'css',
16871   URL: 'url',
16872   // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16873   // url.  (e.g. ng-include, script src, templateUrl)
16874   RESOURCE_URL: 'resourceUrl',
16875   JS: 'js'
16876 };
16877
16878 // Helper functions follow.
16879
16880 function adjustMatcher(matcher) {
16881   if (matcher === 'self') {
16882     return matcher;
16883   } else if (isString(matcher)) {
16884     // Strings match exactly except for 2 wildcards - '*' and '**'.
16885     // '*' matches any character except those from the set ':/.?&'.
16886     // '**' matches any character (like .* in a RegExp).
16887     // More than 2 *'s raises an error as it's ill defined.
16888     if (matcher.indexOf('***') > -1) {
16889       throw $sceMinErr('iwcard',
16890           'Illegal sequence *** in string matcher.  String: {0}', matcher);
16891     }
16892     matcher = escapeForRegexp(matcher).
16893                   replace('\\*\\*', '.*').
16894                   replace('\\*', '[^:/.?&;]*');
16895     return new RegExp('^' + matcher + '$');
16896   } else if (isRegExp(matcher)) {
16897     // The only other type of matcher allowed is a Regexp.
16898     // Match entire URL / disallow partial matches.
16899     // Flags are reset (i.e. no global, ignoreCase or multiline)
16900     return new RegExp('^' + matcher.source + '$');
16901   } else {
16902     throw $sceMinErr('imatcher',
16903         'Matchers may only be "self", string patterns or RegExp objects');
16904   }
16905 }
16906
16907
16908 function adjustMatchers(matchers) {
16909   var adjustedMatchers = [];
16910   if (isDefined(matchers)) {
16911     forEach(matchers, function(matcher) {
16912       adjustedMatchers.push(adjustMatcher(matcher));
16913     });
16914   }
16915   return adjustedMatchers;
16916 }
16917
16918
16919 /**
16920  * @ngdoc service
16921  * @name $sceDelegate
16922  * @kind function
16923  *
16924  * @description
16925  *
16926  * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16927  * Contextual Escaping (SCE)} services to AngularJS.
16928  *
16929  * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16930  * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
16931  * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16932  * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16933  * work because `$sce` delegates to `$sceDelegate` for these operations.
16934  *
16935  * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16936  *
16937  * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
16938  * can override it completely to change the behavior of `$sce`, the common case would
16939  * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16940  * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16941  * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16942  * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16943  * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16944  */
16945
16946 /**
16947  * @ngdoc provider
16948  * @name $sceDelegateProvider
16949  * @description
16950  *
16951  * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16952  * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
16953  * that the URLs used for sourcing Angular templates are safe.  Refer {@link
16954  * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16955  * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16956  *
16957  * For the general details about this service in Angular, read the main page for {@link ng.$sce
16958  * Strict Contextual Escaping (SCE)}.
16959  *
16960  * **Example**:  Consider the following case. <a name="example"></a>
16961  *
16962  * - your app is hosted at url `http://myapp.example.com/`
16963  * - but some of your templates are hosted on other domains you control such as
16964  *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
16965  * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16966  *
16967  * Here is what a secure configuration for this scenario might look like:
16968  *
16969  * ```
16970  *  angular.module('myApp', []).config(function($sceDelegateProvider) {
16971  *    $sceDelegateProvider.resourceUrlWhitelist([
16972  *      // Allow same origin resource loads.
16973  *      'self',
16974  *      // Allow loading from our assets domain.  Notice the difference between * and **.
16975  *      'http://srv*.assets.example.com/**'
16976  *    ]);
16977  *
16978  *    // The blacklist overrides the whitelist so the open redirect here is blocked.
16979  *    $sceDelegateProvider.resourceUrlBlacklist([
16980  *      'http://myapp.example.com/clickThru**'
16981  *    ]);
16982  *  });
16983  * ```
16984  */
16985
16986 function $SceDelegateProvider() {
16987   this.SCE_CONTEXTS = SCE_CONTEXTS;
16988
16989   // Resource URLs can also be trusted by policy.
16990   var resourceUrlWhitelist = ['self'],
16991       resourceUrlBlacklist = [];
16992
16993   /**
16994    * @ngdoc method
16995    * @name $sceDelegateProvider#resourceUrlWhitelist
16996    * @kind function
16997    *
16998    * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16999    *    provided.  This must be an array or null.  A snapshot of this array is used so further
17000    *    changes to the array are ignored.
17001    *
17002    *    Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
17003    *    allowed in this array.
17004    *
17005    *    <div class="alert alert-warning">
17006    *    **Note:** an empty whitelist array will block all URLs!
17007    *    </div>
17008    *
17009    * @return {Array} the currently set whitelist array.
17010    *
17011    * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
17012    * same origin resource requests.
17013    *
17014    * @description
17015    * Sets/Gets the whitelist of trusted resource URLs.
17016    */
17017   this.resourceUrlWhitelist = function(value) {
17018     if (arguments.length) {
17019       resourceUrlWhitelist = adjustMatchers(value);
17020     }
17021     return resourceUrlWhitelist;
17022   };
17023
17024   /**
17025    * @ngdoc method
17026    * @name $sceDelegateProvider#resourceUrlBlacklist
17027    * @kind function
17028    *
17029    * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
17030    *    provided.  This must be an array or null.  A snapshot of this array is used so further
17031    *    changes to the array are ignored.
17032    *
17033    *    Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
17034    *    allowed in this array.
17035    *
17036    *    The typical usage for the blacklist is to **block
17037    *    [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
17038    *    these would otherwise be trusted but actually return content from the redirected domain.
17039    *
17040    *    Finally, **the blacklist overrides the whitelist** and has the final say.
17041    *
17042    * @return {Array} the currently set blacklist array.
17043    *
17044    * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
17045    * is no blacklist.)
17046    *
17047    * @description
17048    * Sets/Gets the blacklist of trusted resource URLs.
17049    */
17050
17051   this.resourceUrlBlacklist = function(value) {
17052     if (arguments.length) {
17053       resourceUrlBlacklist = adjustMatchers(value);
17054     }
17055     return resourceUrlBlacklist;
17056   };
17057
17058   this.$get = ['$injector', function($injector) {
17059
17060     var htmlSanitizer = function htmlSanitizer(html) {
17061       throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
17062     };
17063
17064     if ($injector.has('$sanitize')) {
17065       htmlSanitizer = $injector.get('$sanitize');
17066     }
17067
17068
17069     function matchUrl(matcher, parsedUrl) {
17070       if (matcher === 'self') {
17071         return urlIsSameOrigin(parsedUrl);
17072       } else {
17073         // definitely a regex.  See adjustMatchers()
17074         return !!matcher.exec(parsedUrl.href);
17075       }
17076     }
17077
17078     function isResourceUrlAllowedByPolicy(url) {
17079       var parsedUrl = urlResolve(url.toString());
17080       var i, n, allowed = false;
17081       // Ensure that at least one item from the whitelist allows this url.
17082       for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
17083         if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
17084           allowed = true;
17085           break;
17086         }
17087       }
17088       if (allowed) {
17089         // Ensure that no item from the blacklist blocked this url.
17090         for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
17091           if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
17092             allowed = false;
17093             break;
17094           }
17095         }
17096       }
17097       return allowed;
17098     }
17099
17100     function generateHolderType(Base) {
17101       var holderType = function TrustedValueHolderType(trustedValue) {
17102         this.$$unwrapTrustedValue = function() {
17103           return trustedValue;
17104         };
17105       };
17106       if (Base) {
17107         holderType.prototype = new Base();
17108       }
17109       holderType.prototype.valueOf = function sceValueOf() {
17110         return this.$$unwrapTrustedValue();
17111       };
17112       holderType.prototype.toString = function sceToString() {
17113         return this.$$unwrapTrustedValue().toString();
17114       };
17115       return holderType;
17116     }
17117
17118     var trustedValueHolderBase = generateHolderType(),
17119         byType = {};
17120
17121     byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
17122     byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
17123     byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
17124     byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
17125     byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
17126
17127     /**
17128      * @ngdoc method
17129      * @name $sceDelegate#trustAs
17130      *
17131      * @description
17132      * Returns an object that is trusted by angular for use in specified strict
17133      * contextual escaping contexts (such as ng-bind-html, ng-include, any src
17134      * attribute interpolation, any dom event binding attribute interpolation
17135      * such as for onclick,  etc.) that uses the provided value.
17136      * See {@link ng.$sce $sce} for enabling strict contextual escaping.
17137      *
17138      * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17139      *   resourceUrl, html, js and css.
17140      * @param {*} value The value that that should be considered trusted/safe.
17141      * @returns {*} A value that can be used to stand in for the provided `value` in places
17142      * where Angular expects a $sce.trustAs() return value.
17143      */
17144     function trustAs(type, trustedValue) {
17145       var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
17146       if (!Constructor) {
17147         throw $sceMinErr('icontext',
17148             'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
17149             type, trustedValue);
17150       }
17151       if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
17152         return trustedValue;
17153       }
17154       // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
17155       // mutable objects, we ensure here that the value passed in is actually a string.
17156       if (typeof trustedValue !== 'string') {
17157         throw $sceMinErr('itype',
17158             'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
17159             type);
17160       }
17161       return new Constructor(trustedValue);
17162     }
17163
17164     /**
17165      * @ngdoc method
17166      * @name $sceDelegate#valueOf
17167      *
17168      * @description
17169      * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
17170      * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
17171      * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
17172      *
17173      * If the passed parameter is not a value that had been returned by {@link
17174      * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
17175      *
17176      * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
17177      *      call or anything else.
17178      * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
17179      *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
17180      *     `value` unchanged.
17181      */
17182     function valueOf(maybeTrusted) {
17183       if (maybeTrusted instanceof trustedValueHolderBase) {
17184         return maybeTrusted.$$unwrapTrustedValue();
17185       } else {
17186         return maybeTrusted;
17187       }
17188     }
17189
17190     /**
17191      * @ngdoc method
17192      * @name $sceDelegate#getTrusted
17193      *
17194      * @description
17195      * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
17196      * returns the originally supplied value if the queried context type is a supertype of the
17197      * created type.  If this condition isn't satisfied, throws an exception.
17198      *
17199      * @param {string} type The kind of context in which this value is to be used.
17200      * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
17201      *     `$sceDelegate.trustAs`} call.
17202      * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
17203      *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
17204      */
17205     function getTrusted(type, maybeTrusted) {
17206       if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
17207         return maybeTrusted;
17208       }
17209       var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
17210       if (constructor && maybeTrusted instanceof constructor) {
17211         return maybeTrusted.$$unwrapTrustedValue();
17212       }
17213       // If we get here, then we may only take one of two actions.
17214       // 1. sanitize the value for the requested type, or
17215       // 2. throw an exception.
17216       if (type === SCE_CONTEXTS.RESOURCE_URL) {
17217         if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
17218           return maybeTrusted;
17219         } else {
17220           throw $sceMinErr('insecurl',
17221               'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
17222               maybeTrusted.toString());
17223         }
17224       } else if (type === SCE_CONTEXTS.HTML) {
17225         return htmlSanitizer(maybeTrusted);
17226       }
17227       throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
17228     }
17229
17230     return { trustAs: trustAs,
17231              getTrusted: getTrusted,
17232              valueOf: valueOf };
17233   }];
17234 }
17235
17236
17237 /**
17238  * @ngdoc provider
17239  * @name $sceProvider
17240  * @description
17241  *
17242  * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
17243  * -   enable/disable Strict Contextual Escaping (SCE) in a module
17244  * -   override the default implementation with a custom delegate
17245  *
17246  * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
17247  */
17248
17249 /* jshint maxlen: false*/
17250
17251 /**
17252  * @ngdoc service
17253  * @name $sce
17254  * @kind function
17255  *
17256  * @description
17257  *
17258  * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
17259  *
17260  * # Strict Contextual Escaping
17261  *
17262  * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
17263  * contexts to result in a value that is marked as safe to use for that context.  One example of
17264  * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
17265  * to these contexts as privileged or SCE contexts.
17266  *
17267  * As of version 1.2, Angular ships with SCE enabled by default.
17268  *
17269  * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
17270  * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
17271  * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
17272  * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
17273  * to the top of your HTML document.
17274  *
17275  * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
17276  * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
17277  *
17278  * Here's an example of a binding in a privileged context:
17279  *
17280  * ```
17281  * <input ng-model="userHtml" aria-label="User input">
17282  * <div ng-bind-html="userHtml"></div>
17283  * ```
17284  *
17285  * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
17286  * disabled, this application allows the user to render arbitrary HTML into the DIV.
17287  * In a more realistic example, one may be rendering user comments, blog articles, etc. via
17288  * bindings.  (HTML is just one example of a context where rendering user controlled input creates
17289  * security vulnerabilities.)
17290  *
17291  * For the case of HTML, you might use a library, either on the client side, or on the server side,
17292  * to sanitize unsafe HTML before binding to the value and rendering it in the document.
17293  *
17294  * How would you ensure that every place that used these types of bindings was bound to a value that
17295  * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
17296  * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
17297  * properties/fields and forgot to update the binding to the sanitized value?
17298  *
17299  * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
17300  * determine that something explicitly says it's safe to use a value for binding in that
17301  * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
17302  * for those values that you can easily tell are safe - because they were received from your server,
17303  * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
17304  * allowing only the files in a specific directory to do this.  Ensuring that the internal API
17305  * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
17306  *
17307  * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
17308  * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
17309  * obtain values that will be accepted by SCE / privileged contexts.
17310  *
17311  *
17312  * ## How does it work?
17313  *
17314  * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
17315  * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
17316  * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
17317  * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
17318  *
17319  * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
17320  * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
17321  * simplified):
17322  *
17323  * ```
17324  * var ngBindHtmlDirective = ['$sce', function($sce) {
17325  *   return function(scope, element, attr) {
17326  *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17327  *       element.html(value || '');
17328  *     });
17329  *   };
17330  * }];
17331  * ```
17332  *
17333  * ## Impact on loading templates
17334  *
17335  * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
17336  * `templateUrl`'s specified by {@link guide/directive directives}.
17337  *
17338  * By default, Angular only loads templates from the same domain and protocol as the application
17339  * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
17340  * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
17341  * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17342  * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
17343  *
17344  * *Please note*:
17345  * The browser's
17346  * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
17347  * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
17348  * policy apply in addition to this and may further restrict whether the template is successfully
17349  * loaded.  This means that without the right CORS policy, loading templates from a different domain
17350  * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
17351  * browsers.
17352  *
17353  * ## This feels like too much overhead
17354  *
17355  * It's important to remember that SCE only applies to interpolation expressions.
17356  *
17357  * If your expressions are constant literals, they're automatically trusted and you don't need to
17358  * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
17359  * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
17360  *
17361  * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
17362  * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
17363  *
17364  * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
17365  * templates in `ng-include` from your application's domain without having to even know about SCE.
17366  * It blocks loading templates from other domains or loading templates over http from an https
17367  * served document.  You can change these by setting your own custom {@link
17368  * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
17369  * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
17370  *
17371  * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
17372  * application that's secure and can be audited to verify that with much more ease than bolting
17373  * security onto an application later.
17374  *
17375  * <a name="contexts"></a>
17376  * ## What trusted context types are supported?
17377  *
17378  * | Context             | Notes          |
17379  * |---------------------|----------------|
17380  * | `$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. |
17381  * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
17382  * | `$sce.URL`          | For URLs that are safe to follow as links.  Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |
17383  * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application.  Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.)  <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
17384  * | `$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. |
17385  *
17386  * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
17387  *
17388  *  Each element in these arrays must be one of the following:
17389  *
17390  *  - **'self'**
17391  *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
17392  *      domain** as the application document using the **same protocol**.
17393  *  - **String** (except the special value `'self'`)
17394  *    - The string is matched against the full *normalized / absolute URL* of the resource
17395  *      being tested (substring matches are not good enough.)
17396  *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
17397  *      match themselves.
17398  *    - `*`: matches zero or more occurrences of any character other than one of the following 6
17399  *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
17400  *      in a whitelist.
17401  *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
17402  *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
17403  *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17404  *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
17405  *      http://foo.example.com/templates/**).
17406  *  - **RegExp** (*see caveat below*)
17407  *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
17408  *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
17409  *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17410  *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
17411  *      small number of cases.  A `.` character in the regex used when matching the scheme or a
17412  *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
17413  *      is highly recommended to use the string patterns and only fall back to regular expressions
17414  *      as a last resort.
17415  *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
17416  *      matched against the **entire** *normalized / absolute URL* of the resource being tested
17417  *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
17418  *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17419  *    - If you are generating your JavaScript from some other templating engine (not
17420  *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17421  *      remember to escape your regular expression (and be aware that you might need more than
17422  *      one level of escaping depending on your templating engine and the way you interpolated
17423  *      the value.)  Do make use of your platform's escaping mechanism as it might be good
17424  *      enough before coding your own.  E.g. Ruby has
17425  *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17426  *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17427  *      Javascript lacks a similar built in function for escaping.  Take a look at Google
17428  *      Closure library's [goog.string.regExpEscape(s)](
17429  *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17430  *
17431  * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17432  *
17433  * ## Show me an example using SCE.
17434  *
17435  * <example module="mySceApp" deps="angular-sanitize.js">
17436  * <file name="index.html">
17437  *   <div ng-controller="AppController as myCtrl">
17438  *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17439  *     <b>User comments</b><br>
17440  *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17441  *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
17442  *     exploit.
17443  *     <div class="well">
17444  *       <div ng-repeat="userComment in myCtrl.userComments">
17445  *         <b>{{userComment.name}}</b>:
17446  *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17447  *         <br>
17448  *       </div>
17449  *     </div>
17450  *   </div>
17451  * </file>
17452  *
17453  * <file name="script.js">
17454  *   angular.module('mySceApp', ['ngSanitize'])
17455  *     .controller('AppController', ['$http', '$templateCache', '$sce',
17456  *       function($http, $templateCache, $sce) {
17457  *         var self = this;
17458  *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17459  *           self.userComments = userComments;
17460  *         });
17461  *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
17462  *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17463  *             'sanitization.&quot;">Hover over this text.</span>');
17464  *       }]);
17465  * </file>
17466  *
17467  * <file name="test_data.json">
17468  * [
17469  *   { "name": "Alice",
17470  *     "htmlComment":
17471  *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17472  *   },
17473  *   { "name": "Bob",
17474  *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
17475  *   }
17476  * ]
17477  * </file>
17478  *
17479  * <file name="protractor.js" type="protractor">
17480  *   describe('SCE doc demo', function() {
17481  *     it('should sanitize untrusted values', function() {
17482  *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17483  *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
17484  *     });
17485  *
17486  *     it('should NOT sanitize explicitly trusted values', function() {
17487  *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17488  *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17489  *           'sanitization.&quot;">Hover over this text.</span>');
17490  *     });
17491  *   });
17492  * </file>
17493  * </example>
17494  *
17495  *
17496  *
17497  * ## Can I disable SCE completely?
17498  *
17499  * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
17500  * for little coding overhead.  It will be much harder to take an SCE disabled application and
17501  * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
17502  * for cases where you have a lot of existing code that was written before SCE was introduced and
17503  * you're migrating them a module at a time.
17504  *
17505  * That said, here's how you can completely disable SCE:
17506  *
17507  * ```
17508  * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17509  *   // Completely disable SCE.  For demonstration purposes only!
17510  *   // Do not use in new projects.
17511  *   $sceProvider.enabled(false);
17512  * });
17513  * ```
17514  *
17515  */
17516 /* jshint maxlen: 100 */
17517
17518 function $SceProvider() {
17519   var enabled = true;
17520
17521   /**
17522    * @ngdoc method
17523    * @name $sceProvider#enabled
17524    * @kind function
17525    *
17526    * @param {boolean=} value If provided, then enables/disables SCE.
17527    * @return {boolean} true if SCE is enabled, false otherwise.
17528    *
17529    * @description
17530    * Enables/disables SCE and returns the current value.
17531    */
17532   this.enabled = function(value) {
17533     if (arguments.length) {
17534       enabled = !!value;
17535     }
17536     return enabled;
17537   };
17538
17539
17540   /* Design notes on the default implementation for SCE.
17541    *
17542    * The API contract for the SCE delegate
17543    * -------------------------------------
17544    * The SCE delegate object must provide the following 3 methods:
17545    *
17546    * - trustAs(contextEnum, value)
17547    *     This method is used to tell the SCE service that the provided value is OK to use in the
17548    *     contexts specified by contextEnum.  It must return an object that will be accepted by
17549    *     getTrusted() for a compatible contextEnum and return this value.
17550    *
17551    * - valueOf(value)
17552    *     For values that were not produced by trustAs(), return them as is.  For values that were
17553    *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
17554    *     trustAs is wrapping the given values into some type, this operation unwraps it when given
17555    *     such a value.
17556    *
17557    * - getTrusted(contextEnum, value)
17558    *     This function should return the a value that is safe to use in the context specified by
17559    *     contextEnum or throw and exception otherwise.
17560    *
17561    * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17562    * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
17563    * instance, an implementation could maintain a registry of all trusted objects by context.  In
17564    * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
17565    * return the same object passed in if it was found in the registry under a compatible context or
17566    * throw an exception otherwise.  An implementation might only wrap values some of the time based
17567    * on some criteria.  getTrusted() might return a value and not throw an exception for special
17568    * constants or objects even if not wrapped.  All such implementations fulfill this contract.
17569    *
17570    *
17571    * A note on the inheritance model for SCE contexts
17572    * ------------------------------------------------
17573    * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
17574    * is purely an implementation details.
17575    *
17576    * The contract is simply this:
17577    *
17578    *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17579    *     will also succeed.
17580    *
17581    * Inheritance happens to capture this in a natural way.  In some future, we
17582    * may not use inheritance anymore.  That is OK because no code outside of
17583    * sce.js and sceSpecs.js would need to be aware of this detail.
17584    */
17585
17586   this.$get = ['$parse', '$sceDelegate', function(
17587                 $parse,   $sceDelegate) {
17588     // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
17589     // the "expression(javascript expression)" syntax which is insecure.
17590     if (enabled && msie < 8) {
17591       throw $sceMinErr('iequirks',
17592         'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17593         'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17594         'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
17595     }
17596
17597     var sce = shallowCopy(SCE_CONTEXTS);
17598
17599     /**
17600      * @ngdoc method
17601      * @name $sce#isEnabled
17602      * @kind function
17603      *
17604      * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
17605      * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17606      *
17607      * @description
17608      * Returns a boolean indicating if SCE is enabled.
17609      */
17610     sce.isEnabled = function() {
17611       return enabled;
17612     };
17613     sce.trustAs = $sceDelegate.trustAs;
17614     sce.getTrusted = $sceDelegate.getTrusted;
17615     sce.valueOf = $sceDelegate.valueOf;
17616
17617     if (!enabled) {
17618       sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17619       sce.valueOf = identity;
17620     }
17621
17622     /**
17623      * @ngdoc method
17624      * @name $sce#parseAs
17625      *
17626      * @description
17627      * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
17628      * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
17629      * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17630      * *result*)}
17631      *
17632      * @param {string} type The kind of SCE context in which this result will be used.
17633      * @param {string} expression String expression to compile.
17634      * @returns {function(context, locals)} a function which represents the compiled expression:
17635      *
17636      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17637      *      are evaluated against (typically a scope object).
17638      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17639      *      `context`.
17640      */
17641     sce.parseAs = function sceParseAs(type, expr) {
17642       var parsed = $parse(expr);
17643       if (parsed.literal && parsed.constant) {
17644         return parsed;
17645       } else {
17646         return $parse(expr, function(value) {
17647           return sce.getTrusted(type, value);
17648         });
17649       }
17650     };
17651
17652     /**
17653      * @ngdoc method
17654      * @name $sce#trustAs
17655      *
17656      * @description
17657      * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
17658      * returns an object that is trusted by angular for use in specified strict contextual
17659      * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17660      * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
17661      * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
17662      * escaping.
17663      *
17664      * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17665      *   resourceUrl, html, js and css.
17666      * @param {*} value The value that that should be considered trusted/safe.
17667      * @returns {*} A value that can be used to stand in for the provided `value` in places
17668      * where Angular expects a $sce.trustAs() return value.
17669      */
17670
17671     /**
17672      * @ngdoc method
17673      * @name $sce#trustAsHtml
17674      *
17675      * @description
17676      * Shorthand method.  `$sce.trustAsHtml(value)` →
17677      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17678      *
17679      * @param {*} value The value to trustAs.
17680      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17681      *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
17682      *     only accept expressions that are either literal constants or are the
17683      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17684      */
17685
17686     /**
17687      * @ngdoc method
17688      * @name $sce#trustAsUrl
17689      *
17690      * @description
17691      * Shorthand method.  `$sce.trustAsUrl(value)` →
17692      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17693      *
17694      * @param {*} value The value to trustAs.
17695      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17696      *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
17697      *     only accept expressions that are either literal constants or are the
17698      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17699      */
17700
17701     /**
17702      * @ngdoc method
17703      * @name $sce#trustAsResourceUrl
17704      *
17705      * @description
17706      * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
17707      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17708      *
17709      * @param {*} value The value to trustAs.
17710      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17711      *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
17712      *     only accept expressions that are either literal constants or are the return
17713      *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
17714      */
17715
17716     /**
17717      * @ngdoc method
17718      * @name $sce#trustAsJs
17719      *
17720      * @description
17721      * Shorthand method.  `$sce.trustAsJs(value)` →
17722      *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17723      *
17724      * @param {*} value The value to trustAs.
17725      * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17726      *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
17727      *     only accept expressions that are either literal constants or are the
17728      *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17729      */
17730
17731     /**
17732      * @ngdoc method
17733      * @name $sce#getTrusted
17734      *
17735      * @description
17736      * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
17737      * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17738      * originally supplied value if the queried context type is a supertype of the created type.
17739      * If this condition isn't satisfied, throws an exception.
17740      *
17741      * @param {string} type The kind of context in which this value is to be used.
17742      * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17743      *                         call.
17744      * @returns {*} The value the was originally provided to
17745      *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17746      *              Otherwise, throws an exception.
17747      */
17748
17749     /**
17750      * @ngdoc method
17751      * @name $sce#getTrustedHtml
17752      *
17753      * @description
17754      * Shorthand method.  `$sce.getTrustedHtml(value)` →
17755      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17756      *
17757      * @param {*} value The value to pass to `$sce.getTrusted`.
17758      * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17759      */
17760
17761     /**
17762      * @ngdoc method
17763      * @name $sce#getTrustedCss
17764      *
17765      * @description
17766      * Shorthand method.  `$sce.getTrustedCss(value)` →
17767      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17768      *
17769      * @param {*} value The value to pass to `$sce.getTrusted`.
17770      * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17771      */
17772
17773     /**
17774      * @ngdoc method
17775      * @name $sce#getTrustedUrl
17776      *
17777      * @description
17778      * Shorthand method.  `$sce.getTrustedUrl(value)` →
17779      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17780      *
17781      * @param {*} value The value to pass to `$sce.getTrusted`.
17782      * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17783      */
17784
17785     /**
17786      * @ngdoc method
17787      * @name $sce#getTrustedResourceUrl
17788      *
17789      * @description
17790      * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
17791      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17792      *
17793      * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17794      * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17795      */
17796
17797     /**
17798      * @ngdoc method
17799      * @name $sce#getTrustedJs
17800      *
17801      * @description
17802      * Shorthand method.  `$sce.getTrustedJs(value)` →
17803      *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17804      *
17805      * @param {*} value The value to pass to `$sce.getTrusted`.
17806      * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17807      */
17808
17809     /**
17810      * @ngdoc method
17811      * @name $sce#parseAsHtml
17812      *
17813      * @description
17814      * Shorthand method.  `$sce.parseAsHtml(expression string)` →
17815      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17816      *
17817      * @param {string} expression String expression to compile.
17818      * @returns {function(context, locals)} a function which represents the compiled expression:
17819      *
17820      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17821      *      are evaluated against (typically a scope object).
17822      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17823      *      `context`.
17824      */
17825
17826     /**
17827      * @ngdoc method
17828      * @name $sce#parseAsCss
17829      *
17830      * @description
17831      * Shorthand method.  `$sce.parseAsCss(value)` →
17832      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17833      *
17834      * @param {string} expression String expression to compile.
17835      * @returns {function(context, locals)} a function which represents the compiled expression:
17836      *
17837      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17838      *      are evaluated against (typically a scope object).
17839      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17840      *      `context`.
17841      */
17842
17843     /**
17844      * @ngdoc method
17845      * @name $sce#parseAsUrl
17846      *
17847      * @description
17848      * Shorthand method.  `$sce.parseAsUrl(value)` →
17849      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17850      *
17851      * @param {string} expression String expression to compile.
17852      * @returns {function(context, locals)} a function which represents the compiled expression:
17853      *
17854      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17855      *      are evaluated against (typically a scope object).
17856      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17857      *      `context`.
17858      */
17859
17860     /**
17861      * @ngdoc method
17862      * @name $sce#parseAsResourceUrl
17863      *
17864      * @description
17865      * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
17866      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17867      *
17868      * @param {string} expression String expression to compile.
17869      * @returns {function(context, locals)} a function which represents the compiled expression:
17870      *
17871      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17872      *      are evaluated against (typically a scope object).
17873      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17874      *      `context`.
17875      */
17876
17877     /**
17878      * @ngdoc method
17879      * @name $sce#parseAsJs
17880      *
17881      * @description
17882      * Shorthand method.  `$sce.parseAsJs(value)` →
17883      *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17884      *
17885      * @param {string} expression String expression to compile.
17886      * @returns {function(context, locals)} a function which represents the compiled expression:
17887      *
17888      *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17889      *      are evaluated against (typically a scope object).
17890      *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17891      *      `context`.
17892      */
17893
17894     // Shorthand delegations.
17895     var parse = sce.parseAs,
17896         getTrusted = sce.getTrusted,
17897         trustAs = sce.trustAs;
17898
17899     forEach(SCE_CONTEXTS, function(enumValue, name) {
17900       var lName = lowercase(name);
17901       sce[camelCase("parse_as_" + lName)] = function(expr) {
17902         return parse(enumValue, expr);
17903       };
17904       sce[camelCase("get_trusted_" + lName)] = function(value) {
17905         return getTrusted(enumValue, value);
17906       };
17907       sce[camelCase("trust_as_" + lName)] = function(value) {
17908         return trustAs(enumValue, value);
17909       };
17910     });
17911
17912     return sce;
17913   }];
17914 }
17915
17916 /**
17917  * !!! This is an undocumented "private" service !!!
17918  *
17919  * @name $sniffer
17920  * @requires $window
17921  * @requires $document
17922  *
17923  * @property {boolean} history Does the browser support html5 history api ?
17924  * @property {boolean} transitions Does the browser support CSS transition events ?
17925  * @property {boolean} animations Does the browser support CSS animation events ?
17926  *
17927  * @description
17928  * This is very simple implementation of testing browser's features.
17929  */
17930 function $SnifferProvider() {
17931   this.$get = ['$window', '$document', function($window, $document) {
17932     var eventSupport = {},
17933         android =
17934           toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17935         boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17936         document = $document[0] || {},
17937         vendorPrefix,
17938         vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17939         bodyStyle = document.body && document.body.style,
17940         transitions = false,
17941         animations = false,
17942         match;
17943
17944     if (bodyStyle) {
17945       for (var prop in bodyStyle) {
17946         if (match = vendorRegex.exec(prop)) {
17947           vendorPrefix = match[0];
17948           vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17949           break;
17950         }
17951       }
17952
17953       if (!vendorPrefix) {
17954         vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17955       }
17956
17957       transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17958       animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17959
17960       if (android && (!transitions ||  !animations)) {
17961         transitions = isString(bodyStyle.webkitTransition);
17962         animations = isString(bodyStyle.webkitAnimation);
17963       }
17964     }
17965
17966
17967     return {
17968       // Android has history.pushState, but it does not update location correctly
17969       // so let's not use the history API at all.
17970       // http://code.google.com/p/android/issues/detail?id=17471
17971       // https://github.com/angular/angular.js/issues/904
17972
17973       // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17974       // so let's not use the history API also
17975       // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17976       // jshint -W018
17977       history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17978       // jshint +W018
17979       hasEvent: function(event) {
17980         // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17981         // it. In particular the event is not fired when backspace or delete key are pressed or
17982         // when cut operation is performed.
17983         // IE10+ implements 'input' event but it erroneously fires under various situations,
17984         // e.g. when placeholder changes, or a form is focused.
17985         if (event === 'input' && msie <= 11) return false;
17986
17987         if (isUndefined(eventSupport[event])) {
17988           var divElm = document.createElement('div');
17989           eventSupport[event] = 'on' + event in divElm;
17990         }
17991
17992         return eventSupport[event];
17993       },
17994       csp: csp(),
17995       vendorPrefix: vendorPrefix,
17996       transitions: transitions,
17997       animations: animations,
17998       android: android
17999     };
18000   }];
18001 }
18002
18003 var $compileMinErr = minErr('$compile');
18004
18005 /**
18006  * @ngdoc service
18007  * @name $templateRequest
18008  *
18009  * @description
18010  * The `$templateRequest` service runs security checks then downloads the provided template using
18011  * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
18012  * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
18013  * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
18014  * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
18015  * when `tpl` is of type string and `$templateCache` has the matching entry.
18016  *
18017  * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
18018  * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
18019  *
18020  * @return {Promise} a promise for the HTTP response data of the given URL.
18021  *
18022  * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
18023  */
18024 function $TemplateRequestProvider() {
18025   this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
18026     function handleRequestFn(tpl, ignoreRequestError) {
18027       handleRequestFn.totalPendingRequests++;
18028
18029       // We consider the template cache holds only trusted templates, so
18030       // there's no need to go through whitelisting again for keys that already
18031       // are included in there. This also makes Angular accept any script
18032       // directive, no matter its name. However, we still need to unwrap trusted
18033       // types.
18034       if (!isString(tpl) || !$templateCache.get(tpl)) {
18035         tpl = $sce.getTrustedResourceUrl(tpl);
18036       }
18037
18038       var transformResponse = $http.defaults && $http.defaults.transformResponse;
18039
18040       if (isArray(transformResponse)) {
18041         transformResponse = transformResponse.filter(function(transformer) {
18042           return transformer !== defaultHttpResponseTransform;
18043         });
18044       } else if (transformResponse === defaultHttpResponseTransform) {
18045         transformResponse = null;
18046       }
18047
18048       var httpOptions = {
18049         cache: $templateCache,
18050         transformResponse: transformResponse
18051       };
18052
18053       return $http.get(tpl, httpOptions)
18054         ['finally'](function() {
18055           handleRequestFn.totalPendingRequests--;
18056         })
18057         .then(function(response) {
18058           $templateCache.put(tpl, response.data);
18059           return response.data;
18060         }, handleError);
18061
18062       function handleError(resp) {
18063         if (!ignoreRequestError) {
18064           throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
18065             tpl, resp.status, resp.statusText);
18066         }
18067         return $q.reject(resp);
18068       }
18069     }
18070
18071     handleRequestFn.totalPendingRequests = 0;
18072
18073     return handleRequestFn;
18074   }];
18075 }
18076
18077 function $$TestabilityProvider() {
18078   this.$get = ['$rootScope', '$browser', '$location',
18079        function($rootScope,   $browser,   $location) {
18080
18081     /**
18082      * @name $testability
18083      *
18084      * @description
18085      * The private $$testability service provides a collection of methods for use when debugging
18086      * or by automated test and debugging tools.
18087      */
18088     var testability = {};
18089
18090     /**
18091      * @name $$testability#findBindings
18092      *
18093      * @description
18094      * Returns an array of elements that are bound (via ng-bind or {{}})
18095      * to expressions matching the input.
18096      *
18097      * @param {Element} element The element root to search from.
18098      * @param {string} expression The binding expression to match.
18099      * @param {boolean} opt_exactMatch If true, only returns exact matches
18100      *     for the expression. Filters and whitespace are ignored.
18101      */
18102     testability.findBindings = function(element, expression, opt_exactMatch) {
18103       var bindings = element.getElementsByClassName('ng-binding');
18104       var matches = [];
18105       forEach(bindings, function(binding) {
18106         var dataBinding = angular.element(binding).data('$binding');
18107         if (dataBinding) {
18108           forEach(dataBinding, function(bindingName) {
18109             if (opt_exactMatch) {
18110               var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
18111               if (matcher.test(bindingName)) {
18112                 matches.push(binding);
18113               }
18114             } else {
18115               if (bindingName.indexOf(expression) != -1) {
18116                 matches.push(binding);
18117               }
18118             }
18119           });
18120         }
18121       });
18122       return matches;
18123     };
18124
18125     /**
18126      * @name $$testability#findModels
18127      *
18128      * @description
18129      * Returns an array of elements that are two-way found via ng-model to
18130      * expressions matching the input.
18131      *
18132      * @param {Element} element The element root to search from.
18133      * @param {string} expression The model expression to match.
18134      * @param {boolean} opt_exactMatch If true, only returns exact matches
18135      *     for the expression.
18136      */
18137     testability.findModels = function(element, expression, opt_exactMatch) {
18138       var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
18139       for (var p = 0; p < prefixes.length; ++p) {
18140         var attributeEquals = opt_exactMatch ? '=' : '*=';
18141         var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
18142         var elements = element.querySelectorAll(selector);
18143         if (elements.length) {
18144           return elements;
18145         }
18146       }
18147     };
18148
18149     /**
18150      * @name $$testability#getLocation
18151      *
18152      * @description
18153      * Shortcut for getting the location in a browser agnostic way. Returns
18154      *     the path, search, and hash. (e.g. /path?a=b#hash)
18155      */
18156     testability.getLocation = function() {
18157       return $location.url();
18158     };
18159
18160     /**
18161      * @name $$testability#setLocation
18162      *
18163      * @description
18164      * Shortcut for navigating to a location without doing a full page reload.
18165      *
18166      * @param {string} url The location url (path, search and hash,
18167      *     e.g. /path?a=b#hash) to go to.
18168      */
18169     testability.setLocation = function(url) {
18170       if (url !== $location.url()) {
18171         $location.url(url);
18172         $rootScope.$digest();
18173       }
18174     };
18175
18176     /**
18177      * @name $$testability#whenStable
18178      *
18179      * @description
18180      * Calls the callback when $timeout and $http requests are completed.
18181      *
18182      * @param {function} callback
18183      */
18184     testability.whenStable = function(callback) {
18185       $browser.notifyWhenNoOutstandingRequests(callback);
18186     };
18187
18188     return testability;
18189   }];
18190 }
18191
18192 function $TimeoutProvider() {
18193   this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
18194        function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
18195
18196     var deferreds = {};
18197
18198
18199      /**
18200       * @ngdoc service
18201       * @name $timeout
18202       *
18203       * @description
18204       * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
18205       * block and delegates any exceptions to
18206       * {@link ng.$exceptionHandler $exceptionHandler} service.
18207       *
18208       * The return value of calling `$timeout` is a promise, which will be resolved when
18209       * the delay has passed and the timeout function, if provided, is executed.
18210       *
18211       * To cancel a timeout request, call `$timeout.cancel(promise)`.
18212       *
18213       * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
18214       * synchronously flush the queue of deferred functions.
18215       *
18216       * If you only want a promise that will be resolved after some specified delay
18217       * then you can call `$timeout` without the `fn` function.
18218       *
18219       * @param {function()=} fn A function, whose execution should be delayed.
18220       * @param {number=} [delay=0] Delay in milliseconds.
18221       * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
18222       *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
18223       * @param {...*=} Pass additional parameters to the executed function.
18224       * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
18225       *   will be resolved with the return value of the `fn` function.
18226       *
18227       */
18228     function timeout(fn, delay, invokeApply) {
18229       if (!isFunction(fn)) {
18230         invokeApply = delay;
18231         delay = fn;
18232         fn = noop;
18233       }
18234
18235       var args = sliceArgs(arguments, 3),
18236           skipApply = (isDefined(invokeApply) && !invokeApply),
18237           deferred = (skipApply ? $$q : $q).defer(),
18238           promise = deferred.promise,
18239           timeoutId;
18240
18241       timeoutId = $browser.defer(function() {
18242         try {
18243           deferred.resolve(fn.apply(null, args));
18244         } catch (e) {
18245           deferred.reject(e);
18246           $exceptionHandler(e);
18247         }
18248         finally {
18249           delete deferreds[promise.$$timeoutId];
18250         }
18251
18252         if (!skipApply) $rootScope.$apply();
18253       }, delay);
18254
18255       promise.$$timeoutId = timeoutId;
18256       deferreds[timeoutId] = deferred;
18257
18258       return promise;
18259     }
18260
18261
18262      /**
18263       * @ngdoc method
18264       * @name $timeout#cancel
18265       *
18266       * @description
18267       * Cancels a task associated with the `promise`. As a result of this, the promise will be
18268       * resolved with a rejection.
18269       *
18270       * @param {Promise=} promise Promise returned by the `$timeout` function.
18271       * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
18272       *   canceled.
18273       */
18274     timeout.cancel = function(promise) {
18275       if (promise && promise.$$timeoutId in deferreds) {
18276         deferreds[promise.$$timeoutId].reject('canceled');
18277         delete deferreds[promise.$$timeoutId];
18278         return $browser.defer.cancel(promise.$$timeoutId);
18279       }
18280       return false;
18281     };
18282
18283     return timeout;
18284   }];
18285 }
18286
18287 // NOTE:  The usage of window and document instead of $window and $document here is
18288 // deliberate.  This service depends on the specific behavior of anchor nodes created by the
18289 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
18290 // cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
18291 // doesn't know about mocked locations and resolves URLs to the real document - which is
18292 // exactly the behavior needed here.  There is little value is mocking these out for this
18293 // service.
18294 var urlParsingNode = document.createElement("a");
18295 var originUrl = urlResolve(window.location.href);
18296
18297
18298 /**
18299  *
18300  * Implementation Notes for non-IE browsers
18301  * ----------------------------------------
18302  * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
18303  * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
18304  * URL will be resolved into an absolute URL in the context of the application document.
18305  * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
18306  * properties are all populated to reflect the normalized URL.  This approach has wide
18307  * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
18308  * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18309  *
18310  * Implementation Notes for IE
18311  * ---------------------------
18312  * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
18313  * browsers.  However, the parsed components will not be set if the URL assigned did not specify
18314  * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
18315  * work around that by performing the parsing in a 2nd step by taking a previously normalized
18316  * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
18317  * properties such as protocol, hostname, port, etc.
18318  *
18319  * References:
18320  *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
18321  *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18322  *   http://url.spec.whatwg.org/#urlutils
18323  *   https://github.com/angular/angular.js/pull/2902
18324  *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
18325  *
18326  * @kind function
18327  * @param {string} url The URL to be parsed.
18328  * @description Normalizes and parses a URL.
18329  * @returns {object} Returns the normalized URL as a dictionary.
18330  *
18331  *   | member name   | Description    |
18332  *   |---------------|----------------|
18333  *   | href          | A normalized version of the provided URL if it was not an absolute URL |
18334  *   | protocol      | The protocol including the trailing colon                              |
18335  *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
18336  *   | search        | The search params, minus the question mark                             |
18337  *   | hash          | The hash string, minus the hash symbol
18338  *   | hostname      | The hostname
18339  *   | port          | The port, without ":"
18340  *   | pathname      | The pathname, beginning with "/"
18341  *
18342  */
18343 function urlResolve(url) {
18344   var href = url;
18345
18346   if (msie) {
18347     // Normalize before parse.  Refer Implementation Notes on why this is
18348     // done in two steps on IE.
18349     urlParsingNode.setAttribute("href", href);
18350     href = urlParsingNode.href;
18351   }
18352
18353   urlParsingNode.setAttribute('href', href);
18354
18355   // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
18356   return {
18357     href: urlParsingNode.href,
18358     protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
18359     host: urlParsingNode.host,
18360     search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
18361     hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
18362     hostname: urlParsingNode.hostname,
18363     port: urlParsingNode.port,
18364     pathname: (urlParsingNode.pathname.charAt(0) === '/')
18365       ? urlParsingNode.pathname
18366       : '/' + urlParsingNode.pathname
18367   };
18368 }
18369
18370 /**
18371  * Parse a request URL and determine whether this is a same-origin request as the application document.
18372  *
18373  * @param {string|object} requestUrl The url of the request as a string that will be resolved
18374  * or a parsed URL object.
18375  * @returns {boolean} Whether the request is for the same origin as the application document.
18376  */
18377 function urlIsSameOrigin(requestUrl) {
18378   var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
18379   return (parsed.protocol === originUrl.protocol &&
18380           parsed.host === originUrl.host);
18381 }
18382
18383 /**
18384  * @ngdoc service
18385  * @name $window
18386  *
18387  * @description
18388  * A reference to the browser's `window` object. While `window`
18389  * is globally available in JavaScript, it causes testability problems, because
18390  * it is a global variable. In angular we always refer to it through the
18391  * `$window` service, so it may be overridden, removed or mocked for testing.
18392  *
18393  * Expressions, like the one defined for the `ngClick` directive in the example
18394  * below, are evaluated with respect to the current scope.  Therefore, there is
18395  * no risk of inadvertently coding in a dependency on a global value in such an
18396  * expression.
18397  *
18398  * @example
18399    <example module="windowExample">
18400      <file name="index.html">
18401        <script>
18402          angular.module('windowExample', [])
18403            .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18404              $scope.greeting = 'Hello, World!';
18405              $scope.doGreeting = function(greeting) {
18406                $window.alert(greeting);
18407              };
18408            }]);
18409        </script>
18410        <div ng-controller="ExampleController">
18411          <input type="text" ng-model="greeting" aria-label="greeting" />
18412          <button ng-click="doGreeting(greeting)">ALERT</button>
18413        </div>
18414      </file>
18415      <file name="protractor.js" type="protractor">
18416       it('should display the greeting in the input box', function() {
18417        element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18418        // If we click the button it will block the test runner
18419        // element(':button').click();
18420       });
18421      </file>
18422    </example>
18423  */
18424 function $WindowProvider() {
18425   this.$get = valueFn(window);
18426 }
18427
18428 /**
18429  * @name $$cookieReader
18430  * @requires $document
18431  *
18432  * @description
18433  * This is a private service for reading cookies used by $http and ngCookies
18434  *
18435  * @return {Object} a key/value map of the current cookies
18436  */
18437 function $$CookieReader($document) {
18438   var rawDocument = $document[0] || {};
18439   var lastCookies = {};
18440   var lastCookieString = '';
18441
18442   function safeDecodeURIComponent(str) {
18443     try {
18444       return decodeURIComponent(str);
18445     } catch (e) {
18446       return str;
18447     }
18448   }
18449
18450   return function() {
18451     var cookieArray, cookie, i, index, name;
18452     var currentCookieString = rawDocument.cookie || '';
18453
18454     if (currentCookieString !== lastCookieString) {
18455       lastCookieString = currentCookieString;
18456       cookieArray = lastCookieString.split('; ');
18457       lastCookies = {};
18458
18459       for (i = 0; i < cookieArray.length; i++) {
18460         cookie = cookieArray[i];
18461         index = cookie.indexOf('=');
18462         if (index > 0) { //ignore nameless cookies
18463           name = safeDecodeURIComponent(cookie.substring(0, index));
18464           // the first value that is seen for a cookie is the most
18465           // specific one.  values for the same cookie name that
18466           // follow are for less specific paths.
18467           if (isUndefined(lastCookies[name])) {
18468             lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18469           }
18470         }
18471       }
18472     }
18473     return lastCookies;
18474   };
18475 }
18476
18477 $$CookieReader.$inject = ['$document'];
18478
18479 function $$CookieReaderProvider() {
18480   this.$get = $$CookieReader;
18481 }
18482
18483 /* global currencyFilter: true,
18484  dateFilter: true,
18485  filterFilter: true,
18486  jsonFilter: true,
18487  limitToFilter: true,
18488  lowercaseFilter: true,
18489  numberFilter: true,
18490  orderByFilter: true,
18491  uppercaseFilter: true,
18492  */
18493
18494 /**
18495  * @ngdoc provider
18496  * @name $filterProvider
18497  * @description
18498  *
18499  * Filters are just functions which transform input to an output. However filters need to be
18500  * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18501  * annotated with dependencies and is responsible for creating a filter function.
18502  *
18503  * <div class="alert alert-warning">
18504  * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18505  * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18506  * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18507  * (`myapp_subsection_filterx`).
18508  * </div>
18509  *
18510  * ```js
18511  *   // Filter registration
18512  *   function MyModule($provide, $filterProvider) {
18513  *     // create a service to demonstrate injection (not always needed)
18514  *     $provide.value('greet', function(name){
18515  *       return 'Hello ' + name + '!';
18516  *     });
18517  *
18518  *     // register a filter factory which uses the
18519  *     // greet service to demonstrate DI.
18520  *     $filterProvider.register('greet', function(greet){
18521  *       // return the filter function which uses the greet service
18522  *       // to generate salutation
18523  *       return function(text) {
18524  *         // filters need to be forgiving so check input validity
18525  *         return text && greet(text) || text;
18526  *       };
18527  *     });
18528  *   }
18529  * ```
18530  *
18531  * The filter function is registered with the `$injector` under the filter name suffix with
18532  * `Filter`.
18533  *
18534  * ```js
18535  *   it('should be the same instance', inject(
18536  *     function($filterProvider) {
18537  *       $filterProvider.register('reverse', function(){
18538  *         return ...;
18539  *       });
18540  *     },
18541  *     function($filter, reverseFilter) {
18542  *       expect($filter('reverse')).toBe(reverseFilter);
18543  *     });
18544  * ```
18545  *
18546  *
18547  * For more information about how angular filters work, and how to create your own filters, see
18548  * {@link guide/filter Filters} in the Angular Developer Guide.
18549  */
18550
18551 /**
18552  * @ngdoc service
18553  * @name $filter
18554  * @kind function
18555  * @description
18556  * Filters are used for formatting data displayed to the user.
18557  *
18558  * The general syntax in templates is as follows:
18559  *
18560  *         {{ expression [| filter_name[:parameter_value] ... ] }}
18561  *
18562  * @param {String} name Name of the filter function to retrieve
18563  * @return {Function} the filter function
18564  * @example
18565    <example name="$filter" module="filterExample">
18566      <file name="index.html">
18567        <div ng-controller="MainCtrl">
18568         <h3>{{ originalText }}</h3>
18569         <h3>{{ filteredText }}</h3>
18570        </div>
18571      </file>
18572
18573      <file name="script.js">
18574       angular.module('filterExample', [])
18575       .controller('MainCtrl', function($scope, $filter) {
18576         $scope.originalText = 'hello';
18577         $scope.filteredText = $filter('uppercase')($scope.originalText);
18578       });
18579      </file>
18580    </example>
18581   */
18582 $FilterProvider.$inject = ['$provide'];
18583 function $FilterProvider($provide) {
18584   var suffix = 'Filter';
18585
18586   /**
18587    * @ngdoc method
18588    * @name $filterProvider#register
18589    * @param {string|Object} name Name of the filter function, or an object map of filters where
18590    *    the keys are the filter names and the values are the filter factories.
18591    *
18592    *    <div class="alert alert-warning">
18593    *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18594    *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18595    *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18596    *    (`myapp_subsection_filterx`).
18597    *    </div>
18598     * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18599    * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18600    *    of the registered filter instances.
18601    */
18602   function register(name, factory) {
18603     if (isObject(name)) {
18604       var filters = {};
18605       forEach(name, function(filter, key) {
18606         filters[key] = register(key, filter);
18607       });
18608       return filters;
18609     } else {
18610       return $provide.factory(name + suffix, factory);
18611     }
18612   }
18613   this.register = register;
18614
18615   this.$get = ['$injector', function($injector) {
18616     return function(name) {
18617       return $injector.get(name + suffix);
18618     };
18619   }];
18620
18621   ////////////////////////////////////////
18622
18623   /* global
18624     currencyFilter: false,
18625     dateFilter: false,
18626     filterFilter: false,
18627     jsonFilter: false,
18628     limitToFilter: false,
18629     lowercaseFilter: false,
18630     numberFilter: false,
18631     orderByFilter: false,
18632     uppercaseFilter: false,
18633   */
18634
18635   register('currency', currencyFilter);
18636   register('date', dateFilter);
18637   register('filter', filterFilter);
18638   register('json', jsonFilter);
18639   register('limitTo', limitToFilter);
18640   register('lowercase', lowercaseFilter);
18641   register('number', numberFilter);
18642   register('orderBy', orderByFilter);
18643   register('uppercase', uppercaseFilter);
18644 }
18645
18646 /**
18647  * @ngdoc filter
18648  * @name filter
18649  * @kind function
18650  *
18651  * @description
18652  * Selects a subset of items from `array` and returns it as a new array.
18653  *
18654  * @param {Array} array The source array.
18655  * @param {string|Object|function()} expression The predicate to be used for selecting items from
18656  *   `array`.
18657  *
18658  *   Can be one of:
18659  *
18660  *   - `string`: The string is used for matching against the contents of the `array`. All strings or
18661  *     objects with string properties in `array` that match this string will be returned. This also
18662  *     applies to nested object properties.
18663  *     The predicate can be negated by prefixing the string with `!`.
18664  *
18665  *   - `Object`: A pattern object can be used to filter specific properties on objects contained
18666  *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18667  *     which have property `name` containing "M" and property `phone` containing "1". A special
18668  *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18669  *     property of the object or its nested object properties. That's equivalent to the simple
18670  *     substring match with a `string` as described above. The predicate can be negated by prefixing
18671  *     the string with `!`.
18672  *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
18673  *     not containing "M".
18674  *
18675  *     Note that a named property will match properties on the same level only, while the special
18676  *     `$` property will match properties on the same level or deeper. E.g. an array item like
18677  *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18678  *     **will** be matched by `{$: 'John'}`.
18679  *
18680  *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18681  *     The function is called for each element of the array, with the element, its index, and
18682  *     the entire array itself as arguments.
18683  *
18684  *     The final result is an array of those elements that the predicate returned true for.
18685  *
18686  * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18687  *     determining if the expected value (from the filter expression) and actual value (from
18688  *     the object in the array) should be considered a match.
18689  *
18690  *   Can be one of:
18691  *
18692  *   - `function(actual, expected)`:
18693  *     The function will be given the object value and the predicate value to compare and
18694  *     should return true if both values should be considered equal.
18695  *
18696  *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18697  *     This is essentially strict comparison of expected and actual.
18698  *
18699  *   - `false|undefined`: A short hand for a function which will look for a substring match in case
18700  *     insensitive way.
18701  *
18702  *     Primitive values are converted to strings. Objects are not compared against primitives,
18703  *     unless they have a custom `toString` method (e.g. `Date` objects).
18704  *
18705  * @example
18706    <example>
18707      <file name="index.html">
18708        <div ng-init="friends = [{name:'John', phone:'555-1276'},
18709                                 {name:'Mary', phone:'800-BIG-MARY'},
18710                                 {name:'Mike', phone:'555-4321'},
18711                                 {name:'Adam', phone:'555-5678'},
18712                                 {name:'Julie', phone:'555-8765'},
18713                                 {name:'Juliette', phone:'555-5678'}]"></div>
18714
18715        <label>Search: <input ng-model="searchText"></label>
18716        <table id="searchTextResults">
18717          <tr><th>Name</th><th>Phone</th></tr>
18718          <tr ng-repeat="friend in friends | filter:searchText">
18719            <td>{{friend.name}}</td>
18720            <td>{{friend.phone}}</td>
18721          </tr>
18722        </table>
18723        <hr>
18724        <label>Any: <input ng-model="search.$"></label> <br>
18725        <label>Name only <input ng-model="search.name"></label><br>
18726        <label>Phone only <input ng-model="search.phone"></label><br>
18727        <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18728        <table id="searchObjResults">
18729          <tr><th>Name</th><th>Phone</th></tr>
18730          <tr ng-repeat="friendObj in friends | filter:search:strict">
18731            <td>{{friendObj.name}}</td>
18732            <td>{{friendObj.phone}}</td>
18733          </tr>
18734        </table>
18735      </file>
18736      <file name="protractor.js" type="protractor">
18737        var expectFriendNames = function(expectedNames, key) {
18738          element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18739            arr.forEach(function(wd, i) {
18740              expect(wd.getText()).toMatch(expectedNames[i]);
18741            });
18742          });
18743        };
18744
18745        it('should search across all fields when filtering with a string', function() {
18746          var searchText = element(by.model('searchText'));
18747          searchText.clear();
18748          searchText.sendKeys('m');
18749          expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18750
18751          searchText.clear();
18752          searchText.sendKeys('76');
18753          expectFriendNames(['John', 'Julie'], 'friend');
18754        });
18755
18756        it('should search in specific fields when filtering with a predicate object', function() {
18757          var searchAny = element(by.model('search.$'));
18758          searchAny.clear();
18759          searchAny.sendKeys('i');
18760          expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18761        });
18762        it('should use a equal comparison when comparator is true', function() {
18763          var searchName = element(by.model('search.name'));
18764          var strict = element(by.model('strict'));
18765          searchName.clear();
18766          searchName.sendKeys('Julie');
18767          strict.click();
18768          expectFriendNames(['Julie'], 'friendObj');
18769        });
18770      </file>
18771    </example>
18772  */
18773 function filterFilter() {
18774   return function(array, expression, comparator) {
18775     if (!isArrayLike(array)) {
18776       if (array == null) {
18777         return array;
18778       } else {
18779         throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18780       }
18781     }
18782
18783     var expressionType = getTypeForFilter(expression);
18784     var predicateFn;
18785     var matchAgainstAnyProp;
18786
18787     switch (expressionType) {
18788       case 'function':
18789         predicateFn = expression;
18790         break;
18791       case 'boolean':
18792       case 'null':
18793       case 'number':
18794       case 'string':
18795         matchAgainstAnyProp = true;
18796         //jshint -W086
18797       case 'object':
18798         //jshint +W086
18799         predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18800         break;
18801       default:
18802         return array;
18803     }
18804
18805     return Array.prototype.filter.call(array, predicateFn);
18806   };
18807 }
18808
18809 // Helper functions for `filterFilter`
18810 function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18811   var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18812   var predicateFn;
18813
18814   if (comparator === true) {
18815     comparator = equals;
18816   } else if (!isFunction(comparator)) {
18817     comparator = function(actual, expected) {
18818       if (isUndefined(actual)) {
18819         // No substring matching against `undefined`
18820         return false;
18821       }
18822       if ((actual === null) || (expected === null)) {
18823         // No substring matching against `null`; only match against `null`
18824         return actual === expected;
18825       }
18826       if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18827         // Should not compare primitives against objects, unless they have custom `toString` method
18828         return false;
18829       }
18830
18831       actual = lowercase('' + actual);
18832       expected = lowercase('' + expected);
18833       return actual.indexOf(expected) !== -1;
18834     };
18835   }
18836
18837   predicateFn = function(item) {
18838     if (shouldMatchPrimitives && !isObject(item)) {
18839       return deepCompare(item, expression.$, comparator, false);
18840     }
18841     return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18842   };
18843
18844   return predicateFn;
18845 }
18846
18847 function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18848   var actualType = getTypeForFilter(actual);
18849   var expectedType = getTypeForFilter(expected);
18850
18851   if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18852     return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18853   } else if (isArray(actual)) {
18854     // In case `actual` is an array, consider it a match
18855     // if ANY of it's items matches `expected`
18856     return actual.some(function(item) {
18857       return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18858     });
18859   }
18860
18861   switch (actualType) {
18862     case 'object':
18863       var key;
18864       if (matchAgainstAnyProp) {
18865         for (key in actual) {
18866           if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18867             return true;
18868           }
18869         }
18870         return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18871       } else if (expectedType === 'object') {
18872         for (key in expected) {
18873           var expectedVal = expected[key];
18874           if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18875             continue;
18876           }
18877
18878           var matchAnyProperty = key === '$';
18879           var actualVal = matchAnyProperty ? actual : actual[key];
18880           if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18881             return false;
18882           }
18883         }
18884         return true;
18885       } else {
18886         return comparator(actual, expected);
18887       }
18888       break;
18889     case 'function':
18890       return false;
18891     default:
18892       return comparator(actual, expected);
18893   }
18894 }
18895
18896 // Used for easily differentiating between `null` and actual `object`
18897 function getTypeForFilter(val) {
18898   return (val === null) ? 'null' : typeof val;
18899 }
18900
18901 var MAX_DIGITS = 22;
18902 var DECIMAL_SEP = '.';
18903 var ZERO_CHAR = '0';
18904
18905 /**
18906  * @ngdoc filter
18907  * @name currency
18908  * @kind function
18909  *
18910  * @description
18911  * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18912  * symbol for current locale is used.
18913  *
18914  * @param {number} amount Input to filter.
18915  * @param {string=} symbol Currency symbol or identifier to be displayed.
18916  * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18917  * @returns {string} Formatted number.
18918  *
18919  *
18920  * @example
18921    <example module="currencyExample">
18922      <file name="index.html">
18923        <script>
18924          angular.module('currencyExample', [])
18925            .controller('ExampleController', ['$scope', function($scope) {
18926              $scope.amount = 1234.56;
18927            }]);
18928        </script>
18929        <div ng-controller="ExampleController">
18930          <input type="number" ng-model="amount" aria-label="amount"> <br>
18931          default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18932          custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18933          no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18934        </div>
18935      </file>
18936      <file name="protractor.js" type="protractor">
18937        it('should init with 1234.56', function() {
18938          expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18939          expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18940          expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18941        });
18942        it('should update', function() {
18943          if (browser.params.browser == 'safari') {
18944            // Safari does not understand the minus key. See
18945            // https://github.com/angular/protractor/issues/481
18946            return;
18947          }
18948          element(by.model('amount')).clear();
18949          element(by.model('amount')).sendKeys('-1234');
18950          expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
18951          expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
18952          expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
18953        });
18954      </file>
18955    </example>
18956  */
18957 currencyFilter.$inject = ['$locale'];
18958 function currencyFilter($locale) {
18959   var formats = $locale.NUMBER_FORMATS;
18960   return function(amount, currencySymbol, fractionSize) {
18961     if (isUndefined(currencySymbol)) {
18962       currencySymbol = formats.CURRENCY_SYM;
18963     }
18964
18965     if (isUndefined(fractionSize)) {
18966       fractionSize = formats.PATTERNS[1].maxFrac;
18967     }
18968
18969     // if null or undefined pass it through
18970     return (amount == null)
18971         ? amount
18972         : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18973             replace(/\u00A4/g, currencySymbol);
18974   };
18975 }
18976
18977 /**
18978  * @ngdoc filter
18979  * @name number
18980  * @kind function
18981  *
18982  * @description
18983  * Formats a number as text.
18984  *
18985  * If the input is null or undefined, it will just be returned.
18986  * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
18987  * If the input is not a number an empty string is returned.
18988  *
18989  *
18990  * @param {number|string} number Number to format.
18991  * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18992  * If this is not provided then the fraction size is computed from the current locale's number
18993  * formatting pattern. In the case of the default locale, it will be 3.
18994  * @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
18995  *
18996  * @example
18997    <example module="numberFilterExample">
18998      <file name="index.html">
18999        <script>
19000          angular.module('numberFilterExample', [])
19001            .controller('ExampleController', ['$scope', function($scope) {
19002              $scope.val = 1234.56789;
19003            }]);
19004        </script>
19005        <div ng-controller="ExampleController">
19006          <label>Enter number: <input ng-model='val'></label><br>
19007          Default formatting: <span id='number-default'>{{val | number}}</span><br>
19008          No fractions: <span>{{val | number:0}}</span><br>
19009          Negative number: <span>{{-val | number:4}}</span>
19010        </div>
19011      </file>
19012      <file name="protractor.js" type="protractor">
19013        it('should format numbers', function() {
19014          expect(element(by.id('number-default')).getText()).toBe('1,234.568');
19015          expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
19016          expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
19017        });
19018
19019        it('should update', function() {
19020          element(by.model('val')).clear();
19021          element(by.model('val')).sendKeys('3374.333');
19022          expect(element(by.id('number-default')).getText()).toBe('3,374.333');
19023          expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
19024          expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
19025       });
19026      </file>
19027    </example>
19028  */
19029 numberFilter.$inject = ['$locale'];
19030 function numberFilter($locale) {
19031   var formats = $locale.NUMBER_FORMATS;
19032   return function(number, fractionSize) {
19033
19034     // if null or undefined pass it through
19035     return (number == null)
19036         ? number
19037         : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
19038                        fractionSize);
19039   };
19040 }
19041
19042 /**
19043  * Parse a number (as a string) into three components that can be used
19044  * for formatting the number.
19045  *
19046  * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
19047  *
19048  * @param  {string} numStr The number to parse
19049  * @return {object} An object describing this number, containing the following keys:
19050  *  - d : an array of digits containing leading zeros as necessary
19051  *  - i : the number of the digits in `d` that are to the left of the decimal point
19052  *  - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
19053  *
19054  */
19055 function parse(numStr) {
19056   var exponent = 0, digits, numberOfIntegerDigits;
19057   var i, j, zeros;
19058
19059   // Decimal point?
19060   if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
19061     numStr = numStr.replace(DECIMAL_SEP, '');
19062   }
19063
19064   // Exponential form?
19065   if ((i = numStr.search(/e/i)) > 0) {
19066     // Work out the exponent.
19067     if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
19068     numberOfIntegerDigits += +numStr.slice(i + 1);
19069     numStr = numStr.substring(0, i);
19070   } else if (numberOfIntegerDigits < 0) {
19071     // There was no decimal point or exponent so it is an integer.
19072     numberOfIntegerDigits = numStr.length;
19073   }
19074
19075   // Count the number of leading zeros.
19076   for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
19077
19078   if (i == (zeros = numStr.length)) {
19079     // The digits are all zero.
19080     digits = [0];
19081     numberOfIntegerDigits = 1;
19082   } else {
19083     // Count the number of trailing zeros
19084     zeros--;
19085     while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
19086
19087     // Trailing zeros are insignificant so ignore them
19088     numberOfIntegerDigits -= i;
19089     digits = [];
19090     // Convert string to array of digits without leading/trailing zeros.
19091     for (j = 0; i <= zeros; i++, j++) {
19092       digits[j] = +numStr.charAt(i);
19093     }
19094   }
19095
19096   // If the number overflows the maximum allowed digits then use an exponent.
19097   if (numberOfIntegerDigits > MAX_DIGITS) {
19098     digits = digits.splice(0, MAX_DIGITS - 1);
19099     exponent = numberOfIntegerDigits - 1;
19100     numberOfIntegerDigits = 1;
19101   }
19102
19103   return { d: digits, e: exponent, i: numberOfIntegerDigits };
19104 }
19105
19106 /**
19107  * Round the parsed number to the specified number of decimal places
19108  * This function changed the parsedNumber in-place
19109  */
19110 function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
19111     var digits = parsedNumber.d;
19112     var fractionLen = digits.length - parsedNumber.i;
19113
19114     // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
19115     fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
19116
19117     // The index of the digit to where rounding is to occur
19118     var roundAt = fractionSize + parsedNumber.i;
19119     var digit = digits[roundAt];
19120
19121     if (roundAt > 0) {
19122       digits.splice(roundAt);
19123     } else {
19124       // We rounded to zero so reset the parsedNumber
19125       parsedNumber.i = 1;
19126       digits.length = roundAt = fractionSize + 1;
19127       for (var i=0; i < roundAt; i++) digits[i] = 0;
19128     }
19129
19130     if (digit >= 5) digits[roundAt - 1]++;
19131
19132     // Pad out with zeros to get the required fraction length
19133     for (; fractionLen < fractionSize; fractionLen++) digits.push(0);
19134
19135
19136     // Do any carrying, e.g. a digit was rounded up to 10
19137     var carry = digits.reduceRight(function(carry, d, i, digits) {
19138       d = d + carry;
19139       digits[i] = d % 10;
19140       return Math.floor(d / 10);
19141     }, 0);
19142     if (carry) {
19143       digits.unshift(carry);
19144       parsedNumber.i++;
19145     }
19146 }
19147
19148 /**
19149  * Format a number into a string
19150  * @param  {number} number       The number to format
19151  * @param  {{
19152  *           minFrac, // the minimum number of digits required in the fraction part of the number
19153  *           maxFrac, // the maximum number of digits required in the fraction part of the number
19154  *           gSize,   // number of digits in each group of separated digits
19155  *           lgSize,  // number of digits in the last group of digits before the decimal separator
19156  *           negPre,  // the string to go in front of a negative number (e.g. `-` or `(`))
19157  *           posPre,  // the string to go in front of a positive number
19158  *           negSuf,  // the string to go after a negative number (e.g. `)`)
19159  *           posSuf   // the string to go after a positive number
19160  *         }} pattern
19161  * @param  {string} groupSep     The string to separate groups of number (e.g. `,`)
19162  * @param  {string} decimalSep   The string to act as the decimal separator (e.g. `.`)
19163  * @param  {[type]} fractionSize The size of the fractional part of the number
19164  * @return {string}              The number formatted as a string
19165  */
19166 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
19167
19168   if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
19169
19170   var isInfinity = !isFinite(number);
19171   var isZero = false;
19172   var numStr = Math.abs(number) + '',
19173       formattedText = '',
19174       parsedNumber;
19175
19176   if (isInfinity) {
19177     formattedText = '\u221e';
19178   } else {
19179     parsedNumber = parse(numStr);
19180
19181     roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
19182
19183     var digits = parsedNumber.d;
19184     var integerLen = parsedNumber.i;
19185     var exponent = parsedNumber.e;
19186     var decimals = [];
19187     isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
19188
19189     // pad zeros for small numbers
19190     while (integerLen < 0) {
19191       digits.unshift(0);
19192       integerLen++;
19193     }
19194
19195     // extract decimals digits
19196     if (integerLen > 0) {
19197       decimals = digits.splice(integerLen);
19198     } else {
19199       decimals = digits;
19200       digits = [0];
19201     }
19202
19203     // format the integer digits with grouping separators
19204     var groups = [];
19205     if (digits.length > pattern.lgSize) {
19206       groups.unshift(digits.splice(-pattern.lgSize).join(''));
19207     }
19208     while (digits.length > pattern.gSize) {
19209       groups.unshift(digits.splice(-pattern.gSize).join(''));
19210     }
19211     if (digits.length) {
19212       groups.unshift(digits.join(''));
19213     }
19214     formattedText = groups.join(groupSep);
19215
19216     // append the decimal digits
19217     if (decimals.length) {
19218       formattedText += decimalSep + decimals.join('');
19219     }
19220
19221     if (exponent) {
19222       formattedText += 'e+' + exponent;
19223     }
19224   }
19225   if (number < 0 && !isZero) {
19226     return pattern.negPre + formattedText + pattern.negSuf;
19227   } else {
19228     return pattern.posPre + formattedText + pattern.posSuf;
19229   }
19230 }
19231
19232 function padNumber(num, digits, trim) {
19233   var neg = '';
19234   if (num < 0) {
19235     neg =  '-';
19236     num = -num;
19237   }
19238   num = '' + num;
19239   while (num.length < digits) num = ZERO_CHAR + num;
19240   if (trim) {
19241     num = num.substr(num.length - digits);
19242   }
19243   return neg + num;
19244 }
19245
19246
19247 function dateGetter(name, size, offset, trim) {
19248   offset = offset || 0;
19249   return function(date) {
19250     var value = date['get' + name]();
19251     if (offset > 0 || value > -offset) {
19252       value += offset;
19253     }
19254     if (value === 0 && offset == -12) value = 12;
19255     return padNumber(value, size, trim);
19256   };
19257 }
19258
19259 function dateStrGetter(name, shortForm) {
19260   return function(date, formats) {
19261     var value = date['get' + name]();
19262     var get = uppercase(shortForm ? ('SHORT' + name) : name);
19263
19264     return formats[get][value];
19265   };
19266 }
19267
19268 function timeZoneGetter(date, formats, offset) {
19269   var zone = -1 * offset;
19270   var paddedZone = (zone >= 0) ? "+" : "";
19271
19272   paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
19273                 padNumber(Math.abs(zone % 60), 2);
19274
19275   return paddedZone;
19276 }
19277
19278 function getFirstThursdayOfYear(year) {
19279     // 0 = index of January
19280     var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
19281     // 4 = index of Thursday (+1 to account for 1st = 5)
19282     // 11 = index of *next* Thursday (+1 account for 1st = 12)
19283     return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
19284 }
19285
19286 function getThursdayThisWeek(datetime) {
19287     return new Date(datetime.getFullYear(), datetime.getMonth(),
19288       // 4 = index of Thursday
19289       datetime.getDate() + (4 - datetime.getDay()));
19290 }
19291
19292 function weekGetter(size) {
19293    return function(date) {
19294       var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
19295          thisThurs = getThursdayThisWeek(date);
19296
19297       var diff = +thisThurs - +firstThurs,
19298          result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
19299
19300       return padNumber(result, size);
19301    };
19302 }
19303
19304 function ampmGetter(date, formats) {
19305   return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
19306 }
19307
19308 function eraGetter(date, formats) {
19309   return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
19310 }
19311
19312 function longEraGetter(date, formats) {
19313   return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
19314 }
19315
19316 var DATE_FORMATS = {
19317   yyyy: dateGetter('FullYear', 4),
19318     yy: dateGetter('FullYear', 2, 0, true),
19319      y: dateGetter('FullYear', 1),
19320   MMMM: dateStrGetter('Month'),
19321    MMM: dateStrGetter('Month', true),
19322     MM: dateGetter('Month', 2, 1),
19323      M: dateGetter('Month', 1, 1),
19324     dd: dateGetter('Date', 2),
19325      d: dateGetter('Date', 1),
19326     HH: dateGetter('Hours', 2),
19327      H: dateGetter('Hours', 1),
19328     hh: dateGetter('Hours', 2, -12),
19329      h: dateGetter('Hours', 1, -12),
19330     mm: dateGetter('Minutes', 2),
19331      m: dateGetter('Minutes', 1),
19332     ss: dateGetter('Seconds', 2),
19333      s: dateGetter('Seconds', 1),
19334      // while ISO 8601 requires fractions to be prefixed with `.` or `,`
19335      // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
19336    sss: dateGetter('Milliseconds', 3),
19337   EEEE: dateStrGetter('Day'),
19338    EEE: dateStrGetter('Day', true),
19339      a: ampmGetter,
19340      Z: timeZoneGetter,
19341     ww: weekGetter(2),
19342      w: weekGetter(1),
19343      G: eraGetter,
19344      GG: eraGetter,
19345      GGG: eraGetter,
19346      GGGG: longEraGetter
19347 };
19348
19349 var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
19350     NUMBER_STRING = /^\-?\d+$/;
19351
19352 /**
19353  * @ngdoc filter
19354  * @name date
19355  * @kind function
19356  *
19357  * @description
19358  *   Formats `date` to a string based on the requested `format`.
19359  *
19360  *   `format` string can be composed of the following elements:
19361  *
19362  *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
19363  *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
19364  *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
19365  *   * `'MMMM'`: Month in year (January-December)
19366  *   * `'MMM'`: Month in year (Jan-Dec)
19367  *   * `'MM'`: Month in year, padded (01-12)
19368  *   * `'M'`: Month in year (1-12)
19369  *   * `'dd'`: Day in month, padded (01-31)
19370  *   * `'d'`: Day in month (1-31)
19371  *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
19372  *   * `'EEE'`: Day in Week, (Sun-Sat)
19373  *   * `'HH'`: Hour in day, padded (00-23)
19374  *   * `'H'`: Hour in day (0-23)
19375  *   * `'hh'`: Hour in AM/PM, padded (01-12)
19376  *   * `'h'`: Hour in AM/PM, (1-12)
19377  *   * `'mm'`: Minute in hour, padded (00-59)
19378  *   * `'m'`: Minute in hour (0-59)
19379  *   * `'ss'`: Second in minute, padded (00-59)
19380  *   * `'s'`: Second in minute (0-59)
19381  *   * `'sss'`: Millisecond in second, padded (000-999)
19382  *   * `'a'`: AM/PM marker
19383  *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
19384  *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
19385  *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
19386  *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
19387  *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
19388  *
19389  *   `format` string can also be one of the following predefined
19390  *   {@link guide/i18n localizable formats}:
19391  *
19392  *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
19393  *     (e.g. Sep 3, 2010 12:05:08 PM)
19394  *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
19395  *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
19396  *     (e.g. Friday, September 3, 2010)
19397  *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
19398  *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
19399  *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
19400  *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
19401  *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
19402  *
19403  *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
19404  *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
19405  *   (e.g. `"h 'o''clock'"`).
19406  *
19407  * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
19408  *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
19409  *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
19410  *    specified in the string input, the time is considered to be in the local timezone.
19411  * @param {string=} format Formatting rules (see Description). If not specified,
19412  *    `mediumDate` is used.
19413  * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
19414  *    continental US time zone abbreviations, but for general use, use a time zone offset, for
19415  *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
19416  *    If not specified, the timezone of the browser will be used.
19417  * @returns {string} Formatted string or the input if input is not recognized as date/millis.
19418  *
19419  * @example
19420    <example>
19421      <file name="index.html">
19422        <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
19423            <span>{{1288323623006 | date:'medium'}}</span><br>
19424        <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
19425           <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
19426        <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
19427           <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
19428        <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
19429           <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
19430      </file>
19431      <file name="protractor.js" type="protractor">
19432        it('should format date', function() {
19433          expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
19434             toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19435          expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
19436             toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19437          expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
19438             toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19439          expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
19440             toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
19441        });
19442      </file>
19443    </example>
19444  */
19445 dateFilter.$inject = ['$locale'];
19446 function dateFilter($locale) {
19447
19448
19449   var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
19450                      // 1        2       3         4          5          6          7          8  9     10      11
19451   function jsonStringToDate(string) {
19452     var match;
19453     if (match = string.match(R_ISO8601_STR)) {
19454       var date = new Date(0),
19455           tzHour = 0,
19456           tzMin  = 0,
19457           dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
19458           timeSetter = match[8] ? date.setUTCHours : date.setHours;
19459
19460       if (match[9]) {
19461         tzHour = toInt(match[9] + match[10]);
19462         tzMin = toInt(match[9] + match[11]);
19463       }
19464       dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
19465       var h = toInt(match[4] || 0) - tzHour;
19466       var m = toInt(match[5] || 0) - tzMin;
19467       var s = toInt(match[6] || 0);
19468       var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
19469       timeSetter.call(date, h, m, s, ms);
19470       return date;
19471     }
19472     return string;
19473   }
19474
19475
19476   return function(date, format, timezone) {
19477     var text = '',
19478         parts = [],
19479         fn, match;
19480
19481     format = format || 'mediumDate';
19482     format = $locale.DATETIME_FORMATS[format] || format;
19483     if (isString(date)) {
19484       date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
19485     }
19486
19487     if (isNumber(date)) {
19488       date = new Date(date);
19489     }
19490
19491     if (!isDate(date) || !isFinite(date.getTime())) {
19492       return date;
19493     }
19494
19495     while (format) {
19496       match = DATE_FORMATS_SPLIT.exec(format);
19497       if (match) {
19498         parts = concat(parts, match, 1);
19499         format = parts.pop();
19500       } else {
19501         parts.push(format);
19502         format = null;
19503       }
19504     }
19505
19506     var dateTimezoneOffset = date.getTimezoneOffset();
19507     if (timezone) {
19508       dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
19509       date = convertTimezoneToLocal(date, timezone, true);
19510     }
19511     forEach(parts, function(value) {
19512       fn = DATE_FORMATS[value];
19513       text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19514                  : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19515     });
19516
19517     return text;
19518   };
19519 }
19520
19521
19522 /**
19523  * @ngdoc filter
19524  * @name json
19525  * @kind function
19526  *
19527  * @description
19528  *   Allows you to convert a JavaScript object into JSON string.
19529  *
19530  *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
19531  *   the binding is automatically converted to JSON.
19532  *
19533  * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19534  * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
19535  * @returns {string} JSON string.
19536  *
19537  *
19538  * @example
19539    <example>
19540      <file name="index.html">
19541        <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19542        <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19543      </file>
19544      <file name="protractor.js" type="protractor">
19545        it('should jsonify filtered objects', function() {
19546          expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
19547          expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
19548        });
19549      </file>
19550    </example>
19551  *
19552  */
19553 function jsonFilter() {
19554   return function(object, spacing) {
19555     if (isUndefined(spacing)) {
19556         spacing = 2;
19557     }
19558     return toJson(object, spacing);
19559   };
19560 }
19561
19562
19563 /**
19564  * @ngdoc filter
19565  * @name lowercase
19566  * @kind function
19567  * @description
19568  * Converts string to lowercase.
19569  * @see angular.lowercase
19570  */
19571 var lowercaseFilter = valueFn(lowercase);
19572
19573
19574 /**
19575  * @ngdoc filter
19576  * @name uppercase
19577  * @kind function
19578  * @description
19579  * Converts string to uppercase.
19580  * @see angular.uppercase
19581  */
19582 var uppercaseFilter = valueFn(uppercase);
19583
19584 /**
19585  * @ngdoc filter
19586  * @name limitTo
19587  * @kind function
19588  *
19589  * @description
19590  * Creates a new array or string containing only a specified number of elements. The elements
19591  * are taken from either the beginning or the end of the source array, string or number, as specified by
19592  * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19593  * converted to a string.
19594  *
19595  * @param {Array|string|number} input Source array, string or number to be limited.
19596  * @param {string|number} limit The length of the returned array or string. If the `limit` number
19597  *     is positive, `limit` number of items from the beginning of the source array/string are copied.
19598  *     If the number is negative, `limit` number  of items from the end of the source array/string
19599  *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19600  *     the input will be returned unchanged.
19601  * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19602  *     indicates an offset from the end of `input`. Defaults to `0`.
19603  * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19604  *     had less than `limit` elements.
19605  *
19606  * @example
19607    <example module="limitToExample">
19608      <file name="index.html">
19609        <script>
19610          angular.module('limitToExample', [])
19611            .controller('ExampleController', ['$scope', function($scope) {
19612              $scope.numbers = [1,2,3,4,5,6,7,8,9];
19613              $scope.letters = "abcdefghi";
19614              $scope.longNumber = 2345432342;
19615              $scope.numLimit = 3;
19616              $scope.letterLimit = 3;
19617              $scope.longNumberLimit = 3;
19618            }]);
19619        </script>
19620        <div ng-controller="ExampleController">
19621          <label>
19622             Limit {{numbers}} to:
19623             <input type="number" step="1" ng-model="numLimit">
19624          </label>
19625          <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
19626          <label>
19627             Limit {{letters}} to:
19628             <input type="number" step="1" ng-model="letterLimit">
19629          </label>
19630          <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
19631          <label>
19632             Limit {{longNumber}} to:
19633             <input type="number" step="1" ng-model="longNumberLimit">
19634          </label>
19635          <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
19636        </div>
19637      </file>
19638      <file name="protractor.js" type="protractor">
19639        var numLimitInput = element(by.model('numLimit'));
19640        var letterLimitInput = element(by.model('letterLimit'));
19641        var longNumberLimitInput = element(by.model('longNumberLimit'));
19642        var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
19643        var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
19644        var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
19645
19646        it('should limit the number array to first three items', function() {
19647          expect(numLimitInput.getAttribute('value')).toBe('3');
19648          expect(letterLimitInput.getAttribute('value')).toBe('3');
19649          expect(longNumberLimitInput.getAttribute('value')).toBe('3');
19650          expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
19651          expect(limitedLetters.getText()).toEqual('Output letters: abc');
19652          expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
19653        });
19654
19655        // There is a bug in safari and protractor that doesn't like the minus key
19656        // it('should update the output when -3 is entered', function() {
19657        //   numLimitInput.clear();
19658        //   numLimitInput.sendKeys('-3');
19659        //   letterLimitInput.clear();
19660        //   letterLimitInput.sendKeys('-3');
19661        //   longNumberLimitInput.clear();
19662        //   longNumberLimitInput.sendKeys('-3');
19663        //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19664        //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19665        //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19666        // });
19667
19668        it('should not exceed the maximum size of input array', function() {
19669          numLimitInput.clear();
19670          numLimitInput.sendKeys('100');
19671          letterLimitInput.clear();
19672          letterLimitInput.sendKeys('100');
19673          longNumberLimitInput.clear();
19674          longNumberLimitInput.sendKeys('100');
19675          expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19676          expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19677          expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19678        });
19679      </file>
19680    </example>
19681 */
19682 function limitToFilter() {
19683   return function(input, limit, begin) {
19684     if (Math.abs(Number(limit)) === Infinity) {
19685       limit = Number(limit);
19686     } else {
19687       limit = toInt(limit);
19688     }
19689     if (isNaN(limit)) return input;
19690
19691     if (isNumber(input)) input = input.toString();
19692     if (!isArray(input) && !isString(input)) return input;
19693
19694     begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19695     begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
19696
19697     if (limit >= 0) {
19698       return input.slice(begin, begin + limit);
19699     } else {
19700       if (begin === 0) {
19701         return input.slice(limit, input.length);
19702       } else {
19703         return input.slice(Math.max(0, begin + limit), begin);
19704       }
19705     }
19706   };
19707 }
19708
19709 /**
19710  * @ngdoc filter
19711  * @name orderBy
19712  * @kind function
19713  *
19714  * @description
19715  * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19716  * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19717  * as expected, make sure they are actually being saved as numbers and not strings.
19718  *
19719  * @param {Array} array The array to sort.
19720  * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19721  *    used by the comparator to determine the order of elements.
19722  *
19723  *    Can be one of:
19724  *
19725  *    - `function`: Getter function. The result of this function will be sorted using the
19726  *      `<`, `===`, `>` operator.
19727  *    - `string`: An Angular expression. The result of this expression is used to compare elements
19728  *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19729  *      3 first characters of a property called `name`). The result of a constant expression
19730  *      is interpreted as a property name to be used in comparisons (for example `"special name"`
19731  *      to sort object by the value of their `special name` property). An expression can be
19732  *      optionally prefixed with `+` or `-` to control ascending or descending sort order
19733  *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19734  *      element itself is used to compare where sorting.
19735  *    - `Array`: An array of function or string predicates. The first predicate in the array
19736  *      is used for sorting, but when two items are equivalent, the next predicate is used.
19737  *
19738  *    If the predicate is missing or empty then it defaults to `'+'`.
19739  *
19740  * @param {boolean=} reverse Reverse the order of the array.
19741  * @returns {Array} Sorted copy of the source array.
19742  *
19743  *
19744  * @example
19745  * The example below demonstrates a simple ngRepeat, where the data is sorted
19746  * by age in descending order (predicate is set to `'-age'`).
19747  * `reverse` is not set, which means it defaults to `false`.
19748    <example module="orderByExample">
19749      <file name="index.html">
19750        <div ng-controller="ExampleController">
19751          <table class="friend">
19752            <tr>
19753              <th>Name</th>
19754              <th>Phone Number</th>
19755              <th>Age</th>
19756            </tr>
19757            <tr ng-repeat="friend in friends | orderBy:'-age'">
19758              <td>{{friend.name}}</td>
19759              <td>{{friend.phone}}</td>
19760              <td>{{friend.age}}</td>
19761            </tr>
19762          </table>
19763        </div>
19764      </file>
19765      <file name="script.js">
19766        angular.module('orderByExample', [])
19767          .controller('ExampleController', ['$scope', function($scope) {
19768            $scope.friends =
19769                [{name:'John', phone:'555-1212', age:10},
19770                 {name:'Mary', phone:'555-9876', age:19},
19771                 {name:'Mike', phone:'555-4321', age:21},
19772                 {name:'Adam', phone:'555-5678', age:35},
19773                 {name:'Julie', phone:'555-8765', age:29}];
19774          }]);
19775      </file>
19776    </example>
19777  *
19778  * The predicate and reverse parameters can be controlled dynamically through scope properties,
19779  * as shown in the next example.
19780  * @example
19781    <example module="orderByExample">
19782      <file name="index.html">
19783        <div ng-controller="ExampleController">
19784          <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19785          <hr/>
19786          <button ng-click="predicate=''">Set to unsorted</button>
19787          <table class="friend">
19788            <tr>
19789             <th>
19790                 <button ng-click="order('name')">Name</button>
19791                 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19792             </th>
19793             <th>
19794                 <button ng-click="order('phone')">Phone Number</button>
19795                 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19796             </th>
19797             <th>
19798                 <button ng-click="order('age')">Age</button>
19799                 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19800             </th>
19801            </tr>
19802            <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19803              <td>{{friend.name}}</td>
19804              <td>{{friend.phone}}</td>
19805              <td>{{friend.age}}</td>
19806            </tr>
19807          </table>
19808        </div>
19809      </file>
19810      <file name="script.js">
19811        angular.module('orderByExample', [])
19812          .controller('ExampleController', ['$scope', function($scope) {
19813            $scope.friends =
19814                [{name:'John', phone:'555-1212', age:10},
19815                 {name:'Mary', phone:'555-9876', age:19},
19816                 {name:'Mike', phone:'555-4321', age:21},
19817                 {name:'Adam', phone:'555-5678', age:35},
19818                 {name:'Julie', phone:'555-8765', age:29}];
19819            $scope.predicate = 'age';
19820            $scope.reverse = true;
19821            $scope.order = function(predicate) {
19822              $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19823              $scope.predicate = predicate;
19824            };
19825          }]);
19826       </file>
19827      <file name="style.css">
19828        .sortorder:after {
19829          content: '\25b2';
19830        }
19831        .sortorder.reverse:after {
19832          content: '\25bc';
19833        }
19834      </file>
19835    </example>
19836  *
19837  * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19838  * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19839  * desired parameters.
19840  *
19841  * Example:
19842  *
19843  * @example
19844   <example module="orderByExample">
19845     <file name="index.html">
19846     <div ng-controller="ExampleController">
19847       <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19848       <table class="friend">
19849         <tr>
19850           <th>
19851               <button ng-click="order('name')">Name</button>
19852               <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19853           </th>
19854           <th>
19855               <button ng-click="order('phone')">Phone Number</button>
19856               <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19857           </th>
19858           <th>
19859               <button ng-click="order('age')">Age</button>
19860               <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19861           </th>
19862         </tr>
19863         <tr ng-repeat="friend in friends">
19864           <td>{{friend.name}}</td>
19865           <td>{{friend.phone}}</td>
19866           <td>{{friend.age}}</td>
19867         </tr>
19868       </table>
19869     </div>
19870     </file>
19871
19872     <file name="script.js">
19873       angular.module('orderByExample', [])
19874         .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19875           var orderBy = $filter('orderBy');
19876           $scope.friends = [
19877             { name: 'John',    phone: '555-1212',    age: 10 },
19878             { name: 'Mary',    phone: '555-9876',    age: 19 },
19879             { name: 'Mike',    phone: '555-4321',    age: 21 },
19880             { name: 'Adam',    phone: '555-5678',    age: 35 },
19881             { name: 'Julie',   phone: '555-8765',    age: 29 }
19882           ];
19883           $scope.order = function(predicate) {
19884             $scope.predicate = predicate;
19885             $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19886             $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
19887           };
19888           $scope.order('age', true);
19889         }]);
19890     </file>
19891
19892     <file name="style.css">
19893        .sortorder:after {
19894          content: '\25b2';
19895        }
19896        .sortorder.reverse:after {
19897          content: '\25bc';
19898        }
19899     </file>
19900 </example>
19901  */
19902 orderByFilter.$inject = ['$parse'];
19903 function orderByFilter($parse) {
19904   return function(array, sortPredicate, reverseOrder) {
19905
19906     if (!(isArrayLike(array))) return array;
19907
19908     if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
19909     if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19910
19911     var predicates = processPredicates(sortPredicate, reverseOrder);
19912     // Add a predicate at the end that evaluates to the element index. This makes the
19913     // sort stable as it works as a tie-breaker when all the input predicates cannot
19914     // distinguish between two elements.
19915     predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
19916
19917     // The next three lines are a version of a Swartzian Transform idiom from Perl
19918     // (sometimes called the Decorate-Sort-Undecorate idiom)
19919     // See https://en.wikipedia.org/wiki/Schwartzian_transform
19920     var compareValues = Array.prototype.map.call(array, getComparisonObject);
19921     compareValues.sort(doComparison);
19922     array = compareValues.map(function(item) { return item.value; });
19923
19924     return array;
19925
19926     function getComparisonObject(value, index) {
19927       return {
19928         value: value,
19929         predicateValues: predicates.map(function(predicate) {
19930           return getPredicateValue(predicate.get(value), index);
19931         })
19932       };
19933     }
19934
19935     function doComparison(v1, v2) {
19936       var result = 0;
19937       for (var index=0, length = predicates.length; index < length; ++index) {
19938         result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
19939         if (result) break;
19940       }
19941       return result;
19942     }
19943   };
19944
19945   function processPredicates(sortPredicate, reverseOrder) {
19946     reverseOrder = reverseOrder ? -1 : 1;
19947     return sortPredicate.map(function(predicate) {
19948       var descending = 1, get = identity;
19949
19950       if (isFunction(predicate)) {
19951         get = predicate;
19952       } else if (isString(predicate)) {
19953         if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19954           descending = predicate.charAt(0) == '-' ? -1 : 1;
19955           predicate = predicate.substring(1);
19956         }
19957         if (predicate !== '') {
19958           get = $parse(predicate);
19959           if (get.constant) {
19960             var key = get();
19961             get = function(value) { return value[key]; };
19962           }
19963         }
19964       }
19965       return { get: get, descending: descending * reverseOrder };
19966     });
19967   }
19968
19969   function isPrimitive(value) {
19970     switch (typeof value) {
19971       case 'number': /* falls through */
19972       case 'boolean': /* falls through */
19973       case 'string':
19974         return true;
19975       default:
19976         return false;
19977     }
19978   }
19979
19980   function objectValue(value, index) {
19981     // If `valueOf` is a valid function use that
19982     if (typeof value.valueOf === 'function') {
19983       value = value.valueOf();
19984       if (isPrimitive(value)) return value;
19985     }
19986     // If `toString` is a valid function and not the one from `Object.prototype` use that
19987     if (hasCustomToString(value)) {
19988       value = value.toString();
19989       if (isPrimitive(value)) return value;
19990     }
19991     // We have a basic object so we use the position of the object in the collection
19992     return index;
19993   }
19994
19995   function getPredicateValue(value, index) {
19996     var type = typeof value;
19997     if (value === null) {
19998       type = 'string';
19999       value = 'null';
20000     } else if (type === 'string') {
20001       value = value.toLowerCase();
20002     } else if (type === 'object') {
20003       value = objectValue(value, index);
20004     }
20005     return { value: value, type: type };
20006   }
20007
20008   function compare(v1, v2) {
20009     var result = 0;
20010     if (v1.type === v2.type) {
20011       if (v1.value !== v2.value) {
20012         result = v1.value < v2.value ? -1 : 1;
20013       }
20014     } else {
20015       result = v1.type < v2.type ? -1 : 1;
20016     }
20017     return result;
20018   }
20019 }
20020
20021 function ngDirective(directive) {
20022   if (isFunction(directive)) {
20023     directive = {
20024       link: directive
20025     };
20026   }
20027   directive.restrict = directive.restrict || 'AC';
20028   return valueFn(directive);
20029 }
20030
20031 /**
20032  * @ngdoc directive
20033  * @name a
20034  * @restrict E
20035  *
20036  * @description
20037  * Modifies the default behavior of the html A tag so that the default action is prevented when
20038  * the href attribute is empty.
20039  *
20040  * This change permits the easy creation of action links with the `ngClick` directive
20041  * without changing the location or causing page reloads, e.g.:
20042  * `<a href="" ng-click="list.addItem()">Add Item</a>`
20043  */
20044 var htmlAnchorDirective = valueFn({
20045   restrict: 'E',
20046   compile: function(element, attr) {
20047     if (!attr.href && !attr.xlinkHref) {
20048       return function(scope, element) {
20049         // If the linked element is not an anchor tag anymore, do nothing
20050         if (element[0].nodeName.toLowerCase() !== 'a') return;
20051
20052         // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
20053         var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
20054                    'xlink:href' : 'href';
20055         element.on('click', function(event) {
20056           // if we have no href url, then don't navigate anywhere.
20057           if (!element.attr(href)) {
20058             event.preventDefault();
20059           }
20060         });
20061       };
20062     }
20063   }
20064 });
20065
20066 /**
20067  * @ngdoc directive
20068  * @name ngHref
20069  * @restrict A
20070  * @priority 99
20071  *
20072  * @description
20073  * Using Angular markup like `{{hash}}` in an href attribute will
20074  * make the link go to the wrong URL if the user clicks it before
20075  * Angular has a chance to replace the `{{hash}}` markup with its
20076  * value. Until Angular replaces the markup the link will be broken
20077  * and will most likely return a 404 error. The `ngHref` directive
20078  * solves this problem.
20079  *
20080  * The wrong way to write it:
20081  * ```html
20082  * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
20083  * ```
20084  *
20085  * The correct way to write it:
20086  * ```html
20087  * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
20088  * ```
20089  *
20090  * @element A
20091  * @param {template} ngHref any string which can contain `{{}}` markup.
20092  *
20093  * @example
20094  * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
20095  * in links and their different behaviors:
20096     <example>
20097       <file name="index.html">
20098         <input ng-model="value" /><br />
20099         <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
20100         <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
20101         <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
20102         <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
20103         <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
20104         <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
20105       </file>
20106       <file name="protractor.js" type="protractor">
20107         it('should execute ng-click but not reload when href without value', function() {
20108           element(by.id('link-1')).click();
20109           expect(element(by.model('value')).getAttribute('value')).toEqual('1');
20110           expect(element(by.id('link-1')).getAttribute('href')).toBe('');
20111         });
20112
20113         it('should execute ng-click but not reload when href empty string', function() {
20114           element(by.id('link-2')).click();
20115           expect(element(by.model('value')).getAttribute('value')).toEqual('2');
20116           expect(element(by.id('link-2')).getAttribute('href')).toBe('');
20117         });
20118
20119         it('should execute ng-click and change url when ng-href specified', function() {
20120           expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
20121
20122           element(by.id('link-3')).click();
20123
20124           // At this point, we navigate away from an Angular page, so we need
20125           // to use browser.driver to get the base webdriver.
20126
20127           browser.wait(function() {
20128             return browser.driver.getCurrentUrl().then(function(url) {
20129               return url.match(/\/123$/);
20130             });
20131           }, 5000, 'page should navigate to /123');
20132         });
20133
20134         it('should execute ng-click but not reload when href empty string and name specified', function() {
20135           element(by.id('link-4')).click();
20136           expect(element(by.model('value')).getAttribute('value')).toEqual('4');
20137           expect(element(by.id('link-4')).getAttribute('href')).toBe('');
20138         });
20139
20140         it('should execute ng-click but not reload when no href but name specified', function() {
20141           element(by.id('link-5')).click();
20142           expect(element(by.model('value')).getAttribute('value')).toEqual('5');
20143           expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
20144         });
20145
20146         it('should only change url when only ng-href', function() {
20147           element(by.model('value')).clear();
20148           element(by.model('value')).sendKeys('6');
20149           expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
20150
20151           element(by.id('link-6')).click();
20152
20153           // At this point, we navigate away from an Angular page, so we need
20154           // to use browser.driver to get the base webdriver.
20155           browser.wait(function() {
20156             return browser.driver.getCurrentUrl().then(function(url) {
20157               return url.match(/\/6$/);
20158             });
20159           }, 5000, 'page should navigate to /6');
20160         });
20161       </file>
20162     </example>
20163  */
20164
20165 /**
20166  * @ngdoc directive
20167  * @name ngSrc
20168  * @restrict A
20169  * @priority 99
20170  *
20171  * @description
20172  * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
20173  * work right: The browser will fetch from the URL with the literal
20174  * text `{{hash}}` until Angular replaces the expression inside
20175  * `{{hash}}`. The `ngSrc` directive solves this problem.
20176  *
20177  * The buggy way to write it:
20178  * ```html
20179  * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
20180  * ```
20181  *
20182  * The correct way to write it:
20183  * ```html
20184  * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
20185  * ```
20186  *
20187  * @element IMG
20188  * @param {template} ngSrc any string which can contain `{{}}` markup.
20189  */
20190
20191 /**
20192  * @ngdoc directive
20193  * @name ngSrcset
20194  * @restrict A
20195  * @priority 99
20196  *
20197  * @description
20198  * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
20199  * work right: The browser will fetch from the URL with the literal
20200  * text `{{hash}}` until Angular replaces the expression inside
20201  * `{{hash}}`. The `ngSrcset` directive solves this problem.
20202  *
20203  * The buggy way to write it:
20204  * ```html
20205  * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
20206  * ```
20207  *
20208  * The correct way to write it:
20209  * ```html
20210  * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
20211  * ```
20212  *
20213  * @element IMG
20214  * @param {template} ngSrcset any string which can contain `{{}}` markup.
20215  */
20216
20217 /**
20218  * @ngdoc directive
20219  * @name ngDisabled
20220  * @restrict A
20221  * @priority 100
20222  *
20223  * @description
20224  *
20225  * This directive sets the `disabled` attribute on the element if the
20226  * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
20227  *
20228  * A special directive is necessary because we cannot use interpolation inside the `disabled`
20229  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20230  *
20231  * @example
20232     <example>
20233       <file name="index.html">
20234         <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
20235         <button ng-model="button" ng-disabled="checked">Button</button>
20236       </file>
20237       <file name="protractor.js" type="protractor">
20238         it('should toggle button', function() {
20239           expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
20240           element(by.model('checked')).click();
20241           expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
20242         });
20243       </file>
20244     </example>
20245  *
20246  * @element INPUT
20247  * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
20248  *     then the `disabled` attribute will be set on the element
20249  */
20250
20251
20252 /**
20253  * @ngdoc directive
20254  * @name ngChecked
20255  * @restrict A
20256  * @priority 100
20257  *
20258  * @description
20259  * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
20260  *
20261  * Note that this directive should not be used together with {@link ngModel `ngModel`},
20262  * as this can lead to unexpected behavior.
20263  *
20264  * A special directive is necessary because we cannot use interpolation inside the `checked`
20265  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20266  *
20267  * @example
20268     <example>
20269       <file name="index.html">
20270         <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
20271         <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
20272       </file>
20273       <file name="protractor.js" type="protractor">
20274         it('should check both checkBoxes', function() {
20275           expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
20276           element(by.model('master')).click();
20277           expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
20278         });
20279       </file>
20280     </example>
20281  *
20282  * @element INPUT
20283  * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
20284  *     then the `checked` attribute will be set on the element
20285  */
20286
20287
20288 /**
20289  * @ngdoc directive
20290  * @name ngReadonly
20291  * @restrict A
20292  * @priority 100
20293  *
20294  * @description
20295  *
20296  * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
20297  *
20298  * A special directive is necessary because we cannot use interpolation inside the `readOnly`
20299  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20300  *
20301  * @example
20302     <example>
20303       <file name="index.html">
20304         <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
20305         <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
20306       </file>
20307       <file name="protractor.js" type="protractor">
20308         it('should toggle readonly attr', function() {
20309           expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
20310           element(by.model('checked')).click();
20311           expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
20312         });
20313       </file>
20314     </example>
20315  *
20316  * @element INPUT
20317  * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
20318  *     then special attribute "readonly" will be set on the element
20319  */
20320
20321
20322 /**
20323  * @ngdoc directive
20324  * @name ngSelected
20325  * @restrict A
20326  * @priority 100
20327  *
20328  * @description
20329  *
20330  * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
20331  *
20332  * A special directive is necessary because we cannot use interpolation inside the `selected`
20333  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20334  *
20335  * @example
20336     <example>
20337       <file name="index.html">
20338         <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
20339         <select aria-label="ngSelected demo">
20340           <option>Hello!</option>
20341           <option id="greet" ng-selected="selected">Greetings!</option>
20342         </select>
20343       </file>
20344       <file name="protractor.js" type="protractor">
20345         it('should select Greetings!', function() {
20346           expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
20347           element(by.model('selected')).click();
20348           expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
20349         });
20350       </file>
20351     </example>
20352  *
20353  * @element OPTION
20354  * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
20355  *     then special attribute "selected" will be set on the element
20356  */
20357
20358 /**
20359  * @ngdoc directive
20360  * @name ngOpen
20361  * @restrict A
20362  * @priority 100
20363  *
20364  * @description
20365  *
20366  * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
20367  *
20368  * A special directive is necessary because we cannot use interpolation inside the `open`
20369  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20370  *
20371  * @example
20372      <example>
20373        <file name="index.html">
20374          <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
20375          <details id="details" ng-open="open">
20376             <summary>Show/Hide me</summary>
20377          </details>
20378        </file>
20379        <file name="protractor.js" type="protractor">
20380          it('should toggle open', function() {
20381            expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
20382            element(by.model('open')).click();
20383            expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
20384          });
20385        </file>
20386      </example>
20387  *
20388  * @element DETAILS
20389  * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
20390  *     then special attribute "open" will be set on the element
20391  */
20392
20393 var ngAttributeAliasDirectives = {};
20394
20395 // boolean attrs are evaluated
20396 forEach(BOOLEAN_ATTR, function(propName, attrName) {
20397   // binding to multiple is not supported
20398   if (propName == "multiple") return;
20399
20400   function defaultLinkFn(scope, element, attr) {
20401     scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
20402       attr.$set(attrName, !!value);
20403     });
20404   }
20405
20406   var normalized = directiveNormalize('ng-' + attrName);
20407   var linkFn = defaultLinkFn;
20408
20409   if (propName === 'checked') {
20410     linkFn = function(scope, element, attr) {
20411       // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
20412       if (attr.ngModel !== attr[normalized]) {
20413         defaultLinkFn(scope, element, attr);
20414       }
20415     };
20416   }
20417
20418   ngAttributeAliasDirectives[normalized] = function() {
20419     return {
20420       restrict: 'A',
20421       priority: 100,
20422       link: linkFn
20423     };
20424   };
20425 });
20426
20427 // aliased input attrs are evaluated
20428 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
20429   ngAttributeAliasDirectives[ngAttr] = function() {
20430     return {
20431       priority: 100,
20432       link: function(scope, element, attr) {
20433         //special case ngPattern when a literal regular expression value
20434         //is used as the expression (this way we don't have to watch anything).
20435         if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
20436           var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
20437           if (match) {
20438             attr.$set("ngPattern", new RegExp(match[1], match[2]));
20439             return;
20440           }
20441         }
20442
20443         scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
20444           attr.$set(ngAttr, value);
20445         });
20446       }
20447     };
20448   };
20449 });
20450
20451 // ng-src, ng-srcset, ng-href are interpolated
20452 forEach(['src', 'srcset', 'href'], function(attrName) {
20453   var normalized = directiveNormalize('ng-' + attrName);
20454   ngAttributeAliasDirectives[normalized] = function() {
20455     return {
20456       priority: 99, // it needs to run after the attributes are interpolated
20457       link: function(scope, element, attr) {
20458         var propName = attrName,
20459             name = attrName;
20460
20461         if (attrName === 'href' &&
20462             toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
20463           name = 'xlinkHref';
20464           attr.$attr[name] = 'xlink:href';
20465           propName = null;
20466         }
20467
20468         attr.$observe(normalized, function(value) {
20469           if (!value) {
20470             if (attrName === 'href') {
20471               attr.$set(name, null);
20472             }
20473             return;
20474           }
20475
20476           attr.$set(name, value);
20477
20478           // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
20479           // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
20480           // to set the property as well to achieve the desired effect.
20481           // we use attr[attrName] value since $set can sanitize the url.
20482           if (msie && propName) element.prop(propName, attr[name]);
20483         });
20484       }
20485     };
20486   };
20487 });
20488
20489 /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20490  */
20491 var nullFormCtrl = {
20492   $addControl: noop,
20493   $$renameControl: nullFormRenameControl,
20494   $removeControl: noop,
20495   $setValidity: noop,
20496   $setDirty: noop,
20497   $setPristine: noop,
20498   $setSubmitted: noop
20499 },
20500 SUBMITTED_CLASS = 'ng-submitted';
20501
20502 function nullFormRenameControl(control, name) {
20503   control.$name = name;
20504 }
20505
20506 /**
20507  * @ngdoc type
20508  * @name form.FormController
20509  *
20510  * @property {boolean} $pristine True if user has not interacted with the form yet.
20511  * @property {boolean} $dirty True if user has already interacted with the form.
20512  * @property {boolean} $valid True if all of the containing forms and controls are valid.
20513  * @property {boolean} $invalid True if at least one containing control or form is invalid.
20514  * @property {boolean} $pending True if at least one containing control or form is pending.
20515  * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20516  *
20517  * @property {Object} $error Is an object hash, containing references to controls or
20518  *  forms with failing validators, where:
20519  *
20520  *  - keys are validation tokens (error names),
20521  *  - values are arrays of controls or forms that have a failing validator for given error name.
20522  *
20523  *  Built-in validation tokens:
20524  *
20525  *  - `email`
20526  *  - `max`
20527  *  - `maxlength`
20528  *  - `min`
20529  *  - `minlength`
20530  *  - `number`
20531  *  - `pattern`
20532  *  - `required`
20533  *  - `url`
20534  *  - `date`
20535  *  - `datetimelocal`
20536  *  - `time`
20537  *  - `week`
20538  *  - `month`
20539  *
20540  * @description
20541  * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20542  * such as being valid/invalid or dirty/pristine.
20543  *
20544  * Each {@link ng.directive:form form} directive creates an instance
20545  * of `FormController`.
20546  *
20547  */
20548 //asks for $scope to fool the BC controller module
20549 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20550 function FormController(element, attrs, $scope, $animate, $interpolate) {
20551   var form = this,
20552       controls = [];
20553
20554   // init state
20555   form.$error = {};
20556   form.$$success = {};
20557   form.$pending = undefined;
20558   form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20559   form.$dirty = false;
20560   form.$pristine = true;
20561   form.$valid = true;
20562   form.$invalid = false;
20563   form.$submitted = false;
20564   form.$$parentForm = nullFormCtrl;
20565
20566   /**
20567    * @ngdoc method
20568    * @name form.FormController#$rollbackViewValue
20569    *
20570    * @description
20571    * Rollback all form controls pending updates to the `$modelValue`.
20572    *
20573    * Updates may be pending by a debounced event or because the input is waiting for a some future
20574    * event defined in `ng-model-options`. This method is typically needed by the reset button of
20575    * a form that uses `ng-model-options` to pend updates.
20576    */
20577   form.$rollbackViewValue = function() {
20578     forEach(controls, function(control) {
20579       control.$rollbackViewValue();
20580     });
20581   };
20582
20583   /**
20584    * @ngdoc method
20585    * @name form.FormController#$commitViewValue
20586    *
20587    * @description
20588    * Commit all form controls pending updates to the `$modelValue`.
20589    *
20590    * Updates may be pending by a debounced event or because the input is waiting for a some future
20591    * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20592    * usually handles calling this in response to input events.
20593    */
20594   form.$commitViewValue = function() {
20595     forEach(controls, function(control) {
20596       control.$commitViewValue();
20597     });
20598   };
20599
20600   /**
20601    * @ngdoc method
20602    * @name form.FormController#$addControl
20603    * @param {object} control control object, either a {@link form.FormController} or an
20604    * {@link ngModel.NgModelController}
20605    *
20606    * @description
20607    * Register a control with the form. Input elements using ngModelController do this automatically
20608    * when they are linked.
20609    *
20610    * Note that the current state of the control will not be reflected on the new parent form. This
20611    * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20612    * state.
20613    *
20614    * However, if the method is used programmatically, for example by adding dynamically created controls,
20615    * or controls that have been previously removed without destroying their corresponding DOM element,
20616    * it's the developers responsiblity to make sure the current state propagates to the parent form.
20617    *
20618    * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20619    * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20620    */
20621   form.$addControl = function(control) {
20622     // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
20623     // and not added to the scope.  Now we throw an error.
20624     assertNotHasOwnProperty(control.$name, 'input');
20625     controls.push(control);
20626
20627     if (control.$name) {
20628       form[control.$name] = control;
20629     }
20630
20631     control.$$parentForm = form;
20632   };
20633
20634   // Private API: rename a form control
20635   form.$$renameControl = function(control, newName) {
20636     var oldName = control.$name;
20637
20638     if (form[oldName] === control) {
20639       delete form[oldName];
20640     }
20641     form[newName] = control;
20642     control.$name = newName;
20643   };
20644
20645   /**
20646    * @ngdoc method
20647    * @name form.FormController#$removeControl
20648    * @param {object} control control object, either a {@link form.FormController} or an
20649    * {@link ngModel.NgModelController}
20650    *
20651    * @description
20652    * Deregister a control from the form.
20653    *
20654    * Input elements using ngModelController do this automatically when they are destroyed.
20655    *
20656    * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20657    * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20658    * different from case to case. For example, removing the only `$dirty` control from a form may or
20659    * may not mean that the form is still `$dirty`.
20660    */
20661   form.$removeControl = function(control) {
20662     if (control.$name && form[control.$name] === control) {
20663       delete form[control.$name];
20664     }
20665     forEach(form.$pending, function(value, name) {
20666       form.$setValidity(name, null, control);
20667     });
20668     forEach(form.$error, function(value, name) {
20669       form.$setValidity(name, null, control);
20670     });
20671     forEach(form.$$success, function(value, name) {
20672       form.$setValidity(name, null, control);
20673     });
20674
20675     arrayRemove(controls, control);
20676     control.$$parentForm = nullFormCtrl;
20677   };
20678
20679
20680   /**
20681    * @ngdoc method
20682    * @name form.FormController#$setValidity
20683    *
20684    * @description
20685    * Sets the validity of a form control.
20686    *
20687    * This method will also propagate to parent forms.
20688    */
20689   addSetValidityMethod({
20690     ctrl: this,
20691     $element: element,
20692     set: function(object, property, controller) {
20693       var list = object[property];
20694       if (!list) {
20695         object[property] = [controller];
20696       } else {
20697         var index = list.indexOf(controller);
20698         if (index === -1) {
20699           list.push(controller);
20700         }
20701       }
20702     },
20703     unset: function(object, property, controller) {
20704       var list = object[property];
20705       if (!list) {
20706         return;
20707       }
20708       arrayRemove(list, controller);
20709       if (list.length === 0) {
20710         delete object[property];
20711       }
20712     },
20713     $animate: $animate
20714   });
20715
20716   /**
20717    * @ngdoc method
20718    * @name form.FormController#$setDirty
20719    *
20720    * @description
20721    * Sets the form to a dirty state.
20722    *
20723    * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20724    * state (ng-dirty class). This method will also propagate to parent forms.
20725    */
20726   form.$setDirty = function() {
20727     $animate.removeClass(element, PRISTINE_CLASS);
20728     $animate.addClass(element, DIRTY_CLASS);
20729     form.$dirty = true;
20730     form.$pristine = false;
20731     form.$$parentForm.$setDirty();
20732   };
20733
20734   /**
20735    * @ngdoc method
20736    * @name form.FormController#$setPristine
20737    *
20738    * @description
20739    * Sets the form to its pristine state.
20740    *
20741    * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20742    * state (ng-pristine class). This method will also propagate to all the controls contained
20743    * in this form.
20744    *
20745    * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20746    * saving or resetting it.
20747    */
20748   form.$setPristine = function() {
20749     $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20750     form.$dirty = false;
20751     form.$pristine = true;
20752     form.$submitted = false;
20753     forEach(controls, function(control) {
20754       control.$setPristine();
20755     });
20756   };
20757
20758   /**
20759    * @ngdoc method
20760    * @name form.FormController#$setUntouched
20761    *
20762    * @description
20763    * Sets the form to its untouched state.
20764    *
20765    * This method can be called to remove the 'ng-touched' class and set the form controls to their
20766    * untouched state (ng-untouched class).
20767    *
20768    * Setting a form controls back to their untouched state is often useful when setting the form
20769    * back to its pristine state.
20770    */
20771   form.$setUntouched = function() {
20772     forEach(controls, function(control) {
20773       control.$setUntouched();
20774     });
20775   };
20776
20777   /**
20778    * @ngdoc method
20779    * @name form.FormController#$setSubmitted
20780    *
20781    * @description
20782    * Sets the form to its submitted state.
20783    */
20784   form.$setSubmitted = function() {
20785     $animate.addClass(element, SUBMITTED_CLASS);
20786     form.$submitted = true;
20787     form.$$parentForm.$setSubmitted();
20788   };
20789 }
20790
20791 /**
20792  * @ngdoc directive
20793  * @name ngForm
20794  * @restrict EAC
20795  *
20796  * @description
20797  * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20798  * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20799  * sub-group of controls needs to be determined.
20800  *
20801  * Note: the purpose of `ngForm` is to group controls,
20802  * but not to be a replacement for the `<form>` tag with all of its capabilities
20803  * (e.g. posting to the server, ...).
20804  *
20805  * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20806  *                       related scope, under this name.
20807  *
20808  */
20809
20810  /**
20811  * @ngdoc directive
20812  * @name form
20813  * @restrict E
20814  *
20815  * @description
20816  * Directive that instantiates
20817  * {@link form.FormController FormController}.
20818  *
20819  * If the `name` attribute is specified, the form controller is published onto the current scope under
20820  * this name.
20821  *
20822  * # Alias: {@link ng.directive:ngForm `ngForm`}
20823  *
20824  * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20825  * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20826  * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
20827  * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
20828  * of controls needs to be determined.
20829  *
20830  * # CSS classes
20831  *  - `ng-valid` is set if the form is valid.
20832  *  - `ng-invalid` is set if the form is invalid.
20833  *  - `ng-pending` is set if the form is pending.
20834  *  - `ng-pristine` is set if the form is pristine.
20835  *  - `ng-dirty` is set if the form is dirty.
20836  *  - `ng-submitted` is set if the form was submitted.
20837  *
20838  * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20839  *
20840  *
20841  * # Submitting a form and preventing the default action
20842  *
20843  * Since the role of forms in client-side Angular applications is different than in classical
20844  * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20845  * page reload that sends the data to the server. Instead some javascript logic should be triggered
20846  * to handle the form submission in an application-specific way.
20847  *
20848  * For this reason, Angular prevents the default action (form submission to the server) unless the
20849  * `<form>` element has an `action` attribute specified.
20850  *
20851  * You can use one of the following two ways to specify what javascript method should be called when
20852  * a form is submitted:
20853  *
20854  * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20855  * - {@link ng.directive:ngClick ngClick} directive on the first
20856   *  button or input field of type submit (input[type=submit])
20857  *
20858  * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20859  * or {@link ng.directive:ngClick ngClick} directives.
20860  * This is because of the following form submission rules in the HTML specification:
20861  *
20862  * - If a form has only one input field then hitting enter in this field triggers form submit
20863  * (`ngSubmit`)
20864  * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20865  * doesn't trigger submit
20866  * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20867  * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20868  * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20869  *
20870  * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20871  * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20872  * to have access to the updated model.
20873  *
20874  * ## Animation Hooks
20875  *
20876  * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20877  * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20878  * other validations that are performed within the form. Animations in ngForm are similar to how
20879  * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20880  * as JS animations.
20881  *
20882  * The following example shows a simple way to utilize CSS transitions to style a form element
20883  * that has been rendered as invalid after it has been validated:
20884  *
20885  * <pre>
20886  * //be sure to include ngAnimate as a module to hook into more
20887  * //advanced animations
20888  * .my-form {
20889  *   transition:0.5s linear all;
20890  *   background: white;
20891  * }
20892  * .my-form.ng-invalid {
20893  *   background: red;
20894  *   color:white;
20895  * }
20896  * </pre>
20897  *
20898  * @example
20899     <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20900       <file name="index.html">
20901        <script>
20902          angular.module('formExample', [])
20903            .controller('FormController', ['$scope', function($scope) {
20904              $scope.userType = 'guest';
20905            }]);
20906        </script>
20907        <style>
20908         .my-form {
20909           transition:all linear 0.5s;
20910           background: transparent;
20911         }
20912         .my-form.ng-invalid {
20913           background: red;
20914         }
20915        </style>
20916        <form name="myForm" ng-controller="FormController" class="my-form">
20917          userType: <input name="input" ng-model="userType" required>
20918          <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20919          <code>userType = {{userType}}</code><br>
20920          <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20921          <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20922          <code>myForm.$valid = {{myForm.$valid}}</code><br>
20923          <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20924         </form>
20925       </file>
20926       <file name="protractor.js" type="protractor">
20927         it('should initialize to model', function() {
20928           var userType = element(by.binding('userType'));
20929           var valid = element(by.binding('myForm.input.$valid'));
20930
20931           expect(userType.getText()).toContain('guest');
20932           expect(valid.getText()).toContain('true');
20933         });
20934
20935         it('should be invalid if empty', function() {
20936           var userType = element(by.binding('userType'));
20937           var valid = element(by.binding('myForm.input.$valid'));
20938           var userInput = element(by.model('userType'));
20939
20940           userInput.clear();
20941           userInput.sendKeys('');
20942
20943           expect(userType.getText()).toEqual('userType =');
20944           expect(valid.getText()).toContain('false');
20945         });
20946       </file>
20947     </example>
20948  *
20949  * @param {string=} name Name of the form. If specified, the form controller will be published into
20950  *                       related scope, under this name.
20951  */
20952 var formDirectiveFactory = function(isNgForm) {
20953   return ['$timeout', '$parse', function($timeout, $parse) {
20954     var formDirective = {
20955       name: 'form',
20956       restrict: isNgForm ? 'EAC' : 'E',
20957       require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20958       controller: FormController,
20959       compile: function ngFormCompile(formElement, attr) {
20960         // Setup initial state of the control
20961         formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20962
20963         var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20964
20965         return {
20966           pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20967             var controller = ctrls[0];
20968
20969             // if `action` attr is not present on the form, prevent the default action (submission)
20970             if (!('action' in attr)) {
20971               // we can't use jq events because if a form is destroyed during submission the default
20972               // action is not prevented. see #1238
20973               //
20974               // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20975               // page reload if the form was destroyed by submission of the form via a click handler
20976               // on a button in the form. Looks like an IE9 specific bug.
20977               var handleFormSubmission = function(event) {
20978                 scope.$apply(function() {
20979                   controller.$commitViewValue();
20980                   controller.$setSubmitted();
20981                 });
20982
20983                 event.preventDefault();
20984               };
20985
20986               addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20987
20988               // unregister the preventDefault listener so that we don't not leak memory but in a
20989               // way that will achieve the prevention of the default action.
20990               formElement.on('$destroy', function() {
20991                 $timeout(function() {
20992                   removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20993                 }, 0, false);
20994               });
20995             }
20996
20997             var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20998             parentFormCtrl.$addControl(controller);
20999
21000             var setter = nameAttr ? getSetter(controller.$name) : noop;
21001
21002             if (nameAttr) {
21003               setter(scope, controller);
21004               attr.$observe(nameAttr, function(newValue) {
21005                 if (controller.$name === newValue) return;
21006                 setter(scope, undefined);
21007                 controller.$$parentForm.$$renameControl(controller, newValue);
21008                 setter = getSetter(controller.$name);
21009                 setter(scope, controller);
21010               });
21011             }
21012             formElement.on('$destroy', function() {
21013               controller.$$parentForm.$removeControl(controller);
21014               setter(scope, undefined);
21015               extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
21016             });
21017           }
21018         };
21019       }
21020     };
21021
21022     return formDirective;
21023
21024     function getSetter(expression) {
21025       if (expression === '') {
21026         //create an assignable expression, so forms with an empty name can be renamed later
21027         return $parse('this[""]').assign;
21028       }
21029       return $parse(expression).assign || noop;
21030     }
21031   }];
21032 };
21033
21034 var formDirective = formDirectiveFactory();
21035 var ngFormDirective = formDirectiveFactory(true);
21036
21037 /* global VALID_CLASS: false,
21038   INVALID_CLASS: false,
21039   PRISTINE_CLASS: false,
21040   DIRTY_CLASS: false,
21041   UNTOUCHED_CLASS: false,
21042   TOUCHED_CLASS: false,
21043   ngModelMinErr: false,
21044 */
21045
21046 // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
21047 var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
21048 // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
21049 // Note: We are being more lenient, because browsers are too.
21050 //   1. Scheme
21051 //   2. Slashes
21052 //   3. Username
21053 //   4. Password
21054 //   5. Hostname
21055 //   6. Port
21056 //   7. Path
21057 //   8. Query
21058 //   9. Fragment
21059 //                 1111111111111111 222   333333    44444        555555555555555555555555    666     77777777     8888888     999
21060 var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
21061 var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
21062 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
21063 var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
21064 var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
21065 var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
21066 var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
21067 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
21068
21069 var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
21070 var PARTIAL_VALIDATION_TYPES = createMap();
21071 forEach('date,datetime-local,month,time,week'.split(','), function(type) {
21072   PARTIAL_VALIDATION_TYPES[type] = true;
21073 });
21074
21075 var inputType = {
21076
21077   /**
21078    * @ngdoc input
21079    * @name input[text]
21080    *
21081    * @description
21082    * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
21083    *
21084    *
21085    * @param {string} ngModel Assignable angular expression to data-bind to.
21086    * @param {string=} name Property name of the form under which the control is published.
21087    * @param {string=} required Adds `required` validation error key if the value is not entered.
21088    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21089    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21090    *    `required` when you want to data-bind to the `required` attribute.
21091    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21092    *    minlength.
21093    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21094    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21095    *    any length.
21096    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21097    *    that contains the regular expression body that will be converted to a regular expression
21098    *    as in the ngPattern directive.
21099    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21100    *    a RegExp found by evaluating the Angular expression given in the attribute value.
21101    *    If the expression evaluates to a RegExp object, then this is used directly.
21102    *    If the expression evaluates to a string, then it will be converted to a RegExp
21103    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21104    *    `new RegExp('^abc$')`.<br />
21105    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21106    *    start at the index of the last search's match, thus not taking the whole input value into
21107    *    account.
21108    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21109    *    interaction with the input element.
21110    * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
21111    *    This parameter is ignored for input[type=password] controls, which will never trim the
21112    *    input.
21113    *
21114    * @example
21115       <example name="text-input-directive" module="textInputExample">
21116         <file name="index.html">
21117          <script>
21118            angular.module('textInputExample', [])
21119              .controller('ExampleController', ['$scope', function($scope) {
21120                $scope.example = {
21121                  text: 'guest',
21122                  word: /^\s*\w*\s*$/
21123                };
21124              }]);
21125          </script>
21126          <form name="myForm" ng-controller="ExampleController">
21127            <label>Single word:
21128              <input type="text" name="input" ng-model="example.text"
21129                     ng-pattern="example.word" required ng-trim="false">
21130            </label>
21131            <div role="alert">
21132              <span class="error" ng-show="myForm.input.$error.required">
21133                Required!</span>
21134              <span class="error" ng-show="myForm.input.$error.pattern">
21135                Single word only!</span>
21136            </div>
21137            <tt>text = {{example.text}}</tt><br/>
21138            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21139            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21140            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21141            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21142           </form>
21143         </file>
21144         <file name="protractor.js" type="protractor">
21145           var text = element(by.binding('example.text'));
21146           var valid = element(by.binding('myForm.input.$valid'));
21147           var input = element(by.model('example.text'));
21148
21149           it('should initialize to model', function() {
21150             expect(text.getText()).toContain('guest');
21151             expect(valid.getText()).toContain('true');
21152           });
21153
21154           it('should be invalid if empty', function() {
21155             input.clear();
21156             input.sendKeys('');
21157
21158             expect(text.getText()).toEqual('text =');
21159             expect(valid.getText()).toContain('false');
21160           });
21161
21162           it('should be invalid if multi word', function() {
21163             input.clear();
21164             input.sendKeys('hello world');
21165
21166             expect(valid.getText()).toContain('false');
21167           });
21168         </file>
21169       </example>
21170    */
21171   'text': textInputType,
21172
21173     /**
21174      * @ngdoc input
21175      * @name input[date]
21176      *
21177      * @description
21178      * Input with date validation and transformation. In browsers that do not yet support
21179      * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
21180      * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
21181      * modern browsers do not yet support this input type, it is important to provide cues to users on the
21182      * expected input format via a placeholder or label.
21183      *
21184      * The model must always be a Date object, otherwise Angular will throw an error.
21185      * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21186      *
21187      * The timezone to be used to read/write the `Date` instance in the model can be defined using
21188      * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21189      *
21190      * @param {string} ngModel Assignable angular expression to data-bind to.
21191      * @param {string=} name Property name of the form under which the control is published.
21192      * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
21193      *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
21194      *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
21195      *   constraint validation.
21196      * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
21197      *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
21198      *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
21199      *   constraint validation.
21200      * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
21201      *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21202      * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
21203      *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21204      * @param {string=} required Sets `required` validation error key if the value is not entered.
21205      * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21206      *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21207      *    `required` when you want to data-bind to the `required` attribute.
21208      * @param {string=} ngChange Angular expression to be executed when input changes due to user
21209      *    interaction with the input element.
21210      *
21211      * @example
21212      <example name="date-input-directive" module="dateInputExample">
21213      <file name="index.html">
21214        <script>
21215           angular.module('dateInputExample', [])
21216             .controller('DateController', ['$scope', function($scope) {
21217               $scope.example = {
21218                 value: new Date(2013, 9, 22)
21219               };
21220             }]);
21221        </script>
21222        <form name="myForm" ng-controller="DateController as dateCtrl">
21223           <label for="exampleInput">Pick a date in 2013:</label>
21224           <input type="date" id="exampleInput" name="input" ng-model="example.value"
21225               placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
21226           <div role="alert">
21227             <span class="error" ng-show="myForm.input.$error.required">
21228                 Required!</span>
21229             <span class="error" ng-show="myForm.input.$error.date">
21230                 Not a valid date!</span>
21231            </div>
21232            <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
21233            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21234            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21235            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21236            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21237        </form>
21238      </file>
21239      <file name="protractor.js" type="protractor">
21240         var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
21241         var valid = element(by.binding('myForm.input.$valid'));
21242         var input = element(by.model('example.value'));
21243
21244         // currently protractor/webdriver does not support
21245         // sending keys to all known HTML5 input controls
21246         // for various browsers (see https://github.com/angular/protractor/issues/562).
21247         function setInput(val) {
21248           // set the value of the element and force validation.
21249           var scr = "var ipt = document.getElementById('exampleInput'); " +
21250           "ipt.value = '" + val + "';" +
21251           "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21252           browser.executeScript(scr);
21253         }
21254
21255         it('should initialize to model', function() {
21256           expect(value.getText()).toContain('2013-10-22');
21257           expect(valid.getText()).toContain('myForm.input.$valid = true');
21258         });
21259
21260         it('should be invalid if empty', function() {
21261           setInput('');
21262           expect(value.getText()).toEqual('value =');
21263           expect(valid.getText()).toContain('myForm.input.$valid = false');
21264         });
21265
21266         it('should be invalid if over max', function() {
21267           setInput('2015-01-01');
21268           expect(value.getText()).toContain('');
21269           expect(valid.getText()).toContain('myForm.input.$valid = false');
21270         });
21271      </file>
21272      </example>
21273      */
21274   'date': createDateInputType('date', DATE_REGEXP,
21275          createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
21276          'yyyy-MM-dd'),
21277
21278    /**
21279     * @ngdoc input
21280     * @name input[datetime-local]
21281     *
21282     * @description
21283     * Input with datetime validation and transformation. In browsers that do not yet support
21284     * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21285     * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
21286     *
21287     * The model must always be a Date object, otherwise Angular will throw an error.
21288     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21289     *
21290     * The timezone to be used to read/write the `Date` instance in the model can be defined using
21291     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21292     *
21293     * @param {string} ngModel Assignable angular expression to data-bind to.
21294     * @param {string=} name Property name of the form under which the control is published.
21295     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21296     *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
21297     *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
21298     *   Note that `min` will also add native HTML5 constraint validation.
21299     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21300     *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
21301     *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
21302     *   Note that `max` will also add native HTML5 constraint validation.
21303     * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
21304     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21305     * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
21306     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21307     * @param {string=} required Sets `required` validation error key if the value is not entered.
21308     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21309     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21310     *    `required` when you want to data-bind to the `required` attribute.
21311     * @param {string=} ngChange Angular expression to be executed when input changes due to user
21312     *    interaction with the input element.
21313     *
21314     * @example
21315     <example name="datetimelocal-input-directive" module="dateExample">
21316     <file name="index.html">
21317       <script>
21318         angular.module('dateExample', [])
21319           .controller('DateController', ['$scope', function($scope) {
21320             $scope.example = {
21321               value: new Date(2010, 11, 28, 14, 57)
21322             };
21323           }]);
21324       </script>
21325       <form name="myForm" ng-controller="DateController as dateCtrl">
21326         <label for="exampleInput">Pick a date between in 2013:</label>
21327         <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
21328             placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
21329         <div role="alert">
21330           <span class="error" ng-show="myForm.input.$error.required">
21331               Required!</span>
21332           <span class="error" ng-show="myForm.input.$error.datetimelocal">
21333               Not a valid date!</span>
21334         </div>
21335         <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
21336         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21337         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21338         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21339         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21340       </form>
21341     </file>
21342     <file name="protractor.js" type="protractor">
21343       var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
21344       var valid = element(by.binding('myForm.input.$valid'));
21345       var input = element(by.model('example.value'));
21346
21347       // currently protractor/webdriver does not support
21348       // sending keys to all known HTML5 input controls
21349       // for various browsers (https://github.com/angular/protractor/issues/562).
21350       function setInput(val) {
21351         // set the value of the element and force validation.
21352         var scr = "var ipt = document.getElementById('exampleInput'); " +
21353         "ipt.value = '" + val + "';" +
21354         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21355         browser.executeScript(scr);
21356       }
21357
21358       it('should initialize to model', function() {
21359         expect(value.getText()).toContain('2010-12-28T14:57:00');
21360         expect(valid.getText()).toContain('myForm.input.$valid = true');
21361       });
21362
21363       it('should be invalid if empty', function() {
21364         setInput('');
21365         expect(value.getText()).toEqual('value =');
21366         expect(valid.getText()).toContain('myForm.input.$valid = false');
21367       });
21368
21369       it('should be invalid if over max', function() {
21370         setInput('2015-01-01T23:59:00');
21371         expect(value.getText()).toContain('');
21372         expect(valid.getText()).toContain('myForm.input.$valid = false');
21373       });
21374     </file>
21375     </example>
21376     */
21377   'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
21378       createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
21379       'yyyy-MM-ddTHH:mm:ss.sss'),
21380
21381   /**
21382    * @ngdoc input
21383    * @name input[time]
21384    *
21385    * @description
21386    * Input with time validation and transformation. In browsers that do not yet support
21387    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21388    * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
21389    * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
21390    *
21391    * The model must always be a Date object, otherwise Angular will throw an error.
21392    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21393    *
21394    * The timezone to be used to read/write the `Date` instance in the model can be defined using
21395    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21396    *
21397    * @param {string} ngModel Assignable angular expression to data-bind to.
21398    * @param {string=} name Property name of the form under which the control is published.
21399    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21400    *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21401    *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
21402    *   native HTML5 constraint validation.
21403    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21404    *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21405    *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
21406    *   native HTML5 constraint validation.
21407    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
21408    *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21409    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
21410    *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21411    * @param {string=} required Sets `required` validation error key if the value is not entered.
21412    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21413    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21414    *    `required` when you want to data-bind to the `required` attribute.
21415    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21416    *    interaction with the input element.
21417    *
21418    * @example
21419    <example name="time-input-directive" module="timeExample">
21420    <file name="index.html">
21421      <script>
21422       angular.module('timeExample', [])
21423         .controller('DateController', ['$scope', function($scope) {
21424           $scope.example = {
21425             value: new Date(1970, 0, 1, 14, 57, 0)
21426           };
21427         }]);
21428      </script>
21429      <form name="myForm" ng-controller="DateController as dateCtrl">
21430         <label for="exampleInput">Pick a between 8am and 5pm:</label>
21431         <input type="time" id="exampleInput" name="input" ng-model="example.value"
21432             placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
21433         <div role="alert">
21434           <span class="error" ng-show="myForm.input.$error.required">
21435               Required!</span>
21436           <span class="error" ng-show="myForm.input.$error.time">
21437               Not a valid date!</span>
21438         </div>
21439         <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
21440         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21441         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21442         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21443         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21444      </form>
21445    </file>
21446    <file name="protractor.js" type="protractor">
21447       var value = element(by.binding('example.value | date: "HH:mm:ss"'));
21448       var valid = element(by.binding('myForm.input.$valid'));
21449       var input = element(by.model('example.value'));
21450
21451       // currently protractor/webdriver does not support
21452       // sending keys to all known HTML5 input controls
21453       // for various browsers (https://github.com/angular/protractor/issues/562).
21454       function setInput(val) {
21455         // set the value of the element and force validation.
21456         var scr = "var ipt = document.getElementById('exampleInput'); " +
21457         "ipt.value = '" + val + "';" +
21458         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21459         browser.executeScript(scr);
21460       }
21461
21462       it('should initialize to model', function() {
21463         expect(value.getText()).toContain('14:57:00');
21464         expect(valid.getText()).toContain('myForm.input.$valid = true');
21465       });
21466
21467       it('should be invalid if empty', function() {
21468         setInput('');
21469         expect(value.getText()).toEqual('value =');
21470         expect(valid.getText()).toContain('myForm.input.$valid = false');
21471       });
21472
21473       it('should be invalid if over max', function() {
21474         setInput('23:59:00');
21475         expect(value.getText()).toContain('');
21476         expect(valid.getText()).toContain('myForm.input.$valid = false');
21477       });
21478    </file>
21479    </example>
21480    */
21481   'time': createDateInputType('time', TIME_REGEXP,
21482       createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
21483      'HH:mm:ss.sss'),
21484
21485    /**
21486     * @ngdoc input
21487     * @name input[week]
21488     *
21489     * @description
21490     * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
21491     * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21492     * week format (yyyy-W##), for example: `2013-W02`.
21493     *
21494     * The model must always be a Date object, otherwise Angular will throw an error.
21495     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21496     *
21497     * The timezone to be used to read/write the `Date` instance in the model can be defined using
21498     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21499     *
21500     * @param {string} ngModel Assignable angular expression to data-bind to.
21501     * @param {string=} name Property name of the form under which the control is published.
21502     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21503     *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21504     *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21505     *   native HTML5 constraint validation.
21506     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21507     *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21508     *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21509     *   native HTML5 constraint validation.
21510     * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21511     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21512     * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21513     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21514     * @param {string=} required Sets `required` validation error key if the value is not entered.
21515     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21516     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21517     *    `required` when you want to data-bind to the `required` attribute.
21518     * @param {string=} ngChange Angular expression to be executed when input changes due to user
21519     *    interaction with the input element.
21520     *
21521     * @example
21522     <example name="week-input-directive" module="weekExample">
21523     <file name="index.html">
21524       <script>
21525       angular.module('weekExample', [])
21526         .controller('DateController', ['$scope', function($scope) {
21527           $scope.example = {
21528             value: new Date(2013, 0, 3)
21529           };
21530         }]);
21531       </script>
21532       <form name="myForm" ng-controller="DateController as dateCtrl">
21533         <label>Pick a date between in 2013:
21534           <input id="exampleInput" type="week" name="input" ng-model="example.value"
21535                  placeholder="YYYY-W##" min="2012-W32"
21536                  max="2013-W52" required />
21537         </label>
21538         <div role="alert">
21539           <span class="error" ng-show="myForm.input.$error.required">
21540               Required!</span>
21541           <span class="error" ng-show="myForm.input.$error.week">
21542               Not a valid date!</span>
21543         </div>
21544         <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21545         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21546         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21547         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21548         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21549       </form>
21550     </file>
21551     <file name="protractor.js" type="protractor">
21552       var value = element(by.binding('example.value | date: "yyyy-Www"'));
21553       var valid = element(by.binding('myForm.input.$valid'));
21554       var input = element(by.model('example.value'));
21555
21556       // currently protractor/webdriver does not support
21557       // sending keys to all known HTML5 input controls
21558       // for various browsers (https://github.com/angular/protractor/issues/562).
21559       function setInput(val) {
21560         // set the value of the element and force validation.
21561         var scr = "var ipt = document.getElementById('exampleInput'); " +
21562         "ipt.value = '" + val + "';" +
21563         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21564         browser.executeScript(scr);
21565       }
21566
21567       it('should initialize to model', function() {
21568         expect(value.getText()).toContain('2013-W01');
21569         expect(valid.getText()).toContain('myForm.input.$valid = true');
21570       });
21571
21572       it('should be invalid if empty', function() {
21573         setInput('');
21574         expect(value.getText()).toEqual('value =');
21575         expect(valid.getText()).toContain('myForm.input.$valid = false');
21576       });
21577
21578       it('should be invalid if over max', function() {
21579         setInput('2015-W01');
21580         expect(value.getText()).toContain('');
21581         expect(valid.getText()).toContain('myForm.input.$valid = false');
21582       });
21583     </file>
21584     </example>
21585     */
21586   'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21587
21588   /**
21589    * @ngdoc input
21590    * @name input[month]
21591    *
21592    * @description
21593    * Input with month validation and transformation. In browsers that do not yet support
21594    * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21595    * month format (yyyy-MM), for example: `2009-01`.
21596    *
21597    * The model must always be a Date object, otherwise Angular will throw an error.
21598    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21599    * If the model is not set to the first of the month, the next view to model update will set it
21600    * to the first of the month.
21601    *
21602    * The timezone to be used to read/write the `Date` instance in the model can be defined using
21603    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21604    *
21605    * @param {string} ngModel Assignable angular expression to data-bind to.
21606    * @param {string=} name Property name of the form under which the control is published.
21607    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21608    *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21609    *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21610    *   native HTML5 constraint validation.
21611    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21612    *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21613    *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21614    *   native HTML5 constraint validation.
21615    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21616    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21617    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21618    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21619
21620    * @param {string=} required Sets `required` validation error key if the value is not entered.
21621    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21622    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21623    *    `required` when you want to data-bind to the `required` attribute.
21624    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21625    *    interaction with the input element.
21626    *
21627    * @example
21628    <example name="month-input-directive" module="monthExample">
21629    <file name="index.html">
21630      <script>
21631       angular.module('monthExample', [])
21632         .controller('DateController', ['$scope', function($scope) {
21633           $scope.example = {
21634             value: new Date(2013, 9, 1)
21635           };
21636         }]);
21637      </script>
21638      <form name="myForm" ng-controller="DateController as dateCtrl">
21639        <label for="exampleInput">Pick a month in 2013:</label>
21640        <input id="exampleInput" type="month" name="input" ng-model="example.value"
21641           placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
21642        <div role="alert">
21643          <span class="error" ng-show="myForm.input.$error.required">
21644             Required!</span>
21645          <span class="error" ng-show="myForm.input.$error.month">
21646             Not a valid month!</span>
21647        </div>
21648        <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
21649        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21650        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21651        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21652        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21653      </form>
21654    </file>
21655    <file name="protractor.js" type="protractor">
21656       var value = element(by.binding('example.value | date: "yyyy-MM"'));
21657       var valid = element(by.binding('myForm.input.$valid'));
21658       var input = element(by.model('example.value'));
21659
21660       // currently protractor/webdriver does not support
21661       // sending keys to all known HTML5 input controls
21662       // for various browsers (https://github.com/angular/protractor/issues/562).
21663       function setInput(val) {
21664         // set the value of the element and force validation.
21665         var scr = "var ipt = document.getElementById('exampleInput'); " +
21666         "ipt.value = '" + val + "';" +
21667         "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21668         browser.executeScript(scr);
21669       }
21670
21671       it('should initialize to model', function() {
21672         expect(value.getText()).toContain('2013-10');
21673         expect(valid.getText()).toContain('myForm.input.$valid = true');
21674       });
21675
21676       it('should be invalid if empty', function() {
21677         setInput('');
21678         expect(value.getText()).toEqual('value =');
21679         expect(valid.getText()).toContain('myForm.input.$valid = false');
21680       });
21681
21682       it('should be invalid if over max', function() {
21683         setInput('2015-01');
21684         expect(value.getText()).toContain('');
21685         expect(valid.getText()).toContain('myForm.input.$valid = false');
21686       });
21687    </file>
21688    </example>
21689    */
21690   'month': createDateInputType('month', MONTH_REGEXP,
21691      createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
21692      'yyyy-MM'),
21693
21694   /**
21695    * @ngdoc input
21696    * @name input[number]
21697    *
21698    * @description
21699    * Text input with number validation and transformation. Sets the `number` validation
21700    * error if not a valid number.
21701    *
21702    * <div class="alert alert-warning">
21703    * The model must always be of type `number` otherwise Angular will throw an error.
21704    * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
21705    * error docs for more information and an example of how to convert your model if necessary.
21706    * </div>
21707    *
21708    * ## Issues with HTML5 constraint validation
21709    *
21710    * In browsers that follow the
21711    * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
21712    * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
21713    * If a non-number is entered in the input, the browser will report the value as an empty string,
21714    * which means the view / model values in `ngModel` and subsequently the scope value
21715    * will also be an empty string.
21716    *
21717    *
21718    * @param {string} ngModel Assignable angular expression to data-bind to.
21719    * @param {string=} name Property name of the form under which the control is published.
21720    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21721    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21722    * @param {string=} required Sets `required` validation error key if the value is not entered.
21723    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21724    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21725    *    `required` when you want to data-bind to the `required` attribute.
21726    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21727    *    minlength.
21728    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21729    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21730    *    any length.
21731    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21732    *    that contains the regular expression body that will be converted to a regular expression
21733    *    as in the ngPattern directive.
21734    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21735    *    a RegExp found by evaluating the Angular expression given in the attribute value.
21736    *    If the expression evaluates to a RegExp object, then this is used directly.
21737    *    If the expression evaluates to a string, then it will be converted to a RegExp
21738    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21739    *    `new RegExp('^abc$')`.<br />
21740    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21741    *    start at the index of the last search's match, thus not taking the whole input value into
21742    *    account.
21743    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21744    *    interaction with the input element.
21745    *
21746    * @example
21747       <example name="number-input-directive" module="numberExample">
21748         <file name="index.html">
21749          <script>
21750            angular.module('numberExample', [])
21751              .controller('ExampleController', ['$scope', function($scope) {
21752                $scope.example = {
21753                  value: 12
21754                };
21755              }]);
21756          </script>
21757          <form name="myForm" ng-controller="ExampleController">
21758            <label>Number:
21759              <input type="number" name="input" ng-model="example.value"
21760                     min="0" max="99" required>
21761           </label>
21762            <div role="alert">
21763              <span class="error" ng-show="myForm.input.$error.required">
21764                Required!</span>
21765              <span class="error" ng-show="myForm.input.$error.number">
21766                Not valid number!</span>
21767            </div>
21768            <tt>value = {{example.value}}</tt><br/>
21769            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21770            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21771            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21772            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21773           </form>
21774         </file>
21775         <file name="protractor.js" type="protractor">
21776           var value = element(by.binding('example.value'));
21777           var valid = element(by.binding('myForm.input.$valid'));
21778           var input = element(by.model('example.value'));
21779
21780           it('should initialize to model', function() {
21781             expect(value.getText()).toContain('12');
21782             expect(valid.getText()).toContain('true');
21783           });
21784
21785           it('should be invalid if empty', function() {
21786             input.clear();
21787             input.sendKeys('');
21788             expect(value.getText()).toEqual('value =');
21789             expect(valid.getText()).toContain('false');
21790           });
21791
21792           it('should be invalid if over max', function() {
21793             input.clear();
21794             input.sendKeys('123');
21795             expect(value.getText()).toEqual('value =');
21796             expect(valid.getText()).toContain('false');
21797           });
21798         </file>
21799       </example>
21800    */
21801   'number': numberInputType,
21802
21803
21804   /**
21805    * @ngdoc input
21806    * @name input[url]
21807    *
21808    * @description
21809    * Text input with URL validation. Sets the `url` validation error key if the content is not a
21810    * valid URL.
21811    *
21812    * <div class="alert alert-warning">
21813    * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21814    * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21815    * the built-in validators (see the {@link guide/forms Forms guide})
21816    * </div>
21817    *
21818    * @param {string} ngModel Assignable angular expression to data-bind to.
21819    * @param {string=} name Property name of the form under which the control is published.
21820    * @param {string=} required Sets `required` validation error key if the value is not entered.
21821    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21822    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21823    *    `required` when you want to data-bind to the `required` attribute.
21824    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21825    *    minlength.
21826    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21827    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21828    *    any length.
21829    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21830    *    that contains the regular expression body that will be converted to a regular expression
21831    *    as in the ngPattern directive.
21832    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21833    *    a RegExp found by evaluating the Angular expression given in the attribute value.
21834    *    If the expression evaluates to a RegExp object, then this is used directly.
21835    *    If the expression evaluates to a string, then it will be converted to a RegExp
21836    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21837    *    `new RegExp('^abc$')`.<br />
21838    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21839    *    start at the index of the last search's match, thus not taking the whole input value into
21840    *    account.
21841    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21842    *    interaction with the input element.
21843    *
21844    * @example
21845       <example name="url-input-directive" module="urlExample">
21846         <file name="index.html">
21847          <script>
21848            angular.module('urlExample', [])
21849              .controller('ExampleController', ['$scope', function($scope) {
21850                $scope.url = {
21851                  text: 'http://google.com'
21852                };
21853              }]);
21854          </script>
21855          <form name="myForm" ng-controller="ExampleController">
21856            <label>URL:
21857              <input type="url" name="input" ng-model="url.text" required>
21858            <label>
21859            <div role="alert">
21860              <span class="error" ng-show="myForm.input.$error.required">
21861                Required!</span>
21862              <span class="error" ng-show="myForm.input.$error.url">
21863                Not valid url!</span>
21864            </div>
21865            <tt>text = {{url.text}}</tt><br/>
21866            <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21867            <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21868            <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21869            <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21870            <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21871           </form>
21872         </file>
21873         <file name="protractor.js" type="protractor">
21874           var text = element(by.binding('url.text'));
21875           var valid = element(by.binding('myForm.input.$valid'));
21876           var input = element(by.model('url.text'));
21877
21878           it('should initialize to model', function() {
21879             expect(text.getText()).toContain('http://google.com');
21880             expect(valid.getText()).toContain('true');
21881           });
21882
21883           it('should be invalid if empty', function() {
21884             input.clear();
21885             input.sendKeys('');
21886
21887             expect(text.getText()).toEqual('text =');
21888             expect(valid.getText()).toContain('false');
21889           });
21890
21891           it('should be invalid if not url', function() {
21892             input.clear();
21893             input.sendKeys('box');
21894
21895             expect(valid.getText()).toContain('false');
21896           });
21897         </file>
21898       </example>
21899    */
21900   'url': urlInputType,
21901
21902
21903   /**
21904    * @ngdoc input
21905    * @name input[email]
21906    *
21907    * @description
21908    * Text input with email validation. Sets the `email` validation error key if not a valid email
21909    * address.
21910    *
21911    * <div class="alert alert-warning">
21912    * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21913    * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21914    * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21915    * </div>
21916    *
21917    * @param {string} ngModel Assignable angular expression to data-bind to.
21918    * @param {string=} name Property name of the form under which the control is published.
21919    * @param {string=} required Sets `required` validation error key if the value is not entered.
21920    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21921    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21922    *    `required` when you want to data-bind to the `required` attribute.
21923    * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21924    *    minlength.
21925    * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21926    *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21927    *    any length.
21928    * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21929    *    that contains the regular expression body that will be converted to a regular expression
21930    *    as in the ngPattern directive.
21931    * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21932    *    a RegExp found by evaluating the Angular expression given in the attribute value.
21933    *    If the expression evaluates to a RegExp object, then this is used directly.
21934    *    If the expression evaluates to a string, then it will be converted to a RegExp
21935    *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21936    *    `new RegExp('^abc$')`.<br />
21937    *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21938    *    start at the index of the last search's match, thus not taking the whole input value into
21939    *    account.
21940    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21941    *    interaction with the input element.
21942    *
21943    * @example
21944       <example name="email-input-directive" module="emailExample">
21945         <file name="index.html">
21946          <script>
21947            angular.module('emailExample', [])
21948              .controller('ExampleController', ['$scope', function($scope) {
21949                $scope.email = {
21950                  text: 'me@example.com'
21951                };
21952              }]);
21953          </script>
21954            <form name="myForm" ng-controller="ExampleController">
21955              <label>Email:
21956                <input type="email" name="input" ng-model="email.text" required>
21957              </label>
21958              <div role="alert">
21959                <span class="error" ng-show="myForm.input.$error.required">
21960                  Required!</span>
21961                <span class="error" ng-show="myForm.input.$error.email">
21962                  Not valid email!</span>
21963              </div>
21964              <tt>text = {{email.text}}</tt><br/>
21965              <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21966              <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21967              <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21968              <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21969              <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21970            </form>
21971          </file>
21972         <file name="protractor.js" type="protractor">
21973           var text = element(by.binding('email.text'));
21974           var valid = element(by.binding('myForm.input.$valid'));
21975           var input = element(by.model('email.text'));
21976
21977           it('should initialize to model', function() {
21978             expect(text.getText()).toContain('me@example.com');
21979             expect(valid.getText()).toContain('true');
21980           });
21981
21982           it('should be invalid if empty', function() {
21983             input.clear();
21984             input.sendKeys('');
21985             expect(text.getText()).toEqual('text =');
21986             expect(valid.getText()).toContain('false');
21987           });
21988
21989           it('should be invalid if not email', function() {
21990             input.clear();
21991             input.sendKeys('xxx');
21992
21993             expect(valid.getText()).toContain('false');
21994           });
21995         </file>
21996       </example>
21997    */
21998   'email': emailInputType,
21999
22000
22001   /**
22002    * @ngdoc input
22003    * @name input[radio]
22004    *
22005    * @description
22006    * HTML radio button.
22007    *
22008    * @param {string} ngModel Assignable angular expression to data-bind to.
22009    * @param {string} value The value to which the `ngModel` expression should be set when selected.
22010    *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
22011    *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
22012    * @param {string=} name Property name of the form under which the control is published.
22013    * @param {string=} ngChange Angular expression to be executed when input changes due to user
22014    *    interaction with the input element.
22015    * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
22016    *    is selected. Should be used instead of the `value` attribute if you need
22017    *    a non-string `ngModel` (`boolean`, `array`, ...).
22018    *
22019    * @example
22020       <example name="radio-input-directive" module="radioExample">
22021         <file name="index.html">
22022          <script>
22023            angular.module('radioExample', [])
22024              .controller('ExampleController', ['$scope', function($scope) {
22025                $scope.color = {
22026                  name: 'blue'
22027                };
22028                $scope.specialValue = {
22029                  "id": "12345",
22030                  "value": "green"
22031                };
22032              }]);
22033          </script>
22034          <form name="myForm" ng-controller="ExampleController">
22035            <label>
22036              <input type="radio" ng-model="color.name" value="red">
22037              Red
22038            </label><br/>
22039            <label>
22040              <input type="radio" ng-model="color.name" ng-value="specialValue">
22041              Green
22042            </label><br/>
22043            <label>
22044              <input type="radio" ng-model="color.name" value="blue">
22045              Blue
22046            </label><br/>
22047            <tt>color = {{color.name | json}}</tt><br/>
22048           </form>
22049           Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
22050         </file>
22051         <file name="protractor.js" type="protractor">
22052           it('should change state', function() {
22053             var color = element(by.binding('color.name'));
22054
22055             expect(color.getText()).toContain('blue');
22056
22057             element.all(by.model('color.name')).get(0).click();
22058
22059             expect(color.getText()).toContain('red');
22060           });
22061         </file>
22062       </example>
22063    */
22064   'radio': radioInputType,
22065
22066
22067   /**
22068    * @ngdoc input
22069    * @name input[checkbox]
22070    *
22071    * @description
22072    * HTML checkbox.
22073    *
22074    * @param {string} ngModel Assignable angular expression to data-bind to.
22075    * @param {string=} name Property name of the form under which the control is published.
22076    * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
22077    * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
22078    * @param {string=} ngChange Angular expression to be executed when input changes due to user
22079    *    interaction with the input element.
22080    *
22081    * @example
22082       <example name="checkbox-input-directive" module="checkboxExample">
22083         <file name="index.html">
22084          <script>
22085            angular.module('checkboxExample', [])
22086              .controller('ExampleController', ['$scope', function($scope) {
22087                $scope.checkboxModel = {
22088                 value1 : true,
22089                 value2 : 'YES'
22090               };
22091              }]);
22092          </script>
22093          <form name="myForm" ng-controller="ExampleController">
22094            <label>Value1:
22095              <input type="checkbox" ng-model="checkboxModel.value1">
22096            </label><br/>
22097            <label>Value2:
22098              <input type="checkbox" ng-model="checkboxModel.value2"
22099                     ng-true-value="'YES'" ng-false-value="'NO'">
22100             </label><br/>
22101            <tt>value1 = {{checkboxModel.value1}}</tt><br/>
22102            <tt>value2 = {{checkboxModel.value2}}</tt><br/>
22103           </form>
22104         </file>
22105         <file name="protractor.js" type="protractor">
22106           it('should change state', function() {
22107             var value1 = element(by.binding('checkboxModel.value1'));
22108             var value2 = element(by.binding('checkboxModel.value2'));
22109
22110             expect(value1.getText()).toContain('true');
22111             expect(value2.getText()).toContain('YES');
22112
22113             element(by.model('checkboxModel.value1')).click();
22114             element(by.model('checkboxModel.value2')).click();
22115
22116             expect(value1.getText()).toContain('false');
22117             expect(value2.getText()).toContain('NO');
22118           });
22119         </file>
22120       </example>
22121    */
22122   'checkbox': checkboxInputType,
22123
22124   'hidden': noop,
22125   'button': noop,
22126   'submit': noop,
22127   'reset': noop,
22128   'file': noop
22129 };
22130
22131 function stringBasedInputType(ctrl) {
22132   ctrl.$formatters.push(function(value) {
22133     return ctrl.$isEmpty(value) ? value : value.toString();
22134   });
22135 }
22136
22137 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22138   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22139   stringBasedInputType(ctrl);
22140 }
22141
22142 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22143   var type = lowercase(element[0].type);
22144
22145   // In composition mode, users are still inputing intermediate text buffer,
22146   // hold the listener until composition is done.
22147   // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
22148   if (!$sniffer.android) {
22149     var composing = false;
22150
22151     element.on('compositionstart', function(data) {
22152       composing = true;
22153     });
22154
22155     element.on('compositionend', function() {
22156       composing = false;
22157       listener();
22158     });
22159   }
22160
22161   var timeout;
22162
22163   var listener = function(ev) {
22164     if (timeout) {
22165       $browser.defer.cancel(timeout);
22166       timeout = null;
22167     }
22168     if (composing) return;
22169     var value = element.val(),
22170         event = ev && ev.type;
22171
22172     // By default we will trim the value
22173     // If the attribute ng-trim exists we will avoid trimming
22174     // If input type is 'password', the value is never trimmed
22175     if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
22176       value = trim(value);
22177     }
22178
22179     // If a control is suffering from bad input (due to native validators), browsers discard its
22180     // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
22181     // control's value is the same empty value twice in a row.
22182     if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
22183       ctrl.$setViewValue(value, event);
22184     }
22185   };
22186
22187   // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
22188   // input event on backspace, delete or cut
22189   if ($sniffer.hasEvent('input')) {
22190     element.on('input', listener);
22191   } else {
22192     var deferListener = function(ev, input, origValue) {
22193       if (!timeout) {
22194         timeout = $browser.defer(function() {
22195           timeout = null;
22196           if (!input || input.value !== origValue) {
22197             listener(ev);
22198           }
22199         });
22200       }
22201     };
22202
22203     element.on('keydown', function(event) {
22204       var key = event.keyCode;
22205
22206       // ignore
22207       //    command            modifiers                   arrows
22208       if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
22209
22210       deferListener(event, this, this.value);
22211     });
22212
22213     // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
22214     if ($sniffer.hasEvent('paste')) {
22215       element.on('paste cut', deferListener);
22216     }
22217   }
22218
22219   // if user paste into input using mouse on older browser
22220   // or form autocomplete on newer browser, we need "change" event to catch it
22221   element.on('change', listener);
22222
22223   // Some native input types (date-family) have the ability to change validity without
22224   // firing any input/change events.
22225   // For these event types, when native validators are present and the browser supports the type,
22226   // check for validity changes on various DOM events.
22227   if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
22228     element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
22229       if (!timeout) {
22230         var validity = this[VALIDITY_STATE_PROPERTY];
22231         var origBadInput = validity.badInput;
22232         var origTypeMismatch = validity.typeMismatch;
22233         timeout = $browser.defer(function() {
22234           timeout = null;
22235           if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
22236             listener(ev);
22237           }
22238         });
22239       }
22240     });
22241   }
22242
22243   ctrl.$render = function() {
22244     // Workaround for Firefox validation #12102.
22245     var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
22246     if (element.val() !== value) {
22247       element.val(value);
22248     }
22249   };
22250 }
22251
22252 function weekParser(isoWeek, existingDate) {
22253   if (isDate(isoWeek)) {
22254     return isoWeek;
22255   }
22256
22257   if (isString(isoWeek)) {
22258     WEEK_REGEXP.lastIndex = 0;
22259     var parts = WEEK_REGEXP.exec(isoWeek);
22260     if (parts) {
22261       var year = +parts[1],
22262           week = +parts[2],
22263           hours = 0,
22264           minutes = 0,
22265           seconds = 0,
22266           milliseconds = 0,
22267           firstThurs = getFirstThursdayOfYear(year),
22268           addDays = (week - 1) * 7;
22269
22270       if (existingDate) {
22271         hours = existingDate.getHours();
22272         minutes = existingDate.getMinutes();
22273         seconds = existingDate.getSeconds();
22274         milliseconds = existingDate.getMilliseconds();
22275       }
22276
22277       return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
22278     }
22279   }
22280
22281   return NaN;
22282 }
22283
22284 function createDateParser(regexp, mapping) {
22285   return function(iso, date) {
22286     var parts, map;
22287
22288     if (isDate(iso)) {
22289       return iso;
22290     }
22291
22292     if (isString(iso)) {
22293       // When a date is JSON'ified to wraps itself inside of an extra
22294       // set of double quotes. This makes the date parsing code unable
22295       // to match the date string and parse it as a date.
22296       if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
22297         iso = iso.substring(1, iso.length - 1);
22298       }
22299       if (ISO_DATE_REGEXP.test(iso)) {
22300         return new Date(iso);
22301       }
22302       regexp.lastIndex = 0;
22303       parts = regexp.exec(iso);
22304
22305       if (parts) {
22306         parts.shift();
22307         if (date) {
22308           map = {
22309             yyyy: date.getFullYear(),
22310             MM: date.getMonth() + 1,
22311             dd: date.getDate(),
22312             HH: date.getHours(),
22313             mm: date.getMinutes(),
22314             ss: date.getSeconds(),
22315             sss: date.getMilliseconds() / 1000
22316           };
22317         } else {
22318           map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
22319         }
22320
22321         forEach(parts, function(part, index) {
22322           if (index < mapping.length) {
22323             map[mapping[index]] = +part;
22324           }
22325         });
22326         return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
22327       }
22328     }
22329
22330     return NaN;
22331   };
22332 }
22333
22334 function createDateInputType(type, regexp, parseDate, format) {
22335   return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
22336     badInputChecker(scope, element, attr, ctrl);
22337     baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22338     var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
22339     var previousDate;
22340
22341     ctrl.$$parserName = type;
22342     ctrl.$parsers.push(function(value) {
22343       if (ctrl.$isEmpty(value)) return null;
22344       if (regexp.test(value)) {
22345         // Note: We cannot read ctrl.$modelValue, as there might be a different
22346         // parser/formatter in the processing chain so that the model
22347         // contains some different data format!
22348         var parsedDate = parseDate(value, previousDate);
22349         if (timezone) {
22350           parsedDate = convertTimezoneToLocal(parsedDate, timezone);
22351         }
22352         return parsedDate;
22353       }
22354       return undefined;
22355     });
22356
22357     ctrl.$formatters.push(function(value) {
22358       if (value && !isDate(value)) {
22359         throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
22360       }
22361       if (isValidDate(value)) {
22362         previousDate = value;
22363         if (previousDate && timezone) {
22364           previousDate = convertTimezoneToLocal(previousDate, timezone, true);
22365         }
22366         return $filter('date')(value, format, timezone);
22367       } else {
22368         previousDate = null;
22369         return '';
22370       }
22371     });
22372
22373     if (isDefined(attr.min) || attr.ngMin) {
22374       var minVal;
22375       ctrl.$validators.min = function(value) {
22376         return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
22377       };
22378       attr.$observe('min', function(val) {
22379         minVal = parseObservedDateValue(val);
22380         ctrl.$validate();
22381       });
22382     }
22383
22384     if (isDefined(attr.max) || attr.ngMax) {
22385       var maxVal;
22386       ctrl.$validators.max = function(value) {
22387         return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
22388       };
22389       attr.$observe('max', function(val) {
22390         maxVal = parseObservedDateValue(val);
22391         ctrl.$validate();
22392       });
22393     }
22394
22395     function isValidDate(value) {
22396       // Invalid Date: getTime() returns NaN
22397       return value && !(value.getTime && value.getTime() !== value.getTime());
22398     }
22399
22400     function parseObservedDateValue(val) {
22401       return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
22402     }
22403   };
22404 }
22405
22406 function badInputChecker(scope, element, attr, ctrl) {
22407   var node = element[0];
22408   var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
22409   if (nativeValidation) {
22410     ctrl.$parsers.push(function(value) {
22411       var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
22412       // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
22413       // - also sets validity.badInput (should only be validity.typeMismatch).
22414       // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
22415       // - can ignore this case as we can still read out the erroneous email...
22416       return validity.badInput && !validity.typeMismatch ? undefined : value;
22417     });
22418   }
22419 }
22420
22421 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22422   badInputChecker(scope, element, attr, ctrl);
22423   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22424
22425   ctrl.$$parserName = 'number';
22426   ctrl.$parsers.push(function(value) {
22427     if (ctrl.$isEmpty(value))      return null;
22428     if (NUMBER_REGEXP.test(value)) return parseFloat(value);
22429     return undefined;
22430   });
22431
22432   ctrl.$formatters.push(function(value) {
22433     if (!ctrl.$isEmpty(value)) {
22434       if (!isNumber(value)) {
22435         throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
22436       }
22437       value = value.toString();
22438     }
22439     return value;
22440   });
22441
22442   if (isDefined(attr.min) || attr.ngMin) {
22443     var minVal;
22444     ctrl.$validators.min = function(value) {
22445       return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
22446     };
22447
22448     attr.$observe('min', function(val) {
22449       if (isDefined(val) && !isNumber(val)) {
22450         val = parseFloat(val, 10);
22451       }
22452       minVal = isNumber(val) && !isNaN(val) ? val : undefined;
22453       // TODO(matsko): implement validateLater to reduce number of validations
22454       ctrl.$validate();
22455     });
22456   }
22457
22458   if (isDefined(attr.max) || attr.ngMax) {
22459     var maxVal;
22460     ctrl.$validators.max = function(value) {
22461       return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
22462     };
22463
22464     attr.$observe('max', function(val) {
22465       if (isDefined(val) && !isNumber(val)) {
22466         val = parseFloat(val, 10);
22467       }
22468       maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
22469       // TODO(matsko): implement validateLater to reduce number of validations
22470       ctrl.$validate();
22471     });
22472   }
22473 }
22474
22475 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22476   // Note: no badInputChecker here by purpose as `url` is only a validation
22477   // in browsers, i.e. we can always read out input.value even if it is not valid!
22478   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22479   stringBasedInputType(ctrl);
22480
22481   ctrl.$$parserName = 'url';
22482   ctrl.$validators.url = function(modelValue, viewValue) {
22483     var value = modelValue || viewValue;
22484     return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
22485   };
22486 }
22487
22488 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22489   // Note: no badInputChecker here by purpose as `url` is only a validation
22490   // in browsers, i.e. we can always read out input.value even if it is not valid!
22491   baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22492   stringBasedInputType(ctrl);
22493
22494   ctrl.$$parserName = 'email';
22495   ctrl.$validators.email = function(modelValue, viewValue) {
22496     var value = modelValue || viewValue;
22497     return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
22498   };
22499 }
22500
22501 function radioInputType(scope, element, attr, ctrl) {
22502   // make the name unique, if not defined
22503   if (isUndefined(attr.name)) {
22504     element.attr('name', nextUid());
22505   }
22506
22507   var listener = function(ev) {
22508     if (element[0].checked) {
22509       ctrl.$setViewValue(attr.value, ev && ev.type);
22510     }
22511   };
22512
22513   element.on('click', listener);
22514
22515   ctrl.$render = function() {
22516     var value = attr.value;
22517     element[0].checked = (value == ctrl.$viewValue);
22518   };
22519
22520   attr.$observe('value', ctrl.$render);
22521 }
22522
22523 function parseConstantExpr($parse, context, name, expression, fallback) {
22524   var parseFn;
22525   if (isDefined(expression)) {
22526     parseFn = $parse(expression);
22527     if (!parseFn.constant) {
22528       throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22529                                    '`{1}`.', name, expression);
22530     }
22531     return parseFn(context);
22532   }
22533   return fallback;
22534 }
22535
22536 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22537   var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22538   var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22539
22540   var listener = function(ev) {
22541     ctrl.$setViewValue(element[0].checked, ev && ev.type);
22542   };
22543
22544   element.on('click', listener);
22545
22546   ctrl.$render = function() {
22547     element[0].checked = ctrl.$viewValue;
22548   };
22549
22550   // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22551   // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22552   // it to a boolean.
22553   ctrl.$isEmpty = function(value) {
22554     return value === false;
22555   };
22556
22557   ctrl.$formatters.push(function(value) {
22558     return equals(value, trueValue);
22559   });
22560
22561   ctrl.$parsers.push(function(value) {
22562     return value ? trueValue : falseValue;
22563   });
22564 }
22565
22566
22567 /**
22568  * @ngdoc directive
22569  * @name textarea
22570  * @restrict E
22571  *
22572  * @description
22573  * HTML textarea element control with angular data-binding. The data-binding and validation
22574  * properties of this element are exactly the same as those of the
22575  * {@link ng.directive:input input element}.
22576  *
22577  * @param {string} ngModel Assignable angular expression to data-bind to.
22578  * @param {string=} name Property name of the form under which the control is published.
22579  * @param {string=} required Sets `required` validation error key if the value is not entered.
22580  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22581  *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22582  *    `required` when you want to data-bind to the `required` attribute.
22583  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22584  *    minlength.
22585  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22586  *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22587  *    length.
22588  * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22589  *    a RegExp found by evaluating the Angular expression given in the attribute value.
22590  *    If the expression evaluates to a RegExp object, then this is used directly.
22591  *    If the expression evaluates to a string, then it will be converted to a RegExp
22592  *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22593  *    `new RegExp('^abc$')`.<br />
22594  *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22595  *    start at the index of the last search's match, thus not taking the whole input value into
22596  *    account.
22597  * @param {string=} ngChange Angular expression to be executed when input changes due to user
22598  *    interaction with the input element.
22599  * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22600  */
22601
22602
22603 /**
22604  * @ngdoc directive
22605  * @name input
22606  * @restrict E
22607  *
22608  * @description
22609  * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22610  * input state control, and validation.
22611  * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22612  *
22613  * <div class="alert alert-warning">
22614  * **Note:** Not every feature offered is available for all input types.
22615  * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22616  * </div>
22617  *
22618  * @param {string} ngModel Assignable angular expression to data-bind to.
22619  * @param {string=} name Property name of the form under which the control is published.
22620  * @param {string=} required Sets `required` validation error key if the value is not entered.
22621  * @param {boolean=} ngRequired Sets `required` attribute if set to true
22622  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22623  *    minlength.
22624  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22625  *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22626  *    length.
22627  * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22628  *    a RegExp found by evaluating the Angular expression given in the attribute value.
22629  *    If the expression evaluates to a RegExp object, then this is used directly.
22630  *    If the expression evaluates to a string, then it will be converted to a RegExp
22631  *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22632  *    `new RegExp('^abc$')`.<br />
22633  *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22634  *    start at the index of the last search's match, thus not taking the whole input value into
22635  *    account.
22636  * @param {string=} ngChange Angular expression to be executed when input changes due to user
22637  *    interaction with the input element.
22638  * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22639  *    This parameter is ignored for input[type=password] controls, which will never trim the
22640  *    input.
22641  *
22642  * @example
22643     <example name="input-directive" module="inputExample">
22644       <file name="index.html">
22645        <script>
22646           angular.module('inputExample', [])
22647             .controller('ExampleController', ['$scope', function($scope) {
22648               $scope.user = {name: 'guest', last: 'visitor'};
22649             }]);
22650        </script>
22651        <div ng-controller="ExampleController">
22652          <form name="myForm">
22653            <label>
22654               User name:
22655               <input type="text" name="userName" ng-model="user.name" required>
22656            </label>
22657            <div role="alert">
22658              <span class="error" ng-show="myForm.userName.$error.required">
22659               Required!</span>
22660            </div>
22661            <label>
22662               Last name:
22663               <input type="text" name="lastName" ng-model="user.last"
22664               ng-minlength="3" ng-maxlength="10">
22665            </label>
22666            <div role="alert">
22667              <span class="error" ng-show="myForm.lastName.$error.minlength">
22668                Too short!</span>
22669              <span class="error" ng-show="myForm.lastName.$error.maxlength">
22670                Too long!</span>
22671            </div>
22672          </form>
22673          <hr>
22674          <tt>user = {{user}}</tt><br/>
22675          <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
22676          <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
22677          <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
22678          <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
22679          <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22680          <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22681          <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
22682          <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
22683        </div>
22684       </file>
22685       <file name="protractor.js" type="protractor">
22686         var user = element(by.exactBinding('user'));
22687         var userNameValid = element(by.binding('myForm.userName.$valid'));
22688         var lastNameValid = element(by.binding('myForm.lastName.$valid'));
22689         var lastNameError = element(by.binding('myForm.lastName.$error'));
22690         var formValid = element(by.binding('myForm.$valid'));
22691         var userNameInput = element(by.model('user.name'));
22692         var userLastInput = element(by.model('user.last'));
22693
22694         it('should initialize to model', function() {
22695           expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
22696           expect(userNameValid.getText()).toContain('true');
22697           expect(formValid.getText()).toContain('true');
22698         });
22699
22700         it('should be invalid if empty when required', function() {
22701           userNameInput.clear();
22702           userNameInput.sendKeys('');
22703
22704           expect(user.getText()).toContain('{"last":"visitor"}');
22705           expect(userNameValid.getText()).toContain('false');
22706           expect(formValid.getText()).toContain('false');
22707         });
22708
22709         it('should be valid if empty when min length is set', function() {
22710           userLastInput.clear();
22711           userLastInput.sendKeys('');
22712
22713           expect(user.getText()).toContain('{"name":"guest","last":""}');
22714           expect(lastNameValid.getText()).toContain('true');
22715           expect(formValid.getText()).toContain('true');
22716         });
22717
22718         it('should be invalid if less than required min length', function() {
22719           userLastInput.clear();
22720           userLastInput.sendKeys('xx');
22721
22722           expect(user.getText()).toContain('{"name":"guest"}');
22723           expect(lastNameValid.getText()).toContain('false');
22724           expect(lastNameError.getText()).toContain('minlength');
22725           expect(formValid.getText()).toContain('false');
22726         });
22727
22728         it('should be invalid if longer than max length', function() {
22729           userLastInput.clear();
22730           userLastInput.sendKeys('some ridiculously long name');
22731
22732           expect(user.getText()).toContain('{"name":"guest"}');
22733           expect(lastNameValid.getText()).toContain('false');
22734           expect(lastNameError.getText()).toContain('maxlength');
22735           expect(formValid.getText()).toContain('false');
22736         });
22737       </file>
22738     </example>
22739  */
22740 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
22741     function($browser, $sniffer, $filter, $parse) {
22742   return {
22743     restrict: 'E',
22744     require: ['?ngModel'],
22745     link: {
22746       pre: function(scope, element, attr, ctrls) {
22747         if (ctrls[0]) {
22748           (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
22749                                                               $browser, $filter, $parse);
22750         }
22751       }
22752     }
22753   };
22754 }];
22755
22756
22757
22758 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
22759 /**
22760  * @ngdoc directive
22761  * @name ngValue
22762  *
22763  * @description
22764  * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
22765  * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
22766  * the bound value.
22767  *
22768  * `ngValue` is useful when dynamically generating lists of radio buttons using
22769  * {@link ngRepeat `ngRepeat`}, as shown below.
22770  *
22771  * Likewise, `ngValue` can be used to generate `<option>` elements for
22772  * the {@link select `select`} element. In that case however, only strings are supported
22773  * for the `value `attribute, so the resulting `ngModel` will always be a string.
22774  * Support for `select` models with non-string values is available via `ngOptions`.
22775  *
22776  * @element input
22777  * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
22778  *   of the `input` element
22779  *
22780  * @example
22781     <example name="ngValue-directive" module="valueExample">
22782       <file name="index.html">
22783        <script>
22784           angular.module('valueExample', [])
22785             .controller('ExampleController', ['$scope', function($scope) {
22786               $scope.names = ['pizza', 'unicorns', 'robots'];
22787               $scope.my = { favorite: 'unicorns' };
22788             }]);
22789        </script>
22790         <form ng-controller="ExampleController">
22791           <h2>Which is your favorite?</h2>
22792             <label ng-repeat="name in names" for="{{name}}">
22793               {{name}}
22794               <input type="radio"
22795                      ng-model="my.favorite"
22796                      ng-value="name"
22797                      id="{{name}}"
22798                      name="favorite">
22799             </label>
22800           <div>You chose {{my.favorite}}</div>
22801         </form>
22802       </file>
22803       <file name="protractor.js" type="protractor">
22804         var favorite = element(by.binding('my.favorite'));
22805
22806         it('should initialize to model', function() {
22807           expect(favorite.getText()).toContain('unicorns');
22808         });
22809         it('should bind the values to the inputs', function() {
22810           element.all(by.model('my.favorite')).get(0).click();
22811           expect(favorite.getText()).toContain('pizza');
22812         });
22813       </file>
22814     </example>
22815  */
22816 var ngValueDirective = function() {
22817   return {
22818     restrict: 'A',
22819     priority: 100,
22820     compile: function(tpl, tplAttr) {
22821       if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22822         return function ngValueConstantLink(scope, elm, attr) {
22823           attr.$set('value', scope.$eval(attr.ngValue));
22824         };
22825       } else {
22826         return function ngValueLink(scope, elm, attr) {
22827           scope.$watch(attr.ngValue, function valueWatchAction(value) {
22828             attr.$set('value', value);
22829           });
22830         };
22831       }
22832     }
22833   };
22834 };
22835
22836 /**
22837  * @ngdoc directive
22838  * @name ngBind
22839  * @restrict AC
22840  *
22841  * @description
22842  * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22843  * with the value of a given expression, and to update the text content when the value of that
22844  * expression changes.
22845  *
22846  * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22847  * `{{ expression }}` which is similar but less verbose.
22848  *
22849  * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22850  * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22851  * element attribute, it makes the bindings invisible to the user while the page is loading.
22852  *
22853  * An alternative solution to this problem would be using the
22854  * {@link ng.directive:ngCloak ngCloak} directive.
22855  *
22856  *
22857  * @element ANY
22858  * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22859  *
22860  * @example
22861  * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22862    <example module="bindExample">
22863      <file name="index.html">
22864        <script>
22865          angular.module('bindExample', [])
22866            .controller('ExampleController', ['$scope', function($scope) {
22867              $scope.name = 'Whirled';
22868            }]);
22869        </script>
22870        <div ng-controller="ExampleController">
22871          <label>Enter name: <input type="text" ng-model="name"></label><br>
22872          Hello <span ng-bind="name"></span>!
22873        </div>
22874      </file>
22875      <file name="protractor.js" type="protractor">
22876        it('should check ng-bind', function() {
22877          var nameInput = element(by.model('name'));
22878
22879          expect(element(by.binding('name')).getText()).toBe('Whirled');
22880          nameInput.clear();
22881          nameInput.sendKeys('world');
22882          expect(element(by.binding('name')).getText()).toBe('world');
22883        });
22884      </file>
22885    </example>
22886  */
22887 var ngBindDirective = ['$compile', function($compile) {
22888   return {
22889     restrict: 'AC',
22890     compile: function ngBindCompile(templateElement) {
22891       $compile.$$addBindingClass(templateElement);
22892       return function ngBindLink(scope, element, attr) {
22893         $compile.$$addBindingInfo(element, attr.ngBind);
22894         element = element[0];
22895         scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22896           element.textContent = isUndefined(value) ? '' : value;
22897         });
22898       };
22899     }
22900   };
22901 }];
22902
22903
22904 /**
22905  * @ngdoc directive
22906  * @name ngBindTemplate
22907  *
22908  * @description
22909  * The `ngBindTemplate` directive specifies that the element
22910  * text content should be replaced with the interpolation of the template
22911  * in the `ngBindTemplate` attribute.
22912  * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22913  * expressions. This directive is needed since some HTML elements
22914  * (such as TITLE and OPTION) cannot contain SPAN elements.
22915  *
22916  * @element ANY
22917  * @param {string} ngBindTemplate template of form
22918  *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22919  *
22920  * @example
22921  * Try it here: enter text in text box and watch the greeting change.
22922    <example module="bindExample">
22923      <file name="index.html">
22924        <script>
22925          angular.module('bindExample', [])
22926            .controller('ExampleController', ['$scope', function($scope) {
22927              $scope.salutation = 'Hello';
22928              $scope.name = 'World';
22929            }]);
22930        </script>
22931        <div ng-controller="ExampleController">
22932         <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22933         <label>Name: <input type="text" ng-model="name"></label><br>
22934         <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22935        </div>
22936      </file>
22937      <file name="protractor.js" type="protractor">
22938        it('should check ng-bind', function() {
22939          var salutationElem = element(by.binding('salutation'));
22940          var salutationInput = element(by.model('salutation'));
22941          var nameInput = element(by.model('name'));
22942
22943          expect(salutationElem.getText()).toBe('Hello World!');
22944
22945          salutationInput.clear();
22946          salutationInput.sendKeys('Greetings');
22947          nameInput.clear();
22948          nameInput.sendKeys('user');
22949
22950          expect(salutationElem.getText()).toBe('Greetings user!');
22951        });
22952      </file>
22953    </example>
22954  */
22955 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22956   return {
22957     compile: function ngBindTemplateCompile(templateElement) {
22958       $compile.$$addBindingClass(templateElement);
22959       return function ngBindTemplateLink(scope, element, attr) {
22960         var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22961         $compile.$$addBindingInfo(element, interpolateFn.expressions);
22962         element = element[0];
22963         attr.$observe('ngBindTemplate', function(value) {
22964           element.textContent = isUndefined(value) ? '' : value;
22965         });
22966       };
22967     }
22968   };
22969 }];
22970
22971
22972 /**
22973  * @ngdoc directive
22974  * @name ngBindHtml
22975  *
22976  * @description
22977  * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22978  * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22979  * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22980  * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22981  * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22982  *
22983  * You may also bypass sanitization for values you know are safe. To do so, bind to
22984  * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
22985  * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22986  *
22987  * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22988  * will have an exception (instead of an exploit.)
22989  *
22990  * @element ANY
22991  * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22992  *
22993  * @example
22994
22995    <example module="bindHtmlExample" deps="angular-sanitize.js">
22996      <file name="index.html">
22997        <div ng-controller="ExampleController">
22998         <p ng-bind-html="myHTML"></p>
22999        </div>
23000      </file>
23001
23002      <file name="script.js">
23003        angular.module('bindHtmlExample', ['ngSanitize'])
23004          .controller('ExampleController', ['$scope', function($scope) {
23005            $scope.myHTML =
23006               'I am an <code>HTML</code>string with ' +
23007               '<a href="#">links!</a> and other <em>stuff</em>';
23008          }]);
23009      </file>
23010
23011      <file name="protractor.js" type="protractor">
23012        it('should check ng-bind-html', function() {
23013          expect(element(by.binding('myHTML')).getText()).toBe(
23014              'I am an HTMLstring with links! and other stuff');
23015        });
23016      </file>
23017    </example>
23018  */
23019 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
23020   return {
23021     restrict: 'A',
23022     compile: function ngBindHtmlCompile(tElement, tAttrs) {
23023       var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
23024       var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
23025         return (value || '').toString();
23026       });
23027       $compile.$$addBindingClass(tElement);
23028
23029       return function ngBindHtmlLink(scope, element, attr) {
23030         $compile.$$addBindingInfo(element, attr.ngBindHtml);
23031
23032         scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
23033           // we re-evaluate the expr because we want a TrustedValueHolderType
23034           // for $sce, not a string
23035           element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
23036         });
23037       };
23038     }
23039   };
23040 }];
23041
23042 /**
23043  * @ngdoc directive
23044  * @name ngChange
23045  *
23046  * @description
23047  * Evaluate the given expression when the user changes the input.
23048  * The expression is evaluated immediately, unlike the JavaScript onchange event
23049  * which only triggers at the end of a change (usually, when the user leaves the
23050  * form element or presses the return key).
23051  *
23052  * The `ngChange` expression is only evaluated when a change in the input value causes
23053  * a new value to be committed to the model.
23054  *
23055  * It will not be evaluated:
23056  * * if the value returned from the `$parsers` transformation pipeline has not changed
23057  * * if the input has continued to be invalid since the model will stay `null`
23058  * * if the model is changed programmatically and not by a change to the input value
23059  *
23060  *
23061  * Note, this directive requires `ngModel` to be present.
23062  *
23063  * @element input
23064  * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
23065  * in input value.
23066  *
23067  * @example
23068  * <example name="ngChange-directive" module="changeExample">
23069  *   <file name="index.html">
23070  *     <script>
23071  *       angular.module('changeExample', [])
23072  *         .controller('ExampleController', ['$scope', function($scope) {
23073  *           $scope.counter = 0;
23074  *           $scope.change = function() {
23075  *             $scope.counter++;
23076  *           };
23077  *         }]);
23078  *     </script>
23079  *     <div ng-controller="ExampleController">
23080  *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
23081  *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
23082  *       <label for="ng-change-example2">Confirmed</label><br />
23083  *       <tt>debug = {{confirmed}}</tt><br/>
23084  *       <tt>counter = {{counter}}</tt><br/>
23085  *     </div>
23086  *   </file>
23087  *   <file name="protractor.js" type="protractor">
23088  *     var counter = element(by.binding('counter'));
23089  *     var debug = element(by.binding('confirmed'));
23090  *
23091  *     it('should evaluate the expression if changing from view', function() {
23092  *       expect(counter.getText()).toContain('0');
23093  *
23094  *       element(by.id('ng-change-example1')).click();
23095  *
23096  *       expect(counter.getText()).toContain('1');
23097  *       expect(debug.getText()).toContain('true');
23098  *     });
23099  *
23100  *     it('should not evaluate the expression if changing from model', function() {
23101  *       element(by.id('ng-change-example2')).click();
23102
23103  *       expect(counter.getText()).toContain('0');
23104  *       expect(debug.getText()).toContain('true');
23105  *     });
23106  *   </file>
23107  * </example>
23108  */
23109 var ngChangeDirective = valueFn({
23110   restrict: 'A',
23111   require: 'ngModel',
23112   link: function(scope, element, attr, ctrl) {
23113     ctrl.$viewChangeListeners.push(function() {
23114       scope.$eval(attr.ngChange);
23115     });
23116   }
23117 });
23118
23119 function classDirective(name, selector) {
23120   name = 'ngClass' + name;
23121   return ['$animate', function($animate) {
23122     return {
23123       restrict: 'AC',
23124       link: function(scope, element, attr) {
23125         var oldVal;
23126
23127         scope.$watch(attr[name], ngClassWatchAction, true);
23128
23129         attr.$observe('class', function(value) {
23130           ngClassWatchAction(scope.$eval(attr[name]));
23131         });
23132
23133
23134         if (name !== 'ngClass') {
23135           scope.$watch('$index', function($index, old$index) {
23136             // jshint bitwise: false
23137             var mod = $index & 1;
23138             if (mod !== (old$index & 1)) {
23139               var classes = arrayClasses(scope.$eval(attr[name]));
23140               mod === selector ?
23141                 addClasses(classes) :
23142                 removeClasses(classes);
23143             }
23144           });
23145         }
23146
23147         function addClasses(classes) {
23148           var newClasses = digestClassCounts(classes, 1);
23149           attr.$addClass(newClasses);
23150         }
23151
23152         function removeClasses(classes) {
23153           var newClasses = digestClassCounts(classes, -1);
23154           attr.$removeClass(newClasses);
23155         }
23156
23157         function digestClassCounts(classes, count) {
23158           // Use createMap() to prevent class assumptions involving property
23159           // names in Object.prototype
23160           var classCounts = element.data('$classCounts') || createMap();
23161           var classesToUpdate = [];
23162           forEach(classes, function(className) {
23163             if (count > 0 || classCounts[className]) {
23164               classCounts[className] = (classCounts[className] || 0) + count;
23165               if (classCounts[className] === +(count > 0)) {
23166                 classesToUpdate.push(className);
23167               }
23168             }
23169           });
23170           element.data('$classCounts', classCounts);
23171           return classesToUpdate.join(' ');
23172         }
23173
23174         function updateClasses(oldClasses, newClasses) {
23175           var toAdd = arrayDifference(newClasses, oldClasses);
23176           var toRemove = arrayDifference(oldClasses, newClasses);
23177           toAdd = digestClassCounts(toAdd, 1);
23178           toRemove = digestClassCounts(toRemove, -1);
23179           if (toAdd && toAdd.length) {
23180             $animate.addClass(element, toAdd);
23181           }
23182           if (toRemove && toRemove.length) {
23183             $animate.removeClass(element, toRemove);
23184           }
23185         }
23186
23187         function ngClassWatchAction(newVal) {
23188           if (selector === true || scope.$index % 2 === selector) {
23189             var newClasses = arrayClasses(newVal || []);
23190             if (!oldVal) {
23191               addClasses(newClasses);
23192             } else if (!equals(newVal,oldVal)) {
23193               var oldClasses = arrayClasses(oldVal);
23194               updateClasses(oldClasses, newClasses);
23195             }
23196           }
23197           oldVal = shallowCopy(newVal);
23198         }
23199       }
23200     };
23201
23202     function arrayDifference(tokens1, tokens2) {
23203       var values = [];
23204
23205       outer:
23206       for (var i = 0; i < tokens1.length; i++) {
23207         var token = tokens1[i];
23208         for (var j = 0; j < tokens2.length; j++) {
23209           if (token == tokens2[j]) continue outer;
23210         }
23211         values.push(token);
23212       }
23213       return values;
23214     }
23215
23216     function arrayClasses(classVal) {
23217       var classes = [];
23218       if (isArray(classVal)) {
23219         forEach(classVal, function(v) {
23220           classes = classes.concat(arrayClasses(v));
23221         });
23222         return classes;
23223       } else if (isString(classVal)) {
23224         return classVal.split(' ');
23225       } else if (isObject(classVal)) {
23226         forEach(classVal, function(v, k) {
23227           if (v) {
23228             classes = classes.concat(k.split(' '));
23229           }
23230         });
23231         return classes;
23232       }
23233       return classVal;
23234     }
23235   }];
23236 }
23237
23238 /**
23239  * @ngdoc directive
23240  * @name ngClass
23241  * @restrict AC
23242  *
23243  * @description
23244  * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
23245  * an expression that represents all classes to be added.
23246  *
23247  * The directive operates in three different ways, depending on which of three types the expression
23248  * evaluates to:
23249  *
23250  * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
23251  * names.
23252  *
23253  * 2. If the expression evaluates to an object, then for each key-value pair of the
23254  * object with a truthy value the corresponding key is used as a class name.
23255  *
23256  * 3. If the expression evaluates to an array, each element of the array should either be a string as in
23257  * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
23258  * to give you more control over what CSS classes appear. See the code below for an example of this.
23259  *
23260  *
23261  * The directive won't add duplicate classes if a particular class was already set.
23262  *
23263  * When the expression changes, the previously added classes are removed and only then are the
23264  * new classes added.
23265  *
23266  * @animations
23267  * **add** - happens just before the class is applied to the elements
23268  *
23269  * **remove** - happens just before the class is removed from the element
23270  *
23271  * @element ANY
23272  * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
23273  *   of the evaluation can be a string representing space delimited class
23274  *   names, an array, or a map of class names to boolean values. In the case of a map, the
23275  *   names of the properties whose values are truthy will be added as css classes to the
23276  *   element.
23277  *
23278  * @example Example that demonstrates basic bindings via ngClass directive.
23279    <example>
23280      <file name="index.html">
23281        <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
23282        <label>
23283           <input type="checkbox" ng-model="deleted">
23284           deleted (apply "strike" class)
23285        </label><br>
23286        <label>
23287           <input type="checkbox" ng-model="important">
23288           important (apply "bold" class)
23289        </label><br>
23290        <label>
23291           <input type="checkbox" ng-model="error">
23292           error (apply "has-error" class)
23293        </label>
23294        <hr>
23295        <p ng-class="style">Using String Syntax</p>
23296        <input type="text" ng-model="style"
23297               placeholder="Type: bold strike red" aria-label="Type: bold strike red">
23298        <hr>
23299        <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
23300        <input ng-model="style1"
23301               placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
23302        <input ng-model="style2"
23303               placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
23304        <input ng-model="style3"
23305               placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
23306        <hr>
23307        <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
23308        <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
23309        <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
23310      </file>
23311      <file name="style.css">
23312        .strike {
23313            text-decoration: line-through;
23314        }
23315        .bold {
23316            font-weight: bold;
23317        }
23318        .red {
23319            color: red;
23320        }
23321        .has-error {
23322            color: red;
23323            background-color: yellow;
23324        }
23325        .orange {
23326            color: orange;
23327        }
23328      </file>
23329      <file name="protractor.js" type="protractor">
23330        var ps = element.all(by.css('p'));
23331
23332        it('should let you toggle the class', function() {
23333
23334          expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
23335          expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
23336
23337          element(by.model('important')).click();
23338          expect(ps.first().getAttribute('class')).toMatch(/bold/);
23339
23340          element(by.model('error')).click();
23341          expect(ps.first().getAttribute('class')).toMatch(/has-error/);
23342        });
23343
23344        it('should let you toggle string example', function() {
23345          expect(ps.get(1).getAttribute('class')).toBe('');
23346          element(by.model('style')).clear();
23347          element(by.model('style')).sendKeys('red');
23348          expect(ps.get(1).getAttribute('class')).toBe('red');
23349        });
23350
23351        it('array example should have 3 classes', function() {
23352          expect(ps.get(2).getAttribute('class')).toBe('');
23353          element(by.model('style1')).sendKeys('bold');
23354          element(by.model('style2')).sendKeys('strike');
23355          element(by.model('style3')).sendKeys('red');
23356          expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
23357        });
23358
23359        it('array with map example should have 2 classes', function() {
23360          expect(ps.last().getAttribute('class')).toBe('');
23361          element(by.model('style4')).sendKeys('bold');
23362          element(by.model('warning')).click();
23363          expect(ps.last().getAttribute('class')).toBe('bold orange');
23364        });
23365      </file>
23366    </example>
23367
23368    ## Animations
23369
23370    The example below demonstrates how to perform animations using ngClass.
23371
23372    <example module="ngAnimate" deps="angular-animate.js" animations="true">
23373      <file name="index.html">
23374       <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
23375       <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
23376       <br>
23377       <span class="base-class" ng-class="myVar">Sample Text</span>
23378      </file>
23379      <file name="style.css">
23380        .base-class {
23381          transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23382        }
23383
23384        .base-class.my-class {
23385          color: red;
23386          font-size:3em;
23387        }
23388      </file>
23389      <file name="protractor.js" type="protractor">
23390        it('should check ng-class', function() {
23391          expect(element(by.css('.base-class')).getAttribute('class')).not.
23392            toMatch(/my-class/);
23393
23394          element(by.id('setbtn')).click();
23395
23396          expect(element(by.css('.base-class')).getAttribute('class')).
23397            toMatch(/my-class/);
23398
23399          element(by.id('clearbtn')).click();
23400
23401          expect(element(by.css('.base-class')).getAttribute('class')).not.
23402            toMatch(/my-class/);
23403        });
23404      </file>
23405    </example>
23406
23407
23408    ## ngClass and pre-existing CSS3 Transitions/Animations
23409    The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
23410    Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
23411    any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
23412    to view the step by step details of {@link $animate#addClass $animate.addClass} and
23413    {@link $animate#removeClass $animate.removeClass}.
23414  */
23415 var ngClassDirective = classDirective('', true);
23416
23417 /**
23418  * @ngdoc directive
23419  * @name ngClassOdd
23420  * @restrict AC
23421  *
23422  * @description
23423  * The `ngClassOdd` and `ngClassEven` directives work exactly as
23424  * {@link ng.directive:ngClass ngClass}, except they work in
23425  * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23426  *
23427  * This directive can be applied only within the scope of an
23428  * {@link ng.directive:ngRepeat ngRepeat}.
23429  *
23430  * @element ANY
23431  * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
23432  *   of the evaluation can be a string representing space delimited class names or an array.
23433  *
23434  * @example
23435    <example>
23436      <file name="index.html">
23437         <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23438           <li ng-repeat="name in names">
23439            <span ng-class-odd="'odd'" ng-class-even="'even'">
23440              {{name}}
23441            </span>
23442           </li>
23443         </ol>
23444      </file>
23445      <file name="style.css">
23446        .odd {
23447          color: red;
23448        }
23449        .even {
23450          color: blue;
23451        }
23452      </file>
23453      <file name="protractor.js" type="protractor">
23454        it('should check ng-class-odd and ng-class-even', function() {
23455          expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23456            toMatch(/odd/);
23457          expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23458            toMatch(/even/);
23459        });
23460      </file>
23461    </example>
23462  */
23463 var ngClassOddDirective = classDirective('Odd', 0);
23464
23465 /**
23466  * @ngdoc directive
23467  * @name ngClassEven
23468  * @restrict AC
23469  *
23470  * @description
23471  * The `ngClassOdd` and `ngClassEven` directives work exactly as
23472  * {@link ng.directive:ngClass ngClass}, except they work in
23473  * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23474  *
23475  * This directive can be applied only within the scope of an
23476  * {@link ng.directive:ngRepeat ngRepeat}.
23477  *
23478  * @element ANY
23479  * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
23480  *   result of the evaluation can be a string representing space delimited class names or an array.
23481  *
23482  * @example
23483    <example>
23484      <file name="index.html">
23485         <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23486           <li ng-repeat="name in names">
23487            <span ng-class-odd="'odd'" ng-class-even="'even'">
23488              {{name}} &nbsp; &nbsp; &nbsp;
23489            </span>
23490           </li>
23491         </ol>
23492      </file>
23493      <file name="style.css">
23494        .odd {
23495          color: red;
23496        }
23497        .even {
23498          color: blue;
23499        }
23500      </file>
23501      <file name="protractor.js" type="protractor">
23502        it('should check ng-class-odd and ng-class-even', function() {
23503          expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23504            toMatch(/odd/);
23505          expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23506            toMatch(/even/);
23507        });
23508      </file>
23509    </example>
23510  */
23511 var ngClassEvenDirective = classDirective('Even', 1);
23512
23513 /**
23514  * @ngdoc directive
23515  * @name ngCloak
23516  * @restrict AC
23517  *
23518  * @description
23519  * The `ngCloak` directive is used to prevent the Angular html template from being briefly
23520  * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
23521  * directive to avoid the undesirable flicker effect caused by the html template display.
23522  *
23523  * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23524  * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23525  * of the browser view.
23526  *
23527  * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23528  * `angular.min.js`.
23529  * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23530  *
23531  * ```css
23532  * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23533  *   display: none !important;
23534  * }
23535  * ```
23536  *
23537  * When this css rule is loaded by the browser, all html elements (including their children) that
23538  * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23539  * during the compilation of the template it deletes the `ngCloak` element attribute, making
23540  * the compiled element visible.
23541  *
23542  * For the best result, the `angular.js` script must be loaded in the head section of the html
23543  * document; alternatively, the css rule above must be included in the external stylesheet of the
23544  * application.
23545  *
23546  * @element ANY
23547  *
23548  * @example
23549    <example>
23550      <file name="index.html">
23551         <div id="template1" ng-cloak>{{ 'hello' }}</div>
23552         <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23553      </file>
23554      <file name="protractor.js" type="protractor">
23555        it('should remove the template directive and css class', function() {
23556          expect($('#template1').getAttribute('ng-cloak')).
23557            toBeNull();
23558          expect($('#template2').getAttribute('ng-cloak')).
23559            toBeNull();
23560        });
23561      </file>
23562    </example>
23563  *
23564  */
23565 var ngCloakDirective = ngDirective({
23566   compile: function(element, attr) {
23567     attr.$set('ngCloak', undefined);
23568     element.removeClass('ng-cloak');
23569   }
23570 });
23571
23572 /**
23573  * @ngdoc directive
23574  * @name ngController
23575  *
23576  * @description
23577  * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23578  * supports the principles behind the Model-View-Controller design pattern.
23579  *
23580  * MVC components in angular:
23581  *
23582  * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23583  *   are accessed through bindings.
23584  * * View — The template (HTML with data bindings) that is rendered into the View.
23585  * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23586  *   logic behind the application to decorate the scope with functions and values
23587  *
23588  * Note that you can also attach controllers to the DOM by declaring it in a route definition
23589  * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23590  * again using `ng-controller` in the template itself.  This will cause the controller to be attached
23591  * and executed twice.
23592  *
23593  * @element ANY
23594  * @scope
23595  * @priority 500
23596  * @param {expression} ngController Name of a constructor function registered with the current
23597  * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23598  * that on the current scope evaluates to a constructor function.
23599  *
23600  * The controller instance can be published into a scope property by specifying
23601  * `ng-controller="as propertyName"`.
23602  *
23603  * If the current `$controllerProvider` is configured to use globals (via
23604  * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23605  * also be the name of a globally accessible constructor function (not recommended).
23606  *
23607  * @example
23608  * Here is a simple form for editing user contact information. Adding, removing, clearing, and
23609  * greeting are methods declared on the controller (see source tab). These methods can
23610  * easily be called from the angular markup. Any changes to the data are automatically reflected
23611  * in the View without the need for a manual update.
23612  *
23613  * Two different declaration styles are included below:
23614  *
23615  * * one binds methods and properties directly onto the controller using `this`:
23616  * `ng-controller="SettingsController1 as settings"`
23617  * * one injects `$scope` into the controller:
23618  * `ng-controller="SettingsController2"`
23619  *
23620  * The second option is more common in the Angular community, and is generally used in boilerplates
23621  * and in this guide. However, there are advantages to binding properties directly to the controller
23622  * and avoiding scope.
23623  *
23624  * * Using `controller as` makes it obvious which controller you are accessing in the template when
23625  * multiple controllers apply to an element.
23626  * * If you are writing your controllers as classes you have easier access to the properties and
23627  * methods, which will appear on the scope, from inside the controller code.
23628  * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23629  * inheritance masking primitives.
23630  *
23631  * This example demonstrates the `controller as` syntax.
23632  *
23633  * <example name="ngControllerAs" module="controllerAsExample">
23634  *   <file name="index.html">
23635  *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23636  *      <label>Name: <input type="text" ng-model="settings.name"/></label>
23637  *      <button ng-click="settings.greet()">greet</button><br/>
23638  *      Contact:
23639  *      <ul>
23640  *        <li ng-repeat="contact in settings.contacts">
23641  *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23642  *             <option>phone</option>
23643  *             <option>email</option>
23644  *          </select>
23645  *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23646  *          <button ng-click="settings.clearContact(contact)">clear</button>
23647  *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
23648  *        </li>
23649  *        <li><button ng-click="settings.addContact()">add</button></li>
23650  *     </ul>
23651  *    </div>
23652  *   </file>
23653  *   <file name="app.js">
23654  *    angular.module('controllerAsExample', [])
23655  *      .controller('SettingsController1', SettingsController1);
23656  *
23657  *    function SettingsController1() {
23658  *      this.name = "John Smith";
23659  *      this.contacts = [
23660  *        {type: 'phone', value: '408 555 1212'},
23661  *        {type: 'email', value: 'john.smith@example.org'} ];
23662  *    }
23663  *
23664  *    SettingsController1.prototype.greet = function() {
23665  *      alert(this.name);
23666  *    };
23667  *
23668  *    SettingsController1.prototype.addContact = function() {
23669  *      this.contacts.push({type: 'email', value: 'yourname@example.org'});
23670  *    };
23671  *
23672  *    SettingsController1.prototype.removeContact = function(contactToRemove) {
23673  *     var index = this.contacts.indexOf(contactToRemove);
23674  *      this.contacts.splice(index, 1);
23675  *    };
23676  *
23677  *    SettingsController1.prototype.clearContact = function(contact) {
23678  *      contact.type = 'phone';
23679  *      contact.value = '';
23680  *    };
23681  *   </file>
23682  *   <file name="protractor.js" type="protractor">
23683  *     it('should check controller as', function() {
23684  *       var container = element(by.id('ctrl-as-exmpl'));
23685  *         expect(container.element(by.model('settings.name'))
23686  *           .getAttribute('value')).toBe('John Smith');
23687  *
23688  *       var firstRepeat =
23689  *           container.element(by.repeater('contact in settings.contacts').row(0));
23690  *       var secondRepeat =
23691  *           container.element(by.repeater('contact in settings.contacts').row(1));
23692  *
23693  *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23694  *           .toBe('408 555 1212');
23695  *
23696  *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23697  *           .toBe('john.smith@example.org');
23698  *
23699  *       firstRepeat.element(by.buttonText('clear')).click();
23700  *
23701  *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23702  *           .toBe('');
23703  *
23704  *       container.element(by.buttonText('add')).click();
23705  *
23706  *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
23707  *           .element(by.model('contact.value'))
23708  *           .getAttribute('value'))
23709  *           .toBe('yourname@example.org');
23710  *     });
23711  *   </file>
23712  * </example>
23713  *
23714  * This example demonstrates the "attach to `$scope`" style of controller.
23715  *
23716  * <example name="ngController" module="controllerExample">
23717  *  <file name="index.html">
23718  *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
23719  *     <label>Name: <input type="text" ng-model="name"/></label>
23720  *     <button ng-click="greet()">greet</button><br/>
23721  *     Contact:
23722  *     <ul>
23723  *       <li ng-repeat="contact in contacts">
23724  *         <select ng-model="contact.type" id="select_{{$index}}">
23725  *            <option>phone</option>
23726  *            <option>email</option>
23727  *         </select>
23728  *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23729  *         <button ng-click="clearContact(contact)">clear</button>
23730  *         <button ng-click="removeContact(contact)">X</button>
23731  *       </li>
23732  *       <li>[ <button ng-click="addContact()">add</button> ]</li>
23733  *    </ul>
23734  *   </div>
23735  *  </file>
23736  *  <file name="app.js">
23737  *   angular.module('controllerExample', [])
23738  *     .controller('SettingsController2', ['$scope', SettingsController2]);
23739  *
23740  *   function SettingsController2($scope) {
23741  *     $scope.name = "John Smith";
23742  *     $scope.contacts = [
23743  *       {type:'phone', value:'408 555 1212'},
23744  *       {type:'email', value:'john.smith@example.org'} ];
23745  *
23746  *     $scope.greet = function() {
23747  *       alert($scope.name);
23748  *     };
23749  *
23750  *     $scope.addContact = function() {
23751  *       $scope.contacts.push({type:'email', value:'yourname@example.org'});
23752  *     };
23753  *
23754  *     $scope.removeContact = function(contactToRemove) {
23755  *       var index = $scope.contacts.indexOf(contactToRemove);
23756  *       $scope.contacts.splice(index, 1);
23757  *     };
23758  *
23759  *     $scope.clearContact = function(contact) {
23760  *       contact.type = 'phone';
23761  *       contact.value = '';
23762  *     };
23763  *   }
23764  *  </file>
23765  *  <file name="protractor.js" type="protractor">
23766  *    it('should check controller', function() {
23767  *      var container = element(by.id('ctrl-exmpl'));
23768  *
23769  *      expect(container.element(by.model('name'))
23770  *          .getAttribute('value')).toBe('John Smith');
23771  *
23772  *      var firstRepeat =
23773  *          container.element(by.repeater('contact in contacts').row(0));
23774  *      var secondRepeat =
23775  *          container.element(by.repeater('contact in contacts').row(1));
23776  *
23777  *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23778  *          .toBe('408 555 1212');
23779  *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23780  *          .toBe('john.smith@example.org');
23781  *
23782  *      firstRepeat.element(by.buttonText('clear')).click();
23783  *
23784  *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23785  *          .toBe('');
23786  *
23787  *      container.element(by.buttonText('add')).click();
23788  *
23789  *      expect(container.element(by.repeater('contact in contacts').row(2))
23790  *          .element(by.model('contact.value'))
23791  *          .getAttribute('value'))
23792  *          .toBe('yourname@example.org');
23793  *    });
23794  *  </file>
23795  *</example>
23796
23797  */
23798 var ngControllerDirective = [function() {
23799   return {
23800     restrict: 'A',
23801     scope: true,
23802     controller: '@',
23803     priority: 500
23804   };
23805 }];
23806
23807 /**
23808  * @ngdoc directive
23809  * @name ngCsp
23810  *
23811  * @element html
23812  * @description
23813  *
23814  * Angular has some features that can break certain
23815  * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
23816  *
23817  * If you intend to implement these rules then you must tell Angular not to use these features.
23818  *
23819  * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23820  *
23821  *
23822  * The following rules affect Angular:
23823  *
23824  * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
23825  * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
23826  * increase in the speed of evaluating Angular expressions.
23827  *
23828  * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
23829  * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
23830  * To make these directives work when a CSP rule is blocking inline styles, you must link to the
23831  * `angular-csp.css` in your HTML manually.
23832  *
23833  * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
23834  * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
23835  * however, triggers a CSP error to be logged in the console:
23836  *
23837  * ```
23838  * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23839  * script in the following Content Security Policy directive: "default-src 'self'". Note that
23840  * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23841  * ```
23842  *
23843  * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23844  * directive on an element of the HTML document that appears before the `<script>` tag that loads
23845  * the `angular.js` file.
23846  *
23847  * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23848  *
23849  * You can specify which of the CSP related Angular features should be deactivated by providing
23850  * a value for the `ng-csp` attribute. The options are as follows:
23851  *
23852  * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
23853  *
23854  * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
23855  *
23856  * You can use these values in the following combinations:
23857  *
23858  *
23859  * * No declaration means that Angular will assume that you can do inline styles, but it will do
23860  * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
23861  * of Angular.
23862  *
23863  * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
23864  * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
23865  * of Angular.
23866  *
23867  * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
23868  * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
23869  *
23870  * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
23871  * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
23872  *
23873  * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
23874  * styles nor use eval, which is the same as an empty: ng-csp.
23875  * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
23876  *
23877  * @example
23878  * This example shows how to apply the `ngCsp` directive to the `html` tag.
23879    ```html
23880      <!doctype html>
23881      <html ng-app ng-csp>
23882      ...
23883      ...
23884      </html>
23885    ```
23886   * @example
23887       // Note: the suffix `.csp` in the example name triggers
23888       // csp mode in our http server!
23889       <example name="example.csp" module="cspExample" ng-csp="true">
23890         <file name="index.html">
23891           <div ng-controller="MainController as ctrl">
23892             <div>
23893               <button ng-click="ctrl.inc()" id="inc">Increment</button>
23894               <span id="counter">
23895                 {{ctrl.counter}}
23896               </span>
23897             </div>
23898
23899             <div>
23900               <button ng-click="ctrl.evil()" id="evil">Evil</button>
23901               <span id="evilError">
23902                 {{ctrl.evilError}}
23903               </span>
23904             </div>
23905           </div>
23906         </file>
23907         <file name="script.js">
23908            angular.module('cspExample', [])
23909              .controller('MainController', function() {
23910                 this.counter = 0;
23911                 this.inc = function() {
23912                   this.counter++;
23913                 };
23914                 this.evil = function() {
23915                   // jshint evil:true
23916                   try {
23917                     eval('1+2');
23918                   } catch (e) {
23919                     this.evilError = e.message;
23920                   }
23921                 };
23922               });
23923         </file>
23924         <file name="protractor.js" type="protractor">
23925           var util, webdriver;
23926
23927           var incBtn = element(by.id('inc'));
23928           var counter = element(by.id('counter'));
23929           var evilBtn = element(by.id('evil'));
23930           var evilError = element(by.id('evilError'));
23931
23932           function getAndClearSevereErrors() {
23933             return browser.manage().logs().get('browser').then(function(browserLog) {
23934               return browserLog.filter(function(logEntry) {
23935                 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23936               });
23937             });
23938           }
23939
23940           function clearErrors() {
23941             getAndClearSevereErrors();
23942           }
23943
23944           function expectNoErrors() {
23945             getAndClearSevereErrors().then(function(filteredLog) {
23946               expect(filteredLog.length).toEqual(0);
23947               if (filteredLog.length) {
23948                 console.log('browser console errors: ' + util.inspect(filteredLog));
23949               }
23950             });
23951           }
23952
23953           function expectError(regex) {
23954             getAndClearSevereErrors().then(function(filteredLog) {
23955               var found = false;
23956               filteredLog.forEach(function(log) {
23957                 if (log.message.match(regex)) {
23958                   found = true;
23959                 }
23960               });
23961               if (!found) {
23962                 throw new Error('expected an error that matches ' + regex);
23963               }
23964             });
23965           }
23966
23967           beforeEach(function() {
23968             util = require('util');
23969             webdriver = require('protractor/node_modules/selenium-webdriver');
23970           });
23971
23972           // For now, we only test on Chrome,
23973           // as Safari does not load the page with Protractor's injected scripts,
23974           // and Firefox webdriver always disables content security policy (#6358)
23975           if (browser.params.browser !== 'chrome') {
23976             return;
23977           }
23978
23979           it('should not report errors when the page is loaded', function() {
23980             // clear errors so we are not dependent on previous tests
23981             clearErrors();
23982             // Need to reload the page as the page is already loaded when
23983             // we come here
23984             browser.driver.getCurrentUrl().then(function(url) {
23985               browser.get(url);
23986             });
23987             expectNoErrors();
23988           });
23989
23990           it('should evaluate expressions', function() {
23991             expect(counter.getText()).toEqual('0');
23992             incBtn.click();
23993             expect(counter.getText()).toEqual('1');
23994             expectNoErrors();
23995           });
23996
23997           it('should throw and report an error when using "eval"', function() {
23998             evilBtn.click();
23999             expect(evilError.getText()).toMatch(/Content Security Policy/);
24000             expectError(/Content Security Policy/);
24001           });
24002         </file>
24003       </example>
24004   */
24005
24006 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
24007 // bootstrap the system (before $parse is instantiated), for this reason we just have
24008 // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
24009
24010 /**
24011  * @ngdoc directive
24012  * @name ngClick
24013  *
24014  * @description
24015  * The ngClick directive allows you to specify custom behavior when
24016  * an element is clicked.
24017  *
24018  * @element ANY
24019  * @priority 0
24020  * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
24021  * click. ({@link guide/expression#-event- Event object is available as `$event`})
24022  *
24023  * @example
24024    <example>
24025      <file name="index.html">
24026       <button ng-click="count = count + 1" ng-init="count=0">
24027         Increment
24028       </button>
24029       <span>
24030         count: {{count}}
24031       </span>
24032      </file>
24033      <file name="protractor.js" type="protractor">
24034        it('should check ng-click', function() {
24035          expect(element(by.binding('count')).getText()).toMatch('0');
24036          element(by.css('button')).click();
24037          expect(element(by.binding('count')).getText()).toMatch('1');
24038        });
24039      </file>
24040    </example>
24041  */
24042 /*
24043  * A collection of directives that allows creation of custom event handlers that are defined as
24044  * angular expressions and are compiled and executed within the current scope.
24045  */
24046 var ngEventDirectives = {};
24047
24048 // For events that might fire synchronously during DOM manipulation
24049 // we need to execute their event handlers asynchronously using $evalAsync,
24050 // so that they are not executed in an inconsistent state.
24051 var forceAsyncEvents = {
24052   'blur': true,
24053   'focus': true
24054 };
24055 forEach(
24056   'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
24057   function(eventName) {
24058     var directiveName = directiveNormalize('ng-' + eventName);
24059     ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
24060       return {
24061         restrict: 'A',
24062         compile: function($element, attr) {
24063           // We expose the powerful $event object on the scope that provides access to the Window,
24064           // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
24065           // checks at the cost of speed since event handler expressions are not executed as
24066           // frequently as regular change detection.
24067           var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
24068           return function ngEventHandler(scope, element) {
24069             element.on(eventName, function(event) {
24070               var callback = function() {
24071                 fn(scope, {$event:event});
24072               };
24073               if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
24074                 scope.$evalAsync(callback);
24075               } else {
24076                 scope.$apply(callback);
24077               }
24078             });
24079           };
24080         }
24081       };
24082     }];
24083   }
24084 );
24085
24086 /**
24087  * @ngdoc directive
24088  * @name ngDblclick
24089  *
24090  * @description
24091  * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
24092  *
24093  * @element ANY
24094  * @priority 0
24095  * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
24096  * a dblclick. (The Event object is available as `$event`)
24097  *
24098  * @example
24099    <example>
24100      <file name="index.html">
24101       <button ng-dblclick="count = count + 1" ng-init="count=0">
24102         Increment (on double click)
24103       </button>
24104       count: {{count}}
24105      </file>
24106    </example>
24107  */
24108
24109
24110 /**
24111  * @ngdoc directive
24112  * @name ngMousedown
24113  *
24114  * @description
24115  * The ngMousedown directive allows you to specify custom behavior on mousedown event.
24116  *
24117  * @element ANY
24118  * @priority 0
24119  * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
24120  * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
24121  *
24122  * @example
24123    <example>
24124      <file name="index.html">
24125       <button ng-mousedown="count = count + 1" ng-init="count=0">
24126         Increment (on mouse down)
24127       </button>
24128       count: {{count}}
24129      </file>
24130    </example>
24131  */
24132
24133
24134 /**
24135  * @ngdoc directive
24136  * @name ngMouseup
24137  *
24138  * @description
24139  * Specify custom behavior on mouseup event.
24140  *
24141  * @element ANY
24142  * @priority 0
24143  * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
24144  * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
24145  *
24146  * @example
24147    <example>
24148      <file name="index.html">
24149       <button ng-mouseup="count = count + 1" ng-init="count=0">
24150         Increment (on mouse up)
24151       </button>
24152       count: {{count}}
24153      </file>
24154    </example>
24155  */
24156
24157 /**
24158  * @ngdoc directive
24159  * @name ngMouseover
24160  *
24161  * @description
24162  * Specify custom behavior on mouseover event.
24163  *
24164  * @element ANY
24165  * @priority 0
24166  * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
24167  * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
24168  *
24169  * @example
24170    <example>
24171      <file name="index.html">
24172       <button ng-mouseover="count = count + 1" ng-init="count=0">
24173         Increment (when mouse is over)
24174       </button>
24175       count: {{count}}
24176      </file>
24177    </example>
24178  */
24179
24180
24181 /**
24182  * @ngdoc directive
24183  * @name ngMouseenter
24184  *
24185  * @description
24186  * Specify custom behavior on mouseenter event.
24187  *
24188  * @element ANY
24189  * @priority 0
24190  * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
24191  * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
24192  *
24193  * @example
24194    <example>
24195      <file name="index.html">
24196       <button ng-mouseenter="count = count + 1" ng-init="count=0">
24197         Increment (when mouse enters)
24198       </button>
24199       count: {{count}}
24200      </file>
24201    </example>
24202  */
24203
24204
24205 /**
24206  * @ngdoc directive
24207  * @name ngMouseleave
24208  *
24209  * @description
24210  * Specify custom behavior on mouseleave event.
24211  *
24212  * @element ANY
24213  * @priority 0
24214  * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
24215  * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
24216  *
24217  * @example
24218    <example>
24219      <file name="index.html">
24220       <button ng-mouseleave="count = count + 1" ng-init="count=0">
24221         Increment (when mouse leaves)
24222       </button>
24223       count: {{count}}
24224      </file>
24225    </example>
24226  */
24227
24228
24229 /**
24230  * @ngdoc directive
24231  * @name ngMousemove
24232  *
24233  * @description
24234  * Specify custom behavior on mousemove event.
24235  *
24236  * @element ANY
24237  * @priority 0
24238  * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
24239  * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
24240  *
24241  * @example
24242    <example>
24243      <file name="index.html">
24244       <button ng-mousemove="count = count + 1" ng-init="count=0">
24245         Increment (when mouse moves)
24246       </button>
24247       count: {{count}}
24248      </file>
24249    </example>
24250  */
24251
24252
24253 /**
24254  * @ngdoc directive
24255  * @name ngKeydown
24256  *
24257  * @description
24258  * Specify custom behavior on keydown event.
24259  *
24260  * @element ANY
24261  * @priority 0
24262  * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
24263  * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
24264  *
24265  * @example
24266    <example>
24267      <file name="index.html">
24268       <input ng-keydown="count = count + 1" ng-init="count=0">
24269       key down count: {{count}}
24270      </file>
24271    </example>
24272  */
24273
24274
24275 /**
24276  * @ngdoc directive
24277  * @name ngKeyup
24278  *
24279  * @description
24280  * Specify custom behavior on keyup event.
24281  *
24282  * @element ANY
24283  * @priority 0
24284  * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
24285  * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
24286  *
24287  * @example
24288    <example>
24289      <file name="index.html">
24290        <p>Typing in the input box below updates the key count</p>
24291        <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
24292
24293        <p>Typing in the input box below updates the keycode</p>
24294        <input ng-keyup="event=$event">
24295        <p>event keyCode: {{ event.keyCode }}</p>
24296        <p>event altKey: {{ event.altKey }}</p>
24297      </file>
24298    </example>
24299  */
24300
24301
24302 /**
24303  * @ngdoc directive
24304  * @name ngKeypress
24305  *
24306  * @description
24307  * Specify custom behavior on keypress event.
24308  *
24309  * @element ANY
24310  * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
24311  * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
24312  * and can be interrogated for keyCode, altKey, etc.)
24313  *
24314  * @example
24315    <example>
24316      <file name="index.html">
24317       <input ng-keypress="count = count + 1" ng-init="count=0">
24318       key press count: {{count}}
24319      </file>
24320    </example>
24321  */
24322
24323
24324 /**
24325  * @ngdoc directive
24326  * @name ngSubmit
24327  *
24328  * @description
24329  * Enables binding angular expressions to onsubmit events.
24330  *
24331  * Additionally it prevents the default action (which for form means sending the request to the
24332  * server and reloading the current page), but only if the form does not contain `action`,
24333  * `data-action`, or `x-action` attributes.
24334  *
24335  * <div class="alert alert-warning">
24336  * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
24337  * `ngSubmit` handlers together. See the
24338  * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
24339  * for a detailed discussion of when `ngSubmit` may be triggered.
24340  * </div>
24341  *
24342  * @element form
24343  * @priority 0
24344  * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
24345  * ({@link guide/expression#-event- Event object is available as `$event`})
24346  *
24347  * @example
24348    <example module="submitExample">
24349      <file name="index.html">
24350       <script>
24351         angular.module('submitExample', [])
24352           .controller('ExampleController', ['$scope', function($scope) {
24353             $scope.list = [];
24354             $scope.text = 'hello';
24355             $scope.submit = function() {
24356               if ($scope.text) {
24357                 $scope.list.push(this.text);
24358                 $scope.text = '';
24359               }
24360             };
24361           }]);
24362       </script>
24363       <form ng-submit="submit()" ng-controller="ExampleController">
24364         Enter text and hit enter:
24365         <input type="text" ng-model="text" name="text" />
24366         <input type="submit" id="submit" value="Submit" />
24367         <pre>list={{list}}</pre>
24368       </form>
24369      </file>
24370      <file name="protractor.js" type="protractor">
24371        it('should check ng-submit', function() {
24372          expect(element(by.binding('list')).getText()).toBe('list=[]');
24373          element(by.css('#submit')).click();
24374          expect(element(by.binding('list')).getText()).toContain('hello');
24375          expect(element(by.model('text')).getAttribute('value')).toBe('');
24376        });
24377        it('should ignore empty strings', function() {
24378          expect(element(by.binding('list')).getText()).toBe('list=[]');
24379          element(by.css('#submit')).click();
24380          element(by.css('#submit')).click();
24381          expect(element(by.binding('list')).getText()).toContain('hello');
24382         });
24383      </file>
24384    </example>
24385  */
24386
24387 /**
24388  * @ngdoc directive
24389  * @name ngFocus
24390  *
24391  * @description
24392  * Specify custom behavior on focus event.
24393  *
24394  * Note: As the `focus` event is executed synchronously when calling `input.focus()`
24395  * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24396  * during an `$apply` to ensure a consistent state.
24397  *
24398  * @element window, input, select, textarea, a
24399  * @priority 0
24400  * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
24401  * focus. ({@link guide/expression#-event- Event object is available as `$event`})
24402  *
24403  * @example
24404  * See {@link ng.directive:ngClick ngClick}
24405  */
24406
24407 /**
24408  * @ngdoc directive
24409  * @name ngBlur
24410  *
24411  * @description
24412  * Specify custom behavior on blur event.
24413  *
24414  * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
24415  * an element has lost focus.
24416  *
24417  * Note: As the `blur` event is executed synchronously also during DOM manipulations
24418  * (e.g. removing a focussed input),
24419  * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24420  * during an `$apply` to ensure a consistent state.
24421  *
24422  * @element window, input, select, textarea, a
24423  * @priority 0
24424  * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
24425  * blur. ({@link guide/expression#-event- Event object is available as `$event`})
24426  *
24427  * @example
24428  * See {@link ng.directive:ngClick ngClick}
24429  */
24430
24431 /**
24432  * @ngdoc directive
24433  * @name ngCopy
24434  *
24435  * @description
24436  * Specify custom behavior on copy event.
24437  *
24438  * @element window, input, select, textarea, a
24439  * @priority 0
24440  * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
24441  * copy. ({@link guide/expression#-event- Event object is available as `$event`})
24442  *
24443  * @example
24444    <example>
24445      <file name="index.html">
24446       <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
24447       copied: {{copied}}
24448      </file>
24449    </example>
24450  */
24451
24452 /**
24453  * @ngdoc directive
24454  * @name ngCut
24455  *
24456  * @description
24457  * Specify custom behavior on cut event.
24458  *
24459  * @element window, input, select, textarea, a
24460  * @priority 0
24461  * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
24462  * cut. ({@link guide/expression#-event- Event object is available as `$event`})
24463  *
24464  * @example
24465    <example>
24466      <file name="index.html">
24467       <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
24468       cut: {{cut}}
24469      </file>
24470    </example>
24471  */
24472
24473 /**
24474  * @ngdoc directive
24475  * @name ngPaste
24476  *
24477  * @description
24478  * Specify custom behavior on paste event.
24479  *
24480  * @element window, input, select, textarea, a
24481  * @priority 0
24482  * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
24483  * paste. ({@link guide/expression#-event- Event object is available as `$event`})
24484  *
24485  * @example
24486    <example>
24487      <file name="index.html">
24488       <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
24489       pasted: {{paste}}
24490      </file>
24491    </example>
24492  */
24493
24494 /**
24495  * @ngdoc directive
24496  * @name ngIf
24497  * @restrict A
24498  * @multiElement
24499  *
24500  * @description
24501  * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
24502  * {expression}. If the expression assigned to `ngIf` evaluates to a false
24503  * value then the element is removed from the DOM, otherwise a clone of the
24504  * element is reinserted into the DOM.
24505  *
24506  * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24507  * element in the DOM rather than changing its visibility via the `display` css property.  A common
24508  * case when this difference is significant is when using css selectors that rely on an element's
24509  * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
24510  *
24511  * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
24512  * is created when the element is restored.  The scope created within `ngIf` inherits from
24513  * its parent scope using
24514  * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
24515  * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24516  * a javascript primitive defined in the parent scope. In this case any modifications made to the
24517  * variable within the child scope will override (hide) the value in the parent scope.
24518  *
24519  * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
24520  * is if an element's class attribute is directly modified after it's compiled, using something like
24521  * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24522  * the added class will be lost because the original compiled state is used to regenerate the element.
24523  *
24524  * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24525  * and `leave` effects.
24526  *
24527  * @animations
24528  * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24529  * leave - happens just before the `ngIf` contents are removed from the DOM
24530  *
24531  * @element ANY
24532  * @scope
24533  * @priority 600
24534  * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24535  *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
24536  *     element is added to the DOM tree.
24537  *
24538  * @example
24539   <example module="ngAnimate" deps="angular-animate.js" animations="true">
24540     <file name="index.html">
24541       <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24542       Show when checked:
24543       <span ng-if="checked" class="animate-if">
24544         This is removed when the checkbox is unchecked.
24545       </span>
24546     </file>
24547     <file name="animations.css">
24548       .animate-if {
24549         background:white;
24550         border:1px solid black;
24551         padding:10px;
24552       }
24553
24554       .animate-if.ng-enter, .animate-if.ng-leave {
24555         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24556       }
24557
24558       .animate-if.ng-enter,
24559       .animate-if.ng-leave.ng-leave-active {
24560         opacity:0;
24561       }
24562
24563       .animate-if.ng-leave,
24564       .animate-if.ng-enter.ng-enter-active {
24565         opacity:1;
24566       }
24567     </file>
24568   </example>
24569  */
24570 var ngIfDirective = ['$animate', function($animate) {
24571   return {
24572     multiElement: true,
24573     transclude: 'element',
24574     priority: 600,
24575     terminal: true,
24576     restrict: 'A',
24577     $$tlb: true,
24578     link: function($scope, $element, $attr, ctrl, $transclude) {
24579         var block, childScope, previousElements;
24580         $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24581
24582           if (value) {
24583             if (!childScope) {
24584               $transclude(function(clone, newScope) {
24585                 childScope = newScope;
24586                 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24587                 // Note: We only need the first/last node of the cloned nodes.
24588                 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24589                 // by a directive with templateUrl when its template arrives.
24590                 block = {
24591                   clone: clone
24592                 };
24593                 $animate.enter(clone, $element.parent(), $element);
24594               });
24595             }
24596           } else {
24597             if (previousElements) {
24598               previousElements.remove();
24599               previousElements = null;
24600             }
24601             if (childScope) {
24602               childScope.$destroy();
24603               childScope = null;
24604             }
24605             if (block) {
24606               previousElements = getBlockNodes(block.clone);
24607               $animate.leave(previousElements).then(function() {
24608                 previousElements = null;
24609               });
24610               block = null;
24611             }
24612           }
24613         });
24614     }
24615   };
24616 }];
24617
24618 /**
24619  * @ngdoc directive
24620  * @name ngInclude
24621  * @restrict ECA
24622  *
24623  * @description
24624  * Fetches, compiles and includes an external HTML fragment.
24625  *
24626  * By default, the template URL is restricted to the same domain and protocol as the
24627  * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24628  * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24629  * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24630  * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24631  * ng.$sce Strict Contextual Escaping}.
24632  *
24633  * In addition, the browser's
24634  * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24635  * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24636  * policy may further restrict whether the template is successfully loaded.
24637  * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24638  * access on some browsers.
24639  *
24640  * @animations
24641  * enter - animation is used to bring new content into the browser.
24642  * leave - animation is used to animate existing content away.
24643  *
24644  * The enter and leave animation occur concurrently.
24645  *
24646  * @scope
24647  * @priority 400
24648  *
24649  * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24650  *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24651  * @param {string=} onload Expression to evaluate when a new partial is loaded.
24652  *                  <div class="alert alert-warning">
24653  *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
24654  *                  a function with the name on the window element, which will usually throw a
24655  *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
24656  *                  different form that {@link guide/directive#normalization matches} `onload`.
24657  *                  </div>
24658    *
24659  * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24660  *                  $anchorScroll} to scroll the viewport after the content is loaded.
24661  *
24662  *                  - If the attribute is not set, disable scrolling.
24663  *                  - If the attribute is set without value, enable scrolling.
24664  *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
24665  *
24666  * @example
24667   <example module="includeExample" deps="angular-animate.js" animations="true">
24668     <file name="index.html">
24669      <div ng-controller="ExampleController">
24670        <select ng-model="template" ng-options="t.name for t in templates">
24671         <option value="">(blank)</option>
24672        </select>
24673        url of the template: <code>{{template.url}}</code>
24674        <hr/>
24675        <div class="slide-animate-container">
24676          <div class="slide-animate" ng-include="template.url"></div>
24677        </div>
24678      </div>
24679     </file>
24680     <file name="script.js">
24681       angular.module('includeExample', ['ngAnimate'])
24682         .controller('ExampleController', ['$scope', function($scope) {
24683           $scope.templates =
24684             [ { name: 'template1.html', url: 'template1.html'},
24685               { name: 'template2.html', url: 'template2.html'} ];
24686           $scope.template = $scope.templates[0];
24687         }]);
24688      </file>
24689     <file name="template1.html">
24690       Content of template1.html
24691     </file>
24692     <file name="template2.html">
24693       Content of template2.html
24694     </file>
24695     <file name="animations.css">
24696       .slide-animate-container {
24697         position:relative;
24698         background:white;
24699         border:1px solid black;
24700         height:40px;
24701         overflow:hidden;
24702       }
24703
24704       .slide-animate {
24705         padding:10px;
24706       }
24707
24708       .slide-animate.ng-enter, .slide-animate.ng-leave {
24709         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24710
24711         position:absolute;
24712         top:0;
24713         left:0;
24714         right:0;
24715         bottom:0;
24716         display:block;
24717         padding:10px;
24718       }
24719
24720       .slide-animate.ng-enter {
24721         top:-50px;
24722       }
24723       .slide-animate.ng-enter.ng-enter-active {
24724         top:0;
24725       }
24726
24727       .slide-animate.ng-leave {
24728         top:0;
24729       }
24730       .slide-animate.ng-leave.ng-leave-active {
24731         top:50px;
24732       }
24733     </file>
24734     <file name="protractor.js" type="protractor">
24735       var templateSelect = element(by.model('template'));
24736       var includeElem = element(by.css('[ng-include]'));
24737
24738       it('should load template1.html', function() {
24739         expect(includeElem.getText()).toMatch(/Content of template1.html/);
24740       });
24741
24742       it('should load template2.html', function() {
24743         if (browser.params.browser == 'firefox') {
24744           // Firefox can't handle using selects
24745           // See https://github.com/angular/protractor/issues/480
24746           return;
24747         }
24748         templateSelect.click();
24749         templateSelect.all(by.css('option')).get(2).click();
24750         expect(includeElem.getText()).toMatch(/Content of template2.html/);
24751       });
24752
24753       it('should change to blank', function() {
24754         if (browser.params.browser == 'firefox') {
24755           // Firefox can't handle using selects
24756           return;
24757         }
24758         templateSelect.click();
24759         templateSelect.all(by.css('option')).get(0).click();
24760         expect(includeElem.isPresent()).toBe(false);
24761       });
24762     </file>
24763   </example>
24764  */
24765
24766
24767 /**
24768  * @ngdoc event
24769  * @name ngInclude#$includeContentRequested
24770  * @eventType emit on the scope ngInclude was declared in
24771  * @description
24772  * Emitted every time the ngInclude content is requested.
24773  *
24774  * @param {Object} angularEvent Synthetic event object.
24775  * @param {String} src URL of content to load.
24776  */
24777
24778
24779 /**
24780  * @ngdoc event
24781  * @name ngInclude#$includeContentLoaded
24782  * @eventType emit on the current ngInclude scope
24783  * @description
24784  * Emitted every time the ngInclude content is reloaded.
24785  *
24786  * @param {Object} angularEvent Synthetic event object.
24787  * @param {String} src URL of content to load.
24788  */
24789
24790
24791 /**
24792  * @ngdoc event
24793  * @name ngInclude#$includeContentError
24794  * @eventType emit on the scope ngInclude was declared in
24795  * @description
24796  * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
24797  *
24798  * @param {Object} angularEvent Synthetic event object.
24799  * @param {String} src URL of content to load.
24800  */
24801 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
24802                   function($templateRequest,   $anchorScroll,   $animate) {
24803   return {
24804     restrict: 'ECA',
24805     priority: 400,
24806     terminal: true,
24807     transclude: 'element',
24808     controller: angular.noop,
24809     compile: function(element, attr) {
24810       var srcExp = attr.ngInclude || attr.src,
24811           onloadExp = attr.onload || '',
24812           autoScrollExp = attr.autoscroll;
24813
24814       return function(scope, $element, $attr, ctrl, $transclude) {
24815         var changeCounter = 0,
24816             currentScope,
24817             previousElement,
24818             currentElement;
24819
24820         var cleanupLastIncludeContent = function() {
24821           if (previousElement) {
24822             previousElement.remove();
24823             previousElement = null;
24824           }
24825           if (currentScope) {
24826             currentScope.$destroy();
24827             currentScope = null;
24828           }
24829           if (currentElement) {
24830             $animate.leave(currentElement).then(function() {
24831               previousElement = null;
24832             });
24833             previousElement = currentElement;
24834             currentElement = null;
24835           }
24836         };
24837
24838         scope.$watch(srcExp, function ngIncludeWatchAction(src) {
24839           var afterAnimation = function() {
24840             if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24841               $anchorScroll();
24842             }
24843           };
24844           var thisChangeId = ++changeCounter;
24845
24846           if (src) {
24847             //set the 2nd param to true to ignore the template request error so that the inner
24848             //contents and scope can be cleaned up.
24849             $templateRequest(src, true).then(function(response) {
24850               if (scope.$$destroyed) return;
24851
24852               if (thisChangeId !== changeCounter) return;
24853               var newScope = scope.$new();
24854               ctrl.template = response;
24855
24856               // Note: This will also link all children of ng-include that were contained in the original
24857               // html. If that content contains controllers, ... they could pollute/change the scope.
24858               // However, using ng-include on an element with additional content does not make sense...
24859               // Note: We can't remove them in the cloneAttchFn of $transclude as that
24860               // function is called before linking the content, which would apply child
24861               // directives to non existing elements.
24862               var clone = $transclude(newScope, function(clone) {
24863                 cleanupLastIncludeContent();
24864                 $animate.enter(clone, null, $element).then(afterAnimation);
24865               });
24866
24867               currentScope = newScope;
24868               currentElement = clone;
24869
24870               currentScope.$emit('$includeContentLoaded', src);
24871               scope.$eval(onloadExp);
24872             }, function() {
24873               if (scope.$$destroyed) return;
24874
24875               if (thisChangeId === changeCounter) {
24876                 cleanupLastIncludeContent();
24877                 scope.$emit('$includeContentError', src);
24878               }
24879             });
24880             scope.$emit('$includeContentRequested', src);
24881           } else {
24882             cleanupLastIncludeContent();
24883             ctrl.template = null;
24884           }
24885         });
24886       };
24887     }
24888   };
24889 }];
24890
24891 // This directive is called during the $transclude call of the first `ngInclude` directive.
24892 // It will replace and compile the content of the element with the loaded template.
24893 // We need this directive so that the element content is already filled when
24894 // the link function of another directive on the same element as ngInclude
24895 // is called.
24896 var ngIncludeFillContentDirective = ['$compile',
24897   function($compile) {
24898     return {
24899       restrict: 'ECA',
24900       priority: -400,
24901       require: 'ngInclude',
24902       link: function(scope, $element, $attr, ctrl) {
24903         if (/SVG/.test($element[0].toString())) {
24904           // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24905           // support innerHTML, so detect this here and try to generate the contents
24906           // specially.
24907           $element.empty();
24908           $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24909               function namespaceAdaptedClone(clone) {
24910             $element.append(clone);
24911           }, {futureParentElement: $element});
24912           return;
24913         }
24914
24915         $element.html(ctrl.template);
24916         $compile($element.contents())(scope);
24917       }
24918     };
24919   }];
24920
24921 /**
24922  * @ngdoc directive
24923  * @name ngInit
24924  * @restrict AC
24925  *
24926  * @description
24927  * The `ngInit` directive allows you to evaluate an expression in the
24928  * current scope.
24929  *
24930  * <div class="alert alert-danger">
24931  * This directive can be abused to add unnecessary amounts of logic into your templates.
24932  * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24933  * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24934  * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24935  * rather than `ngInit` to initialize values on a scope.
24936  * </div>
24937  *
24938  * <div class="alert alert-warning">
24939  * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24940  * sure you have parentheses to ensure correct operator precedence:
24941  * <pre class="prettyprint">
24942  * `<div ng-init="test1 = ($index | toString)"></div>`
24943  * </pre>
24944  * </div>
24945  *
24946  * @priority 450
24947  *
24948  * @element ANY
24949  * @param {expression} ngInit {@link guide/expression Expression} to eval.
24950  *
24951  * @example
24952    <example module="initExample">
24953      <file name="index.html">
24954    <script>
24955      angular.module('initExample', [])
24956        .controller('ExampleController', ['$scope', function($scope) {
24957          $scope.list = [['a', 'b'], ['c', 'd']];
24958        }]);
24959    </script>
24960    <div ng-controller="ExampleController">
24961      <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24962        <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24963           <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24964        </div>
24965      </div>
24966    </div>
24967      </file>
24968      <file name="protractor.js" type="protractor">
24969        it('should alias index positions', function() {
24970          var elements = element.all(by.css('.example-init'));
24971          expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24972          expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24973          expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24974          expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24975        });
24976      </file>
24977    </example>
24978  */
24979 var ngInitDirective = ngDirective({
24980   priority: 450,
24981   compile: function() {
24982     return {
24983       pre: function(scope, element, attrs) {
24984         scope.$eval(attrs.ngInit);
24985       }
24986     };
24987   }
24988 });
24989
24990 /**
24991  * @ngdoc directive
24992  * @name ngList
24993  *
24994  * @description
24995  * Text input that converts between a delimited string and an array of strings. The default
24996  * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24997  * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24998  *
24999  * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
25000  * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
25001  *   list item is respected. This implies that the user of the directive is responsible for
25002  *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
25003  *   tab or newline character.
25004  * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
25005  *   when joining the list items back together) and whitespace around each list item is stripped
25006  *   before it is added to the model.
25007  *
25008  * ### Example with Validation
25009  *
25010  * <example name="ngList-directive" module="listExample">
25011  *   <file name="app.js">
25012  *      angular.module('listExample', [])
25013  *        .controller('ExampleController', ['$scope', function($scope) {
25014  *          $scope.names = ['morpheus', 'neo', 'trinity'];
25015  *        }]);
25016  *   </file>
25017  *   <file name="index.html">
25018  *    <form name="myForm" ng-controller="ExampleController">
25019  *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
25020  *      <span role="alert">
25021  *        <span class="error" ng-show="myForm.namesInput.$error.required">
25022  *        Required!</span>
25023  *      </span>
25024  *      <br>
25025  *      <tt>names = {{names}}</tt><br/>
25026  *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
25027  *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
25028  *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25029  *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25030  *     </form>
25031  *   </file>
25032  *   <file name="protractor.js" type="protractor">
25033  *     var listInput = element(by.model('names'));
25034  *     var names = element(by.exactBinding('names'));
25035  *     var valid = element(by.binding('myForm.namesInput.$valid'));
25036  *     var error = element(by.css('span.error'));
25037  *
25038  *     it('should initialize to model', function() {
25039  *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
25040  *       expect(valid.getText()).toContain('true');
25041  *       expect(error.getCssValue('display')).toBe('none');
25042  *     });
25043  *
25044  *     it('should be invalid if empty', function() {
25045  *       listInput.clear();
25046  *       listInput.sendKeys('');
25047  *
25048  *       expect(names.getText()).toContain('');
25049  *       expect(valid.getText()).toContain('false');
25050  *       expect(error.getCssValue('display')).not.toBe('none');
25051  *     });
25052  *   </file>
25053  * </example>
25054  *
25055  * ### Example - splitting on newline
25056  * <example name="ngList-directive-newlines">
25057  *   <file name="index.html">
25058  *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
25059  *    <pre>{{ list | json }}</pre>
25060  *   </file>
25061  *   <file name="protractor.js" type="protractor">
25062  *     it("should split the text by newlines", function() {
25063  *       var listInput = element(by.model('list'));
25064  *       var output = element(by.binding('list | json'));
25065  *       listInput.sendKeys('abc\ndef\nghi');
25066  *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
25067  *     });
25068  *   </file>
25069  * </example>
25070  *
25071  * @element input
25072  * @param {string=} ngList optional delimiter that should be used to split the value.
25073  */
25074 var ngListDirective = function() {
25075   return {
25076     restrict: 'A',
25077     priority: 100,
25078     require: 'ngModel',
25079     link: function(scope, element, attr, ctrl) {
25080       // We want to control whitespace trimming so we use this convoluted approach
25081       // to access the ngList attribute, which doesn't pre-trim the attribute
25082       var ngList = element.attr(attr.$attr.ngList) || ', ';
25083       var trimValues = attr.ngTrim !== 'false';
25084       var separator = trimValues ? trim(ngList) : ngList;
25085
25086       var parse = function(viewValue) {
25087         // If the viewValue is invalid (say required but empty) it will be `undefined`
25088         if (isUndefined(viewValue)) return;
25089
25090         var list = [];
25091
25092         if (viewValue) {
25093           forEach(viewValue.split(separator), function(value) {
25094             if (value) list.push(trimValues ? trim(value) : value);
25095           });
25096         }
25097
25098         return list;
25099       };
25100
25101       ctrl.$parsers.push(parse);
25102       ctrl.$formatters.push(function(value) {
25103         if (isArray(value)) {
25104           return value.join(ngList);
25105         }
25106
25107         return undefined;
25108       });
25109
25110       // Override the standard $isEmpty because an empty array means the input is empty.
25111       ctrl.$isEmpty = function(value) {
25112         return !value || !value.length;
25113       };
25114     }
25115   };
25116 };
25117
25118 /* global VALID_CLASS: true,
25119   INVALID_CLASS: true,
25120   PRISTINE_CLASS: true,
25121   DIRTY_CLASS: true,
25122   UNTOUCHED_CLASS: true,
25123   TOUCHED_CLASS: true,
25124 */
25125
25126 var VALID_CLASS = 'ng-valid',
25127     INVALID_CLASS = 'ng-invalid',
25128     PRISTINE_CLASS = 'ng-pristine',
25129     DIRTY_CLASS = 'ng-dirty',
25130     UNTOUCHED_CLASS = 'ng-untouched',
25131     TOUCHED_CLASS = 'ng-touched',
25132     PENDING_CLASS = 'ng-pending';
25133
25134 var ngModelMinErr = minErr('ngModel');
25135
25136 /**
25137  * @ngdoc type
25138  * @name ngModel.NgModelController
25139  *
25140  * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
25141  * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
25142  * is set.
25143  * @property {*} $modelValue The value in the model that the control is bound to.
25144  * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
25145        the control reads value from the DOM. The functions are called in array order, each passing
25146        its return value through to the next. The last return value is forwarded to the
25147        {@link ngModel.NgModelController#$validators `$validators`} collection.
25148
25149 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
25150 `$viewValue`}.
25151
25152 Returning `undefined` from a parser means a parse error occurred. In that case,
25153 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
25154 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
25155 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
25156
25157  *
25158  * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
25159        the model value changes. The functions are called in reverse array order, each passing the value through to the
25160        next. The last return value is used as the actual DOM value.
25161        Used to format / convert values for display in the control.
25162  * ```js
25163  * function formatter(value) {
25164  *   if (value) {
25165  *     return value.toUpperCase();
25166  *   }
25167  * }
25168  * ngModel.$formatters.push(formatter);
25169  * ```
25170  *
25171  * @property {Object.<string, function>} $validators A collection of validators that are applied
25172  *      whenever the model value changes. The key value within the object refers to the name of the
25173  *      validator while the function refers to the validation operation. The validation operation is
25174  *      provided with the model value as an argument and must return a true or false value depending
25175  *      on the response of that validation.
25176  *
25177  * ```js
25178  * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
25179  *   var value = modelValue || viewValue;
25180  *   return /[0-9]+/.test(value) &&
25181  *          /[a-z]+/.test(value) &&
25182  *          /[A-Z]+/.test(value) &&
25183  *          /\W+/.test(value);
25184  * };
25185  * ```
25186  *
25187  * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
25188  *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
25189  *      is expected to return a promise when it is run during the model validation process. Once the promise
25190  *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
25191  *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
25192  *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
25193  *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
25194  *      will only run once all synchronous validators have passed.
25195  *
25196  * Please note that if $http is used then it is important that the server returns a success HTTP response code
25197  * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
25198  *
25199  * ```js
25200  * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
25201  *   var value = modelValue || viewValue;
25202  *
25203  *   // Lookup user by username
25204  *   return $http.get('/api/users/' + value).
25205  *      then(function resolved() {
25206  *        //username exists, this means validation fails
25207  *        return $q.reject('exists');
25208  *      }, function rejected() {
25209  *        //username does not exist, therefore this validation passes
25210  *        return true;
25211  *      });
25212  * };
25213  * ```
25214  *
25215  * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
25216  *     view value has changed. It is called with no arguments, and its return value is ignored.
25217  *     This can be used in place of additional $watches against the model value.
25218  *
25219  * @property {Object} $error An object hash with all failing validator ids as keys.
25220  * @property {Object} $pending An object hash with all pending validator ids as keys.
25221  *
25222  * @property {boolean} $untouched True if control has not lost focus yet.
25223  * @property {boolean} $touched True if control has lost focus.
25224  * @property {boolean} $pristine True if user has not interacted with the control yet.
25225  * @property {boolean} $dirty True if user has already interacted with the control.
25226  * @property {boolean} $valid True if there is no error.
25227  * @property {boolean} $invalid True if at least one error on the control.
25228  * @property {string} $name The name attribute of the control.
25229  *
25230  * @description
25231  *
25232  * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
25233  * The controller contains services for data-binding, validation, CSS updates, and value formatting
25234  * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
25235  * listening to DOM events.
25236  * Such DOM related logic should be provided by other directives which make use of
25237  * `NgModelController` for data-binding to control elements.
25238  * Angular provides this DOM logic for most {@link input `input`} elements.
25239  * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
25240  * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
25241  *
25242  * @example
25243  * ### Custom Control Example
25244  * This example shows how to use `NgModelController` with a custom control to achieve
25245  * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
25246  * collaborate together to achieve the desired result.
25247  *
25248  * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
25249  * contents be edited in place by the user.
25250  *
25251  * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
25252  * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
25253  * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
25254  * that content using the `$sce` service.
25255  *
25256  * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
25257     <file name="style.css">
25258       [contenteditable] {
25259         border: 1px solid black;
25260         background-color: white;
25261         min-height: 20px;
25262       }
25263
25264       .ng-invalid {
25265         border: 1px solid red;
25266       }
25267
25268     </file>
25269     <file name="script.js">
25270       angular.module('customControl', ['ngSanitize']).
25271         directive('contenteditable', ['$sce', function($sce) {
25272           return {
25273             restrict: 'A', // only activate on element attribute
25274             require: '?ngModel', // get a hold of NgModelController
25275             link: function(scope, element, attrs, ngModel) {
25276               if (!ngModel) return; // do nothing if no ng-model
25277
25278               // Specify how UI should be updated
25279               ngModel.$render = function() {
25280                 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
25281               };
25282
25283               // Listen for change events to enable binding
25284               element.on('blur keyup change', function() {
25285                 scope.$evalAsync(read);
25286               });
25287               read(); // initialize
25288
25289               // Write data to the model
25290               function read() {
25291                 var html = element.html();
25292                 // When we clear the content editable the browser leaves a <br> behind
25293                 // If strip-br attribute is provided then we strip this out
25294                 if ( attrs.stripBr && html == '<br>' ) {
25295                   html = '';
25296                 }
25297                 ngModel.$setViewValue(html);
25298               }
25299             }
25300           };
25301         }]);
25302     </file>
25303     <file name="index.html">
25304       <form name="myForm">
25305        <div contenteditable
25306             name="myWidget" ng-model="userContent"
25307             strip-br="true"
25308             required>Change me!</div>
25309         <span ng-show="myForm.myWidget.$error.required">Required!</span>
25310        <hr>
25311        <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
25312       </form>
25313     </file>
25314     <file name="protractor.js" type="protractor">
25315     it('should data-bind and become invalid', function() {
25316       if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
25317         // SafariDriver can't handle contenteditable
25318         // and Firefox driver can't clear contenteditables very well
25319         return;
25320       }
25321       var contentEditable = element(by.css('[contenteditable]'));
25322       var content = 'Change me!';
25323
25324       expect(contentEditable.getText()).toEqual(content);
25325
25326       contentEditable.clear();
25327       contentEditable.sendKeys(protractor.Key.BACK_SPACE);
25328       expect(contentEditable.getText()).toEqual('');
25329       expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
25330     });
25331     </file>
25332  * </example>
25333  *
25334  *
25335  */
25336 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
25337     function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
25338   this.$viewValue = Number.NaN;
25339   this.$modelValue = Number.NaN;
25340   this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
25341   this.$validators = {};
25342   this.$asyncValidators = {};
25343   this.$parsers = [];
25344   this.$formatters = [];
25345   this.$viewChangeListeners = [];
25346   this.$untouched = true;
25347   this.$touched = false;
25348   this.$pristine = true;
25349   this.$dirty = false;
25350   this.$valid = true;
25351   this.$invalid = false;
25352   this.$error = {}; // keep invalid keys here
25353   this.$$success = {}; // keep valid keys here
25354   this.$pending = undefined; // keep pending keys here
25355   this.$name = $interpolate($attr.name || '', false)($scope);
25356   this.$$parentForm = nullFormCtrl;
25357
25358   var parsedNgModel = $parse($attr.ngModel),
25359       parsedNgModelAssign = parsedNgModel.assign,
25360       ngModelGet = parsedNgModel,
25361       ngModelSet = parsedNgModelAssign,
25362       pendingDebounce = null,
25363       parserValid,
25364       ctrl = this;
25365
25366   this.$$setOptions = function(options) {
25367     ctrl.$options = options;
25368     if (options && options.getterSetter) {
25369       var invokeModelGetter = $parse($attr.ngModel + '()'),
25370           invokeModelSetter = $parse($attr.ngModel + '($$$p)');
25371
25372       ngModelGet = function($scope) {
25373         var modelValue = parsedNgModel($scope);
25374         if (isFunction(modelValue)) {
25375           modelValue = invokeModelGetter($scope);
25376         }
25377         return modelValue;
25378       };
25379       ngModelSet = function($scope, newValue) {
25380         if (isFunction(parsedNgModel($scope))) {
25381           invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
25382         } else {
25383           parsedNgModelAssign($scope, ctrl.$modelValue);
25384         }
25385       };
25386     } else if (!parsedNgModel.assign) {
25387       throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
25388           $attr.ngModel, startingTag($element));
25389     }
25390   };
25391
25392   /**
25393    * @ngdoc method
25394    * @name ngModel.NgModelController#$render
25395    *
25396    * @description
25397    * Called when the view needs to be updated. It is expected that the user of the ng-model
25398    * directive will implement this method.
25399    *
25400    * The `$render()` method is invoked in the following situations:
25401    *
25402    * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
25403    *   committed value then `$render()` is called to update the input control.
25404    * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
25405    *   the `$viewValue` are different from last time.
25406    *
25407    * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
25408    * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
25409    * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
25410    * invoked if you only change a property on the objects.
25411    */
25412   this.$render = noop;
25413
25414   /**
25415    * @ngdoc method
25416    * @name ngModel.NgModelController#$isEmpty
25417    *
25418    * @description
25419    * This is called when we need to determine if the value of an input is empty.
25420    *
25421    * For instance, the required directive does this to work out if the input has data or not.
25422    *
25423    * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
25424    *
25425    * You can override this for input directives whose concept of being empty is different from the
25426    * default. The `checkboxInputType` directive does this because in its case a value of `false`
25427    * implies empty.
25428    *
25429    * @param {*} value The value of the input to check for emptiness.
25430    * @returns {boolean} True if `value` is "empty".
25431    */
25432   this.$isEmpty = function(value) {
25433     return isUndefined(value) || value === '' || value === null || value !== value;
25434   };
25435
25436   var currentValidationRunId = 0;
25437
25438   /**
25439    * @ngdoc method
25440    * @name ngModel.NgModelController#$setValidity
25441    *
25442    * @description
25443    * Change the validity state, and notify the form.
25444    *
25445    * This method can be called within $parsers/$formatters or a custom validation implementation.
25446    * However, in most cases it should be sufficient to use the `ngModel.$validators` and
25447    * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
25448    *
25449    * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
25450    *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
25451    *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
25452    *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
25453    *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
25454    *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
25455    * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
25456    *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
25457    *                          Skipped is used by Angular when validators do not run because of parse errors and
25458    *                          when `$asyncValidators` do not run because any of the `$validators` failed.
25459    */
25460   addSetValidityMethod({
25461     ctrl: this,
25462     $element: $element,
25463     set: function(object, property) {
25464       object[property] = true;
25465     },
25466     unset: function(object, property) {
25467       delete object[property];
25468     },
25469     $animate: $animate
25470   });
25471
25472   /**
25473    * @ngdoc method
25474    * @name ngModel.NgModelController#$setPristine
25475    *
25476    * @description
25477    * Sets the control to its pristine state.
25478    *
25479    * This method can be called to remove the `ng-dirty` class and set the control to its pristine
25480    * state (`ng-pristine` class). A model is considered to be pristine when the control
25481    * has not been changed from when first compiled.
25482    */
25483   this.$setPristine = function() {
25484     ctrl.$dirty = false;
25485     ctrl.$pristine = true;
25486     $animate.removeClass($element, DIRTY_CLASS);
25487     $animate.addClass($element, PRISTINE_CLASS);
25488   };
25489
25490   /**
25491    * @ngdoc method
25492    * @name ngModel.NgModelController#$setDirty
25493    *
25494    * @description
25495    * Sets the control to its dirty state.
25496    *
25497    * This method can be called to remove the `ng-pristine` class and set the control to its dirty
25498    * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
25499    * from when first compiled.
25500    */
25501   this.$setDirty = function() {
25502     ctrl.$dirty = true;
25503     ctrl.$pristine = false;
25504     $animate.removeClass($element, PRISTINE_CLASS);
25505     $animate.addClass($element, DIRTY_CLASS);
25506     ctrl.$$parentForm.$setDirty();
25507   };
25508
25509   /**
25510    * @ngdoc method
25511    * @name ngModel.NgModelController#$setUntouched
25512    *
25513    * @description
25514    * Sets the control to its untouched state.
25515    *
25516    * This method can be called to remove the `ng-touched` class and set the control to its
25517    * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
25518    * by default, however this function can be used to restore that state if the model has
25519    * already been touched by the user.
25520    */
25521   this.$setUntouched = function() {
25522     ctrl.$touched = false;
25523     ctrl.$untouched = true;
25524     $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
25525   };
25526
25527   /**
25528    * @ngdoc method
25529    * @name ngModel.NgModelController#$setTouched
25530    *
25531    * @description
25532    * Sets the control to its touched state.
25533    *
25534    * This method can be called to remove the `ng-untouched` class and set the control to its
25535    * touched state (`ng-touched` class). A model is considered to be touched when the user has
25536    * first focused the control element and then shifted focus away from the control (blur event).
25537    */
25538   this.$setTouched = function() {
25539     ctrl.$touched = true;
25540     ctrl.$untouched = false;
25541     $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25542   };
25543
25544   /**
25545    * @ngdoc method
25546    * @name ngModel.NgModelController#$rollbackViewValue
25547    *
25548    * @description
25549    * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25550    * which may be caused by a pending debounced event or because the input is waiting for a some
25551    * future event.
25552    *
25553    * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
25554    * depend on special events such as blur, you can have a situation where there is a period when
25555    * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
25556    *
25557    * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
25558    * and reset the input to the last committed view value.
25559    *
25560    * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
25561    * programmatically before these debounced/future events have resolved/occurred, because Angular's
25562    * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25563    *
25564    * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25565    * input which may have such events pending. This is important in order to make sure that the
25566    * input field will be updated with the new model value and any pending operations are cancelled.
25567    *
25568    * <example name="ng-model-cancel-update" module="cancel-update-example">
25569    *   <file name="app.js">
25570    *     angular.module('cancel-update-example', [])
25571    *
25572    *     .controller('CancelUpdateController', ['$scope', function($scope) {
25573    *       $scope.model = {};
25574    *
25575    *       $scope.setEmpty = function(e, value, rollback) {
25576    *         if (e.keyCode == 27) {
25577    *           e.preventDefault();
25578    *           if (rollback) {
25579    *             $scope.myForm[value].$rollbackViewValue();
25580    *           }
25581    *           $scope.model[value] = '';
25582    *         }
25583    *       };
25584    *     }]);
25585    *   </file>
25586    *   <file name="index.html">
25587    *     <div ng-controller="CancelUpdateController">
25588    *        <p>Both of these inputs are only updated if they are blurred. Hitting escape should
25589    *        empty them. Follow these steps and observe the difference:</p>
25590    *       <ol>
25591    *         <li>Type something in the input. You will see that the model is not yet updated</li>
25592    *         <li>Press the Escape key.
25593    *           <ol>
25594    *             <li> In the first example, nothing happens, because the model is already '', and no
25595    *             update is detected. If you blur the input, the model will be set to the current view.
25596    *             </li>
25597    *             <li> In the second example, the pending update is cancelled, and the input is set back
25598    *             to the last committed view value (''). Blurring the input does nothing.
25599    *             </li>
25600    *           </ol>
25601    *         </li>
25602    *       </ol>
25603    *
25604    *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25605    *         <div>
25606    *        <p id="inputDescription1">Without $rollbackViewValue():</p>
25607    *         <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
25608    *                ng-keydown="setEmpty($event, 'value1')">
25609    *         value1: "{{ model.value1 }}"
25610    *         </div>
25611    *
25612    *         <div>
25613    *        <p id="inputDescription2">With $rollbackViewValue():</p>
25614    *         <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
25615    *                ng-keydown="setEmpty($event, 'value2', true)">
25616    *         value2: "{{ model.value2 }}"
25617    *         </div>
25618    *       </form>
25619    *     </div>
25620    *   </file>
25621        <file name="style.css">
25622           div {
25623             display: table-cell;
25624           }
25625           div:nth-child(1) {
25626             padding-right: 30px;
25627           }
25628
25629         </file>
25630    * </example>
25631    */
25632   this.$rollbackViewValue = function() {
25633     $timeout.cancel(pendingDebounce);
25634     ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
25635     ctrl.$render();
25636   };
25637
25638   /**
25639    * @ngdoc method
25640    * @name ngModel.NgModelController#$validate
25641    *
25642    * @description
25643    * Runs each of the registered validators (first synchronous validators and then
25644    * asynchronous validators).
25645    * If the validity changes to invalid, the model will be set to `undefined`,
25646    * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
25647    * If the validity changes to valid, it will set the model to the last available valid
25648    * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
25649    */
25650   this.$validate = function() {
25651     // ignore $validate before model is initialized
25652     if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25653       return;
25654     }
25655
25656     var viewValue = ctrl.$$lastCommittedViewValue;
25657     // Note: we use the $$rawModelValue as $modelValue might have been
25658     // set to undefined during a view -> model update that found validation
25659     // errors. We can't parse the view here, since that could change
25660     // the model although neither viewValue nor the model on the scope changed
25661     var modelValue = ctrl.$$rawModelValue;
25662
25663     var prevValid = ctrl.$valid;
25664     var prevModelValue = ctrl.$modelValue;
25665
25666     var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25667
25668     ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
25669       // If there was no change in validity, don't update the model
25670       // This prevents changing an invalid modelValue to undefined
25671       if (!allowInvalid && prevValid !== allValid) {
25672         // Note: Don't check ctrl.$valid here, as we could have
25673         // external validators (e.g. calculated on the server),
25674         // that just call $setValidity and need the model value
25675         // to calculate their validity.
25676         ctrl.$modelValue = allValid ? modelValue : undefined;
25677
25678         if (ctrl.$modelValue !== prevModelValue) {
25679           ctrl.$$writeModelToScope();
25680         }
25681       }
25682     });
25683
25684   };
25685
25686   this.$$runValidators = function(modelValue, viewValue, doneCallback) {
25687     currentValidationRunId++;
25688     var localValidationRunId = currentValidationRunId;
25689
25690     // check parser error
25691     if (!processParseErrors()) {
25692       validationDone(false);
25693       return;
25694     }
25695     if (!processSyncValidators()) {
25696       validationDone(false);
25697       return;
25698     }
25699     processAsyncValidators();
25700
25701     function processParseErrors() {
25702       var errorKey = ctrl.$$parserName || 'parse';
25703       if (isUndefined(parserValid)) {
25704         setValidity(errorKey, null);
25705       } else {
25706         if (!parserValid) {
25707           forEach(ctrl.$validators, function(v, name) {
25708             setValidity(name, null);
25709           });
25710           forEach(ctrl.$asyncValidators, function(v, name) {
25711             setValidity(name, null);
25712           });
25713         }
25714         // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
25715         setValidity(errorKey, parserValid);
25716         return parserValid;
25717       }
25718       return true;
25719     }
25720
25721     function processSyncValidators() {
25722       var syncValidatorsValid = true;
25723       forEach(ctrl.$validators, function(validator, name) {
25724         var result = validator(modelValue, viewValue);
25725         syncValidatorsValid = syncValidatorsValid && result;
25726         setValidity(name, result);
25727       });
25728       if (!syncValidatorsValid) {
25729         forEach(ctrl.$asyncValidators, function(v, name) {
25730           setValidity(name, null);
25731         });
25732         return false;
25733       }
25734       return true;
25735     }
25736
25737     function processAsyncValidators() {
25738       var validatorPromises = [];
25739       var allValid = true;
25740       forEach(ctrl.$asyncValidators, function(validator, name) {
25741         var promise = validator(modelValue, viewValue);
25742         if (!isPromiseLike(promise)) {
25743           throw ngModelMinErr('nopromise',
25744             "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
25745         }
25746         setValidity(name, undefined);
25747         validatorPromises.push(promise.then(function() {
25748           setValidity(name, true);
25749         }, function(error) {
25750           allValid = false;
25751           setValidity(name, false);
25752         }));
25753       });
25754       if (!validatorPromises.length) {
25755         validationDone(true);
25756       } else {
25757         $q.all(validatorPromises).then(function() {
25758           validationDone(allValid);
25759         }, noop);
25760       }
25761     }
25762
25763     function setValidity(name, isValid) {
25764       if (localValidationRunId === currentValidationRunId) {
25765         ctrl.$setValidity(name, isValid);
25766       }
25767     }
25768
25769     function validationDone(allValid) {
25770       if (localValidationRunId === currentValidationRunId) {
25771
25772         doneCallback(allValid);
25773       }
25774     }
25775   };
25776
25777   /**
25778    * @ngdoc method
25779    * @name ngModel.NgModelController#$commitViewValue
25780    *
25781    * @description
25782    * Commit a pending update to the `$modelValue`.
25783    *
25784    * Updates may be pending by a debounced event or because the input is waiting for a some future
25785    * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
25786    * usually handles calling this in response to input events.
25787    */
25788   this.$commitViewValue = function() {
25789     var viewValue = ctrl.$viewValue;
25790
25791     $timeout.cancel(pendingDebounce);
25792
25793     // If the view value has not changed then we should just exit, except in the case where there is
25794     // a native validator on the element. In this case the validation state may have changed even though
25795     // the viewValue has stayed empty.
25796     if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
25797       return;
25798     }
25799     ctrl.$$lastCommittedViewValue = viewValue;
25800
25801     // change to dirty
25802     if (ctrl.$pristine) {
25803       this.$setDirty();
25804     }
25805     this.$$parseAndValidate();
25806   };
25807
25808   this.$$parseAndValidate = function() {
25809     var viewValue = ctrl.$$lastCommittedViewValue;
25810     var modelValue = viewValue;
25811     parserValid = isUndefined(modelValue) ? undefined : true;
25812
25813     if (parserValid) {
25814       for (var i = 0; i < ctrl.$parsers.length; i++) {
25815         modelValue = ctrl.$parsers[i](modelValue);
25816         if (isUndefined(modelValue)) {
25817           parserValid = false;
25818           break;
25819         }
25820       }
25821     }
25822     if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25823       // ctrl.$modelValue has not been touched yet...
25824       ctrl.$modelValue = ngModelGet($scope);
25825     }
25826     var prevModelValue = ctrl.$modelValue;
25827     var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25828     ctrl.$$rawModelValue = modelValue;
25829
25830     if (allowInvalid) {
25831       ctrl.$modelValue = modelValue;
25832       writeToModelIfNeeded();
25833     }
25834
25835     // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
25836     // This can happen if e.g. $setViewValue is called from inside a parser
25837     ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
25838       if (!allowInvalid) {
25839         // Note: Don't check ctrl.$valid here, as we could have
25840         // external validators (e.g. calculated on the server),
25841         // that just call $setValidity and need the model value
25842         // to calculate their validity.
25843         ctrl.$modelValue = allValid ? modelValue : undefined;
25844         writeToModelIfNeeded();
25845       }
25846     });
25847
25848     function writeToModelIfNeeded() {
25849       if (ctrl.$modelValue !== prevModelValue) {
25850         ctrl.$$writeModelToScope();
25851       }
25852     }
25853   };
25854
25855   this.$$writeModelToScope = function() {
25856     ngModelSet($scope, ctrl.$modelValue);
25857     forEach(ctrl.$viewChangeListeners, function(listener) {
25858       try {
25859         listener();
25860       } catch (e) {
25861         $exceptionHandler(e);
25862       }
25863     });
25864   };
25865
25866   /**
25867    * @ngdoc method
25868    * @name ngModel.NgModelController#$setViewValue
25869    *
25870    * @description
25871    * Update the view value.
25872    *
25873    * This method should be called when a control wants to change the view value; typically,
25874    * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25875    * directive calls it when the value of the input changes and {@link ng.directive:select select}
25876    * calls it when an option is selected.
25877    *
25878    * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25879    * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25880    * value sent directly for processing, finally to be applied to `$modelValue` and then the
25881    * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25882    * in the `$viewChangeListeners` list, are called.
25883    *
25884    * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25885    * and the `default` trigger is not listed, all those actions will remain pending until one of the
25886    * `updateOn` events is triggered on the DOM element.
25887    * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25888    * directive is used with a custom debounce for this particular event.
25889    * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25890    * is specified, once the timer runs out.
25891    *
25892    * When used with standard inputs, the view value will always be a string (which is in some cases
25893    * parsed into another type, such as a `Date` object for `input[date]`.)
25894    * However, custom controls might also pass objects to this method. In this case, we should make
25895    * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25896    * perform a deep watch of objects, it only looks for a change of identity. If you only change
25897    * the property of the object then ngModel will not realise that the object has changed and
25898    * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25899    * not change properties of the copy once it has been passed to `$setViewValue`.
25900    * Otherwise you may cause the model value on the scope to change incorrectly.
25901    *
25902    * <div class="alert alert-info">
25903    * In any case, the value passed to the method should always reflect the current value
25904    * of the control. For example, if you are calling `$setViewValue` for an input element,
25905    * you should pass the input DOM value. Otherwise, the control and the scope model become
25906    * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25907    * the control's DOM value in any way. If we want to change the control's DOM value
25908    * programmatically, we should update the `ngModel` scope expression. Its new value will be
25909    * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25910    * to update the DOM, and finally call `$validate` on it.
25911    * </div>
25912    *
25913    * @param {*} value value from the view.
25914    * @param {string} trigger Event that triggered the update.
25915    */
25916   this.$setViewValue = function(value, trigger) {
25917     ctrl.$viewValue = value;
25918     if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25919       ctrl.$$debounceViewValueCommit(trigger);
25920     }
25921   };
25922
25923   this.$$debounceViewValueCommit = function(trigger) {
25924     var debounceDelay = 0,
25925         options = ctrl.$options,
25926         debounce;
25927
25928     if (options && isDefined(options.debounce)) {
25929       debounce = options.debounce;
25930       if (isNumber(debounce)) {
25931         debounceDelay = debounce;
25932       } else if (isNumber(debounce[trigger])) {
25933         debounceDelay = debounce[trigger];
25934       } else if (isNumber(debounce['default'])) {
25935         debounceDelay = debounce['default'];
25936       }
25937     }
25938
25939     $timeout.cancel(pendingDebounce);
25940     if (debounceDelay) {
25941       pendingDebounce = $timeout(function() {
25942         ctrl.$commitViewValue();
25943       }, debounceDelay);
25944     } else if ($rootScope.$$phase) {
25945       ctrl.$commitViewValue();
25946     } else {
25947       $scope.$apply(function() {
25948         ctrl.$commitViewValue();
25949       });
25950     }
25951   };
25952
25953   // model -> value
25954   // Note: we cannot use a normal scope.$watch as we want to detect the following:
25955   // 1. scope value is 'a'
25956   // 2. user enters 'b'
25957   // 3. ng-change kicks in and reverts scope value to 'a'
25958   //    -> scope value did not change since the last digest as
25959   //       ng-change executes in apply phase
25960   // 4. view should be changed back to 'a'
25961   $scope.$watch(function ngModelWatch() {
25962     var modelValue = ngModelGet($scope);
25963
25964     // if scope model value and ngModel value are out of sync
25965     // TODO(perf): why not move this to the action fn?
25966     if (modelValue !== ctrl.$modelValue &&
25967        // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25968        (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25969     ) {
25970       ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25971       parserValid = undefined;
25972
25973       var formatters = ctrl.$formatters,
25974           idx = formatters.length;
25975
25976       var viewValue = modelValue;
25977       while (idx--) {
25978         viewValue = formatters[idx](viewValue);
25979       }
25980       if (ctrl.$viewValue !== viewValue) {
25981         ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25982         ctrl.$render();
25983
25984         ctrl.$$runValidators(modelValue, viewValue, noop);
25985       }
25986     }
25987
25988     return modelValue;
25989   });
25990 }];
25991
25992
25993 /**
25994  * @ngdoc directive
25995  * @name ngModel
25996  *
25997  * @element input
25998  * @priority 1
25999  *
26000  * @description
26001  * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
26002  * property on the scope using {@link ngModel.NgModelController NgModelController},
26003  * which is created and exposed by this directive.
26004  *
26005  * `ngModel` is responsible for:
26006  *
26007  * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
26008  *   require.
26009  * - Providing validation behavior (i.e. required, number, email, url).
26010  * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
26011  * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
26012  * - Registering the control with its parent {@link ng.directive:form form}.
26013  *
26014  * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
26015  * current scope. If the property doesn't already exist on this scope, it will be created
26016  * implicitly and added to the scope.
26017  *
26018  * For best practices on using `ngModel`, see:
26019  *
26020  *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
26021  *
26022  * For basic examples, how to use `ngModel`, see:
26023  *
26024  *  - {@link ng.directive:input input}
26025  *    - {@link input[text] text}
26026  *    - {@link input[checkbox] checkbox}
26027  *    - {@link input[radio] radio}
26028  *    - {@link input[number] number}
26029  *    - {@link input[email] email}
26030  *    - {@link input[url] url}
26031  *    - {@link input[date] date}
26032  *    - {@link input[datetime-local] datetime-local}
26033  *    - {@link input[time] time}
26034  *    - {@link input[month] month}
26035  *    - {@link input[week] week}
26036  *  - {@link ng.directive:select select}
26037  *  - {@link ng.directive:textarea textarea}
26038  *
26039  * # Complex Models (objects or collections)
26040  *
26041  * By default, `ngModel` watches the model by reference, not value. This is important to know when
26042  * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
26043  * object or collection change, `ngModel` will not be notified and so the input will not be  re-rendered.
26044  *
26045  * The model must be assigned an entirely new object or collection before a re-rendering will occur.
26046  *
26047  * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
26048  * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
26049  * if the select is given the `multiple` attribute.
26050  *
26051  * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
26052  * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
26053  * not trigger a re-rendering of the model.
26054  *
26055  * # CSS classes
26056  * The following CSS classes are added and removed on the associated input/select/textarea element
26057  * depending on the validity of the model.
26058  *
26059  *  - `ng-valid`: the model is valid
26060  *  - `ng-invalid`: the model is invalid
26061  *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
26062  *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
26063  *  - `ng-pristine`: the control hasn't been interacted with yet
26064  *  - `ng-dirty`: the control has been interacted with
26065  *  - `ng-touched`: the control has been blurred
26066  *  - `ng-untouched`: the control hasn't been blurred
26067  *  - `ng-pending`: any `$asyncValidators` are unfulfilled
26068  *
26069  * Keep in mind that ngAnimate can detect each of these classes when added and removed.
26070  *
26071  * ## Animation Hooks
26072  *
26073  * Animations within models are triggered when any of the associated CSS classes are added and removed
26074  * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
26075  * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
26076  * The animations that are triggered within ngModel are similar to how they work in ngClass and
26077  * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
26078  *
26079  * The following example shows a simple way to utilize CSS transitions to style an input element
26080  * that has been rendered as invalid after it has been validated:
26081  *
26082  * <pre>
26083  * //be sure to include ngAnimate as a module to hook into more
26084  * //advanced animations
26085  * .my-input {
26086  *   transition:0.5s linear all;
26087  *   background: white;
26088  * }
26089  * .my-input.ng-invalid {
26090  *   background: red;
26091  *   color:white;
26092  * }
26093  * </pre>
26094  *
26095  * @example
26096  * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
26097      <file name="index.html">
26098        <script>
26099         angular.module('inputExample', [])
26100           .controller('ExampleController', ['$scope', function($scope) {
26101             $scope.val = '1';
26102           }]);
26103        </script>
26104        <style>
26105          .my-input {
26106            transition:all linear 0.5s;
26107            background: transparent;
26108          }
26109          .my-input.ng-invalid {
26110            color:white;
26111            background: red;
26112          }
26113        </style>
26114        <p id="inputDescription">
26115         Update input to see transitions when valid/invalid.
26116         Integer is a valid value.
26117        </p>
26118        <form name="testForm" ng-controller="ExampleController">
26119          <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
26120                 aria-describedby="inputDescription" />
26121        </form>
26122      </file>
26123  * </example>
26124  *
26125  * ## Binding to a getter/setter
26126  *
26127  * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
26128  * function that returns a representation of the model when called with zero arguments, and sets
26129  * the internal state of a model when called with an argument. It's sometimes useful to use this
26130  * for models that have an internal representation that's different from what the model exposes
26131  * to the view.
26132  *
26133  * <div class="alert alert-success">
26134  * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
26135  * frequently than other parts of your code.
26136  * </div>
26137  *
26138  * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
26139  * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
26140  * a `<form>`, which will enable this behavior for all `<input>`s within it. See
26141  * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
26142  *
26143  * The following example shows how to use `ngModel` with a getter/setter:
26144  *
26145  * @example
26146  * <example name="ngModel-getter-setter" module="getterSetterExample">
26147      <file name="index.html">
26148        <div ng-controller="ExampleController">
26149          <form name="userForm">
26150            <label>Name:
26151              <input type="text" name="userName"
26152                     ng-model="user.name"
26153                     ng-model-options="{ getterSetter: true }" />
26154            </label>
26155          </form>
26156          <pre>user.name = <span ng-bind="user.name()"></span></pre>
26157        </div>
26158      </file>
26159      <file name="app.js">
26160        angular.module('getterSetterExample', [])
26161          .controller('ExampleController', ['$scope', function($scope) {
26162            var _name = 'Brian';
26163            $scope.user = {
26164              name: function(newName) {
26165               // Note that newName can be undefined for two reasons:
26166               // 1. Because it is called as a getter and thus called with no arguments
26167               // 2. Because the property should actually be set to undefined. This happens e.g. if the
26168               //    input is invalid
26169               return arguments.length ? (_name = newName) : _name;
26170              }
26171            };
26172          }]);
26173      </file>
26174  * </example>
26175  */
26176 var ngModelDirective = ['$rootScope', function($rootScope) {
26177   return {
26178     restrict: 'A',
26179     require: ['ngModel', '^?form', '^?ngModelOptions'],
26180     controller: NgModelController,
26181     // Prelink needs to run before any input directive
26182     // so that we can set the NgModelOptions in NgModelController
26183     // before anyone else uses it.
26184     priority: 1,
26185     compile: function ngModelCompile(element) {
26186       // Setup initial state of the control
26187       element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
26188
26189       return {
26190         pre: function ngModelPreLink(scope, element, attr, ctrls) {
26191           var modelCtrl = ctrls[0],
26192               formCtrl = ctrls[1] || modelCtrl.$$parentForm;
26193
26194           modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
26195
26196           // notify others, especially parent forms
26197           formCtrl.$addControl(modelCtrl);
26198
26199           attr.$observe('name', function(newValue) {
26200             if (modelCtrl.$name !== newValue) {
26201               modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
26202             }
26203           });
26204
26205           scope.$on('$destroy', function() {
26206             modelCtrl.$$parentForm.$removeControl(modelCtrl);
26207           });
26208         },
26209         post: function ngModelPostLink(scope, element, attr, ctrls) {
26210           var modelCtrl = ctrls[0];
26211           if (modelCtrl.$options && modelCtrl.$options.updateOn) {
26212             element.on(modelCtrl.$options.updateOn, function(ev) {
26213               modelCtrl.$$debounceViewValueCommit(ev && ev.type);
26214             });
26215           }
26216
26217           element.on('blur', function(ev) {
26218             if (modelCtrl.$touched) return;
26219
26220             if ($rootScope.$$phase) {
26221               scope.$evalAsync(modelCtrl.$setTouched);
26222             } else {
26223               scope.$apply(modelCtrl.$setTouched);
26224             }
26225           });
26226         }
26227       };
26228     }
26229   };
26230 }];
26231
26232 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
26233
26234 /**
26235  * @ngdoc directive
26236  * @name ngModelOptions
26237  *
26238  * @description
26239  * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
26240  * events that will trigger a model update and/or a debouncing delay so that the actual update only
26241  * takes place when a timer expires; this timer will be reset after another change takes place.
26242  *
26243  * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
26244  * be different from the value in the actual model. This means that if you update the model you
26245  * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
26246  * order to make sure it is synchronized with the model and that any debounced action is canceled.
26247  *
26248  * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
26249  * method is by making sure the input is placed inside a form that has a `name` attribute. This is
26250  * important because `form` controllers are published to the related scope under the name in their
26251  * `name` attribute.
26252  *
26253  * Any pending changes will take place immediately when an enclosing form is submitted via the
26254  * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
26255  * to have access to the updated model.
26256  *
26257  * `ngModelOptions` has an effect on the element it's declared on and its descendants.
26258  *
26259  * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
26260  *   - `updateOn`: string specifying which event should the input be bound to. You can set several
26261  *     events using an space delimited list. There is a special event called `default` that
26262  *     matches the default events belonging of the control.
26263  *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
26264  *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
26265  *     custom value for each event. For example:
26266  *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
26267  *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
26268  *     not validate correctly instead of the default behavior of setting the model to undefined.
26269  *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
26270        `ngModel` as getters/setters.
26271  *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
26272  *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
26273  *     continental US time zone abbreviations, but for general use, use a time zone offset, for
26274  *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
26275  *     If not specified, the timezone of the browser will be used.
26276  *
26277  * @example
26278
26279   The following example shows how to override immediate updates. Changes on the inputs within the
26280   form will update the model only when the control loses focus (blur event). If `escape` key is
26281   pressed while the input field is focused, the value is reset to the value in the current model.
26282
26283   <example name="ngModelOptions-directive-blur" module="optionsExample">
26284     <file name="index.html">
26285       <div ng-controller="ExampleController">
26286         <form name="userForm">
26287           <label>Name:
26288             <input type="text" name="userName"
26289                    ng-model="user.name"
26290                    ng-model-options="{ updateOn: 'blur' }"
26291                    ng-keyup="cancel($event)" />
26292           </label><br />
26293           <label>Other data:
26294             <input type="text" ng-model="user.data" />
26295           </label><br />
26296         </form>
26297         <pre>user.name = <span ng-bind="user.name"></span></pre>
26298         <pre>user.data = <span ng-bind="user.data"></span></pre>
26299       </div>
26300     </file>
26301     <file name="app.js">
26302       angular.module('optionsExample', [])
26303         .controller('ExampleController', ['$scope', function($scope) {
26304           $scope.user = { name: 'John', data: '' };
26305
26306           $scope.cancel = function(e) {
26307             if (e.keyCode == 27) {
26308               $scope.userForm.userName.$rollbackViewValue();
26309             }
26310           };
26311         }]);
26312     </file>
26313     <file name="protractor.js" type="protractor">
26314       var model = element(by.binding('user.name'));
26315       var input = element(by.model('user.name'));
26316       var other = element(by.model('user.data'));
26317
26318       it('should allow custom events', function() {
26319         input.sendKeys(' Doe');
26320         input.click();
26321         expect(model.getText()).toEqual('John');
26322         other.click();
26323         expect(model.getText()).toEqual('John Doe');
26324       });
26325
26326       it('should $rollbackViewValue when model changes', function() {
26327         input.sendKeys(' Doe');
26328         expect(input.getAttribute('value')).toEqual('John Doe');
26329         input.sendKeys(protractor.Key.ESCAPE);
26330         expect(input.getAttribute('value')).toEqual('John');
26331         other.click();
26332         expect(model.getText()).toEqual('John');
26333       });
26334     </file>
26335   </example>
26336
26337   This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
26338   If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
26339
26340   <example name="ngModelOptions-directive-debounce" module="optionsExample">
26341     <file name="index.html">
26342       <div ng-controller="ExampleController">
26343         <form name="userForm">
26344           <label>Name:
26345             <input type="text" name="userName"
26346                    ng-model="user.name"
26347                    ng-model-options="{ debounce: 1000 }" />
26348           </label>
26349           <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
26350           <br />
26351         </form>
26352         <pre>user.name = <span ng-bind="user.name"></span></pre>
26353       </div>
26354     </file>
26355     <file name="app.js">
26356       angular.module('optionsExample', [])
26357         .controller('ExampleController', ['$scope', function($scope) {
26358           $scope.user = { name: 'Igor' };
26359         }]);
26360     </file>
26361   </example>
26362
26363   This one shows how to bind to getter/setters:
26364
26365   <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
26366     <file name="index.html">
26367       <div ng-controller="ExampleController">
26368         <form name="userForm">
26369           <label>Name:
26370             <input type="text" name="userName"
26371                    ng-model="user.name"
26372                    ng-model-options="{ getterSetter: true }" />
26373           </label>
26374         </form>
26375         <pre>user.name = <span ng-bind="user.name()"></span></pre>
26376       </div>
26377     </file>
26378     <file name="app.js">
26379       angular.module('getterSetterExample', [])
26380         .controller('ExampleController', ['$scope', function($scope) {
26381           var _name = 'Brian';
26382           $scope.user = {
26383             name: function(newName) {
26384               // Note that newName can be undefined for two reasons:
26385               // 1. Because it is called as a getter and thus called with no arguments
26386               // 2. Because the property should actually be set to undefined. This happens e.g. if the
26387               //    input is invalid
26388               return arguments.length ? (_name = newName) : _name;
26389             }
26390           };
26391         }]);
26392     </file>
26393   </example>
26394  */
26395 var ngModelOptionsDirective = function() {
26396   return {
26397     restrict: 'A',
26398     controller: ['$scope', '$attrs', function($scope, $attrs) {
26399       var that = this;
26400       this.$options = copy($scope.$eval($attrs.ngModelOptions));
26401       // Allow adding/overriding bound events
26402       if (isDefined(this.$options.updateOn)) {
26403         this.$options.updateOnDefault = false;
26404         // extract "default" pseudo-event from list of events that can trigger a model update
26405         this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
26406           that.$options.updateOnDefault = true;
26407           return ' ';
26408         }));
26409       } else {
26410         this.$options.updateOnDefault = true;
26411       }
26412     }]
26413   };
26414 };
26415
26416
26417
26418 // helper methods
26419 function addSetValidityMethod(context) {
26420   var ctrl = context.ctrl,
26421       $element = context.$element,
26422       classCache = {},
26423       set = context.set,
26424       unset = context.unset,
26425       $animate = context.$animate;
26426
26427   classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
26428
26429   ctrl.$setValidity = setValidity;
26430
26431   function setValidity(validationErrorKey, state, controller) {
26432     if (isUndefined(state)) {
26433       createAndSet('$pending', validationErrorKey, controller);
26434     } else {
26435       unsetAndCleanup('$pending', validationErrorKey, controller);
26436     }
26437     if (!isBoolean(state)) {
26438       unset(ctrl.$error, validationErrorKey, controller);
26439       unset(ctrl.$$success, validationErrorKey, controller);
26440     } else {
26441       if (state) {
26442         unset(ctrl.$error, validationErrorKey, controller);
26443         set(ctrl.$$success, validationErrorKey, controller);
26444       } else {
26445         set(ctrl.$error, validationErrorKey, controller);
26446         unset(ctrl.$$success, validationErrorKey, controller);
26447       }
26448     }
26449     if (ctrl.$pending) {
26450       cachedToggleClass(PENDING_CLASS, true);
26451       ctrl.$valid = ctrl.$invalid = undefined;
26452       toggleValidationCss('', null);
26453     } else {
26454       cachedToggleClass(PENDING_CLASS, false);
26455       ctrl.$valid = isObjectEmpty(ctrl.$error);
26456       ctrl.$invalid = !ctrl.$valid;
26457       toggleValidationCss('', ctrl.$valid);
26458     }
26459
26460     // re-read the state as the set/unset methods could have
26461     // combined state in ctrl.$error[validationError] (used for forms),
26462     // where setting/unsetting only increments/decrements the value,
26463     // and does not replace it.
26464     var combinedState;
26465     if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
26466       combinedState = undefined;
26467     } else if (ctrl.$error[validationErrorKey]) {
26468       combinedState = false;
26469     } else if (ctrl.$$success[validationErrorKey]) {
26470       combinedState = true;
26471     } else {
26472       combinedState = null;
26473     }
26474
26475     toggleValidationCss(validationErrorKey, combinedState);
26476     ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
26477   }
26478
26479   function createAndSet(name, value, controller) {
26480     if (!ctrl[name]) {
26481       ctrl[name] = {};
26482     }
26483     set(ctrl[name], value, controller);
26484   }
26485
26486   function unsetAndCleanup(name, value, controller) {
26487     if (ctrl[name]) {
26488       unset(ctrl[name], value, controller);
26489     }
26490     if (isObjectEmpty(ctrl[name])) {
26491       ctrl[name] = undefined;
26492     }
26493   }
26494
26495   function cachedToggleClass(className, switchValue) {
26496     if (switchValue && !classCache[className]) {
26497       $animate.addClass($element, className);
26498       classCache[className] = true;
26499     } else if (!switchValue && classCache[className]) {
26500       $animate.removeClass($element, className);
26501       classCache[className] = false;
26502     }
26503   }
26504
26505   function toggleValidationCss(validationErrorKey, isValid) {
26506     validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
26507
26508     cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
26509     cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
26510   }
26511 }
26512
26513 function isObjectEmpty(obj) {
26514   if (obj) {
26515     for (var prop in obj) {
26516       if (obj.hasOwnProperty(prop)) {
26517         return false;
26518       }
26519     }
26520   }
26521   return true;
26522 }
26523
26524 /**
26525  * @ngdoc directive
26526  * @name ngNonBindable
26527  * @restrict AC
26528  * @priority 1000
26529  *
26530  * @description
26531  * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
26532  * DOM element. This is useful if the element contains what appears to be Angular directives and
26533  * bindings but which should be ignored by Angular. This could be the case if you have a site that
26534  * displays snippets of code, for instance.
26535  *
26536  * @element ANY
26537  *
26538  * @example
26539  * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
26540  * but the one wrapped in `ngNonBindable` is left alone.
26541  *
26542  * @example
26543     <example>
26544       <file name="index.html">
26545         <div>Normal: {{1 + 2}}</div>
26546         <div ng-non-bindable>Ignored: {{1 + 2}}</div>
26547       </file>
26548       <file name="protractor.js" type="protractor">
26549        it('should check ng-non-bindable', function() {
26550          expect(element(by.binding('1 + 2')).getText()).toContain('3');
26551          expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
26552        });
26553       </file>
26554     </example>
26555  */
26556 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
26557
26558 /* global jqLiteRemove */
26559
26560 var ngOptionsMinErr = minErr('ngOptions');
26561
26562 /**
26563  * @ngdoc directive
26564  * @name ngOptions
26565  * @restrict A
26566  *
26567  * @description
26568  *
26569  * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
26570  * elements for the `<select>` element using the array or object obtained by evaluating the
26571  * `ngOptions` comprehension expression.
26572  *
26573  * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
26574  * similar result. However, `ngOptions` provides some benefits such as reducing memory and
26575  * increasing speed by not creating a new scope for each repeated instance, as well as providing
26576  * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26577  * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26578  *  to a non-string value. This is because an option element can only be bound to string values at
26579  * present.
26580  *
26581  * When an item in the `<select>` menu is selected, the array element or object property
26582  * represented by the selected option will be bound to the model identified by the `ngModel`
26583  * directive.
26584  *
26585  * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26586  * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26587  * option. See example below for demonstration.
26588  *
26589  * ## Complex Models (objects or collections)
26590  *
26591  * By default, `ngModel` watches the model by reference, not value. This is important to know when
26592  * binding the select to a model that is an object or a collection.
26593  *
26594  * One issue occurs if you want to preselect an option. For example, if you set
26595  * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
26596  * because the objects are not identical. So by default, you should always reference the item in your collection
26597  * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
26598  *
26599  * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
26600  * of the item not by reference, but by the result of the `track by` expression. For example, if your
26601  * collection items have an id property, you would `track by item.id`.
26602  *
26603  * A different issue with objects or collections is that ngModel won't detect if an object property or
26604  * a collection item changes. For that reason, `ngOptions` additionally watches the model using
26605  * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
26606  * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
26607  * has not changed identity, but only a property on the object or an item in the collection changes.
26608  *
26609  * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26610  * if the model is an array). This means that changing a property deeper than the first level inside the
26611  * object/collection will not trigger a re-rendering.
26612  *
26613  * ## `select` **`as`**
26614  *
26615  * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26616  * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26617  * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26618  * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26619  *
26620  *
26621  * ### `select` **`as`** and **`track by`**
26622  *
26623  * <div class="alert alert-warning">
26624  * Be careful when using `select` **`as`** and **`track by`** in the same expression.
26625  * </div>
26626  *
26627  * Given this array of items on the $scope:
26628  *
26629  * ```js
26630  * $scope.items = [{
26631  *   id: 1,
26632  *   label: 'aLabel',
26633  *   subItem: { name: 'aSubItem' }
26634  * }, {
26635  *   id: 2,
26636  *   label: 'bLabel',
26637  *   subItem: { name: 'bSubItem' }
26638  * }];
26639  * ```
26640  *
26641  * This will work:
26642  *
26643  * ```html
26644  * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
26645  * ```
26646  * ```js
26647  * $scope.selected = $scope.items[0];
26648  * ```
26649  *
26650  * but this will not work:
26651  *
26652  * ```html
26653  * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
26654  * ```
26655  * ```js
26656  * $scope.selected = $scope.items[0].subItem;
26657  * ```
26658  *
26659  * In both examples, the **`track by`** expression is applied successfully to each `item` in the
26660  * `items` array. Because the selected option has been set programmatically in the controller, the
26661  * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
26662  * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
26663  * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
26664  * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
26665  * is not matched against any `<option>` and the `<select>` appears as having no selected value.
26666  *
26667  *
26668  * @param {string} ngModel Assignable angular expression to data-bind to.
26669  * @param {string=} name Property name of the form under which the control is published.
26670  * @param {string=} required The control is considered valid only if value is entered.
26671  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26672  *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26673  *    `required` when you want to data-bind to the `required` attribute.
26674  * @param {comprehension_expression=} ngOptions in one of the following forms:
26675  *
26676  *   * for array data sources:
26677  *     * `label` **`for`** `value` **`in`** `array`
26678  *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
26679  *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
26680  *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
26681  *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26682  *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26683  *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
26684  *        (for including a filter with `track by`)
26685  *   * for object data sources:
26686  *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26687  *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26688  *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
26689  *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
26690  *     * `select` **`as`** `label` **`group by`** `group`
26691  *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26692  *     * `select` **`as`** `label` **`disable when`** `disable`
26693  *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26694  *
26695  * Where:
26696  *
26697  *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
26698  *   * `value`: local variable which will refer to each item in the `array` or each property value
26699  *      of `object` during iteration.
26700  *   * `key`: local variable which will refer to a property name in `object` during iteration.
26701  *   * `label`: The result of this expression will be the label for `<option>` element. The
26702  *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
26703  *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
26704  *      element. If not specified, `select` expression will default to `value`.
26705  *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
26706  *      DOM element.
26707  *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
26708  *      element. Return `true` to disable.
26709  *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
26710  *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
26711  *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
26712  *      even when the options are recreated (e.g. reloaded from the server).
26713  *
26714  * @example
26715     <example module="selectExample">
26716       <file name="index.html">
26717         <script>
26718         angular.module('selectExample', [])
26719           .controller('ExampleController', ['$scope', function($scope) {
26720             $scope.colors = [
26721               {name:'black', shade:'dark'},
26722               {name:'white', shade:'light', notAnOption: true},
26723               {name:'red', shade:'dark'},
26724               {name:'blue', shade:'dark', notAnOption: true},
26725               {name:'yellow', shade:'light', notAnOption: false}
26726             ];
26727             $scope.myColor = $scope.colors[2]; // red
26728           }]);
26729         </script>
26730         <div ng-controller="ExampleController">
26731           <ul>
26732             <li ng-repeat="color in colors">
26733               <label>Name: <input ng-model="color.name"></label>
26734               <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
26735               <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
26736             </li>
26737             <li>
26738               <button ng-click="colors.push({})">add</button>
26739             </li>
26740           </ul>
26741           <hr/>
26742           <label>Color (null not allowed):
26743             <select ng-model="myColor" ng-options="color.name for color in colors"></select>
26744           </label><br/>
26745           <label>Color (null allowed):
26746           <span  class="nullable">
26747             <select ng-model="myColor" ng-options="color.name for color in colors">
26748               <option value="">-- choose color --</option>
26749             </select>
26750           </span></label><br/>
26751
26752           <label>Color grouped by shade:
26753             <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
26754             </select>
26755           </label><br/>
26756
26757           <label>Color grouped by shade, with some disabled:
26758             <select ng-model="myColor"
26759                   ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
26760             </select>
26761           </label><br/>
26762
26763
26764
26765           Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
26766           <br/>
26767           <hr/>
26768           Currently selected: {{ {selected_color:myColor} }}
26769           <div style="border:solid 1px black; height:20px"
26770                ng-style="{'background-color':myColor.name}">
26771           </div>
26772         </div>
26773       </file>
26774       <file name="protractor.js" type="protractor">
26775          it('should check ng-options', function() {
26776            expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
26777            element.all(by.model('myColor')).first().click();
26778            element.all(by.css('select[ng-model="myColor"] option')).first().click();
26779            expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
26780            element(by.css('.nullable select[ng-model="myColor"]')).click();
26781            element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
26782            expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
26783          });
26784       </file>
26785     </example>
26786  */
26787
26788 // jshint maxlen: false
26789 //                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
26790 var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
26791                         // 1: value expression (valueFn)
26792                         // 2: label expression (displayFn)
26793                         // 3: group by expression (groupByFn)
26794                         // 4: disable when expression (disableWhenFn)
26795                         // 5: array item variable name
26796                         // 6: object item key variable name
26797                         // 7: object item value variable name
26798                         // 8: collection expression
26799                         // 9: track by expression
26800 // jshint maxlen: 100
26801
26802
26803 var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26804
26805   function parseOptionsExpression(optionsExp, selectElement, scope) {
26806
26807     var match = optionsExp.match(NG_OPTIONS_REGEXP);
26808     if (!(match)) {
26809       throw ngOptionsMinErr('iexp',
26810         "Expected expression in form of " +
26811         "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
26812         " but got '{0}'. Element: {1}",
26813         optionsExp, startingTag(selectElement));
26814     }
26815
26816     // Extract the parts from the ngOptions expression
26817
26818     // The variable name for the value of the item in the collection
26819     var valueName = match[5] || match[7];
26820     // The variable name for the key of the item in the collection
26821     var keyName = match[6];
26822
26823     // An expression that generates the viewValue for an option if there is a label expression
26824     var selectAs = / as /.test(match[0]) && match[1];
26825     // An expression that is used to track the id of each object in the options collection
26826     var trackBy = match[9];
26827     // An expression that generates the viewValue for an option if there is no label expression
26828     var valueFn = $parse(match[2] ? match[1] : valueName);
26829     var selectAsFn = selectAs && $parse(selectAs);
26830     var viewValueFn = selectAsFn || valueFn;
26831     var trackByFn = trackBy && $parse(trackBy);
26832
26833     // Get the value by which we are going to track the option
26834     // if we have a trackFn then use that (passing scope and locals)
26835     // otherwise just hash the given viewValue
26836     var getTrackByValueFn = trackBy ?
26837                               function(value, locals) { return trackByFn(scope, locals); } :
26838                               function getHashOfValue(value) { return hashKey(value); };
26839     var getTrackByValue = function(value, key) {
26840       return getTrackByValueFn(value, getLocals(value, key));
26841     };
26842
26843     var displayFn = $parse(match[2] || match[1]);
26844     var groupByFn = $parse(match[3] || '');
26845     var disableWhenFn = $parse(match[4] || '');
26846     var valuesFn = $parse(match[8]);
26847
26848     var locals = {};
26849     var getLocals = keyName ? function(value, key) {
26850       locals[keyName] = key;
26851       locals[valueName] = value;
26852       return locals;
26853     } : function(value) {
26854       locals[valueName] = value;
26855       return locals;
26856     };
26857
26858
26859     function Option(selectValue, viewValue, label, group, disabled) {
26860       this.selectValue = selectValue;
26861       this.viewValue = viewValue;
26862       this.label = label;
26863       this.group = group;
26864       this.disabled = disabled;
26865     }
26866
26867     function getOptionValuesKeys(optionValues) {
26868       var optionValuesKeys;
26869
26870       if (!keyName && isArrayLike(optionValues)) {
26871         optionValuesKeys = optionValues;
26872       } else {
26873         // if object, extract keys, in enumeration order, unsorted
26874         optionValuesKeys = [];
26875         for (var itemKey in optionValues) {
26876           if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26877             optionValuesKeys.push(itemKey);
26878           }
26879         }
26880       }
26881       return optionValuesKeys;
26882     }
26883
26884     return {
26885       trackBy: trackBy,
26886       getTrackByValue: getTrackByValue,
26887       getWatchables: $parse(valuesFn, function(optionValues) {
26888         // Create a collection of things that we would like to watch (watchedArray)
26889         // so that they can all be watched using a single $watchCollection
26890         // that only runs the handler once if anything changes
26891         var watchedArray = [];
26892         optionValues = optionValues || [];
26893
26894         var optionValuesKeys = getOptionValuesKeys(optionValues);
26895         var optionValuesLength = optionValuesKeys.length;
26896         for (var index = 0; index < optionValuesLength; index++) {
26897           var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26898           var value = optionValues[key];
26899
26900           var locals = getLocals(optionValues[key], key);
26901           var selectValue = getTrackByValueFn(optionValues[key], locals);
26902           watchedArray.push(selectValue);
26903
26904           // Only need to watch the displayFn if there is a specific label expression
26905           if (match[2] || match[1]) {
26906             var label = displayFn(scope, locals);
26907             watchedArray.push(label);
26908           }
26909
26910           // Only need to watch the disableWhenFn if there is a specific disable expression
26911           if (match[4]) {
26912             var disableWhen = disableWhenFn(scope, locals);
26913             watchedArray.push(disableWhen);
26914           }
26915         }
26916         return watchedArray;
26917       }),
26918
26919       getOptions: function() {
26920
26921         var optionItems = [];
26922         var selectValueMap = {};
26923
26924         // The option values were already computed in the `getWatchables` fn,
26925         // which must have been called to trigger `getOptions`
26926         var optionValues = valuesFn(scope) || [];
26927         var optionValuesKeys = getOptionValuesKeys(optionValues);
26928         var optionValuesLength = optionValuesKeys.length;
26929
26930         for (var index = 0; index < optionValuesLength; index++) {
26931           var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26932           var value = optionValues[key];
26933           var locals = getLocals(value, key);
26934           var viewValue = viewValueFn(scope, locals);
26935           var selectValue = getTrackByValueFn(viewValue, locals);
26936           var label = displayFn(scope, locals);
26937           var group = groupByFn(scope, locals);
26938           var disabled = disableWhenFn(scope, locals);
26939           var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26940
26941           optionItems.push(optionItem);
26942           selectValueMap[selectValue] = optionItem;
26943         }
26944
26945         return {
26946           items: optionItems,
26947           selectValueMap: selectValueMap,
26948           getOptionFromViewValue: function(value) {
26949             return selectValueMap[getTrackByValue(value)];
26950           },
26951           getViewValueFromOption: function(option) {
26952             // If the viewValue could be an object that may be mutated by the application,
26953             // we need to make a copy and not return the reference to the value on the option.
26954             return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26955           }
26956         };
26957       }
26958     };
26959   }
26960
26961
26962   // we can't just jqLite('<option>') since jqLite is not smart enough
26963   // to create it in <select> and IE barfs otherwise.
26964   var optionTemplate = document.createElement('option'),
26965       optGroupTemplate = document.createElement('optgroup');
26966
26967
26968     function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
26969
26970       // if ngModel is not defined, we don't need to do anything
26971       var ngModelCtrl = ctrls[1];
26972       if (!ngModelCtrl) return;
26973
26974       var selectCtrl = ctrls[0];
26975       var multiple = attr.multiple;
26976
26977       // The emptyOption allows the application developer to provide their own custom "empty"
26978       // option when the viewValue does not match any of the option values.
26979       var emptyOption;
26980       for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26981         if (children[i].value === '') {
26982           emptyOption = children.eq(i);
26983           break;
26984         }
26985       }
26986
26987       var providedEmptyOption = !!emptyOption;
26988
26989       var unknownOption = jqLite(optionTemplate.cloneNode(false));
26990       unknownOption.val('?');
26991
26992       var options;
26993       var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26994
26995
26996       var renderEmptyOption = function() {
26997         if (!providedEmptyOption) {
26998           selectElement.prepend(emptyOption);
26999         }
27000         selectElement.val('');
27001         emptyOption.prop('selected', true); // needed for IE
27002         emptyOption.attr('selected', true);
27003       };
27004
27005       var removeEmptyOption = function() {
27006         if (!providedEmptyOption) {
27007           emptyOption.remove();
27008         }
27009       };
27010
27011
27012       var renderUnknownOption = function() {
27013         selectElement.prepend(unknownOption);
27014         selectElement.val('?');
27015         unknownOption.prop('selected', true); // needed for IE
27016         unknownOption.attr('selected', true);
27017       };
27018
27019       var removeUnknownOption = function() {
27020         unknownOption.remove();
27021       };
27022
27023       // Update the controller methods for multiple selectable options
27024       if (!multiple) {
27025
27026         selectCtrl.writeValue = function writeNgOptionsValue(value) {
27027           var option = options.getOptionFromViewValue(value);
27028
27029           if (option && !option.disabled) {
27030             // Don't update the option when it is already selected.
27031             // For example, the browser will select the first option by default. In that case,
27032             // most properties are set automatically - except the `selected` attribute, which we
27033             // set always
27034
27035             if (selectElement[0].value !== option.selectValue) {
27036               removeUnknownOption();
27037               removeEmptyOption();
27038
27039               selectElement[0].value = option.selectValue;
27040               option.element.selected = true;
27041             }
27042
27043             option.element.setAttribute('selected', 'selected');
27044           } else {
27045             if (value === null || providedEmptyOption) {
27046               removeUnknownOption();
27047               renderEmptyOption();
27048             } else {
27049               removeEmptyOption();
27050               renderUnknownOption();
27051             }
27052           }
27053         };
27054
27055         selectCtrl.readValue = function readNgOptionsValue() {
27056
27057           var selectedOption = options.selectValueMap[selectElement.val()];
27058
27059           if (selectedOption && !selectedOption.disabled) {
27060             removeEmptyOption();
27061             removeUnknownOption();
27062             return options.getViewValueFromOption(selectedOption);
27063           }
27064           return null;
27065         };
27066
27067         // If we are using `track by` then we must watch the tracked value on the model
27068         // since ngModel only watches for object identity change
27069         if (ngOptions.trackBy) {
27070           scope.$watch(
27071             function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
27072             function() { ngModelCtrl.$render(); }
27073           );
27074         }
27075
27076       } else {
27077
27078         ngModelCtrl.$isEmpty = function(value) {
27079           return !value || value.length === 0;
27080         };
27081
27082
27083         selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
27084           options.items.forEach(function(option) {
27085             option.element.selected = false;
27086           });
27087
27088           if (value) {
27089             value.forEach(function(item) {
27090               var option = options.getOptionFromViewValue(item);
27091               if (option && !option.disabled) option.element.selected = true;
27092             });
27093           }
27094         };
27095
27096
27097         selectCtrl.readValue = function readNgOptionsMultiple() {
27098           var selectedValues = selectElement.val() || [],
27099               selections = [];
27100
27101           forEach(selectedValues, function(value) {
27102             var option = options.selectValueMap[value];
27103             if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
27104           });
27105
27106           return selections;
27107         };
27108
27109         // If we are using `track by` then we must watch these tracked values on the model
27110         // since ngModel only watches for object identity change
27111         if (ngOptions.trackBy) {
27112
27113           scope.$watchCollection(function() {
27114             if (isArray(ngModelCtrl.$viewValue)) {
27115               return ngModelCtrl.$viewValue.map(function(value) {
27116                 return ngOptions.getTrackByValue(value);
27117               });
27118             }
27119           }, function() {
27120             ngModelCtrl.$render();
27121           });
27122
27123         }
27124       }
27125
27126
27127       if (providedEmptyOption) {
27128
27129         // we need to remove it before calling selectElement.empty() because otherwise IE will
27130         // remove the label from the element. wtf?
27131         emptyOption.remove();
27132
27133         // compile the element since there might be bindings in it
27134         $compile(emptyOption)(scope);
27135
27136         // remove the class, which is added automatically because we recompile the element and it
27137         // becomes the compilation root
27138         emptyOption.removeClass('ng-scope');
27139       } else {
27140         emptyOption = jqLite(optionTemplate.cloneNode(false));
27141       }
27142
27143       // We need to do this here to ensure that the options object is defined
27144       // when we first hit it in writeNgOptionsValue
27145       updateOptions();
27146
27147       // We will re-render the option elements if the option values or labels change
27148       scope.$watchCollection(ngOptions.getWatchables, updateOptions);
27149
27150       // ------------------------------------------------------------------ //
27151
27152
27153       function updateOptionElement(option, element) {
27154         option.element = element;
27155         element.disabled = option.disabled;
27156         // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
27157         // selects in certain circumstances when multiple selects are next to each other and display
27158         // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
27159         // See https://github.com/angular/angular.js/issues/11314 for more info.
27160         // This is unfortunately untestable with unit / e2e tests
27161         if (option.label !== element.label) {
27162           element.label = option.label;
27163           element.textContent = option.label;
27164         }
27165         if (option.value !== element.value) element.value = option.selectValue;
27166       }
27167
27168       function addOrReuseElement(parent, current, type, templateElement) {
27169         var element;
27170         // Check whether we can reuse the next element
27171         if (current && lowercase(current.nodeName) === type) {
27172           // The next element is the right type so reuse it
27173           element = current;
27174         } else {
27175           // The next element is not the right type so create a new one
27176           element = templateElement.cloneNode(false);
27177           if (!current) {
27178             // There are no more elements so just append it to the select
27179             parent.appendChild(element);
27180           } else {
27181             // The next element is not a group so insert the new one
27182             parent.insertBefore(element, current);
27183           }
27184         }
27185         return element;
27186       }
27187
27188
27189       function removeExcessElements(current) {
27190         var next;
27191         while (current) {
27192           next = current.nextSibling;
27193           jqLiteRemove(current);
27194           current = next;
27195         }
27196       }
27197
27198
27199       function skipEmptyAndUnknownOptions(current) {
27200         var emptyOption_ = emptyOption && emptyOption[0];
27201         var unknownOption_ = unknownOption && unknownOption[0];
27202
27203         // We cannot rely on the extracted empty option being the same as the compiled empty option,
27204         // because the compiled empty option might have been replaced by a comment because
27205         // it had an "element" transclusion directive on it (such as ngIf)
27206         if (emptyOption_ || unknownOption_) {
27207           while (current &&
27208                 (current === emptyOption_ ||
27209                 current === unknownOption_ ||
27210                 current.nodeType === NODE_TYPE_COMMENT ||
27211                 (nodeName_(current) === 'option' && current.value === ''))) {
27212             current = current.nextSibling;
27213           }
27214         }
27215         return current;
27216       }
27217
27218
27219       function updateOptions() {
27220
27221         var previousValue = options && selectCtrl.readValue();
27222
27223         options = ngOptions.getOptions();
27224
27225         var groupMap = {};
27226         var currentElement = selectElement[0].firstChild;
27227
27228         // Ensure that the empty option is always there if it was explicitly provided
27229         if (providedEmptyOption) {
27230           selectElement.prepend(emptyOption);
27231         }
27232
27233         currentElement = skipEmptyAndUnknownOptions(currentElement);
27234
27235         options.items.forEach(function updateOption(option) {
27236           var group;
27237           var groupElement;
27238           var optionElement;
27239
27240           if (option.group) {
27241
27242             // This option is to live in a group
27243             // See if we have already created this group
27244             group = groupMap[option.group];
27245
27246             if (!group) {
27247
27248               // We have not already created this group
27249               groupElement = addOrReuseElement(selectElement[0],
27250                                                currentElement,
27251                                                'optgroup',
27252                                                optGroupTemplate);
27253               // Move to the next element
27254               currentElement = groupElement.nextSibling;
27255
27256               // Update the label on the group element
27257               groupElement.label = option.group;
27258
27259               // Store it for use later
27260               group = groupMap[option.group] = {
27261                 groupElement: groupElement,
27262                 currentOptionElement: groupElement.firstChild
27263               };
27264
27265             }
27266
27267             // So now we have a group for this option we add the option to the group
27268             optionElement = addOrReuseElement(group.groupElement,
27269                                               group.currentOptionElement,
27270                                               'option',
27271                                               optionTemplate);
27272             updateOptionElement(option, optionElement);
27273             // Move to the next element
27274             group.currentOptionElement = optionElement.nextSibling;
27275
27276           } else {
27277
27278             // This option is not in a group
27279             optionElement = addOrReuseElement(selectElement[0],
27280                                               currentElement,
27281                                               'option',
27282                                               optionTemplate);
27283             updateOptionElement(option, optionElement);
27284             // Move to the next element
27285             currentElement = optionElement.nextSibling;
27286           }
27287         });
27288
27289
27290         // Now remove all excess options and group
27291         Object.keys(groupMap).forEach(function(key) {
27292           removeExcessElements(groupMap[key].currentOptionElement);
27293         });
27294         removeExcessElements(currentElement);
27295
27296         ngModelCtrl.$render();
27297
27298         // Check to see if the value has changed due to the update to the options
27299         if (!ngModelCtrl.$isEmpty(previousValue)) {
27300           var nextValue = selectCtrl.readValue();
27301           var isNotPrimitive = ngOptions.trackBy || multiple;
27302           if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
27303             ngModelCtrl.$setViewValue(nextValue);
27304             ngModelCtrl.$render();
27305           }
27306         }
27307
27308       }
27309   }
27310
27311   return {
27312     restrict: 'A',
27313     terminal: true,
27314     require: ['select', '?ngModel'],
27315     link: {
27316       pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
27317         // Deactivate the SelectController.register method to prevent
27318         // option directives from accidentally registering themselves
27319         // (and unwanted $destroy handlers etc.)
27320         ctrls[0].registerOption = noop;
27321       },
27322       post: ngOptionsPostLink
27323     }
27324   };
27325 }];
27326
27327 /**
27328  * @ngdoc directive
27329  * @name ngPluralize
27330  * @restrict EA
27331  *
27332  * @description
27333  * `ngPluralize` is a directive that displays messages according to en-US localization rules.
27334  * These rules are bundled with angular.js, but can be overridden
27335  * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
27336  * by specifying the mappings between
27337  * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
27338  * and the strings to be displayed.
27339  *
27340  * # Plural categories and explicit number rules
27341  * There are two
27342  * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
27343  * in Angular's default en-US locale: "one" and "other".
27344  *
27345  * While a plural category may match many numbers (for example, in en-US locale, "other" can match
27346  * any number that is not 1), an explicit number rule can only match one number. For example, the
27347  * explicit number rule for "3" matches the number 3. There are examples of plural categories
27348  * and explicit number rules throughout the rest of this documentation.
27349  *
27350  * # Configuring ngPluralize
27351  * You configure ngPluralize by providing 2 attributes: `count` and `when`.
27352  * You can also provide an optional attribute, `offset`.
27353  *
27354  * The value of the `count` attribute can be either a string or an {@link guide/expression
27355  * Angular expression}; these are evaluated on the current scope for its bound value.
27356  *
27357  * The `when` attribute specifies the mappings between plural categories and the actual
27358  * string to be displayed. The value of the attribute should be a JSON object.
27359  *
27360  * The following example shows how to configure ngPluralize:
27361  *
27362  * ```html
27363  * <ng-pluralize count="personCount"
27364                  when="{'0': 'Nobody is viewing.',
27365  *                      'one': '1 person is viewing.',
27366  *                      'other': '{} people are viewing.'}">
27367  * </ng-pluralize>
27368  *```
27369  *
27370  * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
27371  * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
27372  * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
27373  * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
27374  * show "a dozen people are viewing".
27375  *
27376  * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
27377  * into pluralized strings. In the previous example, Angular will replace `{}` with
27378  * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
27379  * for <span ng-non-bindable>{{numberExpression}}</span>.
27380  *
27381  * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
27382  * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
27383  *
27384  * # Configuring ngPluralize with offset
27385  * The `offset` attribute allows further customization of pluralized text, which can result in
27386  * a better user experience. For example, instead of the message "4 people are viewing this document",
27387  * you might display "John, Kate and 2 others are viewing this document".
27388  * The offset attribute allows you to offset a number by any desired value.
27389  * Let's take a look at an example:
27390  *
27391  * ```html
27392  * <ng-pluralize count="personCount" offset=2
27393  *               when="{'0': 'Nobody is viewing.',
27394  *                      '1': '{{person1}} is viewing.',
27395  *                      '2': '{{person1}} and {{person2}} are viewing.',
27396  *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
27397  *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27398  * </ng-pluralize>
27399  * ```
27400  *
27401  * Notice that we are still using two plural categories(one, other), but we added
27402  * three explicit number rules 0, 1 and 2.
27403  * When one person, perhaps John, views the document, "John is viewing" will be shown.
27404  * When three people view the document, no explicit number rule is found, so
27405  * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
27406  * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
27407  * is shown.
27408  *
27409  * Note that when you specify offsets, you must provide explicit number rules for
27410  * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
27411  * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
27412  * plural categories "one" and "other".
27413  *
27414  * @param {string|expression} count The variable to be bound to.
27415  * @param {string} when The mapping between plural category to its corresponding strings.
27416  * @param {number=} offset Offset to deduct from the total number.
27417  *
27418  * @example
27419     <example module="pluralizeExample">
27420       <file name="index.html">
27421         <script>
27422           angular.module('pluralizeExample', [])
27423             .controller('ExampleController', ['$scope', function($scope) {
27424               $scope.person1 = 'Igor';
27425               $scope.person2 = 'Misko';
27426               $scope.personCount = 1;
27427             }]);
27428         </script>
27429         <div ng-controller="ExampleController">
27430           <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
27431           <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
27432           <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
27433
27434           <!--- Example with simple pluralization rules for en locale --->
27435           Without Offset:
27436           <ng-pluralize count="personCount"
27437                         when="{'0': 'Nobody is viewing.',
27438                                'one': '1 person is viewing.',
27439                                'other': '{} people are viewing.'}">
27440           </ng-pluralize><br>
27441
27442           <!--- Example with offset --->
27443           With Offset(2):
27444           <ng-pluralize count="personCount" offset=2
27445                         when="{'0': 'Nobody is viewing.',
27446                                '1': '{{person1}} is viewing.',
27447                                '2': '{{person1}} and {{person2}} are viewing.',
27448                                'one': '{{person1}}, {{person2}} and one other person are viewing.',
27449                                'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27450           </ng-pluralize>
27451         </div>
27452       </file>
27453       <file name="protractor.js" type="protractor">
27454         it('should show correct pluralized string', function() {
27455           var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
27456           var withOffset = element.all(by.css('ng-pluralize')).get(1);
27457           var countInput = element(by.model('personCount'));
27458
27459           expect(withoutOffset.getText()).toEqual('1 person is viewing.');
27460           expect(withOffset.getText()).toEqual('Igor is viewing.');
27461
27462           countInput.clear();
27463           countInput.sendKeys('0');
27464
27465           expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
27466           expect(withOffset.getText()).toEqual('Nobody is viewing.');
27467
27468           countInput.clear();
27469           countInput.sendKeys('2');
27470
27471           expect(withoutOffset.getText()).toEqual('2 people are viewing.');
27472           expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
27473
27474           countInput.clear();
27475           countInput.sendKeys('3');
27476
27477           expect(withoutOffset.getText()).toEqual('3 people are viewing.');
27478           expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
27479
27480           countInput.clear();
27481           countInput.sendKeys('4');
27482
27483           expect(withoutOffset.getText()).toEqual('4 people are viewing.');
27484           expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
27485         });
27486         it('should show data-bound names', function() {
27487           var withOffset = element.all(by.css('ng-pluralize')).get(1);
27488           var personCount = element(by.model('personCount'));
27489           var person1 = element(by.model('person1'));
27490           var person2 = element(by.model('person2'));
27491           personCount.clear();
27492           personCount.sendKeys('4');
27493           person1.clear();
27494           person1.sendKeys('Di');
27495           person2.clear();
27496           person2.sendKeys('Vojta');
27497           expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
27498         });
27499       </file>
27500     </example>
27501  */
27502 var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
27503   var BRACE = /{}/g,
27504       IS_WHEN = /^when(Minus)?(.+)$/;
27505
27506   return {
27507     link: function(scope, element, attr) {
27508       var numberExp = attr.count,
27509           whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
27510           offset = attr.offset || 0,
27511           whens = scope.$eval(whenExp) || {},
27512           whensExpFns = {},
27513           startSymbol = $interpolate.startSymbol(),
27514           endSymbol = $interpolate.endSymbol(),
27515           braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
27516           watchRemover = angular.noop,
27517           lastCount;
27518
27519       forEach(attr, function(expression, attributeName) {
27520         var tmpMatch = IS_WHEN.exec(attributeName);
27521         if (tmpMatch) {
27522           var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
27523           whens[whenKey] = element.attr(attr.$attr[attributeName]);
27524         }
27525       });
27526       forEach(whens, function(expression, key) {
27527         whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
27528
27529       });
27530
27531       scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
27532         var count = parseFloat(newVal);
27533         var countIsNaN = isNaN(count);
27534
27535         if (!countIsNaN && !(count in whens)) {
27536           // If an explicit number rule such as 1, 2, 3... is defined, just use it.
27537           // Otherwise, check it against pluralization rules in $locale service.
27538           count = $locale.pluralCat(count - offset);
27539         }
27540
27541         // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
27542         // In JS `NaN !== NaN`, so we have to exlicitly check.
27543         if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
27544           watchRemover();
27545           var whenExpFn = whensExpFns[count];
27546           if (isUndefined(whenExpFn)) {
27547             if (newVal != null) {
27548               $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
27549             }
27550             watchRemover = noop;
27551             updateElementText();
27552           } else {
27553             watchRemover = scope.$watch(whenExpFn, updateElementText);
27554           }
27555           lastCount = count;
27556         }
27557       });
27558
27559       function updateElementText(newText) {
27560         element.text(newText || '');
27561       }
27562     }
27563   };
27564 }];
27565
27566 /**
27567  * @ngdoc directive
27568  * @name ngRepeat
27569  * @multiElement
27570  *
27571  * @description
27572  * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
27573  * instance gets its own scope, where the given loop variable is set to the current collection item,
27574  * and `$index` is set to the item index or key.
27575  *
27576  * Special properties are exposed on the local scope of each template instance, including:
27577  *
27578  * | Variable  | Type            | Details                                                                     |
27579  * |-----------|-----------------|-----------------------------------------------------------------------------|
27580  * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
27581  * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
27582  * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
27583  * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
27584  * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
27585  * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
27586  *
27587  * <div class="alert alert-info">
27588  *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
27589  *   This may be useful when, for instance, nesting ngRepeats.
27590  * </div>
27591  *
27592  *
27593  * # Iterating over object properties
27594  *
27595  * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
27596  * syntax:
27597  *
27598  * ```js
27599  * <div ng-repeat="(key, value) in myObj"> ... </div>
27600  * ```
27601  *
27602  * You need to be aware that the JavaScript specification does not define the order of keys
27603  * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
27604  * used to sort the keys alphabetically.)
27605  *
27606  * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
27607  * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
27608  * keys in the order in which they were defined, although there are exceptions when keys are deleted
27609  * and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
27610  *
27611  * If this is not desired, the recommended workaround is to convert your object into an array
27612  * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
27613  * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27614  * or implement a `$watch` on the object yourself.
27615  *
27616  *
27617  * # Tracking and Duplicates
27618  *
27619  * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
27620  * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
27621  *
27622  * * When an item is added, a new instance of the template is added to the DOM.
27623  * * When an item is removed, its template instance is removed from the DOM.
27624  * * When items are reordered, their respective templates are reordered in the DOM.
27625  *
27626  * To minimize creation of DOM elements, `ngRepeat` uses a function
27627  * to "keep track" of all items in the collection and their corresponding DOM elements.
27628  * For example, if an item is added to the collection, ngRepeat will know that all other items
27629  * already have DOM elements, and will not re-render them.
27630  *
27631  * The default tracking function (which tracks items by their identity) does not allow
27632  * duplicate items in arrays. This is because when there are duplicates, it is not possible
27633  * to maintain a one-to-one mapping between collection items and DOM elements.
27634  *
27635  * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27636  * with your own using the `track by` expression.
27637  *
27638  * For example, you may track items by the index of each item in the collection, using the
27639  * special scope property `$index`:
27640  * ```html
27641  *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27642  *      {{n}}
27643  *    </div>
27644  * ```
27645  *
27646  * You may also use arbitrary expressions in `track by`, including references to custom functions
27647  * on the scope:
27648  * ```html
27649  *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27650  *      {{n}}
27651  *    </div>
27652  * ```
27653  *
27654  * <div class="alert alert-success">
27655  * If you are working with objects that have an identifier property, you should track
27656  * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27657  * will not have to rebuild the DOM elements for items it has already rendered, even if the
27658  * JavaScript objects in the collection have been substituted for new ones. For large collections,
27659  * this signifincantly improves rendering performance. If you don't have a unique identifier,
27660  * `track by $index` can also provide a performance boost.
27661  * </div>
27662  * ```html
27663  *    <div ng-repeat="model in collection track by model.id">
27664  *      {{model.name}}
27665  *    </div>
27666  * ```
27667  *
27668  * When no `track by` expression is provided, it is equivalent to tracking by the built-in
27669  * `$id` function, which tracks items by their identity:
27670  * ```html
27671  *    <div ng-repeat="obj in collection track by $id(obj)">
27672  *      {{obj.prop}}
27673  *    </div>
27674  * ```
27675  *
27676  * <div class="alert alert-warning">
27677  * **Note:** `track by` must always be the last expression:
27678  * </div>
27679  * ```
27680  * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
27681  *     {{model.name}}
27682  * </div>
27683  * ```
27684  *
27685  * # Special repeat start and end points
27686  * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
27687  * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
27688  * 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)
27689  * up to and including the ending HTML tag where **ng-repeat-end** is placed.
27690  *
27691  * The example below makes use of this feature:
27692  * ```html
27693  *   <header ng-repeat-start="item in items">
27694  *     Header {{ item }}
27695  *   </header>
27696  *   <div class="body">
27697  *     Body {{ item }}
27698  *   </div>
27699  *   <footer ng-repeat-end>
27700  *     Footer {{ item }}
27701  *   </footer>
27702  * ```
27703  *
27704  * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
27705  * ```html
27706  *   <header>
27707  *     Header A
27708  *   </header>
27709  *   <div class="body">
27710  *     Body A
27711  *   </div>
27712  *   <footer>
27713  *     Footer A
27714  *   </footer>
27715  *   <header>
27716  *     Header B
27717  *   </header>
27718  *   <div class="body">
27719  *     Body B
27720  *   </div>
27721  *   <footer>
27722  *     Footer B
27723  *   </footer>
27724  * ```
27725  *
27726  * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
27727  * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
27728  *
27729  * @animations
27730  * **.enter** - when a new item is added to the list or when an item is revealed after a filter
27731  *
27732  * **.leave** - when an item is removed from the list or when an item is filtered out
27733  *
27734  * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
27735  *
27736  * @element ANY
27737  * @scope
27738  * @priority 1000
27739  * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
27740  *   formats are currently supported:
27741  *
27742  *   * `variable in expression` – where variable is the user defined loop variable and `expression`
27743  *     is a scope expression giving the collection to enumerate.
27744  *
27745  *     For example: `album in artist.albums`.
27746  *
27747  *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
27748  *     and `expression` is the scope expression giving the collection to enumerate.
27749  *
27750  *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
27751  *
27752  *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
27753  *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
27754  *     is specified, ng-repeat associates elements by identity. It is an error to have
27755  *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
27756  *     mapped to the same DOM element, which is not possible.)
27757  *
27758  *     Note that the tracking expression must come last, after any filters, and the alias expression.
27759  *
27760  *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
27761  *     will be associated by item identity in the array.
27762  *
27763  *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
27764  *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
27765  *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
27766  *     element in the same way in the DOM.
27767  *
27768  *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
27769  *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
27770  *     property is same.
27771  *
27772  *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
27773  *     to items in conjunction with a tracking expression.
27774  *
27775  *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
27776  *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
27777  *     when a filter is active on the repeater, but the filtered result set is empty.
27778  *
27779  *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
27780  *     the items have been processed through the filter.
27781  *
27782  *     Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax so it can be used only at the end
27783  *     (and not as operator, inside an expression).
27784  *
27785  *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
27786  *
27787  * @example
27788  * This example initializes the scope to a list of names and
27789  * then uses `ngRepeat` to display every person:
27790   <example module="ngAnimate" deps="angular-animate.js" animations="true">
27791     <file name="index.html">
27792       <div ng-init="friends = [
27793         {name:'John', age:25, gender:'boy'},
27794         {name:'Jessie', age:30, gender:'girl'},
27795         {name:'Johanna', age:28, gender:'girl'},
27796         {name:'Joy', age:15, gender:'girl'},
27797         {name:'Mary', age:28, gender:'girl'},
27798         {name:'Peter', age:95, gender:'boy'},
27799         {name:'Sebastian', age:50, gender:'boy'},
27800         {name:'Erika', age:27, gender:'girl'},
27801         {name:'Patrick', age:40, gender:'boy'},
27802         {name:'Samantha', age:60, gender:'girl'}
27803       ]">
27804         I have {{friends.length}} friends. They are:
27805         <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
27806         <ul class="example-animate-container">
27807           <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
27808             [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
27809           </li>
27810           <li class="animate-repeat" ng-if="results.length == 0">
27811             <strong>No results found...</strong>
27812           </li>
27813         </ul>
27814       </div>
27815     </file>
27816     <file name="animations.css">
27817       .example-animate-container {
27818         background:white;
27819         border:1px solid black;
27820         list-style:none;
27821         margin:0;
27822         padding:0 10px;
27823       }
27824
27825       .animate-repeat {
27826         line-height:40px;
27827         list-style:none;
27828         box-sizing:border-box;
27829       }
27830
27831       .animate-repeat.ng-move,
27832       .animate-repeat.ng-enter,
27833       .animate-repeat.ng-leave {
27834         transition:all linear 0.5s;
27835       }
27836
27837       .animate-repeat.ng-leave.ng-leave-active,
27838       .animate-repeat.ng-move,
27839       .animate-repeat.ng-enter {
27840         opacity:0;
27841         max-height:0;
27842       }
27843
27844       .animate-repeat.ng-leave,
27845       .animate-repeat.ng-move.ng-move-active,
27846       .animate-repeat.ng-enter.ng-enter-active {
27847         opacity:1;
27848         max-height:40px;
27849       }
27850     </file>
27851     <file name="protractor.js" type="protractor">
27852       var friends = element.all(by.repeater('friend in friends'));
27853
27854       it('should render initial data set', function() {
27855         expect(friends.count()).toBe(10);
27856         expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
27857         expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
27858         expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
27859         expect(element(by.binding('friends.length')).getText())
27860             .toMatch("I have 10 friends. They are:");
27861       });
27862
27863        it('should update repeater when filter predicate changes', function() {
27864          expect(friends.count()).toBe(10);
27865
27866          element(by.model('q')).sendKeys('ma');
27867
27868          expect(friends.count()).toBe(2);
27869          expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
27870          expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
27871        });
27872       </file>
27873     </example>
27874  */
27875 var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27876   var NG_REMOVED = '$$NG_REMOVED';
27877   var ngRepeatMinErr = minErr('ngRepeat');
27878
27879   var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
27880     // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
27881     scope[valueIdentifier] = value;
27882     if (keyIdentifier) scope[keyIdentifier] = key;
27883     scope.$index = index;
27884     scope.$first = (index === 0);
27885     scope.$last = (index === (arrayLength - 1));
27886     scope.$middle = !(scope.$first || scope.$last);
27887     // jshint bitwise: false
27888     scope.$odd = !(scope.$even = (index&1) === 0);
27889     // jshint bitwise: true
27890   };
27891
27892   var getBlockStart = function(block) {
27893     return block.clone[0];
27894   };
27895
27896   var getBlockEnd = function(block) {
27897     return block.clone[block.clone.length - 1];
27898   };
27899
27900
27901   return {
27902     restrict: 'A',
27903     multiElement: true,
27904     transclude: 'element',
27905     priority: 1000,
27906     terminal: true,
27907     $$tlb: true,
27908     compile: function ngRepeatCompile($element, $attr) {
27909       var expression = $attr.ngRepeat;
27910       var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
27911
27912       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*$/);
27913
27914       if (!match) {
27915         throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
27916             expression);
27917       }
27918
27919       var lhs = match[1];
27920       var rhs = match[2];
27921       var aliasAs = match[3];
27922       var trackByExp = match[4];
27923
27924       match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
27925
27926       if (!match) {
27927         throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
27928             lhs);
27929       }
27930       var valueIdentifier = match[3] || match[1];
27931       var keyIdentifier = match[2];
27932
27933       if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
27934           /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
27935         throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
27936           aliasAs);
27937       }
27938
27939       var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
27940       var hashFnLocals = {$id: hashKey};
27941
27942       if (trackByExp) {
27943         trackByExpGetter = $parse(trackByExp);
27944       } else {
27945         trackByIdArrayFn = function(key, value) {
27946           return hashKey(value);
27947         };
27948         trackByIdObjFn = function(key) {
27949           return key;
27950         };
27951       }
27952
27953       return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
27954
27955         if (trackByExpGetter) {
27956           trackByIdExpFn = function(key, value, index) {
27957             // assign key, value, and $index to the locals so that they can be used in hash functions
27958             if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27959             hashFnLocals[valueIdentifier] = value;
27960             hashFnLocals.$index = index;
27961             return trackByExpGetter($scope, hashFnLocals);
27962           };
27963         }
27964
27965         // Store a list of elements from previous run. This is a hash where key is the item from the
27966         // iterator, and the value is objects with following properties.
27967         //   - scope: bound scope
27968         //   - element: previous element.
27969         //   - index: position
27970         //
27971         // We are using no-proto object so that we don't need to guard against inherited props via
27972         // hasOwnProperty.
27973         var lastBlockMap = createMap();
27974
27975         //watch props
27976         $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27977           var index, length,
27978               previousNode = $element[0],     // node that cloned nodes should be inserted after
27979                                               // initialized to the comment node anchor
27980               nextNode,
27981               // Same as lastBlockMap but it has the current state. It will become the
27982               // lastBlockMap on the next iteration.
27983               nextBlockMap = createMap(),
27984               collectionLength,
27985               key, value, // key/value of iteration
27986               trackById,
27987               trackByIdFn,
27988               collectionKeys,
27989               block,       // last object information {scope, element, id}
27990               nextBlockOrder,
27991               elementsToRemove;
27992
27993           if (aliasAs) {
27994             $scope[aliasAs] = collection;
27995           }
27996
27997           if (isArrayLike(collection)) {
27998             collectionKeys = collection;
27999             trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
28000           } else {
28001             trackByIdFn = trackByIdExpFn || trackByIdObjFn;
28002             // if object, extract keys, in enumeration order, unsorted
28003             collectionKeys = [];
28004             for (var itemKey in collection) {
28005               if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
28006                 collectionKeys.push(itemKey);
28007               }
28008             }
28009           }
28010
28011           collectionLength = collectionKeys.length;
28012           nextBlockOrder = new Array(collectionLength);
28013
28014           // locate existing items
28015           for (index = 0; index < collectionLength; index++) {
28016             key = (collection === collectionKeys) ? index : collectionKeys[index];
28017             value = collection[key];
28018             trackById = trackByIdFn(key, value, index);
28019             if (lastBlockMap[trackById]) {
28020               // found previously seen block
28021               block = lastBlockMap[trackById];
28022               delete lastBlockMap[trackById];
28023               nextBlockMap[trackById] = block;
28024               nextBlockOrder[index] = block;
28025             } else if (nextBlockMap[trackById]) {
28026               // if collision detected. restore lastBlockMap and throw an error
28027               forEach(nextBlockOrder, function(block) {
28028                 if (block && block.scope) lastBlockMap[block.id] = block;
28029               });
28030               throw ngRepeatMinErr('dupes',
28031                   "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
28032                   expression, trackById, value);
28033             } else {
28034               // new never before seen block
28035               nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
28036               nextBlockMap[trackById] = true;
28037             }
28038           }
28039
28040           // remove leftover items
28041           for (var blockKey in lastBlockMap) {
28042             block = lastBlockMap[blockKey];
28043             elementsToRemove = getBlockNodes(block.clone);
28044             $animate.leave(elementsToRemove);
28045             if (elementsToRemove[0].parentNode) {
28046               // if the element was not removed yet because of pending animation, mark it as deleted
28047               // so that we can ignore it later
28048               for (index = 0, length = elementsToRemove.length; index < length; index++) {
28049                 elementsToRemove[index][NG_REMOVED] = true;
28050               }
28051             }
28052             block.scope.$destroy();
28053           }
28054
28055           // we are not using forEach for perf reasons (trying to avoid #call)
28056           for (index = 0; index < collectionLength; index++) {
28057             key = (collection === collectionKeys) ? index : collectionKeys[index];
28058             value = collection[key];
28059             block = nextBlockOrder[index];
28060
28061             if (block.scope) {
28062               // if we have already seen this object, then we need to reuse the
28063               // associated scope/element
28064
28065               nextNode = previousNode;
28066
28067               // skip nodes that are already pending removal via leave animation
28068               do {
28069                 nextNode = nextNode.nextSibling;
28070               } while (nextNode && nextNode[NG_REMOVED]);
28071
28072               if (getBlockStart(block) != nextNode) {
28073                 // existing item which got moved
28074                 $animate.move(getBlockNodes(block.clone), null, previousNode);
28075               }
28076               previousNode = getBlockEnd(block);
28077               updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
28078             } else {
28079               // new item which we don't know about
28080               $transclude(function ngRepeatTransclude(clone, scope) {
28081                 block.scope = scope;
28082                 // http://jsperf.com/clone-vs-createcomment
28083                 var endNode = ngRepeatEndComment.cloneNode(false);
28084                 clone[clone.length++] = endNode;
28085
28086                 $animate.enter(clone, null, previousNode);
28087                 previousNode = endNode;
28088                 // Note: We only need the first/last node of the cloned nodes.
28089                 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
28090                 // by a directive with templateUrl when its template arrives.
28091                 block.clone = clone;
28092                 nextBlockMap[block.id] = block;
28093                 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
28094               });
28095             }
28096           }
28097           lastBlockMap = nextBlockMap;
28098         });
28099       };
28100     }
28101   };
28102 }];
28103
28104 var NG_HIDE_CLASS = 'ng-hide';
28105 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
28106 /**
28107  * @ngdoc directive
28108  * @name ngShow
28109  * @multiElement
28110  *
28111  * @description
28112  * The `ngShow` directive shows or hides the given HTML element based on the expression
28113  * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
28114  * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
28115  * in AngularJS and sets the display style to none (using an !important flag).
28116  * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
28117  *
28118  * ```html
28119  * <!-- when $scope.myValue is truthy (element is visible) -->
28120  * <div ng-show="myValue"></div>
28121  *
28122  * <!-- when $scope.myValue is falsy (element is hidden) -->
28123  * <div ng-show="myValue" class="ng-hide"></div>
28124  * ```
28125  *
28126  * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
28127  * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
28128  * from the element causing the element not to appear hidden.
28129  *
28130  * ## Why is !important used?
28131  *
28132  * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
28133  * can be easily overridden by heavier selectors. For example, something as simple
28134  * as changing the display style on a HTML list item would make hidden elements appear visible.
28135  * This also becomes a bigger issue when dealing with CSS frameworks.
28136  *
28137  * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
28138  * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
28139  * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
28140  *
28141  * ### Overriding `.ng-hide`
28142  *
28143  * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
28144  * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
28145  * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
28146  * with extra animation classes that can be added.
28147  *
28148  * ```css
28149  * .ng-hide:not(.ng-hide-animate) {
28150  *   /&#42; this is just another form of hiding an element &#42;/
28151  *   display: block!important;
28152  *   position: absolute;
28153  *   top: -9999px;
28154  *   left: -9999px;
28155  * }
28156  * ```
28157  *
28158  * By default you don't need to override in CSS anything and the animations will work around the display style.
28159  *
28160  * ## A note about animations with `ngShow`
28161  *
28162  * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
28163  * is true and false. This system works like the animation system present with ngClass except that
28164  * you must also include the !important flag to override the display property
28165  * so that you can perform an animation when the element is hidden during the time of the animation.
28166  *
28167  * ```css
28168  * //
28169  * //a working example can be found at the bottom of this page
28170  * //
28171  * .my-element.ng-hide-add, .my-element.ng-hide-remove {
28172  *   /&#42; this is required as of 1.3x to properly
28173  *      apply all styling in a show/hide animation &#42;/
28174  *   transition: 0s linear all;
28175  * }
28176  *
28177  * .my-element.ng-hide-add-active,
28178  * .my-element.ng-hide-remove-active {
28179  *   /&#42; the transition is defined in the active class &#42;/
28180  *   transition: 1s linear all;
28181  * }
28182  *
28183  * .my-element.ng-hide-add { ... }
28184  * .my-element.ng-hide-add.ng-hide-add-active { ... }
28185  * .my-element.ng-hide-remove { ... }
28186  * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
28187  * ```
28188  *
28189  * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
28190  * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
28191  *
28192  * @animations
28193  * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
28194  * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
28195  *
28196  * @element ANY
28197  * @param {expression} ngShow If the {@link guide/expression expression} is truthy
28198  *     then the element is shown or hidden respectively.
28199  *
28200  * @example
28201   <example module="ngAnimate" deps="angular-animate.js" animations="true">
28202     <file name="index.html">
28203       Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
28204       <div>
28205         Show:
28206         <div class="check-element animate-show" ng-show="checked">
28207           <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
28208         </div>
28209       </div>
28210       <div>
28211         Hide:
28212         <div class="check-element animate-show" ng-hide="checked">
28213           <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
28214         </div>
28215       </div>
28216     </file>
28217     <file name="glyphicons.css">
28218       @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
28219     </file>
28220     <file name="animations.css">
28221       .animate-show {
28222         line-height: 20px;
28223         opacity: 1;
28224         padding: 10px;
28225         border: 1px solid black;
28226         background: white;
28227       }
28228
28229       .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
28230         transition: all linear 0.5s;
28231       }
28232
28233       .animate-show.ng-hide {
28234         line-height: 0;
28235         opacity: 0;
28236         padding: 0 10px;
28237       }
28238
28239       .check-element {
28240         padding: 10px;
28241         border: 1px solid black;
28242         background: white;
28243       }
28244     </file>
28245     <file name="protractor.js" type="protractor">
28246       var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
28247       var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
28248
28249       it('should check ng-show / ng-hide', function() {
28250         expect(thumbsUp.isDisplayed()).toBeFalsy();
28251         expect(thumbsDown.isDisplayed()).toBeTruthy();
28252
28253         element(by.model('checked')).click();
28254
28255         expect(thumbsUp.isDisplayed()).toBeTruthy();
28256         expect(thumbsDown.isDisplayed()).toBeFalsy();
28257       });
28258     </file>
28259   </example>
28260  */
28261 var ngShowDirective = ['$animate', function($animate) {
28262   return {
28263     restrict: 'A',
28264     multiElement: true,
28265     link: function(scope, element, attr) {
28266       scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
28267         // we're adding a temporary, animation-specific class for ng-hide since this way
28268         // we can control when the element is actually displayed on screen without having
28269         // to have a global/greedy CSS selector that breaks when other animations are run.
28270         // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
28271         $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
28272           tempClasses: NG_HIDE_IN_PROGRESS_CLASS
28273         });
28274       });
28275     }
28276   };
28277 }];
28278
28279
28280 /**
28281  * @ngdoc directive
28282  * @name ngHide
28283  * @multiElement
28284  *
28285  * @description
28286  * The `ngHide` directive shows or hides the given HTML element based on the expression
28287  * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
28288  * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
28289  * in AngularJS and sets the display style to none (using an !important flag).
28290  * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
28291  *
28292  * ```html
28293  * <!-- when $scope.myValue is truthy (element is hidden) -->
28294  * <div ng-hide="myValue" class="ng-hide"></div>
28295  *
28296  * <!-- when $scope.myValue is falsy (element is visible) -->
28297  * <div ng-hide="myValue"></div>
28298  * ```
28299  *
28300  * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
28301  * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
28302  * from the element causing the element not to appear hidden.
28303  *
28304  * ## Why is !important used?
28305  *
28306  * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
28307  * can be easily overridden by heavier selectors. For example, something as simple
28308  * as changing the display style on a HTML list item would make hidden elements appear visible.
28309  * This also becomes a bigger issue when dealing with CSS frameworks.
28310  *
28311  * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
28312  * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
28313  * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
28314  *
28315  * ### Overriding `.ng-hide`
28316  *
28317  * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
28318  * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
28319  * class in CSS:
28320  *
28321  * ```css
28322  * .ng-hide {
28323  *   /&#42; this is just another form of hiding an element &#42;/
28324  *   display: block!important;
28325  *   position: absolute;
28326  *   top: -9999px;
28327  *   left: -9999px;
28328  * }
28329  * ```
28330  *
28331  * By default you don't need to override in CSS anything and the animations will work around the display style.
28332  *
28333  * ## A note about animations with `ngHide`
28334  *
28335  * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
28336  * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
28337  * CSS class is added and removed for you instead of your own CSS class.
28338  *
28339  * ```css
28340  * //
28341  * //a working example can be found at the bottom of this page
28342  * //
28343  * .my-element.ng-hide-add, .my-element.ng-hide-remove {
28344  *   transition: 0.5s linear all;
28345  * }
28346  *
28347  * .my-element.ng-hide-add { ... }
28348  * .my-element.ng-hide-add.ng-hide-add-active { ... }
28349  * .my-element.ng-hide-remove { ... }
28350  * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
28351  * ```
28352  *
28353  * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
28354  * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
28355  *
28356  * @animations
28357  * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
28358  * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
28359  *
28360  * @element ANY
28361  * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
28362  *     the element is shown or hidden respectively.
28363  *
28364  * @example
28365   <example module="ngAnimate" deps="angular-animate.js" animations="true">
28366     <file name="index.html">
28367       Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
28368       <div>
28369         Show:
28370         <div class="check-element animate-hide" ng-show="checked">
28371           <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
28372         </div>
28373       </div>
28374       <div>
28375         Hide:
28376         <div class="check-element animate-hide" ng-hide="checked">
28377           <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
28378         </div>
28379       </div>
28380     </file>
28381     <file name="glyphicons.css">
28382       @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
28383     </file>
28384     <file name="animations.css">
28385       .animate-hide {
28386         transition: all linear 0.5s;
28387         line-height: 20px;
28388         opacity: 1;
28389         padding: 10px;
28390         border: 1px solid black;
28391         background: white;
28392       }
28393
28394       .animate-hide.ng-hide {
28395         line-height: 0;
28396         opacity: 0;
28397         padding: 0 10px;
28398       }
28399
28400       .check-element {
28401         padding: 10px;
28402         border: 1px solid black;
28403         background: white;
28404       }
28405     </file>
28406     <file name="protractor.js" type="protractor">
28407       var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
28408       var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
28409
28410       it('should check ng-show / ng-hide', function() {
28411         expect(thumbsUp.isDisplayed()).toBeFalsy();
28412         expect(thumbsDown.isDisplayed()).toBeTruthy();
28413
28414         element(by.model('checked')).click();
28415
28416         expect(thumbsUp.isDisplayed()).toBeTruthy();
28417         expect(thumbsDown.isDisplayed()).toBeFalsy();
28418       });
28419     </file>
28420   </example>
28421  */
28422 var ngHideDirective = ['$animate', function($animate) {
28423   return {
28424     restrict: 'A',
28425     multiElement: true,
28426     link: function(scope, element, attr) {
28427       scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
28428         // The comment inside of the ngShowDirective explains why we add and
28429         // remove a temporary class for the show/hide animation
28430         $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
28431           tempClasses: NG_HIDE_IN_PROGRESS_CLASS
28432         });
28433       });
28434     }
28435   };
28436 }];
28437
28438 /**
28439  * @ngdoc directive
28440  * @name ngStyle
28441  * @restrict AC
28442  *
28443  * @description
28444  * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
28445  *
28446  * @element ANY
28447  * @param {expression} ngStyle
28448  *
28449  * {@link guide/expression Expression} which evals to an
28450  * object whose keys are CSS style names and values are corresponding values for those CSS
28451  * keys.
28452  *
28453  * Since some CSS style names are not valid keys for an object, they must be quoted.
28454  * See the 'background-color' style in the example below.
28455  *
28456  * @example
28457    <example>
28458      <file name="index.html">
28459         <input type="button" value="set color" ng-click="myStyle={color:'red'}">
28460         <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
28461         <input type="button" value="clear" ng-click="myStyle={}">
28462         <br/>
28463         <span ng-style="myStyle">Sample Text</span>
28464         <pre>myStyle={{myStyle}}</pre>
28465      </file>
28466      <file name="style.css">
28467        span {
28468          color: black;
28469        }
28470      </file>
28471      <file name="protractor.js" type="protractor">
28472        var colorSpan = element(by.css('span'));
28473
28474        it('should check ng-style', function() {
28475          expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28476          element(by.css('input[value=\'set color\']')).click();
28477          expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
28478          element(by.css('input[value=clear]')).click();
28479          expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28480        });
28481      </file>
28482    </example>
28483  */
28484 var ngStyleDirective = ngDirective(function(scope, element, attr) {
28485   scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
28486     if (oldStyles && (newStyles !== oldStyles)) {
28487       forEach(oldStyles, function(val, style) { element.css(style, '');});
28488     }
28489     if (newStyles) element.css(newStyles);
28490   }, true);
28491 });
28492
28493 /**
28494  * @ngdoc directive
28495  * @name ngSwitch
28496  * @restrict EA
28497  *
28498  * @description
28499  * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
28500  * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
28501  * as specified in the template.
28502  *
28503  * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
28504  * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
28505  * matches the value obtained from the evaluated expression. In other words, you define a container element
28506  * (where you place the directive), place an expression on the **`on="..."` attribute**
28507  * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
28508  * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
28509  * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
28510  * attribute is displayed.
28511  *
28512  * <div class="alert alert-info">
28513  * Be aware that the attribute values to match against cannot be expressions. They are interpreted
28514  * as literal string values to match against.
28515  * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
28516  * value of the expression `$scope.someVal`.
28517  * </div>
28518
28519  * @animations
28520  * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
28521  * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
28522  *
28523  * @usage
28524  *
28525  * ```
28526  * <ANY ng-switch="expression">
28527  *   <ANY ng-switch-when="matchValue1">...</ANY>
28528  *   <ANY ng-switch-when="matchValue2">...</ANY>
28529  *   <ANY ng-switch-default>...</ANY>
28530  * </ANY>
28531  * ```
28532  *
28533  *
28534  * @scope
28535  * @priority 1200
28536  * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
28537  * On child elements add:
28538  *
28539  * * `ngSwitchWhen`: the case statement to match against. If match then this
28540  *   case will be displayed. If the same match appears multiple times, all the
28541  *   elements will be displayed.
28542  * * `ngSwitchDefault`: the default case when no other case match. If there
28543  *   are multiple default cases, all of them will be displayed when no other
28544  *   case match.
28545  *
28546  *
28547  * @example
28548   <example module="switchExample" deps="angular-animate.js" animations="true">
28549     <file name="index.html">
28550       <div ng-controller="ExampleController">
28551         <select ng-model="selection" ng-options="item for item in items">
28552         </select>
28553         <code>selection={{selection}}</code>
28554         <hr/>
28555         <div class="animate-switch-container"
28556           ng-switch on="selection">
28557             <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
28558             <div class="animate-switch" ng-switch-when="home">Home Span</div>
28559             <div class="animate-switch" ng-switch-default>default</div>
28560         </div>
28561       </div>
28562     </file>
28563     <file name="script.js">
28564       angular.module('switchExample', ['ngAnimate'])
28565         .controller('ExampleController', ['$scope', function($scope) {
28566           $scope.items = ['settings', 'home', 'other'];
28567           $scope.selection = $scope.items[0];
28568         }]);
28569     </file>
28570     <file name="animations.css">
28571       .animate-switch-container {
28572         position:relative;
28573         background:white;
28574         border:1px solid black;
28575         height:40px;
28576         overflow:hidden;
28577       }
28578
28579       .animate-switch {
28580         padding:10px;
28581       }
28582
28583       .animate-switch.ng-animate {
28584         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28585
28586         position:absolute;
28587         top:0;
28588         left:0;
28589         right:0;
28590         bottom:0;
28591       }
28592
28593       .animate-switch.ng-leave.ng-leave-active,
28594       .animate-switch.ng-enter {
28595         top:-50px;
28596       }
28597       .animate-switch.ng-leave,
28598       .animate-switch.ng-enter.ng-enter-active {
28599         top:0;
28600       }
28601     </file>
28602     <file name="protractor.js" type="protractor">
28603       var switchElem = element(by.css('[ng-switch]'));
28604       var select = element(by.model('selection'));
28605
28606       it('should start in settings', function() {
28607         expect(switchElem.getText()).toMatch(/Settings Div/);
28608       });
28609       it('should change to home', function() {
28610         select.all(by.css('option')).get(1).click();
28611         expect(switchElem.getText()).toMatch(/Home Span/);
28612       });
28613       it('should select default', function() {
28614         select.all(by.css('option')).get(2).click();
28615         expect(switchElem.getText()).toMatch(/default/);
28616       });
28617     </file>
28618   </example>
28619  */
28620 var ngSwitchDirective = ['$animate', function($animate) {
28621   return {
28622     require: 'ngSwitch',
28623
28624     // asks for $scope to fool the BC controller module
28625     controller: ['$scope', function ngSwitchController() {
28626      this.cases = {};
28627     }],
28628     link: function(scope, element, attr, ngSwitchController) {
28629       var watchExpr = attr.ngSwitch || attr.on,
28630           selectedTranscludes = [],
28631           selectedElements = [],
28632           previousLeaveAnimations = [],
28633           selectedScopes = [];
28634
28635       var spliceFactory = function(array, index) {
28636           return function() { array.splice(index, 1); };
28637       };
28638
28639       scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28640         var i, ii;
28641         for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28642           $animate.cancel(previousLeaveAnimations[i]);
28643         }
28644         previousLeaveAnimations.length = 0;
28645
28646         for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28647           var selected = getBlockNodes(selectedElements[i].clone);
28648           selectedScopes[i].$destroy();
28649           var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28650           promise.then(spliceFactory(previousLeaveAnimations, i));
28651         }
28652
28653         selectedElements.length = 0;
28654         selectedScopes.length = 0;
28655
28656         if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
28657           forEach(selectedTranscludes, function(selectedTransclude) {
28658             selectedTransclude.transclude(function(caseElement, selectedScope) {
28659               selectedScopes.push(selectedScope);
28660               var anchor = selectedTransclude.element;
28661               caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
28662               var block = { clone: caseElement };
28663
28664               selectedElements.push(block);
28665               $animate.enter(caseElement, anchor.parent(), anchor);
28666             });
28667           });
28668         }
28669       });
28670     }
28671   };
28672 }];
28673
28674 var ngSwitchWhenDirective = ngDirective({
28675   transclude: 'element',
28676   priority: 1200,
28677   require: '^ngSwitch',
28678   multiElement: true,
28679   link: function(scope, element, attrs, ctrl, $transclude) {
28680     ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
28681     ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
28682   }
28683 });
28684
28685 var ngSwitchDefaultDirective = ngDirective({
28686   transclude: 'element',
28687   priority: 1200,
28688   require: '^ngSwitch',
28689   multiElement: true,
28690   link: function(scope, element, attr, ctrl, $transclude) {
28691     ctrl.cases['?'] = (ctrl.cases['?'] || []);
28692     ctrl.cases['?'].push({ transclude: $transclude, element: element });
28693    }
28694 });
28695
28696 /**
28697  * @ngdoc directive
28698  * @name ngTransclude
28699  * @restrict EAC
28700  *
28701  * @description
28702  * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
28703  *
28704  * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
28705  *
28706  * @element ANY
28707  *
28708  * @example
28709    <example module="transcludeExample">
28710      <file name="index.html">
28711        <script>
28712          angular.module('transcludeExample', [])
28713           .directive('pane', function(){
28714              return {
28715                restrict: 'E',
28716                transclude: true,
28717                scope: { title:'@' },
28718                template: '<div style="border: 1px solid black;">' +
28719                            '<div style="background-color: gray">{{title}}</div>' +
28720                            '<ng-transclude></ng-transclude>' +
28721                          '</div>'
28722              };
28723          })
28724          .controller('ExampleController', ['$scope', function($scope) {
28725            $scope.title = 'Lorem Ipsum';
28726            $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
28727          }]);
28728        </script>
28729        <div ng-controller="ExampleController">
28730          <input ng-model="title" aria-label="title"> <br/>
28731          <textarea ng-model="text" aria-label="text"></textarea> <br/>
28732          <pane title="{{title}}">{{text}}</pane>
28733        </div>
28734      </file>
28735      <file name="protractor.js" type="protractor">
28736         it('should have transcluded', function() {
28737           var titleElement = element(by.model('title'));
28738           titleElement.clear();
28739           titleElement.sendKeys('TITLE');
28740           var textElement = element(by.model('text'));
28741           textElement.clear();
28742           textElement.sendKeys('TEXT');
28743           expect(element(by.binding('title')).getText()).toEqual('TITLE');
28744           expect(element(by.binding('text')).getText()).toEqual('TEXT');
28745         });
28746      </file>
28747    </example>
28748  *
28749  */
28750 var ngTranscludeDirective = ngDirective({
28751   restrict: 'EAC',
28752   link: function($scope, $element, $attrs, controller, $transclude) {
28753     if (!$transclude) {
28754       throw minErr('ngTransclude')('orphan',
28755        'Illegal use of ngTransclude directive in the template! ' +
28756        'No parent directive that requires a transclusion found. ' +
28757        'Element: {0}',
28758        startingTag($element));
28759     }
28760
28761     $transclude(function(clone) {
28762       $element.empty();
28763       $element.append(clone);
28764     });
28765   }
28766 });
28767
28768 /**
28769  * @ngdoc directive
28770  * @name script
28771  * @restrict E
28772  *
28773  * @description
28774  * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
28775  * template can be used by {@link ng.directive:ngInclude `ngInclude`},
28776  * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
28777  * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
28778  * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
28779  *
28780  * @param {string} type Must be set to `'text/ng-template'`.
28781  * @param {string} id Cache name of the template.
28782  *
28783  * @example
28784   <example>
28785     <file name="index.html">
28786       <script type="text/ng-template" id="/tpl.html">
28787         Content of the template.
28788       </script>
28789
28790       <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
28791       <div id="tpl-content" ng-include src="currentTpl"></div>
28792     </file>
28793     <file name="protractor.js" type="protractor">
28794       it('should load template defined inside script tag', function() {
28795         element(by.css('#tpl-link')).click();
28796         expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
28797       });
28798     </file>
28799   </example>
28800  */
28801 var scriptDirective = ['$templateCache', function($templateCache) {
28802   return {
28803     restrict: 'E',
28804     terminal: true,
28805     compile: function(element, attr) {
28806       if (attr.type == 'text/ng-template') {
28807         var templateUrl = attr.id,
28808             text = element[0].text;
28809
28810         $templateCache.put(templateUrl, text);
28811       }
28812     }
28813   };
28814 }];
28815
28816 var noopNgModelController = { $setViewValue: noop, $render: noop };
28817
28818 function chromeHack(optionElement) {
28819   // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28820   // Adding an <option selected="selected"> element to a <select required="required"> should
28821   // automatically select the new element
28822   if (optionElement[0].hasAttribute('selected')) {
28823     optionElement[0].selected = true;
28824   }
28825 }
28826
28827 /**
28828  * @ngdoc type
28829  * @name  select.SelectController
28830  * @description
28831  * The controller for the `<select>` directive. This provides support for reading
28832  * and writing the selected value(s) of the control and also coordinates dynamically
28833  * added `<option>` elements, perhaps by an `ngRepeat` directive.
28834  */
28835 var SelectController =
28836         ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
28837
28838   var self = this,
28839       optionsMap = new HashMap();
28840
28841   // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
28842   self.ngModelCtrl = noopNgModelController;
28843
28844   // The "unknown" option is one that is prepended to the list if the viewValue
28845   // does not match any of the options. When it is rendered the value of the unknown
28846   // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
28847   //
28848   // We can't just jqLite('<option>') since jqLite is not smart enough
28849   // to create it in <select> and IE barfs otherwise.
28850   self.unknownOption = jqLite(document.createElement('option'));
28851   self.renderUnknownOption = function(val) {
28852     var unknownVal = '? ' + hashKey(val) + ' ?';
28853     self.unknownOption.val(unknownVal);
28854     $element.prepend(self.unknownOption);
28855     $element.val(unknownVal);
28856   };
28857
28858   $scope.$on('$destroy', function() {
28859     // disable unknown option so that we don't do work when the whole select is being destroyed
28860     self.renderUnknownOption = noop;
28861   });
28862
28863   self.removeUnknownOption = function() {
28864     if (self.unknownOption.parent()) self.unknownOption.remove();
28865   };
28866
28867
28868   // Read the value of the select control, the implementation of this changes depending
28869   // upon whether the select can have multiple values and whether ngOptions is at work.
28870   self.readValue = function readSingleValue() {
28871     self.removeUnknownOption();
28872     return $element.val();
28873   };
28874
28875
28876   // Write the value to the select control, the implementation of this changes depending
28877   // upon whether the select can have multiple values and whether ngOptions is at work.
28878   self.writeValue = function writeSingleValue(value) {
28879     if (self.hasOption(value)) {
28880       self.removeUnknownOption();
28881       $element.val(value);
28882       if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
28883     } else {
28884       if (value == null && self.emptyOption) {
28885         self.removeUnknownOption();
28886         $element.val('');
28887       } else {
28888         self.renderUnknownOption(value);
28889       }
28890     }
28891   };
28892
28893
28894   // Tell the select control that an option, with the given value, has been added
28895   self.addOption = function(value, element) {
28896     // Skip comment nodes, as they only pollute the `optionsMap`
28897     if (element[0].nodeType === NODE_TYPE_COMMENT) return;
28898
28899     assertNotHasOwnProperty(value, '"option value"');
28900     if (value === '') {
28901       self.emptyOption = element;
28902     }
28903     var count = optionsMap.get(value) || 0;
28904     optionsMap.put(value, count + 1);
28905     self.ngModelCtrl.$render();
28906     chromeHack(element);
28907   };
28908
28909   // Tell the select control that an option, with the given value, has been removed
28910   self.removeOption = function(value) {
28911     var count = optionsMap.get(value);
28912     if (count) {
28913       if (count === 1) {
28914         optionsMap.remove(value);
28915         if (value === '') {
28916           self.emptyOption = undefined;
28917         }
28918       } else {
28919         optionsMap.put(value, count - 1);
28920       }
28921     }
28922   };
28923
28924   // Check whether the select control has an option matching the given value
28925   self.hasOption = function(value) {
28926     return !!optionsMap.get(value);
28927   };
28928
28929
28930   self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
28931
28932     if (interpolateValueFn) {
28933       // The value attribute is interpolated
28934       var oldVal;
28935       optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
28936         if (isDefined(oldVal)) {
28937           self.removeOption(oldVal);
28938         }
28939         oldVal = newVal;
28940         self.addOption(newVal, optionElement);
28941       });
28942     } else if (interpolateTextFn) {
28943       // The text content is interpolated
28944       optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
28945         optionAttrs.$set('value', newVal);
28946         if (oldVal !== newVal) {
28947           self.removeOption(oldVal);
28948         }
28949         self.addOption(newVal, optionElement);
28950       });
28951     } else {
28952       // The value attribute is static
28953       self.addOption(optionAttrs.value, optionElement);
28954     }
28955
28956     optionElement.on('$destroy', function() {
28957       self.removeOption(optionAttrs.value);
28958       self.ngModelCtrl.$render();
28959     });
28960   };
28961 }];
28962
28963 /**
28964  * @ngdoc directive
28965  * @name select
28966  * @restrict E
28967  *
28968  * @description
28969  * HTML `SELECT` element with angular data-binding.
28970  *
28971  * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28972  * between the scope and the `<select>` control (including setting default values).
28973  * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28974  * {@link ngOptions `ngOptions`} directives.
28975  *
28976  * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28977  * to the model identified by the `ngModel` directive. With static or repeated options, this is
28978  * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28979  * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28980  *
28981  * <div class="alert alert-warning">
28982  * Note that the value of a `select` directive used without `ngOptions` is always a string.
28983  * When the model needs to be bound to a non-string value, you must either explictly convert it
28984  * using a directive (see example below) or use `ngOptions` to specify the set of options.
28985  * This is because an option element can only be bound to string values at present.
28986  * </div>
28987  *
28988  * If the viewValue of `ngModel` does not match any of the options, then the control
28989  * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28990  *
28991  * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28992  * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28993  * option. See example below for demonstration.
28994  *
28995  * <div class="alert alert-info">
28996  * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28997  * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
28998  * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28999  * comprehension expression, and additionally in reducing memory and increasing speed by not creating
29000  * a new scope for each repeated instance.
29001  * </div>
29002  *
29003  *
29004  * @param {string} ngModel Assignable angular expression to data-bind to.
29005  * @param {string=} name Property name of the form under which the control is published.
29006  * @param {string=} multiple Allows multiple options to be selected. The selected values will be
29007  *     bound to the model as an array.
29008  * @param {string=} required Sets `required` validation error key if the value is not entered.
29009  * @param {string=} ngRequired Adds required attribute and required validation constraint to
29010  * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
29011  * when you want to data-bind to the required attribute.
29012  * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
29013  *    interaction with the select element.
29014  * @param {string=} ngOptions sets the options that the select is populated with and defines what is
29015  * set on the model on selection. See {@link ngOptions `ngOptions`}.
29016  *
29017  * @example
29018  * ### Simple `select` elements with static options
29019  *
29020  * <example name="static-select" module="staticSelect">
29021  * <file name="index.html">
29022  * <div ng-controller="ExampleController">
29023  *   <form name="myForm">
29024  *     <label for="singleSelect"> Single select: </label><br>
29025  *     <select name="singleSelect" ng-model="data.singleSelect">
29026  *       <option value="option-1">Option 1</option>
29027  *       <option value="option-2">Option 2</option>
29028  *     </select><br>
29029  *
29030  *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
29031  *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
29032  *       <option value="">---Please select---</option> <!-- not selected / blank option -->
29033  *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
29034  *       <option value="option-2">Option 2</option>
29035  *     </select><br>
29036  *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
29037  *     <tt>singleSelect = {{data.singleSelect}}</tt>
29038  *
29039  *     <hr>
29040  *     <label for="multipleSelect"> Multiple select: </label><br>
29041  *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
29042  *       <option value="option-1">Option 1</option>
29043  *       <option value="option-2">Option 2</option>
29044  *       <option value="option-3">Option 3</option>
29045  *     </select><br>
29046  *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
29047  *   </form>
29048  * </div>
29049  * </file>
29050  * <file name="app.js">
29051  *  angular.module('staticSelect', [])
29052  *    .controller('ExampleController', ['$scope', function($scope) {
29053  *      $scope.data = {
29054  *       singleSelect: null,
29055  *       multipleSelect: [],
29056  *       option1: 'option-1',
29057  *      };
29058  *
29059  *      $scope.forceUnknownOption = function() {
29060  *        $scope.data.singleSelect = 'nonsense';
29061  *      };
29062  *   }]);
29063  * </file>
29064  *</example>
29065  *
29066  * ### Using `ngRepeat` to generate `select` options
29067  * <example name="ngrepeat-select" module="ngrepeatSelect">
29068  * <file name="index.html">
29069  * <div ng-controller="ExampleController">
29070  *   <form name="myForm">
29071  *     <label for="repeatSelect"> Repeat select: </label>
29072  *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
29073  *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
29074  *     </select>
29075  *   </form>
29076  *   <hr>
29077  *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
29078  * </div>
29079  * </file>
29080  * <file name="app.js">
29081  *  angular.module('ngrepeatSelect', [])
29082  *    .controller('ExampleController', ['$scope', function($scope) {
29083  *      $scope.data = {
29084  *       repeatSelect: null,
29085  *       availableOptions: [
29086  *         {id: '1', name: 'Option A'},
29087  *         {id: '2', name: 'Option B'},
29088  *         {id: '3', name: 'Option C'}
29089  *       ],
29090  *      };
29091  *   }]);
29092  * </file>
29093  *</example>
29094  *
29095  *
29096  * ### Using `select` with `ngOptions` and setting a default value
29097  * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
29098  *
29099  * <example name="select-with-default-values" module="defaultValueSelect">
29100  * <file name="index.html">
29101  * <div ng-controller="ExampleController">
29102  *   <form name="myForm">
29103  *     <label for="mySelect">Make a choice:</label>
29104  *     <select name="mySelect" id="mySelect"
29105  *       ng-options="option.name for option in data.availableOptions track by option.id"
29106  *       ng-model="data.selectedOption"></select>
29107  *   </form>
29108  *   <hr>
29109  *   <tt>option = {{data.selectedOption}}</tt><br/>
29110  * </div>
29111  * </file>
29112  * <file name="app.js">
29113  *  angular.module('defaultValueSelect', [])
29114  *    .controller('ExampleController', ['$scope', function($scope) {
29115  *      $scope.data = {
29116  *       availableOptions: [
29117  *         {id: '1', name: 'Option A'},
29118  *         {id: '2', name: 'Option B'},
29119  *         {id: '3', name: 'Option C'}
29120  *       ],
29121  *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
29122  *       };
29123  *   }]);
29124  * </file>
29125  *</example>
29126  *
29127  *
29128  * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
29129  *
29130  * <example name="select-with-non-string-options" module="nonStringSelect">
29131  *   <file name="index.html">
29132  *     <select ng-model="model.id" convert-to-number>
29133  *       <option value="0">Zero</option>
29134  *       <option value="1">One</option>
29135  *       <option value="2">Two</option>
29136  *     </select>
29137  *     {{ model }}
29138  *   </file>
29139  *   <file name="app.js">
29140  *     angular.module('nonStringSelect', [])
29141  *       .run(function($rootScope) {
29142  *         $rootScope.model = { id: 2 };
29143  *       })
29144  *       .directive('convertToNumber', function() {
29145  *         return {
29146  *           require: 'ngModel',
29147  *           link: function(scope, element, attrs, ngModel) {
29148  *             ngModel.$parsers.push(function(val) {
29149  *               return parseInt(val, 10);
29150  *             });
29151  *             ngModel.$formatters.push(function(val) {
29152  *               return '' + val;
29153  *             });
29154  *           }
29155  *         };
29156  *       });
29157  *   </file>
29158  *   <file name="protractor.js" type="protractor">
29159  *     it('should initialize to model', function() {
29160  *       var select = element(by.css('select'));
29161  *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
29162  *     });
29163  *   </file>
29164  * </example>
29165  *
29166  */
29167 var selectDirective = function() {
29168
29169   return {
29170     restrict: 'E',
29171     require: ['select', '?ngModel'],
29172     controller: SelectController,
29173     priority: 1,
29174     link: {
29175       pre: selectPreLink,
29176       post: selectPostLink
29177     }
29178   };
29179
29180   function selectPreLink(scope, element, attr, ctrls) {
29181
29182       // if ngModel is not defined, we don't need to do anything
29183       var ngModelCtrl = ctrls[1];
29184       if (!ngModelCtrl) return;
29185
29186       var selectCtrl = ctrls[0];
29187
29188       selectCtrl.ngModelCtrl = ngModelCtrl;
29189
29190       // When the selected item(s) changes we delegate getting the value of the select control
29191       // to the `readValue` method, which can be changed if the select can have multiple
29192       // selected values or if the options are being generated by `ngOptions`
29193       element.on('change', function() {
29194         scope.$apply(function() {
29195           ngModelCtrl.$setViewValue(selectCtrl.readValue());
29196         });
29197       });
29198
29199       // If the select allows multiple values then we need to modify how we read and write
29200       // values from and to the control; also what it means for the value to be empty and
29201       // we have to add an extra watch since ngModel doesn't work well with arrays - it
29202       // doesn't trigger rendering if only an item in the array changes.
29203       if (attr.multiple) {
29204
29205         // Read value now needs to check each option to see if it is selected
29206         selectCtrl.readValue = function readMultipleValue() {
29207           var array = [];
29208           forEach(element.find('option'), function(option) {
29209             if (option.selected) {
29210               array.push(option.value);
29211             }
29212           });
29213           return array;
29214         };
29215
29216         // Write value now needs to set the selected property of each matching option
29217         selectCtrl.writeValue = function writeMultipleValue(value) {
29218           var items = new HashMap(value);
29219           forEach(element.find('option'), function(option) {
29220             option.selected = isDefined(items.get(option.value));
29221           });
29222         };
29223
29224         // we have to do it on each watch since ngModel watches reference, but
29225         // we need to work of an array, so we need to see if anything was inserted/removed
29226         var lastView, lastViewRef = NaN;
29227         scope.$watch(function selectMultipleWatch() {
29228           if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
29229             lastView = shallowCopy(ngModelCtrl.$viewValue);
29230             ngModelCtrl.$render();
29231           }
29232           lastViewRef = ngModelCtrl.$viewValue;
29233         });
29234
29235         // If we are a multiple select then value is now a collection
29236         // so the meaning of $isEmpty changes
29237         ngModelCtrl.$isEmpty = function(value) {
29238           return !value || value.length === 0;
29239         };
29240
29241       }
29242     }
29243
29244     function selectPostLink(scope, element, attrs, ctrls) {
29245       // if ngModel is not defined, we don't need to do anything
29246       var ngModelCtrl = ctrls[1];
29247       if (!ngModelCtrl) return;
29248
29249       var selectCtrl = ctrls[0];
29250
29251       // We delegate rendering to the `writeValue` method, which can be changed
29252       // if the select can have multiple selected values or if the options are being
29253       // generated by `ngOptions`.
29254       // This must be done in the postLink fn to prevent $render to be called before
29255       // all nodes have been linked correctly.
29256       ngModelCtrl.$render = function() {
29257         selectCtrl.writeValue(ngModelCtrl.$viewValue);
29258       };
29259     }
29260 };
29261
29262
29263 // The option directive is purely designed to communicate the existence (or lack of)
29264 // of dynamically created (and destroyed) option elements to their containing select
29265 // directive via its controller.
29266 var optionDirective = ['$interpolate', function($interpolate) {
29267   return {
29268     restrict: 'E',
29269     priority: 100,
29270     compile: function(element, attr) {
29271       if (isDefined(attr.value)) {
29272         // If the value attribute is defined, check if it contains an interpolation
29273         var interpolateValueFn = $interpolate(attr.value, true);
29274       } else {
29275         // If the value attribute is not defined then we fall back to the
29276         // text content of the option element, which may be interpolated
29277         var interpolateTextFn = $interpolate(element.text(), true);
29278         if (!interpolateTextFn) {
29279           attr.$set('value', element.text());
29280         }
29281       }
29282
29283       return function(scope, element, attr) {
29284         // This is an optimization over using ^^ since we don't want to have to search
29285         // all the way to the root of the DOM for every single option element
29286         var selectCtrlName = '$selectController',
29287             parent = element.parent(),
29288             selectCtrl = parent.data(selectCtrlName) ||
29289               parent.parent().data(selectCtrlName); // in case we are in optgroup
29290
29291         if (selectCtrl) {
29292           selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
29293         }
29294       };
29295     }
29296   };
29297 }];
29298
29299 var styleDirective = valueFn({
29300   restrict: 'E',
29301   terminal: false
29302 });
29303
29304 /**
29305  * @ngdoc directive
29306  * @name ngRequired
29307  *
29308  * @description
29309  *
29310  * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29311  * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
29312  * applied to custom controls.
29313  *
29314  * The directive sets the `required` attribute on the element if the Angular expression inside
29315  * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
29316  * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
29317  * for more info.
29318  *
29319  * The validator will set the `required` error key to true if the `required` attribute is set and
29320  * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
29321  * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
29322  * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
29323  * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
29324  *
29325  * @example
29326  * <example name="ngRequiredDirective" module="ngRequiredExample">
29327  *   <file name="index.html">
29328  *     <script>
29329  *       angular.module('ngRequiredExample', [])
29330  *         .controller('ExampleController', ['$scope', function($scope) {
29331  *           $scope.required = true;
29332  *         }]);
29333  *     </script>
29334  *     <div ng-controller="ExampleController">
29335  *       <form name="form">
29336  *         <label for="required">Toggle required: </label>
29337  *         <input type="checkbox" ng-model="required" id="required" />
29338  *         <br>
29339  *         <label for="input">This input must be filled if `required` is true: </label>
29340  *         <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
29341  *         <hr>
29342  *         required error set? = <code>{{form.input.$error.required}}</code><br>
29343  *         model = <code>{{model}}</code>
29344  *       </form>
29345  *     </div>
29346  *   </file>
29347  *   <file name="protractor.js" type="protractor">
29348        var required = element(by.binding('form.input.$error.required'));
29349        var model = element(by.binding('model'));
29350        var input = element(by.id('input'));
29351
29352        it('should set the required error', function() {
29353          expect(required.getText()).toContain('true');
29354
29355          input.sendKeys('123');
29356          expect(required.getText()).not.toContain('true');
29357          expect(model.getText()).toContain('123');
29358        });
29359  *   </file>
29360  * </example>
29361  */
29362 var requiredDirective = function() {
29363   return {
29364     restrict: 'A',
29365     require: '?ngModel',
29366     link: function(scope, elm, attr, ctrl) {
29367       if (!ctrl) return;
29368       attr.required = true; // force truthy in case we are on non input element
29369
29370       ctrl.$validators.required = function(modelValue, viewValue) {
29371         return !attr.required || !ctrl.$isEmpty(viewValue);
29372       };
29373
29374       attr.$observe('required', function() {
29375         ctrl.$validate();
29376       });
29377     }
29378   };
29379 };
29380
29381 /**
29382  * @ngdoc directive
29383  * @name ngPattern
29384  *
29385  * @description
29386  *
29387  * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29388  * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
29389  *
29390  * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
29391  * does not match a RegExp which is obtained by evaluating the Angular expression given in the
29392  * `ngPattern` attribute value:
29393  * * If the expression evaluates to a RegExp object, then this is used directly.
29394  * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
29395  * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
29396  *
29397  * <div class="alert alert-info">
29398  * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
29399  * start at the index of the last search's match, thus not taking the whole input value into
29400  * account.
29401  * </div>
29402  *
29403  * <div class="alert alert-info">
29404  * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
29405  * differences:
29406  * <ol>
29407  *   <li>
29408  *     `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
29409  *     not available.
29410  *   </li>
29411  *   <li>
29412  *     The `ngPattern` attribute must be an expression, while the `pattern` value must be
29413  *     interpolated.
29414  *   </li>
29415  * </ol>
29416  * </div>
29417  *
29418  * @example
29419  * <example name="ngPatternDirective" module="ngPatternExample">
29420  *   <file name="index.html">
29421  *     <script>
29422  *       angular.module('ngPatternExample', [])
29423  *         .controller('ExampleController', ['$scope', function($scope) {
29424  *           $scope.regex = '\\d+';
29425  *         }]);
29426  *     </script>
29427  *     <div ng-controller="ExampleController">
29428  *       <form name="form">
29429  *         <label for="regex">Set a pattern (regex string): </label>
29430  *         <input type="text" ng-model="regex" id="regex" />
29431  *         <br>
29432  *         <label for="input">This input is restricted by the current pattern: </label>
29433  *         <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
29434  *         <hr>
29435  *         input valid? = <code>{{form.input.$valid}}</code><br>
29436  *         model = <code>{{model}}</code>
29437  *       </form>
29438  *     </div>
29439  *   </file>
29440  *   <file name="protractor.js" type="protractor">
29441        var model = element(by.binding('model'));
29442        var input = element(by.id('input'));
29443
29444        it('should validate the input with the default pattern', function() {
29445          input.sendKeys('aaa');
29446          expect(model.getText()).not.toContain('aaa');
29447
29448          input.clear().then(function() {
29449            input.sendKeys('123');
29450            expect(model.getText()).toContain('123');
29451          });
29452        });
29453  *   </file>
29454  * </example>
29455  */
29456 var patternDirective = function() {
29457   return {
29458     restrict: 'A',
29459     require: '?ngModel',
29460     link: function(scope, elm, attr, ctrl) {
29461       if (!ctrl) return;
29462
29463       var regexp, patternExp = attr.ngPattern || attr.pattern;
29464       attr.$observe('pattern', function(regex) {
29465         if (isString(regex) && regex.length > 0) {
29466           regex = new RegExp('^' + regex + '$');
29467         }
29468
29469         if (regex && !regex.test) {
29470           throw minErr('ngPattern')('noregexp',
29471             'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
29472             regex, startingTag(elm));
29473         }
29474
29475         regexp = regex || undefined;
29476         ctrl.$validate();
29477       });
29478
29479       ctrl.$validators.pattern = function(modelValue, viewValue) {
29480         // HTML5 pattern constraint validates the input value, so we validate the viewValue
29481         return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
29482       };
29483     }
29484   };
29485 };
29486
29487 /**
29488  * @ngdoc directive
29489  * @name ngMaxlength
29490  *
29491  * @description
29492  *
29493  * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29494  * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
29495  *
29496  * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
29497  * is longer than the integer obtained by evaluating the Angular expression given in the
29498  * `ngMaxlength` attribute value.
29499  *
29500  * <div class="alert alert-info">
29501  * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
29502  * differences:
29503  * <ol>
29504  *   <li>
29505  *     `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
29506  *     validation is not available.
29507  *   </li>
29508  *   <li>
29509  *     The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
29510  *     interpolated.
29511  *   </li>
29512  * </ol>
29513  * </div>
29514  *
29515  * @example
29516  * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
29517  *   <file name="index.html">
29518  *     <script>
29519  *       angular.module('ngMaxlengthExample', [])
29520  *         .controller('ExampleController', ['$scope', function($scope) {
29521  *           $scope.maxlength = 5;
29522  *         }]);
29523  *     </script>
29524  *     <div ng-controller="ExampleController">
29525  *       <form name="form">
29526  *         <label for="maxlength">Set a maxlength: </label>
29527  *         <input type="number" ng-model="maxlength" id="maxlength" />
29528  *         <br>
29529  *         <label for="input">This input is restricted by the current maxlength: </label>
29530  *         <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
29531  *         <hr>
29532  *         input valid? = <code>{{form.input.$valid}}</code><br>
29533  *         model = <code>{{model}}</code>
29534  *       </form>
29535  *     </div>
29536  *   </file>
29537  *   <file name="protractor.js" type="protractor">
29538        var model = element(by.binding('model'));
29539        var input = element(by.id('input'));
29540
29541        it('should validate the input with the default maxlength', function() {
29542          input.sendKeys('abcdef');
29543          expect(model.getText()).not.toContain('abcdef');
29544
29545          input.clear().then(function() {
29546            input.sendKeys('abcde');
29547            expect(model.getText()).toContain('abcde');
29548          });
29549        });
29550  *   </file>
29551  * </example>
29552  */
29553 var maxlengthDirective = function() {
29554   return {
29555     restrict: 'A',
29556     require: '?ngModel',
29557     link: function(scope, elm, attr, ctrl) {
29558       if (!ctrl) return;
29559
29560       var maxlength = -1;
29561       attr.$observe('maxlength', function(value) {
29562         var intVal = toInt(value);
29563         maxlength = isNaN(intVal) ? -1 : intVal;
29564         ctrl.$validate();
29565       });
29566       ctrl.$validators.maxlength = function(modelValue, viewValue) {
29567         return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
29568       };
29569     }
29570   };
29571 };
29572
29573 /**
29574  * @ngdoc directive
29575  * @name ngMinlength
29576  *
29577  * @description
29578  *
29579  * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29580  * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
29581  *
29582  * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
29583  * is shorter than the integer obtained by evaluating the Angular expression given in the
29584  * `ngMinlength` attribute value.
29585  *
29586  * <div class="alert alert-info">
29587  * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
29588  * differences:
29589  * <ol>
29590  *   <li>
29591  *     `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
29592  *     validation is not available.
29593  *   </li>
29594  *   <li>
29595  *     The `ngMinlength` value must be an expression, while the `minlength` value must be
29596  *     interpolated.
29597  *   </li>
29598  * </ol>
29599  * </div>
29600  *
29601  * @example
29602  * <example name="ngMinlengthDirective" module="ngMinlengthExample">
29603  *   <file name="index.html">
29604  *     <script>
29605  *       angular.module('ngMinlengthExample', [])
29606  *         .controller('ExampleController', ['$scope', function($scope) {
29607  *           $scope.minlength = 3;
29608  *         }]);
29609  *     </script>
29610  *     <div ng-controller="ExampleController">
29611  *       <form name="form">
29612  *         <label for="minlength">Set a minlength: </label>
29613  *         <input type="number" ng-model="minlength" id="minlength" />
29614  *         <br>
29615  *         <label for="input">This input is restricted by the current minlength: </label>
29616  *         <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
29617  *         <hr>
29618  *         input valid? = <code>{{form.input.$valid}}</code><br>
29619  *         model = <code>{{model}}</code>
29620  *       </form>
29621  *     </div>
29622  *   </file>
29623  *   <file name="protractor.js" type="protractor">
29624        var model = element(by.binding('model'));
29625        var input = element(by.id('input'));
29626
29627        it('should validate the input with the default minlength', function() {
29628          input.sendKeys('ab');
29629          expect(model.getText()).not.toContain('ab');
29630
29631          input.sendKeys('abc');
29632          expect(model.getText()).toContain('abc');
29633        });
29634  *   </file>
29635  * </example>
29636  */
29637 var minlengthDirective = function() {
29638   return {
29639     restrict: 'A',
29640     require: '?ngModel',
29641     link: function(scope, elm, attr, ctrl) {
29642       if (!ctrl) return;
29643
29644       var minlength = 0;
29645       attr.$observe('minlength', function(value) {
29646         minlength = toInt(value) || 0;
29647         ctrl.$validate();
29648       });
29649       ctrl.$validators.minlength = function(modelValue, viewValue) {
29650         return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
29651       };
29652     }
29653   };
29654 };
29655
29656 if (window.angular.bootstrap) {
29657   //AngularJS is already loaded, so we can return here...
29658   if (window.console) {
29659     console.log('WARNING: Tried to load angular more than once.');
29660   }
29661   return;
29662 }
29663
29664 //try to bind to jquery now so that one can write jqLite(document).ready()
29665 //but we will rebind on bootstrap again.
29666 bindJQuery();
29667
29668 publishExternalAPI(angular);
29669
29670 angular.module("ngLocale", [], ["$provide", function($provide) {
29671 var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
29672 function getDecimals(n) {
29673   n = n + '';
29674   var i = n.indexOf('.');
29675   return (i == -1) ? 0 : n.length - i - 1;
29676 }
29677
29678 function getVF(n, opt_precision) {
29679   var v = opt_precision;
29680
29681   if (undefined === v) {
29682     v = Math.min(getDecimals(n), 3);
29683   }
29684
29685   var base = Math.pow(10, v);
29686   var f = ((n * base) | 0) % base;
29687   return {v: v, f: f};
29688 }
29689
29690 $provide.value("$locale", {
29691   "DATETIME_FORMATS": {
29692     "AMPMS": [
29693       "AM",
29694       "PM"
29695     ],
29696     "DAY": [
29697       "Sunday",
29698       "Monday",
29699       "Tuesday",
29700       "Wednesday",
29701       "Thursday",
29702       "Friday",
29703       "Saturday"
29704     ],
29705     "ERANAMES": [
29706       "Before Christ",
29707       "Anno Domini"
29708     ],
29709     "ERAS": [
29710       "BC",
29711       "AD"
29712     ],
29713     "FIRSTDAYOFWEEK": 6,
29714     "MONTH": [
29715       "January",
29716       "February",
29717       "March",
29718       "April",
29719       "May",
29720       "June",
29721       "July",
29722       "August",
29723       "September",
29724       "October",
29725       "November",
29726       "December"
29727     ],
29728     "SHORTDAY": [
29729       "Sun",
29730       "Mon",
29731       "Tue",
29732       "Wed",
29733       "Thu",
29734       "Fri",
29735       "Sat"
29736     ],
29737     "SHORTMONTH": [
29738       "Jan",
29739       "Feb",
29740       "Mar",
29741       "Apr",
29742       "May",
29743       "Jun",
29744       "Jul",
29745       "Aug",
29746       "Sep",
29747       "Oct",
29748       "Nov",
29749       "Dec"
29750     ],
29751     "STANDALONEMONTH": [
29752       "January",
29753       "February",
29754       "March",
29755       "April",
29756       "May",
29757       "June",
29758       "July",
29759       "August",
29760       "September",
29761       "October",
29762       "November",
29763       "December"
29764     ],
29765     "WEEKENDRANGE": [
29766       5,
29767       6
29768     ],
29769     "fullDate": "EEEE, MMMM d, y",
29770     "longDate": "MMMM d, y",
29771     "medium": "MMM d, y h:mm:ss a",
29772     "mediumDate": "MMM d, y",
29773     "mediumTime": "h:mm:ss a",
29774     "short": "M/d/yy h:mm a",
29775     "shortDate": "M/d/yy",
29776     "shortTime": "h:mm a"
29777   },
29778   "NUMBER_FORMATS": {
29779     "CURRENCY_SYM": "$",
29780     "DECIMAL_SEP": ".",
29781     "GROUP_SEP": ",",
29782     "PATTERNS": [
29783       {
29784         "gSize": 3,
29785         "lgSize": 3,
29786         "maxFrac": 3,
29787         "minFrac": 0,
29788         "minInt": 1,
29789         "negPre": "-",
29790         "negSuf": "",
29791         "posPre": "",
29792         "posSuf": ""
29793       },
29794       {
29795         "gSize": 3,
29796         "lgSize": 3,
29797         "maxFrac": 2,
29798         "minFrac": 2,
29799         "minInt": 1,
29800         "negPre": "-\u00a4",
29801         "negSuf": "",
29802         "posPre": "\u00a4",
29803         "posSuf": ""
29804       }
29805     ]
29806   },
29807   "id": "en-us",
29808   "localeID": "en_US",
29809   "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;}
29810 });
29811 }]);
29812
29813   jqLite(document).ready(function() {
29814     angularInit(document, bootstrap);
29815   });
29816
29817 })(window, document);
29818
29819 !window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');