PageSourceSearch

https://www.niosa.org/js/angular/angular.js?sv=8.6.3

js niosa.org collected 2026-10-02 18:05:53 UTC 1,149,453 bytes, 30,282 lines download raw bytes

1/**
2 * @license AngularJS v1.5.0-rc.1
3 * (c) 2010-2016 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
38function 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.5.0-rc.1/' +
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
185var 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.
189var 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 */
201var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
202var 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 */
214var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
215
216
217var manualLowercase = function(s) {
218  /* jshint bitwise: false */
219  return isString(s)
220      ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCo
220deAt(0) | 32);})
221      : s;
222};
223var 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.
234if ('i' !== 'I'.toLowerCase()) {
235  lowercase = manualLowercase;
236  uppercase = manualUppercase;
237}
238
239
240var
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 */
260msie = 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 */
269function 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 existence 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
vendor: 4,301 bytes, lines 280-396
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
326function 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
370function 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 */
384function 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}
vendor: 9,078 bytes, lines 396-771
396 an unique alpha-numeric string
397 */
398function 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 */
408function setHashKey(obj, h) {
409  if (h) {
410    obj.$$hashKey = h;
411  } else {
412    delete obj.$$hashKey;
413  }
414}
415
416
417function 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 */
469function 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*/
492function merge(dst) {
493  return baseExtend(dst, slice.call(arguments, 1), true);
494}
495
496
497
498function toInt(str) {
499  return parseInt(str, 10);
500}
501
502
503function 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 */
523function noop() {}
524noop.$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 */
545function identity($) {return $;}
546identity.$inject = [];
547
548
549function valueFn(value) {return function() {return value;};}
550
551function 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 */
568function 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 */
583function 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 */
599function 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 */
610function 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 */
627function 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 */
648function 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 */
663function 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 */
680var 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 */
694function 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 */
704function 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 */
716function isWindow(obj) {
717  return obj && obj.window === obj;
718}
719
720
721function isScope(obj) {
722  return obj && obj.$evalAsync && obj.$watch;
723}
724
725
726function isFile(obj) {
727  return toString.call(obj) === '[object File]';
728}
729
730
731function isFormData(obj) {
732  return toString.call(obj) === '[object FormData]';
733}
734
735
736function isBlob(obj) {
737  return toString.call(obj) === '[object Blob]';
738}
739
740
741function isBoolean(value) {
742  return typeof value === 'boolean';
743}
744
745
746function isPromiseLike(obj) {
747  return obj && isFunction(obj.then);
748}
749
750
751var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
752function isTypedArray(value) {
753  return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
754}
755
756function isArrayBuffer(obj) {
757  return toString.call(obj) === '[object ArrayBuffer]';
758}
759
760
761var trim = function(value) {
762  return isString(value) ? value.trim() : value;
763};
764
765// Copied from:
766// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
767// Prereq: s is a string.
768var escapeForRegexp = function(s) {
769  return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
770           replace(/\x08/g, '\\x08');
771};
772
773
774/**
775 * @ngdoc function
776 * @name angular.isElement
777 * @module ng
778 * @kind function
779 *
780 * @description
781 * Determines if a reference is a DOM element (or wrapped jQuery element).
782 *
783 * @param {*} value Reference to check.
784 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
785 */
786function isElement(node) {
787  return !!(node &&
788    (node.nodeName  // we are a direct element
789    || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
790}
791
792/**
793 * @param str 'key1,key2,...'
794 * @returns {object} in the form of {key1:true, key2:true, ...}
795 */
796function makeMap(str) {
797  var obj = {}, items = str.split(","), i;
798  for (i = 0; i < items.length; i++) {
799    obj[items[i]] = true;
800  }
801  return obj;
802}
803
804
805function nodeName_(element) {
806  return lowercase(element.nodeName || (element[0] && element[0].nodeName));
807}
808
809function includes(array, obj) {
810  return Array.prototype.indexOf.call(array, obj) != -1;
811}
812
813function arrayRemove(array, value) {
814  var index = array.indexOf(value);
815  if (index >= 0) {
816    array.splice(index, 1);
817  }
818  return index;
819}
820
821/**
822 * @ngdoc function
823 * @name angular.copy
824 * @module ng
825 * @kind function
826 *
827 * @description
828 * Creates a deep copy of `source`, which should be an object or an array.
829 *
830 * * If no destination is supplied, a copy of the object or array is created.
831 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
832 *   are deleted and then all elements/properties from the source are copied to it.
833 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
834 * * If `source` is identical to 'destination' an exception will be thrown.
835 *
836 * @param {*} source The source that will be used to make a copy.
837 *                   Can be any type, including primitives, `null`, and `undefined`.
838 * @param {(Object|Array)=} destination Destination into which the source is copied. If
839 *     provided, must be of the same type as `source`.
840 * @returns {*} The copy or updated `destination`, if `destination` was specified.
841 *
842 * @example
843 <example module="copyExample">
844 <file name="index.html">
845 <div ng-controller="ExampleController">
846 <form novalidate class="simple-form">
847 Name: <input type="text" ng-model="user.name" /><br />
848 E-mail: <input type="email" ng-model="user.email" /><br />
849 Gender: <input type="radio" ng-model="user.gender" value="male" />male
850 <input type="radio" ng-model="user.gender" value="female" />female<br />
851 <button ng-click="reset()">RESET</button>
852 <button ng-click="update(user)">SAVE</button>
853 </form>
854 <pre>form = {{user | json}}</pre>
855 <pre>master = {{master | json}}</pre>
856 </div>
857
858 <script>
859  angular.module('copyExample', [])
860    .controller('ExampleController', ['$scope', function($scope) {
861      $scope.master= {};
862
863      $scope.update = function(user) {
864        // Example with 1 argument
865        $scope.master= angular.copy(user);
866      };
867
868      $scope.reset = function() {
869        // Example with 2 arguments
870        angular.copy($scope.master, $scope.user);
871      };
872
873      $scope.reset();
874    }]);
875 </script>
876 </file>
877 </example>
878 */
879function copy(source, destination) {
880  var stackSource = [];
881  var stackDest = [];
882
883  if (destination) {
884    if (isTypedArray(destination) || isArrayBuffer(destination)) {
885      throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
886    }
887    if (source === destination) {
888      throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
889    }
890
891    // Empty the destination object
892    if (isArray(destination)) {
893      destination.length = 0;
894    } else {
895      forEach(destination, function(value, key) {
896        if (key !== '$$hashKey') {
897          delete destination[key];
898        }
899      });
900    }
901
902    stackSource.push(source);
903    stackDest.push(destination);
904    return copyRecurse(source, destination);
905  }
906
907  return copyElement(source);
908
909  function copyRecurse(source, destination) {
910    var h = destination.$$hashKey;
911    var result, key;
912    if (isArray(source)) {
913      for (var i = 0, ii = source.length; i < ii; i++) {
914        destination.push(copyElement(source[i]));
915      }
916    } else if (isBlankObject(source)) {
917      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
918      for (key in source) {
919        destination[key] = copyElement(source[key]);
920      }
921    } else if (source && typeof source.hasOwnProperty === 'function') {
922      // Slow path, which must rely on hasOwnProperty
923      for (key in source) {
924        if (source.hasOwnProperty(key)) {
925          destination[key] = copyElement(source[key]);
926        }
927      }
928    } else {
929      // Slowest path --- hasOwnProperty can't be called as a method
930      for (key in source) {
931        if (hasOwnProperty.call(source, key)) {
932          destination[key] = copyElement(source[key]);
933        }
934      }
935    }
936    setHashKey(destination, h);
937    return destination;
938  }
939
940  function copyElement(source) {
941    // Simple values
942    if (!isObject(source)) {
943      return source;
944    }
945
946    // Already copied values
947    var index = stackSource.indexOf(source);
948    if (index !== -1) {
949      return stackDest[index];
950    }
951
952    if (isWindow(source) || isScope(source)) {
953      throw ngMinErr('cpws',
954        "Can't copy! Making copies of Window or Scope instances is not supported.");
955    }
956
957    var needsRecurse = false;
958    var destination = copyType(source);
959
960    if (destination === undefined) {
961      destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
962      needsRecurse = true;
963    }
964
965    stackSource.push(source);
966    stackDest.push(destination);
967
968    return needsRecurse
969      ? copyRecurse(source, destination)
970      : destination;
971  }
972
973  function copyType(source) {
974    switch (toString.call(source)) {
975      case '[object Int8Array]':
976      case '[object Int16Array]':
977      case '[object Int32Array]':
978      case '[object Float32Array]':
979      case '[object Float64Array]':
980      case '[object Uint8Array]':
981      case '[object Uint8ClampedArray]':
982      case '[object Uint16Array]':
983      case '[object Uint32Array]':
984        return new source.constructor(copyElement(source.buffer));
985
986      case '[object ArrayBuffer]':
987        //Support: IE10
988        if (!source.slice) {
989          var copied = new ArrayBuffer(source.byteLength);
990          new Uint8Array(copied).set(new Uint8Array(source));
991          return copied;
992        }
993        return source.slice(0);
994
995      case '[object Boolean]':
996      case '[object Number]':
997      case '[object String]':
998      case '[object Date]':
999        return new source.constructor(source.valueOf());
1000
1001      case '[object RegExp]':
1002        var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
1003        re.lastIndex = source.lastIndex;
1004        return re;
1005    }
1006
1007    if (isFunction(source.cloneNode)) {
1008      return source.cloneNode(true);
1009    }
1010  }
1011}
1012
1013/**
1014 * Creates a shallow copy of an object, an array or a primitive.
1015 *
1016 * Assumes that there are no proto properties for objects.
1017 */
1018function shallowCopy(src, dst) {
1019  if (isArray(src)) {
1020    dst = dst || [];
1021
1022    for (var i = 0, ii = src.length; i < ii; i++) {
1023      dst[i] = src[i];
vendor: 14,750 bytes, lines 1024-1491
1024    }
1025  } else if (isObject(src)) {
1026    dst = dst || {};
1027
1028    for (var key in src) {
1029      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1030        dst[key] = src[key];
1031      }
1032    }
1033  }
1034
1035  return dst || src;
1036}
1037
1038
1039/**
1040 * @ngdoc function
1041 * @name angular.equals
1042 * @module ng
1043 * @kind function
1044 *
1045 * @description
1046 * Determines if two objects or two values are equivalent. Supports value types, regular
1047 * expressions, arrays and objects.
1048 *
1049 * Two objects or values are considered equivalent if at least one of the following is true:
1050 *
1051 * * Both objects or values pass `===` comparison.
1052 * * Both objects or values are of the same type and all of their properties are equal by
1053 *   comparing them with `angular.equals`.
1054 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1055 * * Both values represent the same regular expression (In JavaScript,
1056 *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1057 *   representation matches).
1058 *
1059 * During a property comparison, properties of `function` type and properties with names
1060 * that begin with `$` are ignored.
1061 *
1062 * Scope and DOMWindow objects are being compared only by identify (`===`).
1063 *
1064 * @param {*} o1 Object or value to compare.
1065 * @param {*} o2 Object or value to compare.
1066 * @returns {boolean} True if arguments are equal.
1067 */
1068function equals(o1, o2) {
1069  if (o1 === o2) return true;
1070  if (o1 === null || o2 === null) return false;
1071  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1072  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1073  if (t1 == t2) {
1074    if (t1 == 'object') {
1075      if (isArray(o1)) {
1076        if (!isArray(o2)) return false;
1077        if ((length = o1.length) == o2.length) {
1078          for (key = 0; key < length; key++) {
1079            if (!equals(o1[key], o2[key])) return false;
1080          }
1081          return true;
1082        }
1083      } else if (isDate(o1)) {
1084        if (!isDate(o2)) return false;
1085        return equals(o1.getTime(), o2.getTime());
1086      } else if (isRegExp(o1)) {
1087        return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1088      } else {
1089        if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1090          isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1091        keySet = createMap();
1092        for (key in o1) {
1093          if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1094          if (!equals(o1[key], o2[key])) return false;
1095          keySet[key] = true;
1096        }
1097        for (key in o2) {
1098          if (!(key in keySet) &&
1099              key.charAt(0) !== '$' &&
1100              isDefined(o2[key]) &&
1101              !isFunction(o2[key])) return false;
1102        }
1103        return true;
1104      }
1105    }
1106  }
1107  return false;
1108}
1109
1110var csp = function() {
1111  if (!isDefined(csp.rules)) {
1112
1113
1114    var ngCspElement = (document.querySelector('[ng-csp]') ||
1115                    document.querySelector('[data-ng-csp]'));
1116
1117    if (ngCspElement) {
1118      var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1119                    ngCspElement.getAttribute('data-ng-csp');
1120      csp.rules = {
1121        noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1122        noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1123      };
1124    } else {
1125      csp.rules = {
1126        noUnsafeEval: noUnsafeEval(),
1127        noInlineStyle: false
1128      };
1129    }
1130  }
1131
1132  return csp.rules;
1133
1134  function noUnsafeEval() {
1135    try {
1136      /* jshint -W031, -W054 */
1137      new Function('');
1138      /* jshint +W031, +W054 */
1139      return false;
1140    } catch (e) {
1141      return true;
1142    }
1143  }
1144};
1145
1146/**
1147 * @ngdoc directive
1148 * @module ng
1149 * @name ngJq
1150 *
1151 * @element ANY
1152 * @param {string=} ngJq the name of the library available under `window`
1153 * to be used for angular.element
1154 * @description
1155 * Use this directive to force the angular.element library.  This should be
1156 * used to force either jqLite by leaving ng-jq blank or setting the name of
1157 * the jquery variable under window (eg. jQuery).
1158 *
1159 * Since angular looks for this directive when it is loaded (doesn't wait for the
1160 * DOMContentLoaded event), it must be placed on an element that comes before the script
1161 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1162 * others ignored.
1163 *
1164 * @example
1165 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1166 ```html
1167 <!doctype html>
1168 <html ng-app ng-jq>
1169 ...
1170 ...
1171 </html>
1172 ```
1173 * @example
1174 * This example shows how to use a jQuery based library of a different name.
1175 * The library name must be available at the top most 'window'.
1176 ```html
1177 <!doctype html>
1178 <html ng-app ng-jq="jQueryLib">
1179 ...
1180 ...
1181 </html>
1182 ```
1183 */
1184var jq = function() {
1185  if (isDefined(jq.name_)) return jq.name_;
1186  var el;
1187  var i, ii = ngAttrPrefixes.length, prefix, name;
1188  for (i = 0; i < ii; ++i) {
1189    prefix = ngAttrPrefixes[i];
1190    if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1191      name = el.getAttribute(prefix + 'jq');
1192      break;
1193    }
1194  }
1195
1196  return (jq.name_ = name);
1197};
1198
1199function concat(array1, array2, index) {
1200  return array1.concat(slice.call(array2, index));
1201}
1202
1203function sliceArgs(args, startIndex) {
1204  return slice.call(args, startIndex || 0);
1205}
1206
1207
1208/* jshint -W101 */
1209/**
1210 * @ngdoc function
1211 * @name angular.bind
1212 * @module ng
1213 * @kind function
1214 *
1215 * @description
1216 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1217 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1218 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1219 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1220 *
1221 * @param {Object} self Context which `fn` should be evaluated in.
1222 * @param {function()} fn Function to be bound.
1223 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1224 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1225 */
1226/* jshint +W101 */
1227function bind(self, fn) {
1228  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1229  if (isFunction(fn) && !(fn instanceof RegExp)) {
1230    return curryArgs.length
1231      ? function() {
1232          return arguments.length
1233            ? fn.apply(self, concat(curryArgs, arguments, 0))
1234            : fn.apply(self, curryArgs);
1235        }
1236      : function() {
1237          return arguments.length
1238            ? fn.apply(self, arguments)
1239            : fn.call(self);
1240        };
1241  } else {
1242    // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1243    return fn;
1244  }
1245}
1246
1247
1248function toJsonReplacer(key, value) {
1249  var val = value;
1250
1251  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1252    val = undefined;
1253  } else if (isWindow(value)) {
1254    val = '$WINDOW';
1255  } else if (value &&  document === value) {
1256    val = '$DOCUMENT';
1257  } else if (isScope(value)) {
1258    val = '$SCOPE';
1259  }
1260
1261  return val;
1262}
1263
1264
1265/**
1266 * @ngdoc function
1267 * @name angular.toJson
1268 * @module ng
1269 * @kind function
1270 *
1271 * @description
1272 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1273 * stripped since angular uses this notation internally.
1274 *
1275 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1276 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1277 *    If set to an integer, the JSON output will contain that many spaces per indentation.
1278 * @returns {string|undefined} JSON-ified string representing `obj`.
1279 */
1280function toJson(obj, pretty) {
1281  if (typeof obj === 'undefined') return undefined;
1282  if (!isNumber(pretty)) {
1283    pretty = pretty ? 2 : null;
1284  }
1285  return JSON.stringify(obj, toJsonReplacer, pretty);
1286}
1287
1288
1289/**
1290 * @ngdoc function
1291 * @name angular.fromJson
1292 * @module ng
1293 * @kind function
1294 *
1295 * @description
1296 * Deserializes a JSON string.
1297 *
1298 * @param {string} json JSON string to deserialize.
1299 * @returns {Object|Array|string|number} Deserialized JSON string.
1300 */
1301function fromJson(json) {
1302  return isString(json)
1303      ? JSON.parse(json)
1304      : json;
1305}
1306
1307
1308function timezoneToOffset(timezone, fallback) {
1309  var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1310  return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1311}
1312
1313
1314function addDateMinutes(date, minutes) {
1315  date = new Date(date.getTime());
1316  date.setMinutes(date.getMinutes() + minutes);
1317  return date;
1318}
1319
1320
1321function convertTimezoneToLocal(date, timezone, reverse) {
1322  reverse = reverse ? -1 : 1;
1323  var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1324  return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1325}
1326
1327
1328/**
1329 * @returns {string} Returns the string representation of the element.
1330 */
1331function startingTag(element) {
1332  element = jqLite(element).clone();
1333  try {
1334    // turns out IE does not let you set .html() on elements which
1335    // are not allowed to have children. So we just ignore it.
1336    element.empty();
1337  } catch (e) {}
1338  var elemHtml = jqLite('<div>').append(element).html();
1339  try {
1340    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1341        elemHtml.
1342          match(/^(<[^>]+>)/)[1].
1343          replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1344  } catch (e) {
1345    return lowercase(elemHtml);
1346  }
1347
1348}
1349
1350
1351/////////////////////////////////////////////////
1352
1353/**
1354 * Tries to decode the URI component without throwing an exception.
1355 *
1356 * @private
1357 * @param str value potential URI component to check.
1358 * @returns {boolean} True if `value` can be decoded
1359 * with the decodeURIComponent function.
1360 */
1361function tryDecodeURIComponent(value) {
1362  try {
1363    return decodeURIComponent(value);
1364  } catch (e) {
1365    // Ignore any invalid uri component
1366  }
1367}
1368
1369
1370/**
1371 * Parses an escaped url query string into key-value pairs.
1372 * @returns {Object.<string,boolean|Array>}
1373 */
1374function parseKeyValue(/**string*/keyValue) {
1375  var obj = {};
1376  forEach((keyValue || "").split('&'), function(keyValue) {
1377    var splitPoint, key, val;
1378    if (keyValue) {
1379      key = keyValue = keyValue.replace(/\+/g,'%20');
1380      splitPoint = keyValue.indexOf('=');
1381      if (splitPoint !== -1) {
1382        key = keyValue.substring(0, splitPoint);
1383        val = keyValue.substring(splitPoint + 1);
1384      }
1385      key = tryDecodeURIComponent(key);
1386      if (isDefined(key)) {
1387        val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1388        if (!hasOwnProperty.call(obj, key)) {
1389          obj[key] = val;
1390        } else if (isArray(obj[key])) {
1391          obj[key].push(val);
1392        } else {
1393          obj[key] = [obj[key],val];
1394        }
1395      }
1396    }
1397  });
1398  return obj;
1399}
1400
1401function toKeyValue(obj) {
1402  var parts = [];
1403  forEach(obj, function(value, key) {
1404    if (isArray(value)) {
1405      forEach(value, function(arrayValue) {
1406        parts.push(encodeUriQuery(key, true) +
1407                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1408      });
1409    } else {
1410    parts.push(encodeUriQuery(key, true) +
1411               (value === true ? '' : '=' + encodeUriQuery(value, true)));
1412    }
1413  });
1414  return parts.length ? parts.join('&') : '';
1415}
1416
1417
1418/**
1419 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1420 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1421 * segments:
1422 *    segment       = *pchar
1423 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1424 *    pct-encoded   = "%" HEXDIG HEXDIG
1425 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1426 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1427 *                     / "*" / "+" / "," / ";" / "="
1428 */
1429function encodeUriSegment(val) {
1430  return encodeUriQuery(val, true).
1431             replace(/%26/gi, '&').
1432             replace(/%3D/gi, '=').
1433             replace(/%2B/gi, '+');
1434}
1435
1436
1437/**
1438 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1439 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1440 * encoded per http://tools.ietf.org/html/rfc3986:
1441 *    query       = *( pchar / "/" / "?" )
1442 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1443 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1444 *    pct-encoded   = "%" HEXDIG HEXDIG
1445 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1446 *                     / "*" / "+" / "," / ";" / "="
1447 */
1448function encodeUriQuery(val, pctEncodeSpaces) {
1449  return encodeURIComponent(val).
1450             replace(/%40/gi, '@').
1451             replace(/%3A/gi, ':').
1452             replace(/%24/g, '$').
1453             replace(/%2C/gi, ',').
1454             replace(/%3B/gi, ';').
1455             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1456}
1457
1458var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1459
1460function getNgAttribute(element, ngAttr) {
1461  var attr, i, ii = ngAttrPrefixes.length;
1462  for (i = 0; i < ii; ++i) {
1463    attr = ngAttrPrefixes[i] + ngAttr;
1464    if (isString(attr = element.getAttribute(attr))) {
1465      return attr;
1466    }
1467  }
1468  return null;
1469}
1470
1471/**
1472 * @ngdoc directive
1473 * @name ngApp
1474 * @module ng
1475 *
1476 * @element ANY
1477 * @param {angular.Module} ngApp an optional application
1478 *   {@link angular.module module} name to load.
1479 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1480 *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1481 *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1482 *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1483 *   tracking down the root of these bugs.
1484 *
1485 * @description
1486 *
1487 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1488 * designates the **root element** of the application and is typically placed near the root element
1489 * of the page - e.g. on the `<body>` or `<html>` tags.
1490 *
1491 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngAp
1491p`
1492 * found in the document will be used to define the root element to auto-bootstrap as an
1493 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1494 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1495 *
1496 * You can specify an **AngularJS module** to be used as the root module for the application.  This
1497 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1498 * should contain the application code needed or have dependencies on other modules that will
1499 * contain the code. See {@link angular.module} for more information.
1500 *
1501 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1502 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1503 * would not be resolved to `3`.
1504 *
1505 * `ngApp` is the easiest, and most common way to bootstrap an application.
1506 *
1507 <example module="ngAppDemo">
1508   <file name="index.html">
1509   <div ng-controller="ngAppDemoController">
1510     I can add: {{a}} + {{b}} =  {{ a+b }}
1511   </div>
1512   </file>
1513   <file name="script.js">
1514   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1515     $scope.a = 1;
1516     $scope.b = 2;
1517   });
1518   </file>
1519 </example>
1520 *
1521 * Using `ngStrictDi`, you would see something like this:
1522 *
1523 <example ng-app-included="true">
1524   <file name="index.html">
1525   <div ng-app="ngAppStrictDemo" ng-strict-di>
1526       <div ng-controller="GoodController1">
1527           I can add: {{a}} + {{b}} =  {{ a+b }}
1528
1529           <p>This renders because the controller does not fail to
1530              instantiate, by using explicit annotation style (see
1531              script.js for details)
1532           </p>
1533       </div>
1534
1535       <div ng-controller="GoodController2">
1536           Name: <input ng-model="name"><br />
1537           Hello, {{name}}!
1538
1539           <p>This renders because the controller does not fail to
1540              instantiate, by using explicit annotation style
1541              (see script.js for details)
1542           </p>
1543       </div>
1544
1545       <div ng-controller="BadController">
1546           I can add: {{a}} + {{b}} =  {{ a+b }}
1547
1548           <p>The controller could not be instantiated, due to relying
1549              on automatic function annotations (which are disabled in
1550              strict mode). As such, the content of this section is not
1551              interpolated, and there should be an error in your web console.
1552           </p>
1553       </div>
1554   </div>
1555   </file>
1556   <file name="script.js">
1557   angular.module('ngAppStrictDemo', [])
1558     // BadController will fail to instantiate, due to relying on automatic function annotation,
1559     // rather than an explicit annotation
1560     .controller('BadController', function($scope) {
1561       $scope.a = 1;
1562       $scope.b = 2;
1563     })
1564     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1565     // due to using explicit annotations using the array style and $inject property, respectively.
1566     .controller('GoodController1', ['$scope', function($scope) {
1567       $scope.a = 1;
1568       $scope.b = 2;
1569     }])
1570     .controller('GoodController2', GoodController2);
1571     function GoodController2($scope) {
1572       $scope.name = "World";
1573     }
1574     GoodController2.$inject = ['$scope'];
1575   </file>
1576   <file name="style.css">
1577   div[ng-controller] {
1578       margin-bottom: 1em;
1579       -webkit-border-radius: 4px;
1580       border-radius: 4px;
1581       border: 1px solid;
1582       padding: .5em;
1583   }
1584   div[ng-controller^=Good] {
1585       border-color: #d6e9c6;
1586       background-color: #dff0d8;
1587       color: #3c763d;
1588   }
1589   div[ng-controller^=Bad] {
1590       border-color: #ebccd1;
1591       background-color: #f2dede;
1592       color: #a94442;
1593       margin-bottom: 0;
1594   }
1595   </file>
1596 </example>
1597 */
1598function angularInit(element, bootstrap) {
1599  var appElement,
1600      module,
1601      config = {};
1602
1603  // The element `element` has priority over any other element
1604  forEach(ngAttrPrefixes, function(prefix) {
1605    var name = prefix + 'app';
1606
1607    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1608      appElement = element;
1609      module = element.getAttribute(name);
1610    }
1611  });
1612  forEach(ngAttrPrefixes, function(prefix) {
1613    var name = prefix + 'app';
1614    var candidate;
1615
1616    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1617      appElement = candidate;
1618      module = candidate.getAttribute(name);
1619    }
1620  });
1621  if (appElement) {
1622    config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1623    bootstrap(appElement, module ? [module] : [], config);
1624  }
1625}
1626
1627/**
1628 * @ngdoc function
1629 * @name angular.bootstrap
1630 * @module ng
1631 * @description
1632 * Use this function to manually start up angular application.
1633 *
1634 * See: {@link guide/bootstrap Bootstrap}
1635 *
1636 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1637 * They must use {@link ng.directive:ngApp ngApp}.
1638 *
1639 * Angular will detect if it has been loaded into the browser more than once and only allow the
1640 * first loaded script to be bootstrapped and will report a warning to the browser console for
1641 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1642 * multiple instances of Angular try to work on the DOM.
1643 *
1644 * ```html
1645 * <!doctype html>
1646 * <html>
1647 * <body>
1648 * <div ng-controller="WelcomeController">
1649 *   {{greeting}}
1650 * </div>
1651 *
1652 * <script src="angular.js"></script>
1653 * <script>
1654 *   var app = angular.module('demo', [])
1655 *   .controller('WelcomeController', function($scope) {
1656 *       $scope.greeting = 'Welcome!';
1657 *   });
1658 *   angular.bootstrap(document, ['demo']);
1659 * </script>
1660 * </body>
1661 * </html>
1662 * ```
1663 *
1664 * @param {DOMElement} element DOM element which is the root of angular application.
1665 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1666 *     Each item in the array should be the name of a predefined module or a (DI annotated)
1667 *     function that will be invoked by the injector as a `config` block.
1668 *     See: {@link angular.module modules}
1669 * @param {Object=} config an object for defining configuration options for the application. The
1670 *     following keys are supported:
1671 *
1672 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1673 *   assist in finding bugs which break minified code. Defaults to `false`.
1674 *
1675 * @returns {auto.$injector} Returns the newly created injector for this app.
1676 */
1677function bootstrap(element, modules, config) {
1678  if (!isObject(config)) config = {};
1679  var defaultConfig = {
1680    strictDi: false
1681  };
1682  config = extend(defaultConfig, config);
1683  var doBootstrap = function() {
1684    element = jqLite(element);
1685
1686    if (element.injector()) {
1687      var tag = (element[0] === document) ? 'document' : startingTag(element);
1688      //Encode angle brackets to prevent input from being sanitized to empty string #8683
1689      throw ngMinErr(
1690          'btstrpd',
1691          "App Already Bootstrapped with this Element '{0}'",
1692          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1693    }
1694
1695    modules = modules || [];
1696    modules.unshift(['$provide', function($provide) {
1697      $provide.value('$rootElement', element);
1698    }]);
1699
1700    if (config.debugInfoEnabled) {
1701      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1702      modules.push(['$compileProvider', function($compileProvider) {
1703        $compileProvider.debugInfoEnabled(true);
1704      }]);
1705    }
1706
1707    modules.unshift('ng');
1708    var injector = createInjector(modules, config.strictDi);
1709    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1710       function bootstrapApply(scope, element, compile, injector) {
1711        scope.$apply(function() {
1712          element.data('$injector', injector);
1713          compile(element)(scope);
1714        });
1715      }]
1716    );
1717    return injector;
1718  };
1719
1720  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1721  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1722
1723  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1724    config.debugInfoEnabled = true;
1725    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1726  }
1727
1728  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1729    return doBootstrap();
1730  }
1731
1732  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1733  angular.resumeBootstrap = function(extraModules) {
1734    forEach(extraModules, function(module) {
1735      modules.push(module);
1736    });
1737    return doBootstrap();
1738  };
1739
1740  if (isFunction(angular.resumeDeferredBootstrap)) {
1741    angular.resumeDeferredBootstrap();
1742  }
1743}
1744
1745/**
1746 * @ngdoc function
1747 * @name angular.reloadWithDebugInfo
1748 * @module ng
1749 * @description
1750 * Use this function to reload the current application with debug information turned on.
1751 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1752 *
1753 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1754 */
1755function reloadWithDebugInfo() {
1756  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1757  window.location.reload();
1758}
1759
1760/**
1761 * @name angular.getTestability
1762 * @module ng
1763 * @description
1764 * Get the testability service for the instance of Angular on the given
1765 * element.
1766 * @param {DOMElement} element DOM element which is the root of angular application.
1767 */
1768function getTestability(rootElement) {
1769  var injector = angular.element(rootElement).injector();
1770  if (!injector) {
1771    throw ngMinErr('test',
1772      'no injector found for element argument to getTestability');
1773  }
1774  return injector.get('$$testability');
1775}
1776
1777var SNAKE_CASE_REGEXP = /[A-Z]/g;
1778function snake_case(name, separator) {
1779  separator = separator || '_';
1780  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1781    return (pos ? separator : '') + letter.toLowerCase();
1782  });
1783}
1784
1785var bindJQueryFired = false;
1786function bindJQuery() {
1787  var originalCleanData;
1788
1789  if (bindJQueryFired) {
1790    return;
1791  }
1792
1793  // bind to jQuery if present;
1794  var jqName = jq();
1795  jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
1796           !jqName             ? undefined     :   // use jqLite
1797                                 window[jqName];   // use jQuery specified by `ngJq`
1798
1799  // Use jQuery if it exists with proper functionality, otherwise default to us.
1800  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1801  // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1802  // versions. It will not work for sure with jQuery <1.7, though.
1803  if (jQuery && jQuery.fn.on) {
1804    jqLite = jQuery;
1805    extend(jQuery.fn, {
1806      scope: JQLitePrototype.scope,
1807      isolateScope: JQLitePrototype.isolateScope,
1808      controller: JQLitePrototype.controller,
1809      injector: JQLitePrototype.injector,
1810      inheritedData: JQLitePrototype.inheritedData
1811    });
vendor: 4,465 bytes, lines 1812-1971
1812
1813    // All nodes removed from the DOM via various jQuery APIs like .remove()
1814    // are passed through jQuery.cleanData. Monkey-patch this method to fire
1815    // the $destroy event on all removed nodes.
1816    originalCleanData = jQuery.cleanData;
1817    jQuery.cleanData = function(elems) {
1818      var events;
1819      for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1820        events = jQuery._data(elem, "events");
1821        if (events && events.$destroy) {
1822          jQuery(elem).triggerHandler('$destroy');
1823        }
1824      }
1825      originalCleanData(elems);
1826    };
1827  } else {
1828    jqLite = JQLite;
1829  }
1830
1831  angular.element = jqLite;
1832
1833  // Prevent double-proxying.
1834  bindJQueryFired = true;
1835}
1836
1837/**
1838 * throw error if the argument is falsy.
1839 */
1840function assertArg(arg, name, reason) {
1841  if (!arg) {
1842    throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1843  }
1844  return arg;
1845}
1846
1847function assertArgFn(arg, name, acceptArrayAnnotation) {
1848  if (acceptArrayAnnotation && isArray(arg)) {
1849      arg = arg[arg.length - 1];
1850  }
1851
1852  assertArg(isFunction(arg), name, 'not a function, got ' +
1853      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1854  return arg;
1855}
1856
1857/**
1858 * throw error if the name given is hasOwnProperty
1859 * @param  {String} name    the name to test
1860 * @param  {String} context the context in which the name is used, such as module or directive
1861 */
1862function assertNotHasOwnProperty(name, context) {
1863  if (name === 'hasOwnProperty') {
1864    throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1865  }
1866}
1867
1868/**
1869 * Return the value accessible from the object by path. Any undefined traversals are ignored
1870 * @param {Object} obj starting object
1871 * @param {String} path path to traverse
1872 * @param {boolean} [bindFnToScope=true]
1873 * @returns {Object} value as accessible by path
1874 */
1875//TODO(misko): this function needs to be removed
1876function getter(obj, path, bindFnToScope) {
1877  if (!path) return obj;
1878  var keys = path.split('.');
1879  var key;
1880  var lastInstance = obj;
1881  var len = keys.length;
1882
1883  for (var i = 0; i < len; i++) {
1884    key = keys[i];
1885    if (obj) {
1886      obj = (lastInstance = obj)[key];
1887    }
1888  }
1889  if (!bindFnToScope && isFunction(obj)) {
1890    return bind(lastInstance, obj);
1891  }
1892  return obj;
1893}
1894
1895/**
1896 * Return the DOM siblings between the first and last node in the given array.
1897 * @param {Array} array like object
1898 * @returns {Array} the inputted object or a jqLite collection containing the nodes
1899 */
1900function getBlockNodes(nodes) {
1901  // TODO(perf): update `nodes` instead of creating a new object?
1902  var node = nodes[0];
1903  var endNode = nodes[nodes.length - 1];
1904  var blockNodes;
1905
1906  for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1907    if (blockNodes || nodes[i] !== node) {
1908      if (!blockNodes) {
1909        blockNodes = jqLite(slice.call(nodes, 0, i));
1910      }
1911      blockNodes.push(node);
1912    }
1913  }
1914
1915  return blockNodes || nodes;
1916}
1917
1918
1919/**
1920 * Creates a new object without a prototype. This object is useful for lookup without having to
1921 * guard against prototypically inherited properties via hasOwnProperty.
1922 *
1923 * Related micro-benchmarks:
1924 * - http://jsperf.com/object-create2
1925 * - http://jsperf.com/proto-map-lookup/2
1926 * - http://jsperf.com/for-in-vs-object-keys2
1927 *
1928 * @returns {Object}
1929 */
1930function createMap() {
1931  return Object.create(null);
1932}
1933
1934var NODE_TYPE_ELEMENT = 1;
1935var NODE_TYPE_ATTRIBUTE = 2;
1936var NODE_TYPE_TEXT = 3;
1937var NODE_TYPE_COMMENT = 8;
1938var NODE_TYPE_DOCUMENT = 9;
1939var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1940
1941/**
1942 * @ngdoc type
1943 * @name angular.Module
1944 * @module ng
1945 * @description
1946 *
1947 * Interface for configuring angular {@link angular.module modules}.
1948 */
1949
1950function setupModuleLoader(window) {
1951
1952  var $injectorMinErr = minErr('$injector');
1953  var ngMinErr = minErr('ng');
1954
1955  function ensure(obj, name, factory) {
1956    return obj[name] || (obj[name] = factory());
1957  }
1958
1959  var angular = ensure(window, 'angular', Object);
1960
1961  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1962  angular.$$minErr = angular.$$minErr || minErr;
1963
1964  return ensure(angular, 'module', function() {
1965    /** @type {Object.<string, angular.Module>} */
1966    var modules = {};
1967
1968    /**
1969     * @ngdoc function
1970     * @name angular.module
1971     * @module ng
1972     * @description
1973     *
1974     * The `angular.module` is a global place for creating, registering and retrieving Angular
1975     * modules.
1976     * All modules (angular core or 3rd party) that should be available to an application must be
1977     * registered using this mechanism.
1978     *
1979     * Passing one argument retrieves an existing {@link angular.Module},
1980     * whereas passing more than one argument creates a new {@link angular.Module}
1981     *
1982     *
1983     * # Module
1984     *
1985     * A module is a collection of services, directives, controllers, filters, and configuration information.
1986     * `angular.module` is used to configure the {@link auto.$injector $injector}.
1987     *
1988     * ```js
1989     * // Create a new module
1990     * var myModule = angular.module('myModule', []);
1991     *
1992     * // register a new service
1993     * myModule.value('appName', 'MyCoolApp');
1994     *
1995     * // configure existing services inside initialization blocks.
1996     * myModule.config(['$locationProvider', function($locationProvider) {
1997     *   // Configure existing providers
1998     *   $locationProvider.hashPrefix('!');
1999     * }]);
2000     * ```
2001     *
2002     * Then you can create an injector and load your modules like this:
2003     *
2004     * ```js
2005     * var injector = angular.injector(['ng', 'myModule'])
2006     * ```
2007     *
2008     * However it's more likely that you'll just use
2009     * {@link ng.directive:ngApp ngApp} or
2010     * {@link angular.bootstrap} to simplify this process for you.
2011     *
2012     * @param {!string} name The name of the module to create or retrieve.
2013     * @param {!Array.<string>=} requires If specified then new module is being created. If
2014     *        unspecified then the module is being retrieved for further configuration.
2015     * @param {Function=} configFn Optional configuration function for the module. Same as
2016     *        {@link angular.Module#config Module#config()}.
2017     * @returns {angular.Module} new module with the {@link angular.Module} api.
2018     */
2019    return function module(name, requires, configFn) {
2020      var assertNotHasOwnProperty = function(name, context) {
2021        if (name === 'hasOwnProperty') {
2022          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2023        }
2024      };
2025
2026      assertNotHasOwnProperty(name, 'module');
2027      if (requires && modules.hasOwnProperty(name)) {
2028        modules[name] = null;
2029      }
2030      return ensure(modules, name, function() {
2031        if (!requires) {
2032          throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2033             "the module name or forgot to load it. If registering a module ensure that you " +
2034             "specify the dependencies as the second argument.", name);
2035        }
2036
2037        /** @type {!Array.<Array.<*>>} */
2038        var invokeQueue = [];
2039
2040        /** @type {!Array.<Function>} */
2041        var configBlocks = [];
2042
2043        /** @type {!Array.<Function>} */
2044        var runBlocks = [];
2045
2046        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2047
2048        /** @type {angular.Module} */
2049        var moduleInstance = {
2050          // Private state
2051          _invokeQueue: invokeQueue,
2052          _configBlocks: configBlocks,
2053          _runBlocks: runBlocks,
2054
2055          /**
2056           * @ngdoc property
2057           * @name angular.Module#requires
2058           * @module ng
2059           *
2060           * @description
2061           * Holds the list of modules which the injector will load before the current module is
2062           * loaded.
2063           */
2064          requires: requires,
2065
2066          /**
2067           * @ngdoc property
2068           * @name angular.Module#name
2069           * @module ng
2070           *
2071           * @description
2072           * Name of the module.
2073           */
2074          name: name,
2075
2076
2077          /**
2078           * @ngdoc method
2079           * @name angular.Module#provider
2080           * @module ng
2081           * @param {string} name service name
2082           * @param {Function} providerType Construction function for creating new instance of the
2083           *                                service.
2084           * @description
2085           * See {@link auto.$provide#provider $provide.provider()}.
2086           */
2087          provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2088
2089          /**
2090           * @ngdoc method
2091           * @name angular.Module#factory
2092           * @module ng
2093           * @param {string} name service name
2094           * @param {Function} providerFunction Function for creating new instance of the service.
2095           * @description
2096           * See {@link auto.$provide#factory $provide.factory()}.
2097           */
2098          factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2099
2100          /**
2101           * @ngdoc method
2102           * @name angular.Module#service
2103           * @module ng
2104           * @param {string} name service name
2105           * @param {Function} constructor A constructor function that will be instantiated.
2106           * @description
2107           * See {@link auto.$provide#service $provide.service()}.
2108           */
2109          service: invokeLaterAndSetModuleName('$provide', 'service'),
2110
2111          /**
2112           * @ngdoc method
2113           * @name angular.Module#value
2114           * @module ng
2115           * @param {string} name service name
2116           * @param {*} object Service instance object.
2117           * @description
2118           * See {@link auto.$provide#value $provide.value()}.
2119           */
2120          value: invokeLater('$provide', 'value'),
2121
2122          /**
2123           * @ngdoc method
2124           * @name angular.Module#constant
2125           * @module ng
2126           * @param {string} name constant name
2127           * @param {*} object Constant value.
2128           * @description
2129           * Because the constants are fixed, they get applied before other provide methods.
2130           * See {@link auto.$provide#constant $provide.constant()}.
2131           */
2132          constant: invokeLater('$provide', 'constant', 'unshift'),
2133
2134           /**
2135           * @ngdoc method
2136           * @name angular.Module#decorator
2137           * @module ng
2138           * @param {string} The name of the service to decorate.
2139           * @param {Function} This function will be invoked when the service needs to be
2140           *                                    instantiated and should return the decorated service instance.
2141           * @description
2142           * See {@link auto.$provide#decorator $provide.decorator()}.
2143           */
2144          decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2145
2146          /**
2147           * @ngdoc method
2148           * @name angular.Module#animation
2149           * @module ng
2150           * @param {string} name animation name
2151           * @param {Function} animationFactory Factory function for creating new instance of an
2152           *                                    animation.
2153           * @description
2154           *
2155           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2156           *
2157           *
2158           * Defines an animation hook that can be later used with
2159           * {@link $animate $animate} service and directives that use this service.
2160           *
2161           * ```js
2162           * module.animation('.animation-name', function($inject1, $inject2) {
2163           *   return {
2164           *     eventName : function(element, done) {
2165           *       //code to run the animation
2166           *       //once complete, then run done()
2167           *       return function cancellationFunction(element) {
2168           *         //code to cancel the animation
2169           *       }
2170           *     }
2171           *   }
2172           * })
2173           * ```
2174           *
2175           * See {@link ng.$animateProvider#register $animateProvider.register()} and
2176           * {@link ngAnimate ngAnimate module} for more information.
2177           */
2178          animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2179
2180          /**
2181           * @ngdoc method
2182           * @name angular.Module#filter
2183           * @module ng
2184           * @param {string} name Filter name - this must be a valid angular expression identifier
2185           * @param {Function} filterFactory Factory function for creating new instance of filter.
2186           * @description
2187           * See {@link ng.$filterProvider#register $filterProvider.register()}.
2188           *
2189           * <div class="alert alert-warning">
2190           * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2191           * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2192           * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2193           * (`myapp_subsection_filterx`).
2194           * </div>
2195           */
2196          filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2197
2198          /**
2199           * @ngdoc method
2200           * @name angular.Module#controller
2201           * @module ng
2202           * @param {string|Object} name Controller name, or an object map of controllers where the
2203           *    keys are the names and the values are the constructors.
2204           * @param {Function} constructor Controller constructor function.
2205           * @description
2206           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2207           */
2208          controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2209
2210          /**
2211           * @ngdoc method
2212           * @name angular.Module#directive
2213           * @module ng
2214           * @param {string|Object} name Directive name, or an object map of directives where the
2215           *    keys are the names and the values are the factories.
2216           * @param {Function} directiveFactory Factory function for creating new instance of
2217           * directives.
2218           * @description
2219           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2220           */
2221          directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2222
2223          /**
2224           * @ngdoc method
2225           * @name angular.Module#component
2226           * @module ng
2227           * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
2228           * @param {Object} options Component definition object (a simplified
2229           *    {@link ng.$compile#directive-definition-object directive definition object})
2230           *
2231           * @description
2232           * See {@link ng.$compileProvider#component $compileProvider.component()}.
2233           */
2234          component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
2235
2236          /**
2237           * @ngdoc method
2238           * @name angular.Module#config
2239           * @module ng
2240           * @param {Function} configFn Execute this function on module load. Useful for service
2241           *    configuration.
2242           * @description
2243           * Use this method to register work which needs to be performed on module loading.
2244           * For more about how to configure services, see
2245           * {@link providers#provider-recipe Provider Recipe}.
2246           */
2247          config: config,
2248
2249          /**
2250           * @ngdoc method
2251           * @name angular.Module#run
2252           * @module ng
2253           * @param {Function} initializationFn Execute this function after injector creation.
2254           *    Useful for application initialization.
2255           * @description
2256           * Use this method to register work which should be performed when the injector is done
2257           * loading all modules.
2258           */
2259          run: function(block) {
2260            runBlocks.push(block);
2261            return this;
2262          }
2263        };
2264
2265        if (configFn) {
2266          config(configFn);
2267        }
2268
2269        return moduleInstance;
2270
2271        /**
2272         * @param {string} provider
2273         * @param {string} method
2274         * @param {String=} insertMethod
2275         * @returns {angular.Module}
2276         */
2277        function invokeLater(provider, method, insertMethod, queue) {
2278          if (!queue) queue = invokeQueue;
2279          return function() {
2280            queue[insertMethod || 'push']([provider, method, arguments]);
2281            return moduleInstance;
2282          };
2283        }
2284
2285        /**
2286         * @param {string} provider
2287         * @param {string} method
2288         * @returns {angular.Module}
2289         */
vendor: 4,918 bytes, lines 2290-2489
2290        function invokeLaterAndSetModuleName(provider, method) {
2291          return function(recipeName, factoryFunction) {
2292            if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2293            invokeQueue.push([provider, method, arguments]);
2294            return moduleInstance;
2295          };
2296        }
2297      });
2298    };
2299  });
2300
2301}
2302
2303/* global: toDebugString: true */
2304
2305function serializeObject(obj) {
2306  var seen = [];
2307
2308  return JSON.stringify(obj, function(key, val) {
2309    val = toJsonReplacer(key, val);
2310    if (isObject(val)) {
2311
2312      if (seen.indexOf(val) >= 0) return '...';
2313
2314      seen.push(val);
2315    }
2316    return val;
2317  });
2318}
2319
2320function toDebugString(obj) {
2321  if (typeof obj === 'function') {
2322    return obj.toString().replace(/ \{[\s\S]*$/, '');
2323  } else if (isUndefined(obj)) {
2324    return 'undefined';
2325  } else if (typeof obj !== 'string') {
2326    return serializeObject(obj);
2327  }
2328  return obj;
2329}
2330
2331/* global angularModule: true,
2332  version: true,
2333
2334  $CompileProvider,
2335
2336  htmlAnchorDirective,
2337  inputDirective,
2338  inputDirective,
2339  formDirective,
2340  scriptDirective,
2341  selectDirective,
2342  styleDirective,
2343  optionDirective,
2344  ngBindDirective,
2345  ngBindHtmlDirective,
2346  ngBindTemplateDirective,
2347  ngClassDirective,
2348  ngClassEvenDirective,
2349  ngClassOddDirective,
2350  ngCloakDirective,
2351  ngControllerDirective,
2352  ngFormDirective,
2353  ngHideDirective,
2354  ngIfDirective,
2355  ngIncludeDirective,
2356  ngIncludeFillContentDirective,
2357  ngInitDirective,
2358  ngNonBindableDirective,
2359  ngPluralizeDirective,
2360  ngRepeatDirective,
2361  ngShowDirective,
2362  ngStyleDirective,
2363  ngSwitchDirective,
2364  ngSwitchWhenDirective,
2365  ngSwitchDefaultDirective,
2366  ngOptionsDirective,
2367  ngTranscludeDirective,
2368  ngModelDirective,
2369  ngListDirective,
2370  ngChangeDirective,
2371  patternDirective,
2372  patternDirective,
2373  requiredDirective,
2374  requiredDirective,
2375  minlengthDirective,
2376  minlengthDirective,
2377  maxlengthDirective,
2378  maxlengthDirective,
2379  ngValueDirective,
2380  ngModelOptionsDirective,
2381  ngAttributeAliasDirectives,
2382  ngEventDirectives,
2383
2384  $AnchorScrollProvider,
2385  $AnimateProvider,
2386  $CoreAnimateCssProvider,
2387  $$CoreAnimateJsProvider,
2388  $$CoreAnimateQueueProvider,
2389  $$AnimateRunnerFactoryProvider,
2390  $$AnimateAsyncRunFactoryProvider,
2391  $BrowserProvider,
2392  $CacheFactoryProvider,
2393  $ControllerProvider,
2394  $DateProvider,
2395  $DocumentProvider,
2396  $ExceptionHandlerProvider,
2397  $FilterProvider,
2398  $$ForceReflowProvider,
2399  $InterpolateProvider,
2400  $IntervalProvider,
2401  $$HashMapProvider,
2402  $HttpProvider,
2403  $HttpParamSerializerProvider,
2404  $HttpParamSerializerJQLikeProvider,
2405  $HttpBackendProvider,
2406  $xhrFactoryProvider,
2407  $LocationProvider,
2408  $LogProvider,
2409  $ParseProvider,
2410  $RootScopeProvider,
2411  $QProvider,
2412  $$QProvider,
2413  $$SanitizeUriProvider,
2414  $SceProvider,
2415  $SceDelegateProvider,
2416  $SnifferProvider,
2417  $TemplateCacheProvider,
2418  $TemplateRequestProvider,
2419  $$TestabilityProvider,
2420  $TimeoutProvider,
2421  $$RAFProvider,
2422  $WindowProvider,
2423  $$jqLiteProvider,
2424  $$CookieReaderProvider
2425*/
2426
2427
2428/**
2429 * @ngdoc object
2430 * @name angular.version
2431 * @module ng
2432 * @description
2433 * An object that contains information about the current AngularJS version.
2434 *
2435 * This object has the following properties:
2436 *
2437 * - `full` – `{string}` – Full version string, such as "0.9.18".
2438 * - `major` – `{number}` – Major version number, such as "0".
2439 * - `minor` – `{number}` – Minor version number, such as "9".
2440 * - `dot` – `{number}` – Dot version number, such as "18".
2441 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2442 */
2443var version = {
2444  full: '1.5.0-rc.1',    // all of these placeholder strings will be replaced by grunt's
2445  major: 1,    // package task
2446  minor: 5,
2447  dot: 0,
2448  codeName: 'quantum-fermentation'
2449};
2450
2451
2452function publishExternalAPI(angular) {
2453  extend(angular, {
2454    'bootstrap': bootstrap,
2455    'copy': copy,
2456    'extend': extend,
2457    'merge': merge,
2458    'equals': equals,
2459    'element': jqLite,
2460    'forEach': forEach,
2461    'injector': createInjector,
2462    'noop': noop,
2463    'bind': bind,
2464    'toJson': toJson,
2465    'fromJson': fromJson,
2466    'identity': identity,
2467    'isUndefined': isUndefined,
2468    'isDefined': isDefined,
2469    'isString': isString,
2470    'isFunction': isFunction,
2471    'isObject': isObject,
2472    'isNumber': isNumber,
2473    'isElement': isElement,
2474    'isArray': isArray,
2475    'version': version,
2476    'isDate': isDate,
2477    'lowercase': lowercase,
2478    'uppercase': uppercase,
2479    'callbacks': {counter: 0},
2480    'getTestability': getTestability,
2481    '$$minErr': minErr,
2482    '$$csp': csp,
2483    'reloadWithDebugInfo': reloadWithDebugInfo
2484  });
2485
2486  angularModule = setupModuleLoader(window);
2487
2488  angularModule('ng', ['ngLocale'], ['$provide',
2489    function ngModule($provide) {
2490      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2491      $provide.provider({
2492        $$sanitizeUri: $$SanitizeUriProvider
2493      });
2494      $provide.provider('$compile', $CompileProvider).
2495        directive({
2496            a: htmlAnchorDirective,
2497            input: inputDirective,
2498            textarea: inputDirective,
2499            form: formDirective,
2500            script: scriptDirective,
2501            select: selectDirective,
2502            style: styleDirective,
2503            option: optionDirective,
2504            ngBind: ngBindDirective,
2505            ngBindHtml: ngBindHtmlDirective,
2506            ngBindTemplate: ngBindTemplateDirective,
2507            ngClass: ngClassDirective,
2508            ngClassEven: ngClassEvenDirective,
2509            ngClassOdd: ngClassOddDirective,
2510            ngCloak: ngCloakDirective,
2511            ngController: ngControllerDirective,
2512            ngForm: ngFormDirective,
2513            ngHide: ngHideDirective,
2514            ngIf: ngIfDirective,
2515            ngInclude: ngIncludeDirective,
2516            ngInit: ngInitDirective,
2517            ngNonBindable: ngNonBindableDirective,
2518            ngPluralize: ngPluralizeDirective,
2519            ngRepeat: ngRepeatDirective,
2520            ngShow: ngShowDirective,
2521            ngStyle: ngStyleDirective,
2522            ngSwitch: ngSwitchDirective,
2523            ngSwitchWhen: ngSwitchWhenDirective,
2524            ngSwitchDefault: ngSwitchDefaultDirective,
2525            ngOptions: ngOptionsDirective,
2526            ngTransclude: ngTranscludeDirective,
2527            ngModel: ngModelDirective,
2528            ngList: ngListDirective,
2529            ngChange: ngChangeDirective,
2530            pattern: patternDirective,
2531            ngPattern: patternDirective,
2532            required: requiredDirective,
2533            ngRequired: requiredDirective,
2534            minlength: minlengthDirective,
2535            ngMinlength: minlengthDirective,
2536            maxlength: maxlengthDirective,
2537            ngMaxlength: maxlengthDirective,
2538            ngValue: ngValueDirective,
2539            ngModelOptions: ngModelOptionsDirective
2540        }).
2541        directive({
2542          ngInclude: ngIncludeFillContentDirective
2543        }).
2544        directive(ngAttributeAliasDirectives).
2545        directive(ngEventDirectives);
2546      $provide.provider({
2547        $anchorScroll: $AnchorScrollProvider,
2548        $animate: $AnimateProvider,
2549        $animateCss: $CoreAnimateCssProvider,
2550        $$animateJs: $$CoreAnimateJsProvider,
2551        $$animateQueue: $$CoreAnimateQueueProvider,
2552        $$AnimateRunner: $$AnimateRunnerFactoryProvider,
2553        $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
2554        $browser: $BrowserProvider,
2555        $cacheFactory: $CacheFactoryProvider,
2556        $controller: $ControllerProvider,
2557        $document: $DocumentProvider,
2558        $exceptionHandler: $ExceptionHandlerProvider,
2559        $filter: $FilterProvider,
2560        $$forceReflow: $$ForceReflowProvider,
2561        $interpolate: $InterpolateProvider,
2562        $interval: $IntervalProvider,
2563        $http: $HttpProvider,
2564        $httpParamSerializer: $HttpParamSerializerProvider,
2565        $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2566        $httpBackend: $HttpBackendProvider,
2567        $xhrFactory: $xhrFactoryProvider,
2568        $location: $LocationProvider,
2569        $log: $LogProvider,
2570        $parse: $ParseProvider,
2571        $rootScope: $RootScopeProvider,
2572        $q: $QProvider,
2573        $$q: $$QProvider,
2574        $sce: $SceProvider,
2575        $sceDelegate: $SceDelegateProvider,
2576        $sniffer: $SnifferProvider,
2577        $templateCache: $TemplateCacheProvider,
2578        $templateRequest: $TemplateRequestProvider,
2579        $$testability: $$TestabilityProvider,
2580        $timeout: $TimeoutProvider,
2581        $window: $WindowProvider,
2582        $$rAF: $$RAFProvider,
2583        $$jqLite: $$jqLiteProvider,
2584        $$HashMap: $$HashMapProvider,
2585        $$cookieReader: $$CookieReaderProvider
2586      });
2587    }
2588  ]);
2589}
2590
2591/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2592 *     Any commits to this file should be reviewed with security in mind.  *
2593 *   Changes to this file can potentially create security vulnerabilities. *
2594 *          An approval from 2 Core members with history of modifying      *
2595 *                         this file is required.                          *
2596 *                                                                         *
2597 *  Does the change somehow allow for arbitrary javascript to be executed? *
2598 *    Or allows for someone to change the prototype of built-in objects?   *
2599 *     Or gives undesired access to variables likes document or window?    *
2600 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2601
2602/* global JQLitePrototype: true,
2603  addEventListenerFn: true,
2604  removeEventListenerFn: true,
2605  BOOLEAN_ATTR: true,
2606  ALIASED_ATTR: true,
2607*/
2608
2609//////////////////////////////////
2610//JQLite
2611//////////////////////////////////
2612
2613/**
2614 * @ngdoc function
2615 * @name angular.element
2616 * @module ng
2617 * @kind function
2618 *
2619 * @description
2620 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2621 *
2622 * If jQuery is available, `angular.element` is an alias for the
2623 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2624 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
2625 *
2626 * jqLite is a tiny, API-compatible subset of jQuery that allows
2627 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
2628 * commonly needed functionality with the goal of having a very small footprint.
2629 *
2630 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
2631 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
2632 * specific version of jQuery if multiple versions exist on the page.
2633 *
2634 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with 
2634jQuery or
2635 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
2636 *
2637 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
2638 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
2639 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
2640 *
2641 * ## Angular's jqLite
2642 * jqLite provides only the following jQuery methods:
2643 *
2644 * - [`addClass()`](http://api.jquery.com/addClass/)
2645 * - [`after()`](http://api.jquery.com/after/)
2646 * - [`append()`](http://api.jquery.com/append/)
2647 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2648 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2649 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2650 * - [`clone()`](http://api.jquery.com/clone/)
2651 * - [`contents()`](http://api.jquery.com/contents/)
2652 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
2653 *   As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
2654 * - [`data()`](http://api.jquery.com/data/)
2655 * - [`detach()`](http://api.jquery.com/detach/)
2656 * - [`empty()`](http://api.jquery.com/empty/)
2657 * - [`eq()`](http://api.jquery.com/eq/)
2658 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2659 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2660 * - [`html()`](http://api.jquery.com/html/)
2661 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2662 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2663 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2664 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2665 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2666 * - [`prepend()`](http://api.jquery.com/prepend/)
2667 * - [`prop()`](http://api.jquery.com/prop/)
2668 * - [`ready()`](http://api.jquery.com/ready/)
2669 * - [`remove()`](http://api.jquery.com/remove/)
2670 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2671 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2672 * - [`removeData()`](http://api.jquery.com/removeData/)
2673 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2674 * - [`text()`](http://api.jquery.com/text/)
2675 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2676 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2677 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2678 * - [`val()`](http://api.jquery.com/val/)
2679 * - [`wrap()`](http://api.jquery.com/wrap/)
2680 *
2681 * ## jQuery/jqLite Extras
2682 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2683 *
2684 * ### Events
2685 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2686 *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2687 *    element before it is removed.
2688 *
2689 * ### Methods
2690 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2691 *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2692 *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2693 *   `'ngModel'`).
2694 * - `injector()` - retrieves the injector of the current element or its parent.
2695 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2696 *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2697 *   be enabled.
2698 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2699 *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2700 *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2701 *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2702 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2703 *   parent element is reached.
2704 *
2705 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2706 * @returns {Object} jQuery object.
2707 */
2708
2709JQLite.expando = 'ng339';
2710
2711var jqCache = JQLite.cache = {},
2712    jqId = 1,
2713    addEventListenerFn = function(element, type, fn) {
2714      element.addEventListener(type, fn, false);
2715    },
2716    removeEventListenerFn = function(element, type, fn) {
2717      element.removeEventListener(type, fn, false);
2718    };
2719
2720/*
2721 * !!! This is an undocumented "private" function !!!
2722 */
2723JQLite._data = function(node) {
2724  //jQuery always returns an object on cache miss
2725  return this.cache[node[this.expando]] || {};
2726};
2727
2728function jqNextId() { return ++jqId; }
2729
2730
2731var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2732var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2733var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2734var jqLiteMinErr = minErr('jqLite');
2735
2736/**
2737 * Converts snake_case to camelCase.
2738 * Also there is special case for Moz prefix starting with upper case letter.
2739 * @param name Name to normalize
2740 */
2741function camelCase(name) {
2742  return name.
2743    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2744      return offset ? letter.toUpperCase() : letter;
2745    }).
2746    replace(MOZ_HACK_REGEXP, 'Moz$1');
2747}
2748
2749var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2750var HTML_REGEXP = /<|&#?\w+;/;
2751var TAG_NAME_REGEXP = /<([\w:-]+)/;
2752var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2753
2754var wrapMap = {
2755  'option': [1, '<select multiple="multiple">', '</select>'],
2756
2757  'thead': [1, '<table>', '</table>'],
2758  'col': [2, '<table><colgroup>', '</colgroup></table>'],
2759  'tr': [2, '<table><tbody>', '</tbody></table>'],
2760  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2761  '_default': [0, "", ""]
2762};
2763
2764wrapMap.optgroup = wrapMap.option;
2765wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2766wrapMap.th = wrapMap.td;
2767
2768
2769function jqLiteIsTextNode(html) {
2770  return !HTML_REGEXP.test(html);
2771}
2772
2773function jqLiteAcceptsData(node) {
2774  // The window object can accept data but has no nodeType
2775  // Otherwise we are only interested in elements (1) and documents (9)
2776  var nodeType = node.nodeType;
2777  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2778}
2779
2780function jqLiteHasData(node) {
2781  for (var key in jqCache[node.ng339]) {
2782    return true;
2783  }
2784  return false;
2785}
2786
2787function jqLiteCleanData(nodes) {
2788  for (var i = 0, ii = nodes.length; i < ii; i++) {
2789    jqLiteRemoveData(nodes[i]);
2790  }
2791}
2792
2793function jqLiteBuildFragment(html, context) {
2794  var tmp, tag, wrap,
2795      fragment = context.createDocumentFragment(),
2796      nodes = [], i;
2797
2798  if (jqLiteIsTextNode(html)) {
2799    // Convert non-html into a text node
2800    nodes.push(context.createTextNode(html));
2801  } else {
2802    // Convert html into DOM nodes
2803    tmp = tmp || fragment.appendChild(context.createElement("div"));
2804    tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2805    wrap = wrapMap[tag] || wrapMap._default;
2806    tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2807
2808    // Descend through wrappers to the right content
2809    i = wrap[0];
2810    while (i--) {
2811      tmp = tmp.lastChild;
2812    }
2813
2814    nodes = concat(nodes, tmp.childNodes);
2815
2816    tmp = fragment.firstChild;
2817    tmp.textContent = "";
2818  }
2819
2820  // Remove wrapper from fragment
2821  fragment.textContent = "";
2822  fragment.innerHTML = ""; // Clear inner HTML
2823  forEach(nodes, function(node) {
2824    fragment.appendChild(node);
2825  });
2826
2827  return fragment;
2828}
2829
2830function jqLiteParseHTML(html, context) {
2831  context = context || document;
2832  var parsed;
2833
2834  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2835    return [context.createElement(parsed[1])];
2836  }
2837
2838  if ((parsed = jqLiteBuildFragment(html, context))) {
2839    return parsed.childNodes;
2840  }
2841
2842  return [];
2843}
2844
2845
2846// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2847var jqLiteContains = Node.prototype.contains || function(arg) {
2848  // jshint bitwise: false
2849  return !!(this.compareDocumentPosition(arg) & 16);
2850  // jshint bitwise: true
2851};
2852
vendor: 23,307 bytes, lines 2853-3624
2853/////////////////////////////////////////////
2854function JQLite(element) {
2855  if (element instanceof JQLite) {
2856    return element;
2857  }
2858
2859  var argIsString;
2860
2861  if (isString(element)) {
2862    element = trim(element);
2863    argIsString = true;
2864  }
2865  if (!(this instanceof JQLite)) {
2866    if (argIsString && element.charAt(0) != '<') {
2867      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2868    }
2869    return new JQLite(element);
2870  }
2871
2872  if (argIsString) {
2873    jqLiteAddNodes(this, jqLiteParseHTML(element));
2874  } else {
2875    jqLiteAddNodes(this, element);
2876  }
2877}
2878
2879function jqLiteClone(element) {
2880  return element.cloneNode(true);
2881}
2882
2883function jqLiteDealoc(element, onlyDescendants) {
2884  if (!onlyDescendants) jqLiteRemoveData(element);
2885
2886  if (element.querySelectorAll) {
2887    var descendants = element.querySelectorAll('*');
2888    for (var i = 0, l = descendants.length; i < l; i++) {
2889      jqLiteRemoveData(descendants[i]);
2890    }
2891  }
2892}
2893
2894function jqLiteOff(element, type, fn, unsupported) {
2895  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2896
2897  var expandoStore = jqLiteExpandoStore(element);
2898  var events = expandoStore && expandoStore.events;
2899  var handle = expandoStore && expandoStore.handle;
2900
2901  if (!handle) return; //no listeners registered
2902
2903  if (!type) {
2904    for (type in events) {
2905      if (type !== '$destroy') {
2906        removeEventListenerFn(element, type, handle);
2907      }
2908      delete events[type];
2909    }
2910  } else {
2911
2912    var removeHandler = function(type) {
2913      var listenerFns = events[type];
2914      if (isDefined(fn)) {
2915        arrayRemove(listenerFns || [], fn);
2916      }
2917      if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2918        removeEventListenerFn(element, type, handle);
2919        delete events[type];
2920      }
2921    };
2922
2923    forEach(type.split(' '), function(type) {
2924      removeHandler(type);
2925      if (MOUSE_EVENT_MAP[type]) {
2926        removeHandler(MOUSE_EVENT_MAP[type]);
2927      }
2928    });
2929  }
2930}
2931
2932function jqLiteRemoveData(element, name) {
2933  var expandoId = element.ng339;
2934  var expandoStore = expandoId && jqCache[expandoId];
2935
2936  if (expandoStore) {
2937    if (name) {
2938      delete expandoStore.data[name];
2939      return;
2940    }
2941
2942    if (expandoStore.handle) {
2943      if (expandoStore.events.$destroy) {
2944        expandoStore.handle({}, '$destroy');
2945      }
2946      jqLiteOff(element);
2947    }
2948    delete jqCache[expandoId];
2949    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2950  }
2951}
2952
2953
2954function jqLiteExpandoStore(element, createIfNecessary) {
2955  var expandoId = element.ng339,
2956      expandoStore = expandoId && jqCache[expandoId];
2957
2958  if (createIfNecessary && !expandoStore) {
2959    element.ng339 = expandoId = jqNextId();
2960    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2961  }
2962
2963  return expandoStore;
2964}
2965
2966
2967function jqLiteData(element, key, value) {
2968  if (jqLiteAcceptsData(element)) {
2969
2970    var isSimpleSetter = isDefined(value);
2971    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2972    var massGetter = !key;
2973    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2974    var data = expandoStore && expandoStore.data;
2975
2976    if (isSimpleSetter) { // data('key', value)
2977      data[key] = value;
2978    } else {
2979      if (massGetter) {  // data()
2980        return data;
2981      } else {
2982        if (isSimpleGetter) { // data('key')
2983          // don't force creation of expandoStore if it doesn't exist yet
2984          return data && data[key];
2985        } else { // mass-setter: data({key1: val1, key2: val2})
2986          extend(data, key);
2987        }
2988      }
2989    }
2990  }
2991}
2992
2993function jqLiteHasClass(element, selector) {
2994  if (!element.getAttribute) return false;
2995  return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2996      indexOf(" " + selector + " ") > -1);
2997}
2998
2999function jqLiteRemoveClass(element, cssClasses) {
3000  if (cssClasses && element.setAttribute) {
3001    forEach(cssClasses.split(' '), function(cssClass) {
3002      element.setAttribute('class', trim(
3003          (" " + (element.getAttribute('class') || '') + " ")
3004          .replace(/[\n\t]/g, " ")
3005          .replace(" " + trim(cssClass) + " ", " "))
3006      );
3007    });
3008  }
3009}
3010
3011function jqLiteAddClass(element, cssClasses) {
3012  if (cssClasses && element.setAttribute) {
3013    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3014                            .replace(/[\n\t]/g, " ");
3015
3016    forEach(cssClasses.split(' '), function(cssClass) {
3017      cssClass = trim(cssClass);
3018      if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3019        existingClasses += cssClass + ' ';
3020      }
3021    });
3022
3023    element.setAttribute('class', trim(existingClasses));
3024  }
3025}
3026
3027
3028function jqLiteAddNodes(root, elements) {
3029  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3030
3031  if (elements) {
3032
3033    // if a Node (the most common case)
3034    if (elements.nodeType) {
3035      root[root.length++] = elements;
3036    } else {
3037      var length = elements.length;
3038
3039      // if an Array or NodeList and not a Window
3040      if (typeof length === 'number' && elements.window !== elements) {
3041        if (length) {
3042          for (var i = 0; i < length; i++) {
3043            root[root.length++] = elements[i];
3044          }
3045        }
3046      } else {
3047        root[root.length++] = elements;
3048      }
3049    }
3050  }
3051}
3052
3053
3054function jqLiteController(element, name) {
3055  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3056}
3057
3058function jqLiteInheritedData(element, name, value) {
3059  // if element is the document object work with the html element instead
3060  // this makes $(document).scope() possible
3061  if (element.nodeType == NODE_TYPE_DOCUMENT) {
3062    element = element.documentElement;
3063  }
3064  var names = isArray(name) ? name : [name];
3065
3066  while (element) {
3067    for (var i = 0, ii = names.length; i < ii; i++) {
3068      if (isDefined(value = jqLite.data(element, names[i]))) return value;
3069    }
3070
3071    // If dealing with a document fragment node with a host element, and no parent, use the host
3072    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3073    // to lookup parent controllers.
3074    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3075  }
3076}
3077
3078function jqLiteEmpty(element) {
3079  jqLiteDealoc(element, true);
3080  while (element.firstChild) {
3081    element.removeChild(element.firstChild);
3082  }
3083}
3084
3085function jqLiteRemove(element, keepData) {
3086  if (!keepData) jqLiteDealoc(element);
3087  var parent = element.parentNode;
3088  if (parent) parent.removeChild(element);
3089}
3090
3091
3092function jqLiteDocumentLoaded(action, win) {
3093  win = win || window;
3094  if (win.document.readyState === 'complete') {
3095    // Force the action to be run async for consistent behavior
3096    // from the action's point of view
3097    // i.e. it will definitely not be in a $apply
3098    win.setTimeout(action);
3099  } else {
3100    // No need to unbind this handler as load is only ever called once
3101    jqLite(win).on('load', action);
3102  }
3103}
3104
3105//////////////////////////////////////////
3106// Functions which are declared directly.
3107//////////////////////////////////////////
3108var JQLitePrototype = JQLite.prototype = {
3109  ready: function(fn) {
3110    var fired = false;
3111
3112    function trigger() {
3113      if (fired) return;
3114      fired = true;
3115      fn();
3116    }
3117
3118    // check if document is already loaded
3119    if (document.readyState === 'complete') {
3120      setTimeout(trigger);
3121    } else {
3122      this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3123      // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3124      // jshint -W064
3125      JQLite(window).on('load', trigger); // fallback to window.onload for others
3126      // jshint +W064
3127    }
3128  },
3129  toString: function() {
3130    var value = [];
3131    forEach(this, function(e) { value.push('' + e);});
3132    return '[' + value.join(', ') + ']';
3133  },
3134
3135  eq: function(index) {
3136      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3137  },
3138
3139  length: 0,
3140  push: push,
3141  sort: [].sort,
3142  splice: [].splice
3143};
3144
3145//////////////////////////////////////////
3146// Functions iterating getter/setters.
3147// these functions return self on setter and
3148// value on get.
3149//////////////////////////////////////////
3150var BOOLEAN_ATTR = {};
3151forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3152  BOOLEAN_ATTR[lowercase(value)] = value;
3153});
3154var BOOLEAN_ELEMENTS = {};
3155forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3156  BOOLEAN_ELEMENTS[value] = true;
3157});
3158var ALIASED_ATTR = {
3159  'ngMinlength': 'minlength',
3160  'ngMaxlength': 'maxlength',
3161  'ngMin': 'min',
3162  'ngMax': 'max',
3163  'ngPattern': 'pattern'
3164};
3165
3166function getBooleanAttrName(element, name) {
3167  // check dom last since we will most likely fail on name
3168  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3169
3170  // booleanAttr is here twice to minimize DOM access
3171  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3172}
3173
3174function getAliasedAttrName(name) {
3175  return ALIASED_ATTR[name];
3176}
3177
3178forEach({
3179  data: jqLiteData,
3180  removeData: jqLiteRemoveData,
3181  hasData: jqLiteHasData,
3182  cleanData: jqLiteCleanData
3183}, function(fn, name) {
3184  JQLite[name] = fn;
3185});
3186
3187forEach({
3188  data: jqLiteData,
3189  inheritedData: jqLiteInheritedData,
3190
3191  scope: function(element) {
3192    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3193    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3194  },
3195
3196  isolateScope: function(element) {
3197    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3198    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3199  },
3200
3201  controller: jqLiteController,
3202
3203  injector: function(element) {
3204    return jqLiteInheritedData(element, '$injector');
3205  },
3206
3207  removeAttr: function(element, name) {
3208    element.removeAttribute(name);
3209  },
3210
3211  hasClass: jqLiteHasClass,
3212
3213  css: function(element, name, value) {
3214    name = camelCase(name);
3215
3216    if (isDefined(value)) {
3217      element.style[name] = value;
3218    } else {
3219      return element.style[name];
3220    }
3221  },
3222
3223  attr: function(element, name, value) {
3224    var nodeType = element.nodeType;
3225    if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3226      return;
3227    }
3228    var lowercasedName = lowercase(name);
3229    if (BOOLEAN_ATTR[lowercasedName]) {
3230      if (isDefined(value)) {
3231        if (!!value) {
3232          element[name] = true;
3233          element.setAttribute(name, lowercasedName);
3234        } else {
3235          element[name] = false;
3236          element.removeAttribute(lowercasedName);
3237        }
3238      } else {
3239        return (element[name] ||
3240                 (element.attributes.getNamedItem(name) || noop).specified)
3241               ? lowercasedName
3242               : undefined;
3243      }
3244    } else if (isDefined(value)) {
3245      element.setAttribute(name, value);
3246    } else if (element.getAttribute) {
3247      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3248      // some elements (e.g. Document) don't have get attribute, so return undefined
3249      var ret = element.getAttribute(name, 2);
3250      // normalize non-existing attributes to undefined (as jQuery)
3251      return ret === null ? undefined : ret;
3252    }
3253  },
3254
3255  prop: function(element, name, value) {
3256    if (isDefined(value)) {
3257      element[name] = value;
3258    } else {
3259      return element[name];
3260    }
3261  },
3262
3263  text: (function() {
3264    getText.$dv = '';
3265    return getText;
3266
3267    function getText(element, value) {
3268      if (isUndefined(value)) {
3269        var nodeType = element.nodeType;
3270        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3271      }
3272      element.textContent = value;
3273    }
3274  })(),
3275
3276  val: function(element, value) {
3277    if (isUndefined(value)) {
3278      if (element.multiple && nodeName_(element) === 'select') {
3279        var result = [];
3280        forEach(element.options, function(option) {
3281          if (option.selected) {
3282            result.push(option.value || option.text);
3283          }
3284        });
3285        return result.length === 0 ? null : result;
3286      }
3287      return element.value;
3288    }
3289    element.value = value;
3290  },
3291
3292  html: function(element, value) {
3293    if (isUndefined(value)) {
3294      return element.innerHTML;
3295    }
3296    jqLiteDealoc(element, true);
3297    element.innerHTML = value;
3298  },
3299
3300  empty: jqLiteEmpty
3301}, function(fn, name) {
3302  /**
3303   * Properties: writes return selection, reads return first value
3304   */
3305  JQLite.prototype[name] = function(arg1, arg2) {
3306    var i, key;
3307    var nodeCount = this.length;
3308
3309    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3310    // in a way that survives minification.
3311    // jqLiteEmpty takes no arguments but is a setter.
3312    if (fn !== jqLiteEmpty &&
3313        (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3314      if (isObject(arg1)) {
3315
3316        // we are a write, but the object properties are the key/values
3317        for (i = 0; i < nodeCount; i++) {
3318          if (fn === jqLiteData) {
3319            // data() takes the whole object in jQuery
3320            fn(this[i], arg1);
3321          } else {
3322            for (key in arg1) {
3323              fn(this[i], key, arg1[key]);
3324            }
3325          }
3326        }
3327        // return self for chaining
3328        return this;
3329      } else {
3330        // we are a read, so read the first child.
3331        // TODO: do we still need this?
3332        var value = fn.$dv;
3333        // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3334        var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3335        for (var j = 0; j < jj; j++) {
3336          var nodeValue = fn(this[j], arg1, arg2);
3337          value = value ? value + nodeValue : nodeValue;
3338        }
3339        return value;
3340      }
3341    } else {
3342      // we are a write, so apply to all children
3343      for (i = 0; i < nodeCount; i++) {
3344        fn(this[i], arg1, arg2);
3345      }
3346      // return self for chaining
3347      return this;
3348    }
3349  };
3350});
3351
3352function createEventHandler(element, events) {
3353  var eventHandler = function(event, type) {
3354    // jQuery specific api
3355    event.isDefaultPrevented = function() {
3356      return event.defaultPrevented;
3357    };
3358
3359    var eventFns = events[type || event.type];
3360    var eventFnsLength = eventFns ? eventFns.length : 0;
3361
3362    if (!eventFnsLength) return;
3363
3364    if (isUndefined(event.immediatePropagationStopped)) {
3365      var originalStopImmediatePropagation = event.stopImmediatePropagation;
3366      event.stopImmediatePropagation = function() {
3367        event.immediatePropagationStopped = true;
3368
3369        if (event.stopPropagation) {
3370          event.stopPropagation();
3371        }
3372
3373        if (originalStopImmediatePropagation) {
3374          originalStopImmediatePropagation.call(event);
3375        }
3376      };
3377    }
3378
3379    event.isImmediatePropagationStopped = function() {
3380      return event.immediatePropagationStopped === true;
3381    };
3382
3383    // Some events have special handlers that wrap the real handler
3384    var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3385
3386    // Copy event handlers in case event handlers array is modified during execution.
3387    if ((eventFnsLength > 1)) {
3388      eventFns = shallowCopy(eventFns);
3389    }
3390
3391    for (var i = 0; i < eventFnsLength; i++) {
3392      if (!event.isImmediatePropagationStopped()) {
3393        handlerWrapper(element, event, eventFns[i]);
3394      }
3395    }
3396  };
3397
3398  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3399  //       events on `element`
3400  eventHandler.elem = element;
3401  return eventHandler;
3402}
3403
3404function defaultHandlerWrapper(element, event, handler) {
3405  handler.call(element, event);
3406}
3407
3408function specialMouseHandlerWrapper(target, event, handler) {
3409  // Refer to jQuery's implementation of mouseenter & mouseleave
3410  // Read about mouseenter and mouseleave:
3411  // http://www.quirksmode.org/js/events_mouse.html#link8
3412  var related = event.relatedTarget;
3413  // For mousenter/leave call the handler if related is outside the target.
3414  // NB: No relatedTarget if the mouse left/entered the browser window
3415  if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3416    handler.call(target, event);
3417  }
3418}
3419
3420//////////////////////////////////////////
3421// Functions iterating traversal.
3422// These functions chain results into a single
3423// selector.
3424//////////////////////////////////////////
3425forEach({
3426  removeData: jqLiteRemoveData,
3427
3428  on: function jqLiteOn(element, type, fn, unsupported) {
3429    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3430
3431    // Do not add event handlers to non-elements because they will not be cleaned up.
3432    if (!jqLiteAcceptsData(element)) {
3433      return;
3434    }
3435
3436    var expandoStore = jqLiteExpandoStore(element, true);
3437    var events = expandoStore.events;
3438    var handle = expandoStore.handle;
3439
3440    if (!handle) {
3441      handle = expandoStore.handle = createEventHandler(element, events);
3442    }
3443
3444    // http://jsperf.com/string-indexof-vs-split
3445    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3446    var i = types.length;
3447
3448    var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3449      var eventFns = events[type];
3450
3451      if (!eventFns) {
3452        eventFns = events[type] = [];
3453        eventFns.specialHandlerWrapper = specialHandlerWrapper;
3454        if (type !== '$destroy' && !noEventListener) {
3455          addEventListenerFn(element, type, handle);
3456        }
3457      }
3458
3459      eventFns.push(fn);
3460    };
3461
3462    while (i--) {
3463      type = types[i];
3464      if (MOUSE_EVENT_MAP[type]) {
3465        addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3466        addHandler(type, undefined, true);
3467      } else {
3468        addHandler(type);
3469      }
3470    }
3471  },
3472
3473  off: jqLiteOff,
3474
3475  one: function(element, type, fn) {
3476    element = jqLite(element);
3477
3478    //add the listener twice so that when it is called
3479    //you can remove the original function and still be
3480    //able to call element.off(ev, fn) normally
3481    element.on(type, function onFn() {
3482      element.off(type, fn);
3483      element.off(type, onFn);
3484    });
3485    element.on(type, fn);
3486  },
3487
3488  replaceWith: function(element, replaceNode) {
3489    var index, parent = element.parentNode;
3490    jqLiteDealoc(element);
3491    forEach(new JQLite(replaceNode), function(node) {
3492      if (index) {
3493        parent.insertBefore(node, index.nextSibling);
3494      } else {
3495        parent.replaceChild(node, element);
3496      }
3497      index = node;
3498    });
3499  },
3500
3501  children: function(element) {
3502    var children = [];
3503    forEach(element.childNodes, function(element) {
3504      if (element.nodeType === NODE_TYPE_ELEMENT) {
3505        children.push(element);
3506      }
3507    });
3508    return children;
3509  },
3510
3511  contents: function(element) {
3512    return element.contentDocument || element.childNodes || [];
3513  },
3514
3515  append: function(element, node) {
3516    var nodeType = element.nodeType;
3517    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3518
3519    node = new JQLite(node);
3520
3521    for (var i = 0, ii = node.length; i < ii; i++) {
3522      var child = node[i];
3523      element.appendChild(child);
3524    }
3525  },
3526
3527  prepend: function(element, node) {
3528    if (element.nodeType === NODE_TYPE_ELEMENT) {
3529      var index = element.firstChild;
3530      forEach(new JQLite(node), function(child) {
3531        element.insertBefore(child, index);
3532      });
3533    }
3534  },
3535
3536  wrap: function(element, wrapNode) {
3537    wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3538    var parent = element.parentNode;
3539    if (parent) {
3540      parent.replaceChild(wrapNode, element);
3541    }
3542    wrapNode.appendChild(element);
3543  },
3544
3545  remove: jqLiteRemove,
3546
3547  detach: function(element) {
3548    jqLiteRemove(element, true);
3549  },
3550
3551  after: function(element, newElement) {
3552    var index = element, parent = element.parentNode;
3553    newElement = new JQLite(newElement);
3554
3555    for (var i = 0, ii = newElement.length; i < ii; i++) {
3556      var node = newElement[i];
3557      parent.insertBefore(node, index.nextSibling);
3558      index = node;
3559    }
3560  },
3561
3562  addClass: jqLiteAddClass,
3563  removeClass: jqLiteRemoveClass,
3564
3565  toggleClass: function(element, selector, condition) {
3566    if (selector) {
3567      forEach(selector.split(' '), function(className) {
3568        var classCondition = condition;
3569        if (isUndefined(classCondition)) {
3570          classCondition = !jqLiteHasClass(element, className);
3571        }
3572        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3573      });
3574    }
3575  },
3576
3577  parent: function(element) {
3578    var parent = element.parentNode;
3579    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3580  },
3581
3582  next: function(element) {
3583    return element.nextElementSibling;
3584  },
3585
3586  find: function(element, selector) {
3587    if (element.getElementsByTagName) {
3588      return element.getElementsByTagName(selector);
3589    } else {
3590      return [];
3591    }
3592  },
3593
3594  clone: jqLiteClone,
3595
3596  triggerHandler: function(element, event, extraParameters) {
3597
3598    var dummyEvent, eventFnsCopy, handlerArgs;
3599    var eventName = event.type || event;
3600    var expandoStore = jqLiteExpandoStore(element);
3601    var events = expandoStore && expandoStore.events;
3602    var eventFns = events && events[eventName];
3603
3604    if (eventFns) {
3605      // Create a dummy event to pass to the handlers
3606      dummyEvent = {
3607        preventDefault: function() { this.defaultPrevented = true; },
3608        isDefaultPrevented: function() { return this.defaultPrevented === true; },
3609        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3610        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3611        stopPropagation: noop,
3612        type: eventName,
3613        target: element
3614      };
3615
3616      // If a custom event was provided then extend our dummy event with it
3617      if (event.type) {
3618        dummyEvent = extend(dummyEvent, event);
3619      }
3620
3621      // Copy event handlers in case event handlers array is modified during execution.
3622      eventFnsCopy = shallowCopy(eventFns);
3623      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3624
3625      forEach(eventFnsCopy, function(fn) {
3626        if (!dummyEvent.isImmediatePropagationStopped()) {
3627          fn.apply(element, handlerArgs);
3628        }
3629      });
3630    }
3631  }
3632}, function(fn, name) {
3633  /**
3634   * chaining functions
3635   */
3636  JQLite.prototype[name] = function(arg1, arg2, arg3) {
3637    var value;
3638
3639    for (var i = 0, ii = this.length; i < ii; i++) {
3640      if (isUndefined(value)) {
3641        value = fn(this[i], arg1, arg2, arg3);
3642        if (isDefined(value)) {
3643          // any function which returns a value needs to be wrapped
3644          value = jqLite(value);
3645        }
3646      } else {
3647        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3648      }
3649    }
3650    return isDefined(value) ? value : this;
3651  };
3652
3653  // bind legacy bind/unbind to on/off
3654  JQLite.prototype.bind = JQLite.prototype.on;
3655  JQLite.prototype.unbind = JQLite.prototype.off;
3656});
3657
3658
3659// Provider for private $$jqLite service
3660function $$jqLiteProvider() {
3661  this.$get = function $$jqLite() {
3662    return extend(JQLite, {
3663      hasClass: function(node, classes) {
3664        if (node.attr) node = node[0];
3665        return jqLiteHasClass(node, classes);
3666      },
3667      addClass: function(node, classes) {
3668        if (node.attr) node = node[0];
3669        return jqLiteAddClass(node, classes);
3670      },
3671      removeClass: function(node, classes) {
3672        if (node.attr) node = node[0];
3673        return jqLiteRemoveClass(node, classes);
3674      }
3675    });
3676  };
3677}
3678
3679/**
3680 * Computes a hash of an 'obj'.
3681 * Hash of a:
3682 *  string is string
3683 *  number is number as string
3684 *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3685 *         that is also assigned to the $$hashKey property of the object.
3686 *
3687 * @param obj
3688 * @returns {string} hash string such that the same input will have the same hash string.
3689 *         The resulting string key is in 'type:hashKey' format.
3690 */
3691function hashKey(obj, nextUidFn) {
3692  var key = obj && obj.$$hashKey;
3693
3694  if (key) {
3695    if (typeof key === 'function') {
3696      key = obj.$$hashKey();
3697    }
3698    return key;
3699  }
3700
3701  var objType = typeof obj;
3702  if (objType == 'function' || (objType == 'object' && obj !== null)) {
3703    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3704  } else {
3705    key = objType + ':' + obj;
3706  }
3707
3708  return key;
3709}
3710
3711/**
3712 * HashMap which can use objects as keys
3713 */
3714function HashMap(array, isolatedUid) {
3715  if (isolatedUid) {
3716    var uid = 0;
3717    this.nextUid = function() {
3718      return ++uid;
3719    };
3720  }
3721  forEach(array, this.put, this);
3722}
3723HashMap.prototype = {
3724  /**
3725   * Store key value pair
3726   * @param key key to store can be any type
3727   * @param value value to store can be any type
3728   */
3729  put: function(key, value) {
3730    this[hashKey(key, this.nextUid)] = value;
3731  },
3732
3733  /**
3734   * @param key
3735   * @returns {Object} the value for the key
3736   */
3737  get: function(key) {
3738    return this[hashKey(key, this.nextUid)];
3739  },
3740
3741  /**
3742   * Remove the key/value pair
3743   * @param key
3744   */
3745  remove: function(key) {
3746    var value = this[key = hashKey(key, this.nextUid)];
3747    delete this[key];
3748    return value;
3749  }
3750};
3751
3752var $$HashMapProvider = [function() {
3753  this.$get = [function() {
3754    return HashMap;
3755  }];
3756}];
3757
3758/**
3759 * @ngdoc function
3760 * @module ng
3761 * @name angular.injector
3762 * @kind function
3763 *
3764 * @description
3765 * Creates an injector object that can be used for retrieving services as well as for
3766 * dependency injection (see {@link guide/di dependency injection}).
3767 *
3768 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3769 *     {@link angular.module}. The `ng` module must be explicitly added.
3770 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3771 *     disallows argument name annotation inference.
3772 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3773 *
3774 * @example
3775 * Typical usage
3776 * ```js
3777 *   // create an injector
3778 *   var $injector = angular.injector(['ng']);
3779 *
3780 *   // use the injector to kick off your application
3781 *   // use the type inference to auto inject arguments, or use impl
3781icit injection
3782 *   $injector.invoke(function($rootScope, $compile, $document) {
3783 *     $compile($document)($rootScope);
3784 *     $rootScope.$digest();
3785 *   });
3786 * ```
3787 *
3788 * Sometimes you want to get access to the injector of a currently running Angular app
3789 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3790 * application has been bootstrapped. You can do this using the extra `injector()` added
3791 * to JQuery/jqLite elements. See {@link angular.element}.
3792 *
3793 * *This is fairly rare but could be the case if a third party library is injecting the
3794 * markup.*
3795 *
3796 * In the following example a new block of HTML containing a `ng-controller`
3797 * directive is added to the end of the document body by JQuery. We then compile and link
3798 * it into the current AngularJS scope.
3799 *
3800 * ```js
3801 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3802 * $(document.body).append($div);
3803 *
3804 * angular.element(document).injector().invoke(function($compile) {
3805 *   var scope = angular.element($div).scope();
3806 *   $compile($div)(scope);
3807 * });
3808 * ```
3809 */
3810
3811
3812/**
3813 * @ngdoc module
3814 * @name auto
3815 * @description
3816 *
3817 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3818 */
3819
3820var ARROW_ARG = /^([^\(]+?)=>/;
3821var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3822var FN_ARG_SPLIT = /,/;
3823var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3824var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3825var $injectorMinErr = minErr('$injector');
3826
3827function extractArgs(fn) {
3828  var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3829      args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
3830  return args;
3831}
3832
3833function anonFn(fn) {
3834  // For anonymous functions, showing at the very least the function signature can help in
3835  // debugging.
3836  var args = extractArgs(fn);
3837  if (args) {
3838    return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3839  }
3840  return 'fn';
3841}
3842
3843function annotate(fn, strictDi, name) {
3844  var $inject,
3845      argDecl,
3846      last;
3847
3848  if (typeof fn === 'function') {
3849    if (!($inject = fn.$inject)) {
3850      $inject = [];
3851      if (fn.length) {
3852        if (strictDi) {
3853          if (!isString(name) || !name) {
3854            name = fn.name || anonFn(fn);
3855          }
3856          throw $injectorMinErr('strictdi',
3857            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3858        }
3859        argDecl = extractArgs(fn);
3860        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3861          arg.replace(FN_ARG, function(all, underscore, name) {
3862            $inject.push(name);
3863          });
3864        });
3865      }
3866      fn.$inject = $inject;
3867    }
3868  } else if (isArray(fn)) {
3869    last = fn.length - 1;
3870    assertArgFn(fn[last], 'fn');
3871    $inject = fn.slice(0, last);
3872  } else {
3873    assertArgFn(fn, 'fn', true);
3874  }
3875  return $inject;
3876}
3877
3878///////////////////////////////////////
3879
3880/**
3881 * @ngdoc service
3882 * @name $injector
3883 *
3884 * @description
3885 *
3886 * `$injector` is used to retrieve object instances as defined by
3887 * {@link auto.$provide provider}, instantiate types, invoke methods,
3888 * and load modules.
3889 *
3890 * The following always holds true:
3891 *
3892 * ```js
3893 *   var $injector = angular.injector();
3894 *   expect($injector.get('$injector')).toBe($injector);
3895 *   expect($injector.invoke(function($injector) {
3896 *     return $injector;
3897 *   })).toBe($injector);
3898 * ```
3899 *
3900 * # Injection Function Annotation
3901 *
3902 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3903 * following are all valid ways of annotating function with injection arguments and are equivalent.
3904 *
3905 * ```js
3906 *   // inferred (only works if code not minified/obfuscated)
3907 *   $injector.invoke(function(serviceA){});
3908 *
3909 *   // annotated
3910 *   function explicit(serviceA) {};
3911 *   explicit.$inject = ['serviceA'];
3912 *   $injector.invoke(explicit);
3913 *
3914 *   // inline
3915 *   $injector.invoke(['serviceA', function(serviceA){}]);
3916 * ```
3917 *
3918 * ## Inference
3919 *
3920 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3921 * can then be parsed and the function arguments can be extracted. This method of discovering
3922 * annotations is disallowed when the injector is in strict mode.
3923 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3924 * argument names.
3925 *
3926 * ## `$inject` Annotation
3927 * By adding an `$inject` property onto a function the injection parameters can be specified.
3928 *
3929 * ## Inline
3930 * As an array of injection names, where the last item in the array is the function to call.
3931 */
3932
3933/**
3934 * @ngdoc method
3935 * @name $injector#get
3936 *
3937 * @description
3938 * Return an instance of the service.
3939 *
3940 * @param {string} name The name of the instance to retrieve.
3941 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3942 * @return {*} The instance.
3943 */
3944
3945/**
3946 * @ngdoc method
3947 * @name $injector#invoke
3948 *
3949 * @description
3950 * Invoke the method and supply the method arguments from the `$injector`.
3951 *
3952 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3953 *   injected according to the {@link guide/di $inject Annotation} rules.
3954 * @param {Object=} self The `this` for the invoked method.
3955 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3956 *                         object first, before the `$injector` is consulted.
3957 * @returns {*} the value returned by the invoked `fn` function.
3958 */
3959
3960/**
3961 * @ngdoc method
3962 * @name $injector#has
3963 *
3964 * @description
3965 * Allows the user to query if the particular service exists.
3966 *
3967 * @param {string} name Name of the service to query.
3968 * @returns {boolean} `true` if injector has given service.
3969 */
3970
3971/**
3972 * @ngdoc method
3973 * @name $injector#instantiate
3974 * @description
3975 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3976 * operator, and supplies all of the arguments to the constructor function as specified by the
3977 * constructor annotation.
3978 *
3979 * @param {Function} Type Annotated constructor function.
3980 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3981 * object first, before the `$injector` is consulted.
3982 * @returns {Object} new instance of `Type`.
3983 */
3984
3985/**
3986 * @ngdoc method
3987 * @name $injector#annotate
3988 *
3989 * @description
3990 * Returns an array of service names which the function is requesting for injection. This API is
3991 * used by the injector to determine which services need to be injected into the function when the
3992 * function is invoked. There are three ways in which the function can be annotated with the needed
3993 * dependencies.
3994 *
3995 * # Argument names
3996 *
3997 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3998 * by converting the function into a string using `toString()` method and extracting the argument
3999 * names.
4000 * ```js
4001 *   // Given
4002 *   function MyController($scope, $route) {
4003 *     // ...
4004 *   }
4005 *
4006 *   // Then
4007 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4008 * ```
4009 *
4010 * You can disallow this method by using strict injection mode.
4011 *
4012 * This method does not work with code minification / obfuscation. For this reason the following
4013 * annotation strategies are supported.
4014 *
4015 * # The `$inject` property
4016 *
4017 * If a function has an `$inject` property and its value is an array of strings, then the strings
4018 * represent names of services to be injected into the function.
4019 * ```js
4020 *   // Given
4021 *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
4022 *     // ...
4023 *   }
4024 *   // Define function dependencies
4025 *   MyController['$inject'] = ['$scope', '$route'];
4026 *
4027 *   // Then
4028 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4029 * ```
4030 *
4031 * # The array notation
4032 *
4033 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4034 * is very inconvenient. In these situations using the array notation to specify the dependencies in
4035 * a way that survives minification is a better choice:
4036 *
4037 * ```js
4038 *   // We wish to write this (not minification / obfuscation safe)
4039 *   injector.invoke(function($compile, $rootScope) {
4040 *     // ...
4041 *   });
4042 *
4043 *   // We are forced to write break inlining
4044 *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4045 *     // ...
4046 *   };
4047 *   tmpFn.$inject = ['$compile', '$rootScope'];
4048 *   injector.invoke(tmpFn);
4049 *
4050 *   // To better support inline function the inline annotation is supported
4051 *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4052 *     // ...
4053 *   }]);
4054 *
4055 *   // Therefore
4056 *   expect(injector.annotate(
4057 *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4058 *    ).toEqual(['$compile', '$rootScope']);
4059 * ```
4060 *
4061 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4062 * be retrieved as described above.
4063 *
4064 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4065 *
4066 * @returns {Array.<string>} The names of the services which the function requires.
4067 */
4068
4069
4070
4071
4072/**
4073 * @ngdoc service
4074 * @name $provide
4075 *
4076 * @description
4077 *
4078 * The {@link auto.$provide $provide} service has a number of methods for registering components
4079 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4080 * {@link angular.Module}.
4081 *
4082 * An Angular **service** is a singleton object created by a **service factory**.  These **service
4083 * factories** are functions which, in turn, are created by a **service provider**.
4084 * The **service providers** are constructor functions. When instantiated they must contain a
4085 * property called `$get`, which holds the **service factory** function.
4086 *
4087 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4088 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4089 * function to get the instance of the **service**.
4090 *
4091 * Often services have no configuration options and there is no need to add methods to the service
4092 * provider.  The provider will be no more than a constructor function with a `$get` property. For
4093 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4094 * services without specifying a provider.
4095 *
4096 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4097 *     {@link auto.$injector $injector}
4098 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4099 *     providers and services.
4100 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4101 *     services, not providers.
4102 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4103 *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
4104 *     given factory function.
4105 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4106 *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4107 *      a new object using the given constructor function.
4108 *
4109 * See the individual methods for more information and examples.
4110 */
4111
4112/**
4113 * @ngdoc method
4114 * @name $provide#provider
4115 * @description
4116 *
4117 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4118 * are constructor functions, whose instances are responsible for "providing" a factory for a
4119 * service.
4120 *
4121 * Service provider names start with the name of the service they provide followed by `Provider`.
4122 * For example, the {@link ng.$log $log} service has a provider called
4123 * {@link ng.$logProvider $logProvider}.
4124 *
4125 * Service provider objects can have additional methods which allow configuration of the provider
4126 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4127 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4128 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4129 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4130 * console or not.
4131 *
4132 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4133                        'Provider'` key.
4134 * @param {(Object|function())} provider If the provider is:
4135 *
4136 *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4137 *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4138 *   - `Constructor`: a new instance of the provider will be created using
4139 *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4140 *
4141 * @returns {Object} registered provider instance
4142
4143 * @example
4144 *
4145 * The following example shows how to create a simple event tracking service and register it using
4146 * {@link auto.$provide#provider $provide.provider()}.
4147 *
4148 * ```js
4149 *  // Define the eventTracker provider
4150 *  function EventTrackerProvider() {
4151 *    var trackingUrl = '/track';
4152 *
4153 *    // A provider method for configuring where the tracked events should been saved
4154 *    this.setTrackingUrl = function(url) {
4155 *      trackingUrl = url;
4156 *    };
4157 *
4158 *    // The service factory function
4159 *    this.$get = ['$http', function($http) {
4160 *      var trackedEvents = {};
4161 *      return {
4162 *        // Call this to track an event
4163 *        event: function(event) {
4164 *          var count = trackedEvents[event] || 0;
4165 *          count += 1;
4166 *          trackedEvents[event] = count;
4167 *          return count;
4168 *        },
4169 *        // Call this to save the tracked events to the trackingUrl
4170 *        save: function() {
4171 *          $http.post(trackingUrl, trackedEvents);
4172 *        }
4173 *      };
4174 *    }];
4175 *  }
4176 *
4177 *  describe('eventTracker', function() {
4178 *    var postSpy;
4179 *
4180 *    beforeEach(module(function($provide) {
4181 *      // Register the eventTracker provider
4182 *      $provide.provider('eventTracker', EventTrackerProvider);
4183 *    }));
4184 *
4185 *    beforeEach(module(function(eventTrackerProvider) {
4186 *      // Configure eventTracker provider
4187 *      eventTrackerProvider.setTrackingUrl('/custom-track');
4188 *    }));
4189 *
4190 *    it('tracks events', inject(function(eventTracker) {
4191 *      expect(eventTracker.event('login')).toEqual(1);
4192 *      expect(eventTracker.event('login')).toEqual(2);
4193 *    }));
4194 *
4195 *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4196 *      postSpy = spyOn($http, 'post');
4197 *      eventTracker.event('login');
4198 *      eventTracker.save();
4199 *      expect(postSpy).toHaveBeenCalled();
4200 *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4201 *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4202 *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4203 *    }));
4204 *  });
4205 * ```
4206 */
4207
4208/**
4209 * @ngdoc method
4210 * @name $provide#factory
4211 * @description
4212 *
4213 * Register a **service factory**, which will be called to return the service instance.
4214 * This is short for registering a service where its provider consists of only a `$get` property,
4215 * which is the given service factory function.
4216 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4217 * configure your service in a provider.
4218 *
4219 * @param {string} name The name of the instance.
4220 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4221 *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4222 * @returns {Object} registered provider instance
4223 *
4224 * @example
4225 * Here is an example of registering a service
4226 * ```js
4227 *   $provide.factory('ping', ['$http', function($http) {
4228 *     return function ping() {
4229 *       return $http.send('/ping');
4230 *     };
4231 *   }]);
4232 * ```
4233 * You would then inject and use this service like this:
4234 * ```js
4235 *   someModule.controller('Ctrl', ['ping', function(ping) {
4236 *     ping();
4237 *   }]);
4238 * ```
4239 */
4240
4241
4242/**
4243 * @ngdoc method
4244 * @name $provide#service
4245 * @description
4246 *
4247 * Register a **service constructor**, which will be invoked with `new` to create the service
4248 * instance.
4249 * This is short for registering a service where its provider's `$get` property is the service
4250 * constructor function that will be used to instantiate the service instance.
4251 *
4252 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4253 * as a type/class.
4254 *
4255 * @param {string} name The name of the instance.
4256 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4257 *     that will be instantiated.
4258 * @returns {Object} registered provider instance
4259 *
4260 * @example
4261 * Here is an example of registering a service using
4262 * {@link auto.$provide#service $provide.service(class)}.
4263 * ```js
4264 *   var Ping = function($http) {
4265 *     this.$http = $http;
4266 *   };
4267 *
4268 *   Ping.$inject = ['$http'];
4269 *
4270 *   Ping.prototype.send = function() {
4271 *     return this.$http.get('/ping');
4272 *   };
4273 *   $provide.service('ping', Ping);
4274 * ```
4275 * You would then inject and use this service like this:
4276 * ```js
4277 *   someModule.controller('Ctrl', ['ping', function(ping) {
4278 *     ping.send();
4279 *   }]);
4280 * ```
4281 */
4282
4283
4284/**
4285 * @ngdoc method
4286 * @name $provide#value
4287 * @description
4288 *
4289 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4290 * number, an array, an object or a function.  This is short for registering a service where its
4291 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4292 * service**.
4293 *
4294 * Value services are similar to constant services, except that they cannot be injected into a
4295 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4296 * an Angular
4297 * {@link auto.$provide#decorator decorator}.
4298 *
4299 * @param {string} name The name of the instance.
4300 * @param {*} value The value.
4301 * @returns {Object} registered provider instance
4302 *
4303 * @example
4304 * Here are some examples of creating value services.
4305 * ```js
4306 *   $provide.value('ADMIN_USER', 'admin');
4307 *
4308 *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4309 *
4310 *   $provide.value('halfOf', function(value) {
4311 *     return value / 2;
4312 *   });
4313 * ```
4314 */
4315
4316
4317/**
4318 * @ngdoc method
4319 * @name $provide#constant
4320 * @description
4321 *
4322 * Register a **constant service**, such as a string, a number, an array, an object or a function,
4323 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
4324 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4325 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4326 *
4327 * @param {string} name The name of the constant.
4328 * @param {*} value The constant value.
4329 * @returns {Object} registered instance
4330 *
4331 * @example
4332 * Here a some examples of creating constants:
4333 * ```js
4334 *   $provide.constant('SHARD_HEIGHT', 306);
4335 *
4336 *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4337 *
4338 *   $provide.constant('double', function(value) {
4339 *     return value * 2;
4340 *   });
4341 * ```
4342 */
4343
4344
4345/**
4346 * @ngdoc method
4347 * @name $provide#decorator
4348 * @description
4349 *
4350 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4351 * intercepts the creation of a service, allowing it to override or modify the behavior of the
4352 * service. The object returned by the decorator may be the original service, or a new service
4353 * object which replaces or wraps and delegates to the original service.
4354 *
4355 * @param {string} name The name of the service to decorate.
4356 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4357 *    instantiated and should return the decorated service instance. The function is called using
4358 *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4359 *    Local injection arguments:
4360 *
4361 *    * `$delegate` - The original service instance, which can be monkey patched, configured,
4362 *      decorated or delegated to.
4363 *
4364 * @example
4365 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4366 * calls to {@link ng.$log#error $log.warn()}.
4367 * ```js
4368 *   $provide.decorator('$log', ['$delegate', function($delegate) {
4369 *     $delegate.warn = $delegate.error;
4370 *     return $delegate;
4371 *   }]);
4372 * ```
4373 */
4374
4375
4376function createInjector(modulesToLoad, strictDi) {
4377  strictDi = (strictDi === true);
4378  var INSTANTIATING = {},
4379      providerSuffix = 'Provider',
4380      path = [],
4381      loadedModules = new HashMap([], true),
4382      providerCache = {
4383        $provide: {
4384            provider: supportObject(provider),
4385            factory: supportObject(factory),
4386            service: supportObject(service),
4387            value: supportObject(value),
4388            constant: supportObject(constant),
4389            decorator: decorator
4390          }
4391      },
4392      providerInjector = (providerCache.$injector =
4393          createInternalInjector(providerCache, function(serviceName, caller) {
4394            if (angular.isString(caller)) {
4395              path.push(caller);
4396            }
4397            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4398          })),
4399      instanceCache = {},
4400      protoInstanceInjector =
4401          createInternalInjector(instanceCache, function(serviceName, caller) {
4402            var provider = providerInjector.get(serviceName + providerSuffix, caller);
4403            return instanceInjector.invoke(
4404                provider.$get, provider, undefined, serviceName);
4405          }),
4406      instanceInjector = protoInstanceInjector;
4407
4408  providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
4409  var runBlocks = loadModules(modulesToLoad);
4410  instanceInjector = protoInstanceInjector.get('$injector');
4411  instanceInjector.strictDi = strictDi;
4412  forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
4413
4414  return instanceInjector;
4415
4416  ////////////////////////////////////
4417  // $provider
4418  ////////////////////////////////////
4419
4420  function supportObject(delegate) {
4421    return function(key, value) {
4422      if (isObject(key)) {
vendor: 10,531 bytes, lines 4423-4705
4423        forEach(key, reverseParams(delegate));
4424      } else {
4425        return delegate(key, value);
4426      }
4427    };
4428  }
4429
4430  function provider(name, provider_) {
4431    assertNotHasOwnProperty(name, 'service');
4432    if (isFunction(provider_) || isArray(provider_)) {
4433      provider_ = providerInjector.instantiate(provider_);
4434    }
4435    if (!provider_.$get) {
4436      throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4437    }
4438    return providerCache[name + providerSuffix] = provider_;
4439  }
4440
4441  function enforceReturnValue(name, factory) {
4442    return function enforcedReturnValue() {
4443      var result = instanceInjector.invoke(factory, this);
4444      if (isUndefined(result)) {
4445        throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4446      }
4447      return result;
4448    };
4449  }
4450
4451  function factory(name, factoryFn, enforce) {
4452    return provider(name, {
4453      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4454    });
4455  }
4456
4457  function service(name, constructor) {
4458    return factory(name, ['$injector', function($injector) {
4459      return $injector.instantiate(constructor);
4460    }]);
4461  }
4462
4463  function value(name, val) { return factory(name, valueFn(val), false); }
4464
4465  function constant(name, value) {
4466    assertNotHasOwnProperty(name, 'constant');
4467    providerCache[name] = value;
4468    instanceCache[name] = value;
4469  }
4470
4471  function decorator(serviceName, decorFn) {
4472    var origProvider = providerInjector.get(serviceName + providerSuffix),
4473        orig$get = origProvider.$get;
4474
4475    origProvider.$get = function() {
4476      var origInstance = instanceInjector.invoke(orig$get, origProvider);
4477      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4478    };
4479  }
4480
4481  ////////////////////////////////////
4482  // Module Loading
4483  ////////////////////////////////////
4484  function loadModules(modulesToLoad) {
4485    assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4486    var runBlocks = [], moduleFn;
4487    forEach(modulesToLoad, function(module) {
4488      if (loadedModules.get(module)) return;
4489      loadedModules.put(module, true);
4490
4491      function runInvokeQueue(queue) {
4492        var i, ii;
4493        for (i = 0, ii = queue.length; i < ii; i++) {
4494          var invokeArgs = queue[i],
4495              provider = providerInjector.get(invokeArgs[0]);
4496
4497          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4498        }
4499      }
4500
4501      try {
4502        if (isString(module)) {
4503          moduleFn = angularModule(module);
4504          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4505          runInvokeQueue(moduleFn._invokeQueue);
4506          runInvokeQueue(moduleFn._configBlocks);
4507        } else if (isFunction(module)) {
4508            runBlocks.push(providerInjector.invoke(module));
4509        } else if (isArray(module)) {
4510            runBlocks.push(providerInjector.invoke(module));
4511        } else {
4512          assertArgFn(module, 'module');
4513        }
4514      } catch (e) {
4515        if (isArray(module)) {
4516          module = module[module.length - 1];
4517        }
4518        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4519          // Safari & FF's stack traces don't contain error.message content
4520          // unlike those of Chrome and IE
4521          // So if stack doesn't contain message, we create a new string that contains both.
4522          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4523          /* jshint -W022 */
4524          e = e.message + '\n' + e.stack;
4525        }
4526        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4527                  module, e.stack || e.message || e);
4528      }
4529    });
4530    return runBlocks;
4531  }
4532
4533  ////////////////////////////////////
4534  // internal Injector
4535  ////////////////////////////////////
4536
4537  function createInternalInjector(cache, factory) {
4538
4539    function getService(serviceName, caller) {
4540      if (cache.hasOwnProperty(serviceName)) {
4541        if (cache[serviceName] === INSTANTIATING) {
4542          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4543                    serviceName + ' <- ' + path.join(' <- '));
4544        }
4545        return cache[serviceName];
4546      } else {
4547        try {
4548          path.unshift(serviceName);
4549          cache[serviceName] = INSTANTIATING;
4550          return cache[serviceName] = factory(serviceName, caller);
4551        } catch (err) {
4552          if (cache[serviceName] === INSTANTIATING) {
4553            delete cache[serviceName];
4554          }
4555          throw err;
4556        } finally {
4557          path.shift();
4558        }
4559      }
4560    }
4561
4562
4563    function injectionArgs(fn, locals, serviceName) {
4564      var args = [],
4565          $inject = createInjector.$$annotate(fn, strictDi, serviceName);
4566
4567      for (var i = 0, length = $inject.length; i < length; i++) {
4568        var key = $inject[i];
4569        if (typeof key !== 'string') {
4570          throw $injectorMinErr('itkn',
4571                  'Incorrect injection token! Expected service name as string, got {0}', key);
4572        }
4573        args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
4574                                                         getService(key, serviceName));
4575      }
4576      return args;
4577    }
4578
4579    function isClass(func) {
4580      // IE 9-11 do not support classes and IE9 leaks with the code below.
4581      if (msie <= 11) {
4582        return false;
4583      }
4584      // Workaround for MS Edge.
4585      // Check https://connect.microsoft.com/IE/Feedback/Details/2211653
4586      return typeof func === 'function'
4587        && /^(?:class\s|constructor\()/.test(Function.prototype.toString.call(func));
4588    }
4589
4590    function invoke(fn, self, locals, serviceName) {
4591      if (typeof locals === 'string') {
4592        serviceName = locals;
4593        locals = null;
4594      }
4595
4596      var args = injectionArgs(fn, locals, serviceName);
4597      if (isArray(fn)) {
4598        fn = fn[fn.length - 1];
4599      }
4600
4601      if (!isClass(fn)) {
4602        // http://jsperf.com/angularjs-invoke-apply-vs-switch
4603        // #5388
4604        return fn.apply(self, args);
4605      } else {
4606        args.unshift(null);
4607        /*jshint -W058 */ // Applying a constructor without immediate parentheses is the point here.
4608        return new (Function.prototype.bind.apply(fn, args));
4609        /*jshint +W058 */
4610      }
4611    }
4612
4613
4614    function instantiate(Type, locals, serviceName) {
4615      // Check if Type is annotated and use just the given function at n-1 as parameter
4616      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4617      var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
4618      var args = injectionArgs(Type, locals, serviceName);
4619      // Empty object at position 0 is ignored for invocation with `new`, but required.
4620      args.unshift(null);
4621      /*jshint -W058 */ // Applying a constructor without immediate parentheses is the point here.
4622      return new (Function.prototype.bind.apply(ctor, args));
4623      /*jshint +W058 */
4624    }
4625
4626
4627    return {
4628      invoke: invoke,
4629      instantiate: instantiate,
4630      get: getService,
4631      annotate: createInjector.$$annotate,
4632      has: function(name) {
4633        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4634      }
4635    };
4636  }
4637}
4638
4639createInjector.$$annotate = annotate;
4640
4641/**
4642 * @ngdoc provider
4643 * @name $anchorScrollProvider
4644 *
4645 * @description
4646 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4647 * {@link ng.$location#hash $location.hash()} changes.
4648 */
4649function $AnchorScrollProvider() {
4650
4651  var autoScrollingEnabled = true;
4652
4653  /**
4654   * @ngdoc method
4655   * @name $anchorScrollProvider#disableAutoScrolling
4656   *
4657   * @description
4658   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4659   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4660   * Use this method to disable automatic scrolling.
4661   *
4662   * If automatic scrolling is disabled, one must explicitly call
4663   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4664   * current hash.
4665   */
4666  this.disableAutoScrolling = function() {
4667    autoScrollingEnabled = false;
4668  };
4669
4670  /**
4671   * @ngdoc service
4672   * @name $anchorScroll
4673   * @kind function
4674   * @requires $window
4675   * @requires $location
4676   * @requires $rootScope
4677   *
4678   * @description
4679   * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4680   * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4681   * in the
4682   * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4683   *
4684   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4685   * match any anchor whenever it changes. This can be disabled by calling
4686   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4687   *
4688   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4689   * vertical scroll-offset (either fixed or dynamic).
4690   *
4691   * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4692   *                       {@link ng.$location#hash $location.hash()} will be used.
4693   *
4694   * @property {(number|function|jqLite)} yOffset
4695   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4696   * positioned elements at the top of the page, such as navbars, headers etc.
4697   *
4698   * `yOffset` can be specified in various ways:
4699   * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4700   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4701   *   a number representing the offset (in pixels).<br /><br />
4702   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4703   *   the top of the page to the element's bottom will be used as offset.<br />
4704   *   **Note**: The element will be taken into account only as long as its `position` is set to
4705   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adju
4705st
4706   *   their height and/or positioning according to the viewport's size.
4707   *
4708   * <br />
4709   * <div class="alert alert-warning">
4710   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4711   * not some child element.
4712   * </div>
4713   *
4714   * @example
4715     <example module="anchorScrollExample">
4716       <file name="index.html">
4717         <div id="scrollArea" ng-controller="ScrollController">
4718           <a ng-click="gotoBottom()">Go to bottom</a>
4719           <a id="bottom"></a> You're at the bottom!
4720         </div>
4721       </file>
4722       <file name="script.js">
4723         angular.module('anchorScrollExample', [])
4724           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4725             function ($scope, $location, $anchorScroll) {
4726               $scope.gotoBottom = function() {
4727                 // set the location.hash to the id of
4728                 // the element you wish to scroll to.
4729                 $location.hash('bottom');
4730
4731                 // call $anchorScroll()
4732                 $anchorScroll();
4733               };
4734             }]);
4735       </file>
4736       <file name="style.css">
4737         #scrollArea {
4738           height: 280px;
4739           overflow: auto;
4740         }
4741
4742         #bottom {
4743           display: block;
4744           margin-top: 2000px;
4745         }
4746       </file>
4747     </example>
4748   *
4749   * <hr />
4750   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4751   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4752   *
4753   * @example
4754     <example module="anchorScrollOffsetExample">
4755       <file name="index.html">
4756         <div class="fixed-header" ng-controller="headerCtrl">
4757           <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4758             Go to anchor {{x}}
4759           </a>
4760         </div>
4761         <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4762           Anchor {{x}} of 5
4763         </div>
4764       </file>
4765       <file name="script.js">
4766         angular.module('anchorScrollOffsetExample', [])
4767           .run(['$anchorScroll', function($anchorScroll) {
4768             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4769           }])
4770           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4771             function ($anchorScroll, $location, $scope) {
vendor: 10,461 bytes, lines 4772-5097
4772               $scope.gotoAnchor = function(x) {
4773                 var newHash = 'anchor' + x;
4774                 if ($location.hash() !== newHash) {
4775                   // set the $location.hash to `newHash` and
4776                   // $anchorScroll will automatically scroll to it
4777                   $location.hash('anchor' + x);
4778                 } else {
4779                   // call $anchorScroll() explicitly,
4780                   // since $location.hash hasn't changed
4781                   $anchorScroll();
4782                 }
4783               };
4784             }
4785           ]);
4786       </file>
4787       <file name="style.css">
4788         body {
4789           padding-top: 50px;
4790         }
4791
4792         .anchor {
4793           border: 2px dashed DarkOrchid;
4794           padding: 10px 10px 200px 10px;
4795         }
4796
4797         .fixed-header {
4798           background-color: rgba(0, 0, 0, 0.2);
4799           height: 50px;
4800           position: fixed;
4801           top: 0; left: 0; right: 0;
4802         }
4803
4804         .fixed-header > a {
4805           display: inline-block;
4806           margin: 5px 15px;
4807         }
4808       </file>
4809     </example>
4810   */
4811  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4812    var document = $window.document;
4813
4814    // Helper function to get first anchor from a NodeList
4815    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4816    //  and working in all supported browsers.)
4817    function getFirstAnchor(list) {
4818      var result = null;
4819      Array.prototype.some.call(list, function(element) {
4820        if (nodeName_(element) === 'a') {
4821          result = element;
4822          return true;
4823        }
4824      });
4825      return result;
4826    }
4827
4828    function getYOffset() {
4829
4830      var offset = scroll.yOffset;
4831
4832      if (isFunction(offset)) {
4833        offset = offset();
4834      } else if (isElement(offset)) {
4835        var elem = offset[0];
4836        var style = $window.getComputedStyle(elem);
4837        if (style.position !== 'fixed') {
4838          offset = 0;
4839        } else {
4840          offset = elem.getBoundingClientRect().bottom;
4841        }
4842      } else if (!isNumber(offset)) {
4843        offset = 0;
4844      }
4845
4846      return offset;
4847    }
4848
4849    function scrollTo(elem) {
4850      if (elem) {
4851        elem.scrollIntoView();
4852
4853        var offset = getYOffset();
4854
4855        if (offset) {
4856          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4857          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4858          // top of the viewport.
4859          //
4860          // IF the number of pixels from the top of `elem` to the end of the page's content is less
4861          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4862          // way down the page.
4863          //
4864          // This is often the case for elements near the bottom of the page.
4865          //
4866          // In such cases we do not need to scroll the whole `offset` up, just the difference between
4867          // the top of the element and the offset, which is enough to align the top of `elem` at the
4868          // desired position.
4869          var elemTop = elem.getBoundingClientRect().top;
4870          $window.scrollBy(0, elemTop - offset);
4871        }
4872      } else {
4873        $window.scrollTo(0, 0);
4874      }
4875    }
4876
4877    function scroll(hash) {
4878      hash = isString(hash) ? hash : $location.hash();
4879      var elm;
4880
4881      // empty hash, scroll to the top of the page
4882      if (!hash) scrollTo(null);
4883
4884      // element with given id
4885      else if ((elm = document.getElementById(hash))) scrollTo(elm);
4886
4887      // first anchor with given name :-D
4888      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4889
4890      // no element and hash == 'top', scroll to the top of the page
4891      else if (hash === 'top') scrollTo(null);
4892    }
4893
4894    // does not scroll when user clicks on anchor link that is currently on
4895    // (no url change, no $location.hash() change), browser native does scroll
4896    if (autoScrollingEnabled) {
4897      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4898        function autoScrollWatchAction(newVal, oldVal) {
4899          // skip the initial scroll if $location.hash is empty
4900          if (newVal === oldVal && newVal === '') return;
4901
4902          jqLiteDocumentLoaded(function() {
4903            $rootScope.$evalAsync(scroll);
4904          });
4905        });
4906    }
4907
4908    return scroll;
4909  }];
4910}
4911
4912var $animateMinErr = minErr('$animate');
4913var ELEMENT_NODE = 1;
4914var NG_ANIMATE_CLASSNAME = 'ng-animate';
4915
4916function mergeClasses(a,b) {
4917  if (!a && !b) return '';
4918  if (!a) return b;
4919  if (!b) return a;
4920  if (isArray(a)) a = a.join(' ');
4921  if (isArray(b)) b = b.join(' ');
4922  return a + ' ' + b;
4923}
4924
4925function extractElementNode(element) {
4926  for (var i = 0; i < element.length; i++) {
4927    var elm = element[i];
4928    if (elm.nodeType === ELEMENT_NODE) {
4929      return elm;
4930    }
4931  }
4932}
4933
4934function splitClasses(classes) {
4935  if (isString(classes)) {
4936    classes = classes.split(' ');
4937  }
4938
4939  // Use createMap() to prevent class assumptions involving property names in
4940  // Object.prototype
4941  var obj = createMap();
4942  forEach(classes, function(klass) {
4943    // sometimes the split leaves empty string values
4944    // incase extra spaces were applied to the options
4945    if (klass.length) {
4946      obj[klass] = true;
4947    }
4948  });
4949  return obj;
4950}
4951
4952// if any other type of options value besides an Object value is
4953// passed into the $animate.method() animation then this helper code
4954// will be run which will ignore it. While this patch is not the
4955// greatest solution to this, a lot of existing plugins depend on
4956// $animate to either call the callback (< 1.2) or return a promise
4957// that can be changed. This helper function ensures that the options
4958// are wiped clean incase a callback function is provided.
4959function prepareAnimateOptions(options) {
4960  return isObject(options)
4961      ? options
4962      : {};
4963}
4964
4965var $$CoreAnimateJsProvider = function() {
4966  this.$get = function() {};
4967};
4968
4969// this is prefixed with Core since it conflicts with
4970// the animateQueueProvider defined in ngAnimate/animateQueue.js
4971var $$CoreAnimateQueueProvider = function() {
4972  var postDigestQueue = new HashMap();
4973  var postDigestElements = [];
4974
4975  this.$get = ['$$AnimateRunner', '$rootScope',
4976       function($$AnimateRunner,   $rootScope) {
4977    return {
4978      enabled: noop,
4979      on: noop,
4980      off: noop,
4981      pin: noop,
4982
4983      push: function(element, event, options, domOperation) {
4984        domOperation        && domOperation();
4985
4986        options = options || {};
4987        options.from        && element.css(options.from);
4988        options.to          && element.css(options.to);
4989
4990        if (options.addClass || options.removeClass) {
4991          addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
4992        }
4993
4994        var runner = new $$AnimateRunner(); // jshint ignore:line
4995
4996        // since there are no animations to run the runner needs to be
4997        // notified that the animation call is complete.
4998        runner.complete();
4999        return runner;
5000      }
5001    };
5002
5003
5004    function updateData(data, classes, value) {
5005      var changed = false;
5006      if (classes) {
5007        classes = isString(classes) ? classes.split(' ') :
5008                  isArray(classes) ? classes : [];
5009        forEach(classes, function(className) {
5010          if (className) {
5011            changed = true;
5012            data[className] = value;
5013          }
5014        });
5015      }
5016      return changed;
5017    }
5018
5019    function handleCSSClassChanges() {
5020      forEach(postDigestElements, function(element) {
5021        var data = postDigestQueue.get(element);
5022        if (data) {
5023          var existing = splitClasses(element.attr('class'));
5024          var toAdd = '';
5025          var toRemove = '';
5026          forEach(data, function(status, className) {
5027            var hasClass = !!existing[className];
5028            if (status !== hasClass) {
5029              if (status) {
5030                toAdd += (toAdd.length ? ' ' : '') + className;
5031              } else {
5032                toRemove += (toRemove.length ? ' ' : '') + className;
5033              }
5034            }
5035          });
5036
5037          forEach(element, function(elm) {
5038            toAdd    && jqLiteAddClass(elm, toAdd);
5039            toRemove && jqLiteRemoveClass(elm, toRemove);
5040          });
5041          postDigestQueue.remove(element);
5042        }
5043      });
5044      postDigestElements.length = 0;
5045    }
5046
5047
5048    function addRemoveClassesPostDigest(element, add, remove) {
5049      var data = postDigestQueue.get(element) || {};
5050
5051      var classesAdded = updateData(data, add, true);
5052      var classesRemoved = updateData(data, remove, false);
5053
5054      if (classesAdded || classesRemoved) {
5055
5056        postDigestQueue.put(element, data);
5057        postDigestElements.push(element);
5058
5059        if (postDigestElements.length === 1) {
5060          $rootScope.$$postDigest(handleCSSClassChanges);
5061        }
5062      }
5063    }
5064  }];
5065};
5066
5067/**
5068 * @ngdoc provider
5069 * @name $animateProvider
5070 *
5071 * @description
5072 * Default implementation of $animate that doesn't perform any animations, instead just
5073 * synchronously performs DOM updates and resolves the returned runner promise.
5074 *
5075 * In order to enable animations the `ngAnimate` module has to be loaded.
5076 *
5077 * To see the functional implementation check out `src/ngAnimate/animate.js`.
5078 */
5079var $AnimateProvider = ['$provide', function($provide) {
5080  var provider = this;
5081
5082  this.$$registeredAnimations = Object.create(null);
5083
5084   /**
5085   * @ngdoc method
5086   * @name $animateProvider#register
5087   *
5088   * @description
5089   * Registers a new injectable animation factory function. The factory function produces the
5090   * animation object which contains callback functions for each event that is expected to be
5091   * animated.
5092   *
5093   *   * `eventFn`: `function(element, ... , doneFunction, options)`
5094   *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
5095   *   on the type of animation additional arguments will be injected into the animation function. The
5096   *   list below explains the function signatures for the different animation methods:
5097   *
5098   *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5099   *   - addClass: function(element, addedClasses, doneFunction, options)
5100   *   - removeClass: function(element, removedClasses, doneFunction, options)
5101   *   - enter, leave, move: function(element, doneFunction, options)
5102   *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
5103   *
5104   *   Make sure to trigger the `doneFunction` once the animation is fully complete.
5105   *
5106   * ```js
5107   *   return {
5108   *     //enter, leave, move signature
5109   *     eventFn : function(element, done, options) {
5110   *       //code to run the animation
5111   *       //once complete, then run done()
5112   *       return function endFunction(wasCancelled) {
5113   *         //code to cancel the animation
5114   *       }
5115   *     }
5116   *   }
5117   * ```
5118   *
5119   * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5120   * @param {Function} factory The factory function that will be executed to return the animation
5121   *                           object.
5122   */
5123  this.register = function(name, factory) {
5124    if (name && name.charAt(0) !== '.') {
5125      throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5126    }
5127
5128    var key = name + '-animation';
5129    provider.$$registeredAnimations[name.substr(1)] = key;
5130    $provide.factory(key, factory);
5131  };
5132
5133  /**
5134   * @ngdoc method
5135   * @name $animateProvider#classNameFilter
5136   *
5137   * @description
5138   * Sets and/or returns the CSS class regular expression that is checked when performing
5139   * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5140   * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5141   * When setting the `classNameFilter` value, animations will only be performed on elements
5142   * that successfully match the filter expression. This in turn can boost performance
5143   * for low-powered devices as well as applications containing a lot of structural operations.
5144   * @param {RegExp=} expression The className expression which will be checked against all animations
5145   * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5146   */
5147  this.classNameFilter = function(expression) {
5148    if (arguments.length === 1) {
5149      this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5150      if (this.$$classNameFilter) {
5151        var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5152        if (reservedRegex.test(this.$$classNameFilter.toString())) {
5153          throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5154
5155        }
5156      }
5157    }
5158    return this.$$classNameFilter;
5159  };
5160
5161  this.$get = ['$$animateQueue', function($$animateQueue) {
5162    function domInsert(element, parentElement, afterElement) {
5163      // if for some reason the previous element was removed
5164      // from the dom sometime before this code runs then let's
5165      // just stick to using the parent element as the anchor
5166      if (afterElement) {
5167        var afterNode = extractElementNode(afterElement);
5168        if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5169          afterElement = null;
5170        }
5171      }
5172      afterElement ? afterElement.after(element) : parentElement.prepend(element);
5173    }
5174
5175    /**
5176     * @ngdoc service
5177     * @name $animate
5178     * @description The $animate service exposes a series of DOM utility methods that provide support
5179     * for animation hooks. The default behavior is the application of DOM operations, however,
5180     * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5181     * to ensure that animation runs with the triggered DOM operation.
5182     *
5183     * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5184     * included and only when it is active then the animation hooks that `$animate` triggers will be
5185     * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5186     * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5187     * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5188     *
5189     * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5190     *
5191     * To learn more about enabling animation support, click here to visit the
5192     * {@link ngAnimate ngAnimate module page}.
5193     */
5194    return {
5195      // we don't call it directly since non-existant arguments may
5196      // be interpreted as null within the sub enabled function
5197
5198      /**
5199       *
5200       * @ngdoc method
5201       * @name $animate#on
5202       * @kind function
5203       * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5204       *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5205       *    is fired with the following params:
5206       *
5207       * ```js
5208       * $animate.on('enter', container,
5209       *    function callback(element, phase) {
5210       *      // cool we detected an enter animation within the container
5211       *    }
5212       * );
5213       * ```
5214       *
5215       * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5216       * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5217       *     as well as among its children
5218       * @param {Function} callback the callback function that will be fired when the listener is triggered
5219       *
5220       * The arguments present in the callback function are:
5221       * * `element` - The captured DOM element that the animation was fired on.
5222       * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5223       */
5224      on: $$animateQueue.on,
5225
5226      /**
5227       *
5228       * @ngdoc method
5229       * @name $animate#off
5230       * @kind function
5231       * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5232       * can be used in three different ways depending on the arguments:
5233       *
5234       * ```js
5235       * // remove all the animation event listeners listening for `enter`
5236       * $animate.off('enter');
5237       *
5238       * // remove all the animation event listeners listening for `enter` on the given element and its children
5239       * $animate.off('enter', container);
5240       *
5241       * // remove the event listener function provided by `listenerFn` that is set
5242       * // to listen for `enter` on the given `element` as well as its children
5243       * $animate.off('enter', container, callback);
5244       * ```
5245       *
5246       * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5247       * @param {DOMElement=} container the container element the event listener was placed on
5248       * @param {Function=} callback the callback function that was registered as the listener
5249       */
5250      off: $$animateQueue.off,
5251
5252      /**
5253       * @ngdoc method
5254       * @name $animate#pin
5255       * @kind function
5256       * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5257       *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5258       *    element despite being outside the realm of the application or within another application. Say for example if the application
5259       *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5260       *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5261       *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5262       *
5263       *    Note that this feature is only active when the `ngAnimate` module is used.
5264       *
5265       * @param {DOMElement} element the external element that will be pinned
5266       * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5267       */
5268      pin: $$animateQueue.pin,
5269
5270      /**
5271       *
5272       * @ngdoc method
5273       * @name $animate#enabled
5274       * @kind function
5275       * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5276       * function can be called in four ways:
5277       *
5278       * ```js
5279       * // returns true or false
5280       * $animate.enabled();
5281       *
5282       * // changes the enabled state for all animations
5283       * $animate.enabled(false);
5284       * $animate.enabled(true);
5285       *
5286       * // returns true or false if animations are enabled for an element
5287       * $animate.enabled(element);
5288       *
5289       * // changes the enabled state for an element and its children
5290       * $animate.enabled(element, true);
5291       * $animate.enabled(element, false);
5292       * ```
5293       *
5294       * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5295       * @param {boolean=} enabled whether or not the animations will be enabled for the element
5296       *
5297       * @return {boolean} whether or not animations are enabled
5298       */
5299      enabled: $$animateQueue.enabled,
5300
5301      /**
5302       * @ngdoc method
5303       * @name $animate#cancel
5304       * @kind function
5305       * @description Cancels the provided animation.
5306       *
5307       * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5308       */
5309      cancel: function(runner) {
5310        runner.end && runner.end();
5311      },
5312
5313      /**
5314       *
5315       * @ngdoc method
5316       * @name $animate#enter
5317       * @kind function
5318       * @description Inserts the element into the DOM either after the `after` element (if provided) or
5319       *   as the first child within the `parent` element and then triggers an animation.
5320       *   A promise is returned that will be resolved during the next digest once the animation
5321       *   has completed.
5322       *
5323       * @param {DOMElement} element the element which will be inserted into the DOM
5324       * @param {DOMElement} parent the parent element which will append the element as
5325       *   a child (so long as the after element is not present)
5326       * @param {DOMElement=} after the sibling element after which the element will be appended
5327       * @param {object=} options an optional collection of options/styles that will be applied to the element
5328       *
5329       * @return {Promise} the animation callback promise
5330       */
5331      enter: function(element, parent, after, options) {
5332        parent = parent && jqLite(parent);
5333        after = after && jqLite(after);
5334        parent = parent || after.parent();
5335        domInsert(element, parent, after);
5336        return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5337      },
5338
5339      /**
5340       *
5341       * @ngdoc method
5342       * @name $animate#move
5343       * @kind function
5344       * @description Inserts (moves) the element into its new position in the DOM either after
5345       *   the `after` element (if provided) or as the first child within the `parent` element
5346       *   and then triggers an animation. A promise is returned that will be resolved
5347       *   during the next digest once the animation has completed.
5348       *
5349       * @param {DOMElement} element the element which will be moved into the new DOM position
5350       * @param {DOMElement} parent the parent element which will append the element as
5351       *   a child (so long as the after element is not present)
5352       * @param {DOMElement=} after the sibling element after which the element will be appended
5353       * @param {object=} options an optional collection of options/styles that will be applied to the element
5354       *
5355       * @return {Promise} the animation callback promise
5356       */
5357      move: function(element, parent, after, options) {
5358        parent = parent && jqLite(parent);
5359        after = after && jqLite(after);
5360        parent = parent || after.parent();
5361        domInsert(element, parent, after);
5362        return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5363      },
5364
5365      /**
5366       * @ngdoc method
5367       * @name $animate#leave
5368       * @kind function
5369       * @description Triggers an animation and then removes the element from the DOM.
5370       * When the function is called a promise is returned that will be resolved during the next
5371       * digest once the animation has completed.
5372       *
5373       * @param {DOMElement} element the element which will be removed from the DOM
5374       * @param {object=} options an optional collection of options/styles that will be applied to the element
5375       *
5376       * @return {Promise} the animation callback promise
5377       */
5378      leave: function(element, options) {
5379        return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5380          element.remove();
5381        });
5382      },
5383
5384      /**
5385       * @ngdoc method
5386       * @name $animate#addClass
5387       * @kind function
5388       *
5389       * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5390       *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5391       *   animation if element already contains the CSS class or if the class is removed at a later step.
5392       *   Note that class-based animations are treated differently compared to structural animations
5393       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5394       *   depending if CSS or JavaScript animations are used.
5395       *
5396       * @param {DOMElement} element the element which the CSS classes will be applied to
5397       * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5398       * @param {object=} options an optional collection of options/styles that will be applied to the element
5399       *
5400       * @return {Promise} the animation callback promise
5401       */
5402      addClass: function(element, className, options) {
5403        options = prepareAnimateOptions(options);
5404        options.addClass = mergeClasses(options.addclass, className);
5405        return $$animateQueue.push(element, 'addClass', options);
5406      },
5407
5408      /**
5409       * @ngdoc method
5410       * @name $animate#removeClass
5411       * @kind function
5412       *
5413       * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5414       *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5415       *   animation if element does not contain the CSS class or if the class is added at a later step.
5416       *   Note that class-based animations are treated differently compared to structural animations
5417       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5418       *   depending if CSS or JavaScript animations are used.
5419       *
5420       * @param {DOMElement} element the element which the CSS classes will be applied to
5421       * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5422       * @param {object=} options an optional collection of options/styles that will be applied to the element
5423       *
5424       * @return {Promise} the animation callback promise
5425       */
5426      removeClass: function(element, className, options) {
5427        options = prepareAnimateOptions(options);
5428        options.removeClass = mergeClasses(options.removeClass, className);
5429        return $$animateQueue.push(element, 'removeClass', options);
5430      },
5431
5432      /**
5433       * @ngdoc method
5434       * @name $animate#setClass
5435       * @kind function
5436       *
5437       * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5438       *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5439       *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5440       *    passed. Note that class-based animations are treated differently compared to structural animations
5441       *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5442       *    depending if CSS or JavaScript animations are used.
5443       *
5444       * @param {DOMElement} element the element which the CSS classes will be applied to
5445       * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5446       * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5447       * @param {object=} options an optional collection of options/styles that will be applied to the element
5448       *
5449       * @return {Promise} the animation callback promise
5450       */
5451      setClass: function(element, add, remove, options) {
5452        options = prepareAnimateOptions(options);
5453        options.addClass = mergeClasses(options.addClass, add);
5454        options.removeClass = mergeClasses(options.removeClass, remove);
5455        return $$animateQueue.push(element, 'setClass', options);
5456      },
5457
5458      /**
5459       * @ngdoc method
5460       * @name $animate#animate
5461       * @kind function
5462       *
5463       * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5464       * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
5465       * on the provided styles. For example, if a transition animation is set for the given classNamem, then the provided `from` and
5466       * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
5467       * style in `to`, the style in `from` is applied immediately, and no animation is run.
5468       * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
5469       * method (or as part of the `options` parameter):
5470       *
5471       * ```js
5472       * ngModule.animation('.my-inline-animation', function() {
5473       *   return {
5474       *     animate : function(element, from, to, done, options) {
5475       *       //animation
5476       *       done();
5477       *     }
5478       *   }
5479       * });
5480       * ```
5481       *
5482       * @param {DOMElement} element the element which the CSS styles will be applied to
5483       * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5484       * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5485       * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5486       *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5487       *    (Note that if no animation is detected then this value will not be applied to the element.)
5488       * @param {object=} options an optional collection of options/styles that will be applied to the element
5489       *
5490       * @return {Promise} the animation callback promise
5491       */
5492      animate: function(element, from, to, className, options) {
5493        options = prepareAnimateOptions(options);
5494        options.from = options.from ? extend(options.from, from) : from;
5495        options.to   = options.to   ? extend(options.to, to)     : to;
5496
5497        className = className || 'ng-inline-animate';
vendor: 6,625 bytes, lines 5498-5759
5498        options.tempClasses = mergeClasses(options.tempClasses, className);
5499        return $$animateQueue.push(element, 'animate', options);
5500      }
5501    };
5502  }];
5503}];
5504
5505var $$AnimateAsyncRunFactoryProvider = function() {
5506  this.$get = ['$$rAF', function($$rAF) {
5507    var waitQueue = [];
5508
5509    function waitForTick(fn) {
5510      waitQueue.push(fn);
5511      if (waitQueue.length > 1) return;
5512      $$rAF(function() {
5513        for (var i = 0; i < waitQueue.length; i++) {
5514          waitQueue[i]();
5515        }
5516        waitQueue = [];
5517      });
5518    }
5519
5520    return function() {
5521      var passed = false;
5522      waitForTick(function() {
5523        passed = true;
5524      });
5525      return function(callback) {
5526        passed ? callback() : waitForTick(callback);
5527      };
5528    };
5529  }];
5530};
5531
5532var $$AnimateRunnerFactoryProvider = function() {
5533  this.$get = ['$q', '$sniffer', '$$animateAsyncRun',
5534       function($q,   $sniffer,   $$animateAsyncRun) {
5535
5536    var INITIAL_STATE = 0;
5537    var DONE_PENDING_STATE = 1;
5538    var DONE_COMPLETE_STATE = 2;
5539
5540    AnimateRunner.chain = function(chain, callback) {
5541      var index = 0;
5542
5543      next();
5544      function next() {
5545        if (index === chain.length) {
5546          callback(true);
5547          return;
5548        }
5549
5550        chain[index](function(response) {
5551          if (response === false) {
5552            callback(false);
5553            return;
5554          }
5555          index++;
5556          next();
5557        });
5558      }
5559    };
5560
5561    AnimateRunner.all = function(runners, callback) {
5562      var count = 0;
5563      var status = true;
5564      forEach(runners, function(runner) {
5565        runner.done(onProgress);
5566      });
5567
5568      function onProgress(response) {
5569        status = status && response;
5570        if (++count === runners.length) {
5571          callback(status);
5572        }
5573      }
5574    };
5575
5576    function AnimateRunner(host) {
5577      this.setHost(host);
5578
5579      this._doneCallbacks = [];
5580      this._runInAnimationFrame = $$animateAsyncRun();
5581      this._state = 0;
5582    }
5583
5584    AnimateRunner.prototype = {
5585      setHost: function(host) {
5586        this.host = host || {};
5587      },
5588
5589      done: function(fn) {
5590        if (this._state === DONE_COMPLETE_STATE) {
5591          fn();
5592        } else {
5593          this._doneCallbacks.push(fn);
5594        }
5595      },
5596
5597      progress: noop,
5598
5599      getPromise: function() {
5600        if (!this.promise) {
5601          var self = this;
5602          this.promise = $q(function(resolve, reject) {
5603            self.done(function(status) {
5604              status === false ? reject() : resolve();
5605            });
5606          });
5607        }
5608        return this.promise;
5609      },
5610
5611      then: function(resolveHandler, rejectHandler) {
5612        return this.getPromise().then(resolveHandler, rejectHandler);
5613      },
5614
5615      'catch': function(handler) {
5616        return this.getPromise()['catch'](handler);
5617      },
5618
5619      'finally': function(handler) {
5620        return this.getPromise()['finally'](handler);
5621      },
5622
5623      pause: function() {
5624        if (this.host.pause) {
5625          this.host.pause();
5626        }
5627      },
5628
5629      resume: function() {
5630        if (this.host.resume) {
5631          this.host.resume();
5632        }
5633      },
5634
5635      end: function() {
5636        if (this.host.end) {
5637          this.host.end();
5638        }
5639        this._resolve(true);
5640      },
5641
5642      cancel: function() {
5643        if (this.host.cancel) {
5644          this.host.cancel();
5645        }
5646        this._resolve(false);
5647      },
5648
5649      complete: function(response) {
5650        var self = this;
5651        if (self._state === INITIAL_STATE) {
5652          self._state = DONE_PENDING_STATE;
5653          self._runInAnimationFrame(function() {
5654            self._resolve(response);
5655          });
5656        }
5657      },
5658
5659      _resolve: function(response) {
5660        if (this._state !== DONE_COMPLETE_STATE) {
5661          forEach(this._doneCallbacks, function(fn) {
5662            fn(response);
5663          });
5664          this._doneCallbacks.length = 0;
5665          this._state = DONE_COMPLETE_STATE;
5666        }
5667      }
5668    };
5669
5670    return AnimateRunner;
5671  }];
5672};
5673
5674/**
5675 * @ngdoc service
5676 * @name $animateCss
5677 * @kind object
5678 *
5679 * @description
5680 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5681 * then the `$animateCss` service will actually perform animations.
5682 *
5683 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5684 */
5685var $CoreAnimateCssProvider = function() {
5686  this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
5687
5688    return function(element, initialOptions) {
5689      // all of the animation functions should create
5690      // a copy of the options data, however, if a
5691      // parent service has already created a copy then
5692      // we should stick to using that
5693      var options = initialOptions || {};
5694      if (!options.$$prepared) {
5695        options = copy(options);
5696      }
5697
5698      // there is no point in applying the styles since
5699      // there is no animation that goes on at all in
5700      // this version of $animateCss.
5701      if (options.cleanupStyles) {
5702        options.from = options.to = null;
5703      }
5704
5705      if (options.from) {
5706        element.css(options.from);
5707        options.from = null;
5708      }
5709
5710      /* jshint newcap: false */
5711      var closed, runner = new $$AnimateRunner();
5712      return {
5713        start: run,
5714        end: run
5715      };
5716
5717      function run() {
5718        $$rAF(function() {
5719          applyAnimationContents();
5720          if (!closed) {
5721            runner.complete();
5722          }
5723          closed = true;
5724        });
5725        return runner;
5726      }
5727
5728      function applyAnimationContents() {
5729        if (options.addClass) {
5730          element.addClass(options.addClass);
5731          options.addClass = null;
5732        }
5733        if (options.removeClass) {
5734          element.removeClass(options.removeClass);
5735          options.removeClass = null;
5736        }
5737        if (options.to) {
5738          element.css(options.to);
5739          options.to = null;
5740        }
5741      }
5742    };
5743  }];
5744};
5745
5746/* global stripHash: true */
5747
5748/**
5749 * ! This is a private undocumented service !
5750 *
5751 * @name $browser
5752 * @requires $log
5753 * @description
5754 * This object has two goals:
5755 *
5756 * - hide all the global state in the browser caused by the window object
5757 * - abstract away all the browser specific features and inconsistencies
5758 *
5759 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5760 * service, which can be used for convenient testing of the application without the interaction with
5761 * the real browser apis.
5762 */
5763/**
5764 * @param {object} window The global window object.
5765 * @param {object} document jQuery wrapped document.
5766 * @param {object} $log window.console or an object with the same interface.
5767 * @param {object} $sniffer $sniffer service
5768 */
5769function Browser(window, document, $log, $sniffer) {
5770  var self = this,
5771      rawDocument = document[0],
5772      location = window.location,
5773      history = window.history,
5774      setTimeout = window.setTimeout,
5775      clearTimeout = window.clearTimeout,
5776      pendingDeferIds = {};
5777
5778  self.isMock = false;
5779
5780  var outstandingRequestCount = 0;
5781  var outstandingRequestCallbacks = [];
5782
5783  // TODO(vojta): remove this temporary api
5784  self.$$completeOutstandingRequest = completeOutstandingRequest;
5785  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5786
5787  /**
5788   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5789   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5790   */
5791  function completeOutstandingRequest(fn) {
5792    try {
5793      fn.apply(null, sliceArgs(arguments, 1));
5794    } finally {
5795      outstandingRequestCount--;
5796      if (outstandingRequestCount === 0) {
5797        while (outstandingRequestCallbacks.length) {
5798          try {
5799            outstandingRequestCallbacks.pop()();
5800          } catch (e) {
5801            $log.error(e);
5802          }
5803        }
5804      }
5805    }
5806  }
5807
5808  function getHash(url) {
5809    var index = url.indexOf('#');
5810    return index === -1 ? '' : url.substr(index);
5811  }
5812
5813  /**
5814   * @private
5815   * Note: this method is used only by scenario runner
5816   * TODO(vojta): prefix this method with $$ ?
5817   * @param {function()} callback Function that will be called when no outstanding request
5818   */
5819  self.notifyWhenNoOutstandingRequests = function(callback) {
5820    if (outstandingRequestCount === 0) {
5821      callback();
5822    } else {
5823      outstandingRequestCallbacks.push(callback);
5824    }
5825  };
5826
5827  //////////////////////////////////////////////////////////////
5828  // URL API
5829  //////////////////////////////////////////////////////////////
5830
5831  var cachedState, lastHistoryState,
5832      lastBrowserUrl = location.href,
5833      baseElement = document.find('base'),
5834      pendingLocation = null;
5835
5836  cacheState();
5837  lastHistoryState = cachedState;
5838
5839  /**
5840   * @name $browser#url
5841   *
5842   * @description
5843   * GETTER:
5844   * Without any argument, this method just returns current value of location.href.
5845   *
5846   * SETTER:
5847   * With at least one argument, this method sets url to new value.
5848   * If html5 history api supported, pushState/replaceState is used, otherwise
5849   * location.href/location.replace is used.
5850   * Returns its own instance to allow chaining
5851   *
5852   * NOTE: this api is intended for use only by the $location service. Please use the
5853   * {@link ng.$location $location service} to change url.
5854   *
5855   * @param {string} url New url (when used as setter)
5856   * @param {boolean=} replace Should new url replace current history record?
5857   * @param {object=} state object to use with pushState/replaceState
5858   */
5859  self.url = function(url, replace, state) {
5860    // In modern browsers `history.state` is `null` by default; treating it separately
5861    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5862    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5863    if (isUndefined(state)) {
5864      state = null;
5865    }
5866
5867    // Android Browser BFCache causes location, history reference to become stale.
5868    if (location !== window.location) location = window.location;
5869    if (history !== window.history) history = window.history;
5870
5871    // setter
5872    if (url) {
5873      var sameState = lastHistoryState === state;
5874
5875      // Don't change anything if previous and current URLs and states match. This also prevents
5876      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5877      // See https://github.com/angular/angular.js/commit/ffb2701
5878      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5879        return self;
5880      }
5881      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5882      lastBrowserUrl = url;
5883      lastHistoryState = state;
5884      // Don't use history API if only the hash changed
5885      // due to a bug in IE10/IE11 which leads
5886      // to not firing a `hashchange` nor `popstate` event
5887      // in some cases (see #9143).
5888      if ($sniffer.history && (!sameBase || !sameState)) {
5889        history[replace ? 'replaceState' : 'pushState'](state, '', url);
5890        cacheState();
5891        // Do the assignment again so that those two variables are referentially identical.
5892        lastHistoryState = cachedState;
5893      } else {
5894        if (!sameBase || pendingLocation) {
5895          pendingLocation = url;
5896        }
5897        if (replace) {
5898          location.replace(url);
5899        } else if (!sameBase) {
5900          location.href = url;
5901        } else {
5902          location.hash = getHash(url);
5903        }
5904        if (location.href !== url) {
5905          pendingLocation = url;
5906        }
5907      }
5908      return self;
5909    // getter
5910    } else {
5911      // - pendingLocation is needed as browsers don't allow to read out
5912      //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5913      //   https://openradar.appspot.com/22186109).
5914      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5915      return pendingLocation || location.href.replace(/%27/g,"'");
5916    }
5917  };
5918
5919  /**
5920   * @name $browser#state
5921   *
5922   * @description
5923   * This method is a getter.
5924   *
5925   * Return history.state or null if history.state is undefined.
5926   *
5927   * @returns {object} state
5928   */
5929  self.state = function() {
5930    return cachedState;
5931  };
5932
5933  var urlChangeListeners = [],
5934      urlChangeInit = false;
5935
5936  function cacheStateAndFireUrlChange() {
5937    pendingLocation = null;
5938    cacheState();
5939    fireUrlChange();
5940  }
5941
5942  function getCurrentState() {
5943    try {
5944      return history.state;
5945    } catch (e) {
5946      // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5947    }
5948  }
5949
5950  // This variable should be used *only* inside the cacheState function.
5951  var lastCachedState = null;
5952  function cacheState() {
5953    // This should be the only place in $browser where `history.state` is read.
5954    cachedState = getCurrentState();
5955    cachedState = isUndefined(cachedState) ? null : cachedState;
5956
5957    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5958    if (equals(cachedState, lastCachedState)) {
5959      cachedState = lastCachedState;
5960    }
5961    lastCachedState = cachedState;
5962  }
5963
5964  function fireUrlChange() {
5965    if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5966      return;
5967    }
5968
5969    lastBrowserUrl = self.url();
5970    lastHistoryState = cachedState;
5971    forEach(urlChangeListeners, function(listener) {
5972      listener(self.url(), cachedState);
5973    });
5974  }
5975
5976  /**
5977   * @name $browser#onUrlChange
5978   *
5979   * @description
5980   * Register callback function that will be called, when url changes.
5981   *
5982   * It's only called when the url is changed from outside of angular:
5983   * - user types different url into address bar
5984   * - user clicks on history (forward/back) button
5985   * - user clicks on a link
5986   *
5987   * It's not called when url is changed by $browser.url() method
5988   *
5989   * The listener gets called with new url as parameter.
5990   *
5991   * NOTE: this api is intended for use only by the $location service. Please use the
5992   * {@link ng.$location $location service} to monitor url changes in angular apps.
5993   *
5994   * @param {function(string)} listener Listener function to be called when url changes.
5995   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5996   */
5997  self.onUrlChange = function(callback) {
5998    // TODO(vojta): refactor to use node's syntax for events
5999    if (!urlChangeInit) {
6000      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
6001      // don't fire popstate when user change the address bar and don't fire hashchange when url
6002      // changed by push/replaceState
6003
6004      // html5 history api - popstate event
6005      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
6006      // hashchange event
6007      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
6008
6009      urlChangeInit = true;
6010    }
6011
6012    urlChangeListeners.push(callback);
6013    return callback;
6014  };
6015
6016  /**
6017   * @private
6018   * Remove popstate and hashchange handler from window.
6019   *
6020   * NOTE: this api is intended for use only by $rootScope.
6021   */
6022  self.$$applicationDestroyed = function() {
6023    jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
6024  };
6025
6026  /**
6027   * Checks whether the url has changed outside of Angular.
6028   * Needs to be exported to be able to check for changes that have been done in sync,
6029   * as hashchange/popstate events fire in async.
6030   */
6031  self.$$checkUrlChange = fireUrlChange;
6032
6033  //////////////////////////////////////////////////////////////
6034  // Misc API
6035  //////////////////////////////////////////////////////////////
6036
6037  /**
6038   * @name $browser#baseHref
6039   *
6040   * @description
6041   * Returns current <base href>
6042   * (always relative - without domain)
6043   *
6044   * @returns {string} The current base href
6045   */
6046  self.baseHref = function() {
6047    var href = baseElement.attr('href');
6048    return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
6049  };
6050
6051  /**
6052   * @name $browser#defer
6053   * @param {function()} fn A function, who's execution should be deferred.
6054   * @param {number=} [delay=0] of milliseconds to defer the function execution.
6055   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
6056   *
6057   * @description
6058   * Executes a fn asynchronously via `setTimeout(fn, delay)`.
6059   *
6060   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
6061   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
6062   * via `$browser.defer.flush()`.
6063   *
6064   */
6065  self.defer = function(fn, delay) {
6066    var timeoutId;
6067    outstandingRequestCount++;
6068    timeoutId = setTimeout(function() {
6069      delete pendingDeferIds[timeoutId];
6070      completeOutstandingRequest(fn);
6071    }, delay || 0);
6072    pendingDeferIds[timeoutId] = true;
6073    return timeoutId;
6074  };
6075
6076
6077  /**
6078   * @name $browser#defer.cancel
6079   *
6080   * @description
6081   * Cancels a deferred task identified with `deferId`.
6082   *
6083   * @param {*} deferId Token returned by the `$browser.defer` function.
6084   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
6085   *                    canceled.
6086   */
6087  self.defer.cancel = function(deferId) {
6088    if (pendingDeferIds[deferId]) {
vendor: 4,704 bytes, lines 6089-6231
6089      delete pendingDeferIds[deferId];
6090      clearTimeout(deferId);
6091      completeOutstandingRequest(noop);
6092      return true;
6093    }
6094    return false;
6095  };
6096
6097}
6098
6099function $BrowserProvider() {
6100  this.$get = ['$window', '$log', '$sniffer', '$document',
6101      function($window, $log, $sniffer, $document) {
6102        return new Browser($window, $document, $log, $sniffer);
6103      }];
6104}
6105
6106/**
6107 * @ngdoc service
6108 * @name $cacheFactory
6109 *
6110 * @description
6111 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
6112 * them.
6113 *
6114 * ```js
6115 *
6116 *  var cache = $cacheFactory('cacheId');
6117 *  expect($cacheFactory.get('cacheId')).toBe(cache);
6118 *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
6119 *
6120 *  cache.put("key", "value");
6121 *  cache.put("another key", "another value");
6122 *
6123 *  // We've specified no options on creation
6124 *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
6125 *
6126 * ```
6127 *
6128 *
6129 * @param {string} cacheId Name or id of the newly created cache.
6130 * @param {object=} options Options object that specifies the cache behavior. Properties:
6131 *
6132 *   - `{number=}` `capacity` — turns the cache into LRU cache.
6133 *
6134 * @returns {object} Newly created cache object with the following set of methods:
6135 *
6136 * - `{object}` `info()` — Returns id, size, and options of cache.
6137 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
6138 *   it.
6139 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
6140 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
6141 * - `{void}` `removeAll()` — Removes all cached values.
6142 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
6143 *
6144 * @example
6145   <example module="cacheExampleApp">
6146     <file name="index.html">
6147       <div ng-controller="CacheController">
6148         <input ng-model="newCacheKey" placeholder="Key">
6149         <input ng-model="newCacheValue" placeholder="Value">
6150         <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
6151
6152         <p ng-if="keys.length">Cached Values</p>
6153         <div ng-repeat="key in keys">
6154           <span ng-bind="key"></span>
6155           <span>: </span>
6156           <b ng-bind="cache.get(key)"></b>
6157         </div>
6158
6159         <p>Cache Info</p>
6160         <div ng-repeat="(key, value) in cache.info()">
6161           <span ng-bind="key"></span>
6162           <span>: </span>
6163           <b ng-bind="value"></b>
6164         </div>
6165       </div>
6166     </file>
6167     <file name="script.js">
6168       angular.module('cacheExampleApp', []).
6169         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6170           $scope.keys = [];
6171           $scope.cache = $cacheFactory('cacheId');
6172           $scope.put = function(key, value) {
6173             if (angular.isUndefined($scope.cache.get(key))) {
6174               $scope.keys.push(key);
6175             }
6176             $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6177           };
6178         }]);
6179     </file>
6180     <file name="style.css">
6181       p {
6182         margin: 10px 0 3px;
6183       }
6184     </file>
6185   </example>
6186 */
6187function $CacheFactoryProvider() {
6188
6189  this.$get = function() {
6190    var caches = {};
6191
6192    function cacheFactory(cacheId, options) {
6193      if (cacheId in caches) {
6194        throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6195      }
6196
6197      var size = 0,
6198          stats = extend({}, options, {id: cacheId}),
6199          data = createMap(),
6200          capacity = (options && options.capacity) || Number.MAX_VALUE,
6201          lruHash = createMap(),
6202          freshEnd = null,
6203          staleEnd = null;
6204
6205      /**
6206       * @ngdoc type
6207       * @name $cacheFactory.Cache
6208       *
6209       * @description
6210       * A cache object used to store and retrieve data, primarily used by
6211       * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6212       * templates and other data.
6213       *
6214       * ```js
6215       *  angular.module('superCache')
6216       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6217       *      return $cacheFactory('super-cache');
6218       *    }]);
6219       * ```
6220       *
6221       * Example test:
6222       *
6223       * ```js
6224       *  it('should behave like a cache', inject(function(superCache) {
6225       *    superCache.put('key', 'value');
6226       *    superCache.put('another key', 'another value');
6227       *
6228       *    expect(superCache.info()).toEqual({
6229       *      id: 'super-cache',
6230       *      size: 2
6231       *    });
6232       *
6233       *    superCache.remove('another key');
6234       *    expect(superCache.get('another key')).toBeUndefined();
6235       *
6236       *    superCache.removeAll();
6237       *    expect(superCache.info()).toEqual({
6238       *      id: 'super-cache',
6239       *      size: 0
6240       *    });
6241       *  }));
6242       * ```
6243       */
6244      return caches[cacheId] = {
6245
6246        /**
6247         * @ngdoc method
6248         * @name $cacheFactory.Cache#put
6249         * @kind function
6250         *
6251         * @description
6252         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6253         * retrieved later, and incrementing the size of the cache if the key was not already
6254         * present in the cache. If behaving like an LRU cache, it will also remove stale
6255         * entries from the set.
6256         *
6257         * It will not insert undefined values into the cache.
6258         *
6259         * @param {string} key the key under which the cached data is stored.
6260         * @param {*} value the value to store alongside the key. If it is undefined, the key
6261         *    will not be stored.
6262         * @returns {*} the value stored.
6263         */
6264        put: function(key, value) {
6265          if (isUndefined(value)) return;
6266          if (capacity < Number.MAX_VALUE) {
6267            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6268
6269            refresh(lruEntry);
6270          }
6271
6272          if (!(key in data)) size++;
6273          data[key] = value;
6274
6275          if (size > capacity) {
6276            this.remove(staleEnd.key);
6277          }
6278
6279          return value;
6280        },
6281
6282        /**
6283         * @ngdoc method
6284         * @name $cacheFactory.Cache#get
6285         * @kind function
6286         *
6287         * @description
6288         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6289         *
6290         * @param {string} key the key of the data to be retrieved
6291         * @returns {*} the value stored.
6292         */
6293        get: function(key) {
6294          if (capacity < Number.MAX_VALUE) {
6295            var lruEntry = lruHash[key];
6296
6297            if (!lruEntry) return;
6298
6299            refresh(lruEntry);
6300          }
6301
6302          return data[key];
6303        },
6304
6305
6306        /**
6307         * @ngdoc method
6308         * @name $cacheFactory.Cache#remove
6309         * @kind function
6310         *
6311         * @description
6312         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6313         *
6314         * @param {string} key the key of the entry to be removed
6315         */
6316        remove: function(key) {
6317          if (capacity < Number.MAX_VALUE) {
6318            var lruEntry = lruHash[key];
6319
6320            if (!lruEntry) return;
6321
6322            if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6323            if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6324            link(lruEntry.n,lruEntry.p);
6325
6326            delete lruHash[key];
6327          }
6328
6329          if (!(key in data)) return;
6330
6331          delete data[key];
6332          size--;
6333        },
6334
6335
6336        /**
6337         * @ngdoc method
6338         * @name $cacheFactory.Cache#removeAll
6339         * @kind function
6340         *
6341         * @description
6342         * Clears the cache object of any entries.
6343         */
6344        removeAll: function() {
6345          data = createMap();
6346          size = 0;
6347          lruHash = createMap();
6348          freshEnd = staleEnd = null;
6349        },
6350
6351
6352        /**
6353         * @ngdoc method
6354         * @name $cacheFactory.Cache#destroy
6355         * @kind function
6356         *
6357         * @description
6358         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6359         * removing it from the {@link $cacheFactory $cacheFactory} set.
6360         */
6361        destroy: function() {
6362          data = null;
6363          stats = null;
6364          lruHash = null;
6365          delete caches[cacheId];
6366        },
6367
6368
6369        /**
6370         * @ngdoc method
6371         * @name $cacheFactory.Cache#info
6372         * @kind function
6373         *
6374         * @description
6375         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6376         *
6377         * @returns {object} an object with the following properties:
6378         *   <ul>
6379         *     <li>**id**: the id of the cache instance</li>
6380         *     <li>**size**: the number of entries kept in the cache instance</li>
6381         *     <li>**...**: any additional properties from the options object when creating the
6382         *       cache.</li>
6383         *   </ul>
6384         */
6385        info: function() {
6386          return extend({}, stats, {size: size});
vendor: 6,110 bytes, lines 6387-6575
6387        }
6388      };
6389
6390
6391      /**
6392       * makes the `entry` the freshEnd of the LRU linked list
6393       */
6394      function refresh(entry) {
6395        if (entry != freshEnd) {
6396          if (!staleEnd) {
6397            staleEnd = entry;
6398          } else if (staleEnd == entry) {
6399            staleEnd = entry.n;
6400          }
6401
6402          link(entry.n, entry.p);
6403          link(entry, freshEnd);
6404          freshEnd = entry;
6405          freshEnd.n = null;
6406        }
6407      }
6408
6409
6410      /**
6411       * bidirectionally links two entries of the LRU linked list
6412       */
6413      function link(nextEntry, prevEntry) {
6414        if (nextEntry != prevEntry) {
6415          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6416          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6417        }
6418      }
6419    }
6420
6421
6422  /**
6423   * @ngdoc method
6424   * @name $cacheFactory#info
6425   *
6426   * @description
6427   * Get information about all the caches that have been created
6428   *
6429   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6430   */
6431    cacheFactory.info = function() {
6432      var info = {};
6433      forEach(caches, function(cache, cacheId) {
6434        info[cacheId] = cache.info();
6435      });
6436      return info;
6437    };
6438
6439
6440  /**
6441   * @ngdoc method
6442   * @name $cacheFactory#get
6443   *
6444   * @description
6445   * Get access to a cache object by the `cacheId` used when it was created.
6446   *
6447   * @param {string} cacheId Name or id of a cache to access.
6448   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6449   */
6450    cacheFactory.get = function(cacheId) {
6451      return caches[cacheId];
6452    };
6453
6454
6455    return cacheFactory;
6456  };
6457}
6458
6459/**
6460 * @ngdoc service
6461 * @name $templateCache
6462 *
6463 * @description
6464 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6465 * can load templates directly into the cache in a `script` tag, or by consuming the
6466 * `$templateCache` service directly.
6467 *
6468 * Adding via the `script` tag:
6469 *
6470 * ```html
6471 *   <script type="text/ng-template" id="templateId.html">
6472 *     <p>This is the content of the template</p>
6473 *   </script>
6474 * ```
6475 *
6476 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6477 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6478 * element with ng-app attribute), otherwise the template will be ignored.
6479 *
6480 * Adding via the `$templateCache` service:
6481 *
6482 * ```js
6483 * var myApp = angular.module('myApp', []);
6484 * myApp.run(function($templateCache) {
6485 *   $templateCache.put('templateId.html', 'This is the content of the template');
6486 * });
6487 * ```
6488 *
6489 * To retrieve the template later, simply use it in your HTML:
6490 * ```html
6491 * <div ng-include=" 'templateId.html' "></div>
6492 * ```
6493 *
6494 * or get it via Javascript:
6495 * ```js
6496 * $templateCache.get('templateId.html')
6497 * ```
6498 *
6499 * See {@link ng.$cacheFactory $cacheFactory}.
6500 *
6501 */
6502function $TemplateCacheProvider() {
6503  this.$get = ['$cacheFactory', function($cacheFactory) {
6504    return $cacheFactory('templates');
6505  }];
6506}
6507
6508/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6509 *     Any commits to this file should be reviewed with security in mind.  *
6510 *   Changes to this file can potentially create security vulnerabilities. *
6511 *          An approval from 2 Core members with history of modifying      *
6512 *                         this file is required.                          *
6513 *                                                                         *
6514 *  Does the change somehow allow for arbitrary javascript to be executed? *
6515 *    Or allows for someone to change the prototype of built-in objects?   *
6516 *     Or gives undesired access to variables likes document or window?    *
6517 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6518
6519/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6520 *
6521 * DOM-related variables:
6522 *
6523 * - "node" - DOM Node
6524 * - "element" - DOM Element or Node
6525 * - "$node" or "$element" - jqLite-wrapped node or element
6526 *
6527 *
6528 * Compiler related stuff:
6529 *
6530 * - "linkFn" - linking fn of a single directive
6531 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6532 * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
6533 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6534 */
6535
6536
6537/**
6538 * @ngdoc service
6539 * @name $compile
6540 * @kind function
6541 *
6542 * @description
6543 * Compiles an HTML string or DOM into a template and produces a template function, which
6544 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6545 *
6546 * The compilation is a process of walking the DOM tree and matching DOM elements to
6547 * {@link ng.$compileProvider#directive directives}.
6548 *
6549 * <div class="alert alert-warning">
6550 * **Note:** This document is an in-depth reference of all directive options.
6551 * For a gentle introduction to directives with examples of common use cases,
6552 * see the {@link guide/directive directive guide}.
6553 * </div>
6554 *
6555 * ## Comprehensive Directive API
6556 *
6557 * There are many different options for a directive.
6558 *
6559 * The difference resides in the return value of the factory function.
6560 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6561 * or just the `postLink` function (all other properties will have the default values).
6562 *
6563 * <div class="alert alert-success">
6564 * **Best Practice:** It's recommended to use the "directive definition object" form.
6565 * </div>
6566 *
6567 * Here's an example directive declared with a Directive Definition Object:
6568 *
6569 * ```js
6570 *   var myModule = angular.module(...);
6571 *
6572 *   myModule.directive('directiveName', function factory(injectables) {
6573 *     var directiveDefinitionObject = {
6574 *       priority: 0,
6575 *       template: '<div></div>
6575', // or // function(tElement, tAttrs) { ... },
6576 *       // or
6577 *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6578 *       transclude: false,
6579 *       restrict: 'A',
6580 *       templateNamespace: 'html',
6581 *       scope: false,
6582 *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6583 *       controllerAs: 'stringIdentifier',
6584 *       bindToController: false,
6585 *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6586 *       compile: function compile(tElement, tAttrs, transclude) {
6587 *         return {
6588 *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6589 *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
6590 *         }
6591 *         // or
6592 *         // return function postLink( ... ) { ... }
6593 *       },
6594 *       // or
6595 *       // link: {
6596 *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6597 *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
6598 *       // }
6599 *       // or
6600 *       // link: function postLink( ... ) { ... }
6601 *     };
6602 *     return directiveDefinitionObject;
6603 *   });
6604 * ```
6605 *
6606 * <div class="alert alert-warning">
6607 * **Note:** Any unspecified options will use the default value. You can see the default values below.
6608 * </div>
6609 *
6610 * Therefore the above can be simplified as:
6611 *
6612 * ```js
6613 *   var myModule = angular.module(...);
6614 *
6615 *   myModule.directive('directiveName', function factory(injectables) {
6616 *     var directiveDefinitionObject = {
6617 *       link: function postLink(scope, iElement, iAttrs) { ... }
6618 *     };
6619 *     return directiveDefinitionObject;
6620 *     // or
6621 *     // return function postLink(scope, iElement, iAttrs) { ... }
6622 *   });
6623 * ```
6624 *
6625 *
6626 *
6627 * ### Directive Definition Object
6628 *
6629 * The directive definition object provides instructions to the {@link ng.$compile
6630 * compiler}. The attributes are:
6631 *
6632 * #### `multiElement`
6633 * When this property is set to true, the HTML compiler will collect DOM nodes between
6634 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6635 * together as the directive elements. It is recommended that this feature be used on directives
6636 * which are not strictly behavioral (such as {@link ngClick}), and which
6637 * do not manipulate or replace child nodes (such as {@link ngInclude}).
6638 *
6639 * #### `priority`
6640 * When there are multiple directives defined on a single DOM element, sometimes it
6641 * is necessary to specify the order in which the directives are applied. The `priority` is used
6642 * to sort the directives before their `compile` functions get called. Priority is defined as a
6643 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6644 * are also run in priority order, but post-link functions are run in reverse order. The order
6645 * of directives with the same priority is undefined. The default priority is `0`.
6646 *
6647 * #### `terminal`
6648 * If set to true then the current `priority` will be the last set of directives
6649 * which will execute (any directives at the current priority will still execute
6650 * as the order of execution on same `priority` is undefined). Note that expressions
6651 * and other directives used in the directive's template will also be excluded from execution.
6652 *
6653 * #### `scope`
6654 * The scope property can be `true`, an object or a falsy value:
6655 *
6656 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6657 *
6658 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6659 * the directive's element. If multiple directives on the same element request a new scope,
6660 * only one new scope is created. The new scope rule does not apply for the root of the template
6661 * since the root of the template always gets a new scope.
6662 *
6663 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6664 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6665 * scope. This is useful when creating reusable components, which should not accidentally read or modify
6666 * data in the parent scope.
6667 *
6668 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6669 * directive's element. These local properties are useful for aliasing values for templates. The keys in
6670 * the object hash map to the name of the property on the isolate scope; the values define how the property
6671 * is bound to the parent scope, via matching attributes on the directive's element:
6672 *
6673 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6674 *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
6675 *   attribute name is assumed to be the same as the local name.
6676 *   Given `<widget my-attr="hello {{name}}">` and widget definition
6677 *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
6678 *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
6679 *   `localName` property on the widget scope. The `name` is read from the parent scope (not
6680 *   component scope).
6681 *
6682 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
6683 *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
6684 *   name is specified then the attribute name is assumed to be the same as the local name.
6685 *   Given `<widget my-attr="parentModel">` and widget definition of
6686 *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
6687 *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6688 *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
6689 *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
6690 *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
6691 *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
6692 *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
6693 *
6694 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
6695 *   If no `attr` name is specified then the attribute name is assumed to be the same as the
6696 *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
6697 *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
6698 *   a function wrapper for the `count = count + value` expression. Often it's desirable to
6699 *   pass data from the isolated scope via an expression to the parent scope, this can be
6700 *   done by passing a map of local variable names and values into the expression wrapper fn.
6701 *   For example, if the expression is `increment(amount)` then we can specify the amount value
6702 *   by calling the `localFn` as `localFn({amount: 22})`.
6703 *
6704 * In general it's possible to apply more than one directive to one element, but there might be limitations
6705 * depending on the type of scope required by the directives. The following points will help explain these limitations.
6706 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6707 *
6708 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6709 * * **child scope** + **no scope** =>  Both directives will share one single child scope
6710 * * **child scope** + **child scope** =>  Both directives will share one single child scope
6711 * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
6712 * its parent's scope
6713 * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6714 * be applied to the same element.
6715 * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
6716 * cannot be applied to the same element.
6717 *
6718 *
6719 * #### `bindToController`
6720 * This property is used to bind scope properties directly to the controller. It can be either
6721 * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
6722 * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
6723 * definition: `controller: 'myCtrl as myAlias'`.
6724 *
6725 * When an isolate scope is used for a directive (see above), `bindToController: true` will
6726 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6727 * is instantiated, the initial values of the isolate scope bindings will be available if the controller is not an ES6 class.
6728 *
6729 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
6730 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
6731 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
6732 * scope (useful for component directives).
6733 *
6734 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
6735 *
6736 *
6737 * #### `controller`
6738 * Controller constructor function. The controller is instantiated before the
6739 * pre-linking phase and can be accessed by other directives (see
6740 * `require` attribute). This allows the directives to communicate with each other and augment
6741 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6742 *
6743 * * `$scope` - Current scope associated with the element
6744 * * `$element` - Current element
6745 * * `$attrs` - Current attributes object for the element
6746 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6747 *   `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
6748 *    * `scope`: (optional) override the scope.
6749 *    * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
6750 *    * `futureParentElement` (optional):
6751 *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6752 *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6753 *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6754 *          and when the `cloneLinkinFn` is passed,
6755 *          as those elements need to created and cloned in a special way when they are defined outside their
6756 *          usual containers (e.g. like `<svg>`).
6757 *        * See also the `directive.templateNamespace` property.
6758 *    * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
6759 *      then the default translusion is provided.
6760 *    The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
6761 *    `true` if the specified slot contains content (i.e. one or more DOM nodes).
6762 *
6763 * #### `require`
6764 * Require another directive and inject its controller as the fourth argument to the linking function. The
6765 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6766 * injected argument will be an array in corresponding order. If no such directive can be
6767 * found, or if the directive does not have a controller, then an error is raised (unless no link function
6768 * is specified, in which case error checking is skipped). The name can be prefixed with:
6769 *
6770 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6771 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6772 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6773 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6774 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6775 *   `null` to the `link` fn if not found.
6776 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6777 *   `null` to the `link` fn if not found.
6778 *
6779 *
6780 * #### `controllerAs`
6781 * Identifier name for a reference to the controller in the directive's scope.
6782 * This allows the controller to be referenced from the directive template. This is especially
6783 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6784 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6785 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6786 *
6787 *
6788 * #### `restrict`
6789 * String of subset of `EACM` which restricts the directive to a specific directive
6790 * declaration style. If omitted, the defaults (elements and attributes) are used.
6791 *
6792 * * `E` - Element name (default): `<my-directive></my-directive>`
6793 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6794 * * `C` - Class: `<div class="my-directive: exp;"></div>`
6795 * * `M` - Comment: `<!-- directive: my-directive exp -->`
6796 *
6797 *
6798 * #### `templateNamespace`
6799 * String representing the document type used by the markup in the template.
6800 * AngularJS needs this information as those elements need to be created and cloned
6801 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6802 *
6803 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6804 *   top-level elements such as `<svg>` or `<math>`.
6805 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6806 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6807 *
6808 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6809 *
6810 * #### `template`
6811 * HTML markup that may:
6812 * * Replace the contents of the directive's element (default).
6813 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6814 * * Wrap the contents of the directive's element (if `transclude` is true).
6815 *
6816 * Value may be:
6817 *
6818 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6819 * * A function which takes two arguments `tElement` and `tAttrs` (described 
6819in the `compile`
6820 *   function api below) and returns a string value.
6821 *
6822 *
6823 * #### `templateUrl`
6824 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6825 *
6826 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6827 * for later when the template has been resolved.  In the meantime it will continue to compile and link
6828 * sibling and parent elements as though this element had not contained any directives.
6829 *
6830 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6831 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6832 * case when only one deeply nested directive has `templateUrl`.
6833 *
6834 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6835 *
6836 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6837 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6838 * a string value representing the url.  In either case, the template URL is passed through {@link
6839 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6840 *
6841 *
6842 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6843 * specify what the template should replace. Defaults to `false`.
6844 *
6845 * * `true` - the template will replace the directive's element.
6846 * * `false` - the template will replace the contents of the directive's element.
6847 *
6848 * The replacement process migrates all of the attributes / classes from the old element to the new
6849 * one. See the {@link guide/directive#template-expanding-directive
6850 * Directives Guide} for an example.
6851 *
6852 * There are very few scenarios where element replacement is required for the application function,
6853 * the main one being reusable custom components that are used within SVG contexts
6854 * (because SVG doesn't work with custom elements in the DOM tree).
6855 *
6856 * #### `transclude`
6857 * Extract the contents of the element where the directive appears and make it available to the directive.
6858 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6859 * {@link $compile#transclusion Transclusion} section below.
6860 *
6861 *
6862 * #### `compile`
6863 *
6864 * ```js
6865 *   function compile(tElement, tAttrs, transclude) { ... }
6866 * ```
6867 *
6868 * The compile function deals with transforming the template DOM. Since most directives do not do
6869 * template transformation, it is not used often. The compile function takes the following arguments:
6870 *
6871 *   * `tElement` - template element - The element where the directive has been declared. It is
6872 *     safe to do template transformation on the element and child elements only.
6873 *
6874 *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6875 *     between all directive compile functions.
6876 *
6877 *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6878 *
6879 * <div class="alert alert-warning">
6880 * **Note:** The template instance and the link instance may be different objects if the template has
6881 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6882 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6883 * should be done in a linking function rather than in a compile function.
6884 * </div>
6885
6886 * <div class="alert alert-warning">
6887 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6888 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6889 * stack overflow errors.
6890 *
6891 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6892 * a directive's template instead of relying on automatic template compilation via `template` or
6893 * `templateUrl` declaration or manual compilation inside the compile function.
6894 * </div>
6895 *
6896 * <div class="alert alert-danger">
6897 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6898 *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6899 *   to the link function instead.
6900 * </div>
6901
6902 * A compile function can have a return value which can be either a function or an object.
6903 *
6904 * * returning a (post-link) function - is equivalent to registering the linking function via the
6905 *   `link` property of the config object when the compile function is empty.
6906 *
6907 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6908 *   control when a linking function should be called during the linking phase. See info about
6909 *   pre-linking and post-linking functions below.
6910 *
6911 *
6912 * #### `link`
6913 * This property is used only if the `compile` property is not defined.
6914 *
6915 * ```js
6916 *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6917 * ```
6918 *
6919 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6920 * executed after the template has been cloned. This is where most of the directive logic will be
6921 * put.
6922 *
6923 *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6924 *     directive for registering {@link ng.$rootScope.Scope#$watch w
6924atches}.
6925 *
6926 *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6927 *     manipulate the children of the element only in `postLink` function since the children have
6928 *     already been linked.
6929 *
6930 *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6931 *     between all directive linking functions.
6932 *
6933 *   * `controller` - the directive's required controller instance(s) - Instances are shared
6934 *     among all directives, which allows the directives to use the controllers as a communication
6935 *     channel. The exact value depends on the directive's `require` property:
6936 *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6937 *       * `string`: the controller instance
6938 *       * `array`: array of controller instances
6939 *
6940 *     If a required controller cannot be found, and it is optional, the instance is `null`,
6941 *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6942 *
6943 *     Note that you can also require the directive's own controller - it will be made available like
6944 *     any other controller.
6945 *
6946 *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6947 *     This is the same as the `$transclude`
6948 *     parameter of directive controllers, see there for details.
6949 *     `function([scope], cloneLinkingFn, futureParentElement)`.
6950 *
6951 * #### Pre-linking function
6952 *
6953 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6954 * compiler linking function will fail to locate the correct elements for linking.
6955 *
6956 * #### Post-linking function
6957 *
6958 * Executed after the child elements are linked.
6959 *
6960 * Note that child elements that contain `templateUrl` directives will not have been compiled
6961 * and linked since they are waiting for their template to load asynchronously and their own
6962 * compilation and linking has been suspended until that occurs.
6963 *
6964 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6965 * for their async templates to be resolved.
6966 *
6967 *
6968 * ### Transclusion
6969 *
6970 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6971 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6972 * scope from where they were taken.
6973 *
6974 * Transclusion is used (often with {@link ngTransclude}) to insert the
6975 * original contents of a directive's element into a specified place in the template of the directive.
6976 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6977 * content has access to the properties on the scope from which it was taken, even if the directive
6978 * has isolated scope.
6979 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6980 *
6981 * This makes it possible for the widget to have private state for its template, while the transcluded
6982 * content has access to its originating scope.
6983 *
6984 * <div class="alert alert-warning">
6985 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6986 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6987 * Testing Transclusion Directives}.
6988 * </div>
6989 *
6990 * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
6991 * directive's element, the entire element or multiple parts of the element contents:
6992 *
6993 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6994 * * `'element'` - transclude the whole of the directive's element including any directives on this
6995 *   element that defined at a lower priority than this directive. When used, the `template`
6996 *   property is ignored.
6997 * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
6998 *
6999 * **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
7000 *
7001 * This object is a map where the keys are the name of the slot to fill and the value is an element selector
7002 * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
7003 * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
7004 *
7005 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7006 *
7007 * If the element selector is prefixed with a `?` then that slot is optional.
7008 *
7009 * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
7010 * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
7011 *
7012 * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
7013 * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
7014 * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
7015 * injectable into the directive's controller.
7016 *
7017 *
7018 * #### Transclusion Functions
7019 *
7020 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
7021 * function** to the directive's `link` function and `controller`. This transclusion function is a special
7022 * **linking function** that will return the compiled contents linked to a new transclusion scope.
7023 *
7024 * <div class="alert alert-info">
7025 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
7026 * ngTransclude will deal with it for us.
7027 * </div>
7028 *
7029 * If you want to manually control the insertion and removal of the transcluded content in your directive
7030 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
7031 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
7032 *
7033 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
7034 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
7035 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
7036 *
7037 * <div class="alert alert-info">
7038 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
7039 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
7040 * </div>
7041 *
7042 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
7043 * attach function**:
7044 *
7045 * ```js
7046 * var transcludedContent, transclusionScope;
7047 *
7048 * $transclude(function(clone, scope) {
7049 *   element.append(clone);
7050 *   transcludedContent = clone;
7051 *   transclusionScope = scope;
7052 * });
7053 * ```
7054 *
7055 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
7056 * associated transclusion scope:
7057 *
7058 * ```js
7059 * transcludedContent.remove();
7060 * transclusionScope.$destroy();
7061 * ```
7062 *
7063 * <div class="alert alert-info">
7064 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
7065 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
7066 * then you are also responsible for calling `$destroy` on the transclusion scope.
7067 * </div>
7068 *
7069 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
7070 * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
7071 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
7072 *
7073 *
7074 * #### Transclusion Scopes
7075 *
7076 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
7077 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
7078 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
7079 * was taken.
7080 *
7081 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
7082 * like this:
7083 *
7084 * ```html
7085 * <div ng-app>
7086 *   <div isolate>
7087 *     <div transclusion>
7088 *     </div>
7089 *   </div>
7090 * </div>
7091 * ```
7092 *
7093 * The `$parent` scope hierarchy will look like this:
7094 *
7095   ```
7096   - $rootScope
7097     - isolate
7098       - transclusion
7099   ```
7100 *
7101 * but the scopes will inherit prototypically from different scopes to their `$parent`.
7102 *
7103   ```
7104   - $rootScope
7105     - transclusion
7106   - isolate
7107   ```
7108 *
7109 *
7110 * ### Attributes
7111 *
7112 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
7113 * `link()` or `compile()` functions. It has a variety of uses.
7114 *
7115 * accessing *Normalized attribute names:*
7116 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
7117 * the attributes object allows for normalized access to
7118 *   the attributes.
7119 *
7120 * * *Directive inter-communication:* All directives share the same instance of the attributes
7121 *   object which allows the directives to use the attributes object as inter directive
7122 *   communication.
7123 *
7124 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
7125 *   allowing other directives to read the interpolated value.
7126 *
7127 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
7128 *   that contain interpolation (e.g. `src="{{bar}}
7128"`). Not only is this very efficient but it's also
7129 *   the only way to easily get the actual value because during the linking phase the interpolation
7130 *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
7131 *
7132 * ```js
7133 * function linkingFn(scope, elm, attrs, ctrl) {
7134 *   // get the attribute value
7135 *   console.log(attrs.ngModel);
7136 *
7137 *   // change the attribute
7138 *   attrs.$set('ngModel', 'new value');
7139 *
7140 *   // observe changes to interpolated attribute
7141 *   attrs.$observe('ngModel', function(value) {
7142 *     console.log('ngModel has changed value to ' + value);
7143 *   });
7144 * }
7145 * ```
7146 *
7147 * ## Example
7148 *
7149 * <div class="alert alert-warning">
7150 * **Note**: Typically directives are registered with `module.directive`. The example below is
7151 * to illustrate how `$compile` works.
7152 * </div>
7153 *
7154 <example module="compileExample">
7155   <file name="index.html">
7156    <script>
7157      angular.module('compileExample', [], function($compileProvider) {
7158        // configure new 'compile' directive by passing a directive
7159        // factory function. The factory function injects the '$compile'
7160        $compileProvider.directive('compile', function($compile) {
7161          // directive factory creates a link function
7162          return function(scope, element, attrs) {
7163            scope.$watch(
7164              function(scope) {
7165                 // watch the 'compile' expression for changes
7166                return scope.$eval(attrs.compile);
7167              },
7168              function(value) {
7169                // when the 'compile' expression changes
7170                // assign it into the current DOM
7171                element.html(value);
7172
7173                // compile the new DOM and link it to the current
7174                // scope.
7175                // NOTE: we only compile .childNodes so that
7176                // we don't get into infinite loop compiling ourselves
7177                $compile(element.contents())(scope);
7178              }
7179            );
7180          };
7181        });
7182      })
7183      .controller('GreeterController', ['$scope', function($scope) {
7184        $scope.name = 'Angular';
7185        $scope.html = 'Hello {{name}}';
7186      }]);
7187    </script>
7188    <div ng-controller="GreeterController">
7189      <input ng-model="name"> <br/>
7190      <textarea ng-model="html"></textarea> <br/>
7191      <div compile="html"></div>
7192    </div>
7193   </file>
7194   <file name="protractor.js" type="protractor">
7195     it('should auto compile', function() {
7196       var textarea = $('textarea');
7197       var output = $('div[compile]');
7198       // The initial state reads 'Hello Angular'.
7199       expect(output.getText()).toBe('Hello Angular');
7200       textarea.clear();
7201       textarea.sendKeys('{{name}}!');
7202       expect(output.getText()).toBe('Angular!');
7203     });
7204   </file>
7205 </example>
7206
7207 *
7208 *
7209 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7210 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7211 *
7212 * <div class="alert alert-danger">
7213 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7214 *   e.g. will not use the right outer scope. Please pass the transclude function as a
7215 *   `parentBoundTranscludeFn` to the link function instead.
7216 * </div>
7217 *
7218 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7219 *                 root element(s), not their children)
7220 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7221 * (a DOM element/tree) to a scope. Where:
7222 *
7223 *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7224 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7225 *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
7226 *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7227 *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7228 *
7229 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
7230 *      * `scope` - is the current scope with which the linking function is working with.
7231 *
7232 *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
7233 *  keys may be used to control linking behavior:
7234 *
7235 *      * `parentBoundTranscludeFn` - the transclude function made available to
7236 *        directives; if given, it will be passed through to the link functions of
7237 *        directives found in `element` during compilation.
7238 *      * `transcludeControllers` - an object hash with keys that map controller names
7239 *        to controller instances; if given, it will make the controllers
7240 *        available to directives.
7241 *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7242 *        the cloned elements; only needed for transcludes that are allowed to contain non html
7243 *        elements (e.g. SVG elements). See also the directive.controller property.
7244 *
7245 * Calling the linking function returns the element of the template. It is either the original
7246 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7247 *
7248 * After linking the view is not updated until after a call to $digest which typically is done by
7249 * Angular automatically.
7250 *
7251 * If you need access to the bound view, there are two ways to do it:
7252 *
7253 * - If you are not asking the linking function to clone the template, create the DOM element(s)
7254 *   before you send them to the compiler and keep this reference around.
7255 *   ```js
7256 *     var element = $compile('<p>{{total}}</p>')(scope);
7257 *   ```
7258 *
7259 * - if on the other hand, you need the element to be cloned, the view reference from the original
7260 *   example would not point to the clone, but rather to the original template that was cloned. In
7261 *   this case, you can access the clone via the cloneAttachFn:
7262 *   ```js
7263 *     var templateElement = angular.element('<p>{{total}}</p>'),
7264 *         scope = ....;
7265 *
7266 *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7267 *       //attach the clone to DOM document at the right place
7268 *     });
7269 *
7270 *     //now we have reference to the cloned DOM via `clonedElement`
7271 *   ```
7272 *
7273 *
7274 * For information on how the compiler works, see the
7275 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7276 */
7277
7278var $compileMinErr = minErr('$compile');
7279
7280/**
7281 * @ngdoc provider
7282 * @name $compileProvider
7283 *
7284 * @description
7285 */
vendor: 8,227 bytes, lines 7286-7470
7286$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7287function $CompileProvider($provide, $$sanitizeUriProvider) {
7288  var hasDirectives = {},
7289      Suffix = 'Directive',
7290      COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7291      CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7292      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7293      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7294
7295  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7296  // The assumption is that future DOM event attribute names will begin with
7297  // 'on' and be composed of only English letters.
7298  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7299
7300  function parseIsolateBindings(scope, directiveName, isController) {
7301    var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
7302
7303    var bindings = {};
7304
7305    forEach(scope, function(definition, scopeName) {
7306      var match = definition.match(LOCAL_REGEXP);
7307
7308      if (!match) {
7309        throw $compileMinErr('iscp',
7310            "Invalid {3} for directive '{0}'." +
7311            " Definition: {... {1}: '{2}' ...}",
7312            directiveName, scopeName, definition,
7313            (isController ? "controller bindings definition" :
7314            "isolate scope definition"));
7315      }
7316
7317      bindings[scopeName] = {
7318        mode: match[1][0],
7319        collection: match[2] === '*',
7320        optional: match[3] === '?',
7321        attrName: match[4] || scopeName
7322      };
7323    });
7324
7325    return bindings;
7326  }
7327
7328  function parseDirectiveBindings(directive, directiveName) {
7329    var bindings = {
7330      isolateScope: null,
7331      bindToController: null
7332    };
7333    if (isObject(directive.scope)) {
7334      if (directive.bindToController === true) {
7335        bindings.bindToController = parseIsolateBindings(directive.scope,
7336                                                         directiveName, true);
7337        bindings.isolateScope = {};
7338      } else {
7339        bindings.isolateScope = parseIsolateBindings(directive.scope,
7340                                                     directiveName, false);
7341      }
7342    }
7343    if (isObject(directive.bindToController)) {
7344      bindings.bindToController =
7345          parseIsolateBindings(directive.bindToController, directiveName, true);
7346    }
7347    if (isObject(bindings.bindToController)) {
7348      var controller = directive.controller;
7349      var controllerAs = directive.controllerAs;
7350      if (!controller) {
7351        // There is no controller, there may or may not be a controllerAs property
7352        throw $compileMinErr('noctrl',
7353              "Cannot bind to controller without directive '{0}'s controller.",
7354              directiveName);
7355      } else if (!identifierForController(controller, controllerAs)) {
7356        // There is a controller, but no identifier or controllerAs property
7357        throw $compileMinErr('noident',
7358              "Cannot bind to controller without identifier for directive '{0}'.",
7359              directiveName);
7360      }
7361    }
7362    return bindings;
7363  }
7364
7365  function assertValidDirectiveName(name) {
7366    var letter = name.charAt(0);
7367    if (!letter || letter !== lowercase(letter)) {
7368      throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
7369    }
7370    if (name !== name.trim()) {
7371      throw $compileMinErr('baddir',
7372            "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7373            name);
7374    }
7375  }
7376
7377  /**
7378   * @ngdoc method
7379   * @name $compileProvider#directive
7380   * @kind function
7381   *
7382   * @description
7383   * Register a new directive with the compiler.
7384   *
7385   * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7386   *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7387   *    names and the values are the factories.
7388   * @param {Function|Array} directiveFactory An injectable directive factory function. See the
7389   *    {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
7390   * @returns {ng.$compileProvider} Self for chaining.
7391   */
7392   this.directive = function registerDirective(name, directiveFactory) {
7393    assertNotHasOwnProperty(name, 'directive');
7394    if (isString(name)) {
7395      assertValidDirectiveName(name);
7396      assertArg(directiveFactory, 'directiveFactory');
7397      if (!hasDirectives.hasOwnProperty(name)) {
7398        hasDirectives[name] = [];
7399        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7400          function($injector, $exceptionHandler) {
7401            var directives = [];
7402            forEach(hasDirectives[name], function(directiveFactory, index) {
7403              try {
7404                var directive = $injector.invoke(directiveFactory);
7405                if (isFunction(directive)) {
7406                  directive = { compile: valueFn(directive) };
7407                } else if (!directive.compile && directive.link) {
7408                  directive.compile = valueFn(directive.link);
7409                }
7410                directive.priority = directive.priority || 0;
7411                directive.index = index;
7412                directive.name = directive.name || name;
7413                directive.require = directive.require || (directive.controller && directive.name);
7414                directive.restrict = directive.restrict || 'EA';
7415                var bindings = directive.$$bindings =
7416                    parseDirectiveBindings(directive, directive.name);
7417                if (isObject(bindings.isolateScope)) {
7418                  directive.$$isolateBindings = bindings.isolateScope;
7419                }
7420                directive.$$moduleName = directiveFactory.$$moduleName;
7421                directives.push(directive);
7422              } catch (e) {
7423                $exceptionHandler(e);
7424              }
7425            });
7426            return directives;
7427          }]);
7428      }
7429      hasDirectives[name].push(directiveFactory);
7430    } else {
7431      forEach(name, reverseParams(registerDirective));
7432    }
7433    return this;
7434  };
7435
7436  /**
7437   * @ngdoc method
7438   * @name $compileProvider#component
7439   * @module ng
7440   * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
7441   * @param {Object} options Component definition object (a simplified
7442   *    {@link ng.$compile#directive-definition-object directive definition object}),
7443   *    with the following properties (all optional):
7444   *
7445   *    - `controller` – `{(string|function()=}` – controller constructor function that should be
7446   *      associated with newly created scope or the name of a {@link ng.$compile#-controller-
7447   *      registered controller} if passed as a string. An empty `noop` function by default.
7448   *    - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
7449   *      If present, the controller will be published to scope under the `controllerAs` name.
7450   *      If not present, this will default to be `$ctrl`.
7451   *    - `template` – `{string=|function()=}` – html template as a string or a function that
7452   *      returns an html template as a string which should be used as the contents of this component.
7453   *      Empty string by default.
7454   *
7455   *      If `template` is a function, then it is {@link auto.$injector#invoke injected} with
7456   *      the following locals:
7457   *
7458   *      - `$element` - Current element
7459   *      - `$attrs` - Current attributes object for the element
7460   *
7461   *    - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
7462   *      template that should be used  as the contents of this component.
7463   *
7464   *      If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
7465   *      the following locals:
7466   *
7467   *      - `$element` - Current element
7468   *      - `$attrs` - Current attributes object for the element
7469   *
7470   *    - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
7471   *      Component properties are always bound to the component controller and not to the scope.
7472   *      See {@link ng.$compile#-bindtocontroller- `bindToController`}.
7473   *    - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
7474   *      Disabled by default.
7475   *    - `$...` – `{function()=}` – additional annotations to provide to the directive factory function.
7476   *
7477   * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
7478   * @description
7479   * Register a **component definition** with the compiler. This is a shorthand for registering a special
7480   * type of directive, which represents a self-contained UI component in your application. Such components
7481   * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
7482   *
7483   * Component definitions are very simple and do not require as much configuration as defining general
7484   * directives. Component definitions usually consist only of a template and a controller backing it.
7485   *
7486   * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
7487   * `bindToController`. They always have **isolate scope** and are restricted to elements.
7488   *
7489   * Here are a few examples of how you would usually define components:
7490   *
7491   * ```js
7492   *   var myMod = angular.module(...);
7493   *   myMod.component('myComp', {
7494   *     template: '<div>My name is {{$ctrl.name}}</div>',
7495   *     controller: function() {
7496   *       this.name = 'shahar';
7497   *     }
7498   *   });
7499   *
7500   *   myMod.component('myComp', {
7501   *     template: '<div>My name is {{$ctrl.name}}</div>',
7502   *     bindings: {name: '@'}
7503   *   });
7504   *
7505   *   myMod.component('myComp', {
7506   *     templateUrl: 'views/my-comp.html',
7507   *     controller: 'MyCtrl as ctrl',
7508   *     bindings: {name: '@'}
7509   *   });
7510   *
7511   * ```
7512   *
7513   * <br />
7514   * Components are also useful as route templates (e.g. when using
7515   * {@link ngRoute ngRoute}):
7516   *
7517   * ```js
7518   *   var myMod = angular.module('myMod', ['ngRoute']);
7519   *
7520   *   myMod.component('home', {
7521   *     template: '<h1>Home</h1><p>Hello, {{ home.user.name }} !</p>',
7522   *     controller: function() {
7523   *       this.user = {name: 'world'};
7524   *     }
7525   *   });
7526   *
7527   *   myMod.config(function($routeProvider) {
7528   *     $routeProvider.when('/', {
7529   *       template: '<home></home>'
7530   *     });
7531   *   });
7532   * ```
7533   *
7534   * <br />
7535   * When using {@link ngRoute.$routeProvider $routeProvider}, you can often avoid some
7536   * boilerplate, by assigning the resolved dependencies directly on the route scope:
7537   *
7538   * ```js
7539   *   var myMod = angular.module('myMod', ['ngRoute']);
7540   *
7541   *   myMod.component('home', {
7542   *     template: '<h1>Home</h1><p>Hello, {{ home.user.name }} !</p>',
7543   *     bindings: {user: '='}
7544   *   });
7545   *
7546   *   myMod.config(function($routeProvider) {
7547   *     $routeProvider.when('/', {
7548   *       template: '<home user="$resolve.user"></home>',
7549   *       resolve: {user: function($http) { return $http.get('...'); }}
7550   *     });
7551   *   });
7552   * ```
7553   *
7554   * <br />
7555   * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
7556   */
7557  this.component = function registerComponent(name, options) {
7558    var controller = options.controller || function() {};
7559
7560    function factory($injector) {
7561      function makeInjectable(fn) {
7562        if (isFunction(fn) || isArray(fn)) {
vendor: 10,466 bytes, lines 7563-7838
7563          return function(tElement, tAttrs) {
7564            return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
7565          };
7566        } else {
7567          return fn;
7568        }
7569      }
7570
7571      var template = (!options.template && !options.templateUrl ? '' : options.template);
7572      return {
7573        controller: controller,
7574        controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
7575        template: makeInjectable(template),
7576        templateUrl: makeInjectable(options.templateUrl),
7577        transclude: options.transclude,
7578        scope: {},
7579        bindToController: options.bindings || {},
7580        restrict: 'E'
7581      };
7582    }
7583
7584    // Copy any annotation properties (starting with $) over to the factory function
7585    // These could be used by libraries such as the new component router
7586    forEach(options, function(val, key) {
7587      if (key.charAt(0) === '$') {
7588        factory[key] = val;
7589      }
7590    });
7591
7592    factory.$inject = ['$injector'];
7593
7594    return this.directive(name, factory);
7595  };
7596
7597
7598  /**
7599   * @ngdoc method
7600   * @name $compileProvider#aHrefSanitizationWhitelist
7601   * @kind function
7602   *
7603   * @description
7604   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7605   * urls during a[href] sanitization.
7606   *
7607   * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7608   *
7609   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7610   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7611   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7612   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7613   *
7614   * @param {RegExp=} regexp New regexp to whitelist urls with.
7615   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7616   *    chaining otherwise.
7617   */
7618  this.aHrefSanitizationWhitelist = function(regexp) {
7619    if (isDefined(regexp)) {
7620      $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7621      return this;
7622    } else {
7623      return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7624    }
7625  };
7626
7627
7628  /**
7629   * @ngdoc method
7630   * @name $compileProvider#imgSrcSanitizationWhitelist
7631   * @kind function
7632   *
7633   * @description
7634   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7635   * urls during img[src] sanitization.
7636   *
7637   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7638   *
7639   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7640   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7641   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7642   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7643   *
7644   * @param {RegExp=} regexp New regexp to whitelist urls with.
7645   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7646   *    chaining otherwise.
7647   */
7648  this.imgSrcSanitizationWhitelist = function(regexp) {
7649    if (isDefined(regexp)) {
7650      $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7651      return this;
7652    } else {
7653      return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7654    }
7655  };
7656
7657  /**
7658   * @ngdoc method
7659   * @name  $compileProvider#debugInfoEnabled
7660   *
7661   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7662   * current debugInfoEnabled state
7663   * @returns {*} current value if used as getter or itself (chaining) if used as setter
7664   *
7665   * @kind function
7666   *
7667   * @description
7668   * Call this method to enable/disable various debug runtime information in the compiler such as adding
7669   * binding information and a reference to the current scope on to DOM elements.
7670   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7671   * * `ng-binding` CSS class
7672   * * `$binding` data property containing an array of the binding expressions
7673   *
7674   * You may want to disable this in production for a significant performance boost. See
7675   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7676   *
7677   * The default value is true.
7678   */
7679  var debugInfoEnabled = true;
7680  this.debugInfoEnabled = function(enabled) {
7681    if (isDefined(enabled)) {
7682      debugInfoEnabled = enabled;
7683      return this;
7684    }
7685    return debugInfoEnabled;
7686  };
7687
7688  this.$get = [
7689            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7690            '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
7691    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
7692             $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
7693
7694    var SIMPLE_ATTR_NAME = /^\w/;
7695    var specialAttrHolder = document.createElement('div');
7696    var Attributes = function(element, attributesToCopy) {
7697      if (attributesToCopy) {
7698        var keys = Object.keys(attributesToCopy);
7699        var i, l, key;
7700
7701        for (i = 0, l = keys.length; i < l; i++) {
7702          key = keys[i];
7703          this[key] = attributesToCopy[key];
7704        }
7705      } else {
7706        this.$attr = {};
7707      }
7708
7709      this.$$element = element;
7710    };
7711
7712    Attributes.prototype = {
7713      /**
7714       * @ngdoc method
7715       * @name $compile.directive.Attributes#$normalize
7716       * @kind function
7717       *
7718       * @description
7719       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7720       * `data-`) to its normalized, camelCase form.
7721       *
7722       * Also there is special case for Moz prefix starting with upper case letter.
7723       *
7724       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7725       *
7726       * @param {string} name Name to normalize
7727       */
7728      $normalize: directiveNormalize,
7729
7730
7731      /**
7732       * @ngdoc method
7733       * @name $compile.directive.Attributes#$addClass
7734       * @kind function
7735       *
7736       * @description
7737       * Adds the CSS class value specified by the classVal parameter to the element. If animations
7738       * are enabled then an animation will be triggered for the class addition.
7739       *
7740       * @param {string} classVal The className value that will be added to the element
7741       */
7742      $addClass: function(classVal) {
7743        if (classVal && classVal.length > 0) {
7744          $animate.addClass(this.$$element, classVal);
7745        }
7746      },
7747
7748      /**
7749       * @ngdoc method
7750       * @name $compile.directive.Attributes#$removeClass
7751       * @kind function
7752       *
7753       * @description
7754       * Removes the CSS class value specified by the classVal parameter from the element. If
7755       * animations are enabled then an animation will be triggered for the class removal.
7756       *
7757       * @param {string} classVal The className value that will be removed from the element
7758       */
7759      $removeClass: function(classVal) {
7760        if (classVal && classVal.length > 0) {
7761          $animate.removeClass(this.$$element, classVal);
7762        }
7763      },
7764
7765      /**
7766       * @ngdoc method
7767       * @name $compile.directive.Attributes#$updateClass
7768       * @kind function
7769       *
7770       * @description
7771       * Adds and removes the appropriate CSS class values to the element based on the difference
7772       * between the new and old CSS class values (specified as newClasses and oldClasses).
7773       *
7774       * @param {string} newClasses The current CSS className value
7775       * @param {string} oldClasses The former CSS className value
7776       */
7777      $updateClass: function(newClasses, oldClasses) {
7778        var toAdd = tokenDifference(newClasses, oldClasses);
7779        if (toAdd && toAdd.length) {
7780          $animate.addClass(this.$$element, toAdd);
7781        }
7782
7783        var toRemove = tokenDifference(oldClasses, newClasses);
7784        if (toRemove && toRemove.length) {
7785          $animate.removeClass(this.$$element, toRemove);
7786        }
7787      },
7788
7789      /**
7790       * Set a normalized attribute on the element in a way such that all directives
7791       * can share the attribute. This function properly handles boolean attributes.
7792       * @param {string} key Normalized key. (ie ngAttribute)
7793       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7794       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7795       *     Defaults to true.
7796       * @param {string=} attrName Optional none normalized name. Defaults to key.
7797       */
7798      $set: function(key, value, writeAttr, attrName) {
7799        // TODO: decide whether or not to throw an error if "class"
7800        //is set through this function since it may cause $updateClass to
7801        //become unstable.
7802
7803        var node = this.$$element[0],
7804            booleanKey = getBooleanAttrName(node, key),
7805            aliasedKey = getAliasedAttrName(key),
7806            observer = key,
7807            nodeName;
7808
7809        if (booleanKey) {
7810          this.$$element.prop(key, value);
7811          attrName = booleanKey;
7812        } else if (aliasedKey) {
7813          this[aliasedKey] = value;
7814          observer = aliasedKey;
7815        }
7816
7817        this[key] = value;
7818
7819        // translate normalized key to actual key
7820        if (attrName) {
7821          this.$attr[key] = attrName;
7822        } else {
7823          attrName = this.$attr[key];
7824          if (!attrName) {
7825            this.$attr[key] = attrName = snake_case(key, '-');
7826          }
7827        }
7828
7829        nodeName = nodeName_(this.$$element);
7830
7831        if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
7832            (nodeName === 'img' && key === 'src')) {
7833          // sanitize a[href] and img[src] values
7834          this[key] = value = $$sanitizeUri(value, key === 'src');
7835        } else if (nodeName === 'img' && key === 'srcset') {
7836          // sanitize img[srcset] values
7837          var result = "";
7838
7839          // first check if there are spaces because it's not the same pattern
7840          var trimmedSrcset = trim(value);
7841          //                (   999x   ,|   999w   ,|   ,|,   )
7842          var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7843          var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7844
7845          // split srcset into tuple of uri and descriptor except for the last item
7846          var rawUris = trimmedSrcset.split(pattern);
7847
7848          // for each tuples
7849          var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7850          for (var i = 0; i < nbrUrisWith2parts; i++) {
7851            var innerIdx = i * 2;
7852            // sanitize the uri
7853            result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7854            // add the descriptor
7855            result += (" " + trim(rawUris[innerIdx + 1]));
7856          }
7857
7858          // split the last item into uri and descriptor
7859          var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7860
7861          // sanitize the last uri
7862          result += $$sanitizeUri(trim(lastTuple[0]), true);
7863
7864          // and add the last descriptor if any
7865          if (lastTuple.length === 2) {
7866            result += (" " + trim(lastTuple[1]));
7867          }
7868          this[key] = value = result;
7869        }
7870
7871        if (writeAttr !== false) {
7872          if (value === null || isUndefined(value)) {
7873            this.$$element.removeAttr(attrName);
7874          } else {
7875            if (SIMPLE_ATTR_NAME.test(attrName)) {
7876              this.$$element.attr(attrName, value);
7877            } else {
7878              setSpecialAttr(this.$$element[0], attrName, value);
7879            }
7880          }
7881        }
7882
7883        // fire observers
7884        var $$observers = this.$$observers;
7885        $$observers && forEach($$observers[observer], function(fn) {
7886          try {
7887            fn(value);
7888          } catch (e) {
7889            $exceptionHandler(e);
7890          }
7891        });
7892      },
7893
7894
7895      /**
7896       * @ngdoc method
7897       * @name $compile.directive.Attributes#$observe
7898       * @kind function
7899       *
7900       * @description
7901       * Observes an interpolated attribute.
7902       *
7903       * The observer function will be invoked once during the next `$digest` following
7904       * compilation. The observer is then invoked whenever the interpolated value
7905       * changes.
7906       *
7907       * @param {string} key Normalized key. (ie ngAttribute) .
7908       * @param {function(interpolatedValue)} fn Function that will be called whenever
7909                the interpolated value of the attribute changes.
7910       *        See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
7911       *        guide} for more info.
7912       * @returns {function()} Returns a deregistration function for this observer.
7913       */
7914      $observe: function(key, fn) {
7915        var attrs = this,
7916            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7917            listeners = ($$observers[key] || ($$observers[key] = []));
7918
7919        listeners.push(fn);
7920        $rootScope.$evalAsync(function() {
7921          if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7922            // no one registered attribute interpolation function, so lets call it manually
7923            fn(attrs[key]);
7924          }
7925        });
7926
7927        return function() {
7928          arrayRemove(listeners, fn);
7929        };
7930      }
7931    };
7932
7933    function setSpecialAttr(element, attrName, value) {
7934      // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
7935      // so we have to jump through some hoops to get such an attribute
7936      // https://github.com/angular/angular.js/pull/13318
7937      specialAttrHolder.innerHTML = "<span " + attrName + ">";
7938      var attributes = specialAttrHolder.firstChild.attributes;
7939      var attribute = attributes[0];
7940      // We have to remove the attribute from its container element before we can add it to the destination element
7941      attributes.removeNamedItem(attribute.name);
7942      attribute.value = value;
7943      element.attributes.setNamedItem(attribute);
7944    }
7945
7946    function safeAddClass($element, className) {
vendor: 10,122 bytes, lines 7947-8176
7947      try {
7948        $element.addClass(className);
7949      } catch (e) {
7950        // ignore, since it means that we are trying to set class on
7951        // SVG element, where class name is read-only.
7952      }
7953    }
7954
7955
7956    var startSymbol = $interpolate.startSymbol(),
7957        endSymbol = $interpolate.endSymbol(),
7958        denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
7959            ? identity
7960            : function denormalizeTemplate(template) {
7961              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7962        },
7963        NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7964    var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
7965
7966    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7967      var bindings = $element.data('$binding') || [];
7968
7969      if (isArray(binding)) {
7970        bindings = bindings.concat(binding);
7971      } else {
7972        bindings.push(binding);
7973      }
7974
7975      $element.data('$binding', bindings);
7976    } : noop;
7977
7978    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7979      safeAddClass($element, 'ng-binding');
7980    } : noop;
7981
7982    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7983      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7984      $element.data(dataName, scope);
7985    } : noop;
7986
7987    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7988      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7989    } : noop;
7990
7991    return compile;
7992
7993    //================================
7994
7995    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7996                        previousCompileContext) {
7997      if (!($compileNodes instanceof jqLite)) {
7998        // jquery always rewraps, whereas we need to preserve the original selector so that we can
7999        // modify it.
8000        $compileNodes = jqLite($compileNodes);
8001      }
8002      // We can not compile top level text elements since text nodes can be merged and we will
8003      // not be able to attach scope data to them, so we will wrap them in <span>
8004      forEach($compileNodes, function(node, index) {
8005        if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */) {
8006          $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
8007        }
8008      });
8009      var compositeLinkFn =
8010              compileNodes($compileNodes, transcludeFn, $compileNodes,
8011                           maxPriority, ignoreDirective, previousCompileContext);
8012      compile.$$addScopeClass($compileNodes);
8013      var namespace = null;
8014      return function publicLinkFn(scope, cloneConnectFn, options) {
8015        assertArg(scope, 'scope');
8016
8017        if (previousCompileContext && previousCompileContext.needsNewScope) {
8018          // A parent directive did a replace and a directive on this element asked
8019          // for transclusion, which caused us to lose a layer of element on which
8020          // we could hold the new transclusion scope, so we will create it manually
8021          // here.
8022          scope = scope.$parent.$new();
8023        }
8024
8025        options = options || {};
8026        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
8027          transcludeControllers = options.transcludeControllers,
8028          futureParentElement = options.futureParentElement;
8029
8030        // When `parentBoundTranscludeFn` is passed, it is a
8031        // `controllersBoundTransclude` function (it was previously passed
8032        // as `transclude` to directive.link) so we must unwrap it to get
8033        // its `boundTranscludeFn`
8034        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
8035          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
8036        }
8037
8038        if (!namespace) {
8039          namespace = detectNamespaceForChildElements(futureParentElement);
8040        }
8041        var $linkNode;
8042        if (namespace !== 'html') {
8043          // When using a directive with replace:true and templateUrl the $compileNodes
8044          // (or a child element inside of them)
8045          // might change, so we need to recreate the namespace adapted compileNodes
8046          // for call to the link function.
8047          // Note: This will already clone the nodes...
8048          $linkNode = jqLite(
8049            wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
8050          );
8051        } else if (cloneConnectFn) {
8052          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
8053          // and sometimes changes the structure of the DOM.
8054          $linkNode = JQLitePrototype.clone.call($compileNodes);
8055        } else {
8056          $linkNode = $compileNodes;
8057        }
8058
8059        if (transcludeControllers) {
8060          for (var controllerName in transcludeControllers) {
8061            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
8062          }
8063        }
8064
8065        compile.$$addScopeInfo($linkNode, scope);
8066
8067        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
8068        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
8069        return $linkNode;
8070      };
8071    }
8072
8073    function detectNamespaceForChildElements(parentElement) {
8074      // TODO: Make this detect MathML as well...
8075      var node = parentElement && parentElement[0];
8076      if (!node) {
8077        return 'html';
8078      } else {
8079        return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
8080      }
8081    }
8082
8083    /**
8084     * Compile function matches each node in nodeList against the directives. Once all directives
8085     * for a particular node are collected their compile functions are executed. The compile
8086     * functions return values - the linking functions - are combined into a composite linking
8087     * function, which is the a linking function for the node.
8088     *
8089     * @param {NodeList} nodeList an array of nodes or NodeList to compile
8090     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8091     *        scope argument is auto-generated to the new child of the transcluded parent scope.
8092     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
8093     *        the rootElement must be set the jqLite collection of the compile root. This is
8094     *        needed so that the jqLite collection items can be replaced with widgets.
8095     * @param {number=} maxPriority Max directive priority.
8096     * @returns {Function} A composite linking function of all of the matched directives or null.
8097     */
8098    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
8099                            previousCompileContext) {
8100      var linkFns = [],
8101          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
8102
8103      for (var i = 0; i < nodeList.length; i++) {
8104        attrs = new Attributes();
8105
8106        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
8107        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
8108                                        ignoreDirective);
8109
8110        nodeLinkFn = (directives.length)
8111            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
8112                                      null, [], [], previousCompileContext)
8113            : null;
8114
8115        if (nodeLinkFn && nodeLinkFn.scope) {
8116          compile.$$addScopeClass(attrs.$$element);
8117        }
8118
8119        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
8120                      !(childNodes = nodeList[i].childNodes) ||
8121                      !childNodes.length)
8122            ? null
8123            : compileNodes(childNodes,
8124                 nodeLinkFn ? (
8125                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
8126                     && nodeLinkFn.transclude) : transcludeFn);
8127
8128        if (nodeLinkFn || childLinkFn) {
8129          linkFns.push(i, nodeLinkFn, childLinkFn);
8130          linkFnFound = true;
8131          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
8132        }
8133
8134        //use the previous context only for the first element in the virtual group
8135        previousCompileContext = null;
8136      }
8137
8138      // return a linking function if we have found anything, null otherwise
8139      return linkFnFound ? compositeLinkFn : null;
8140
8141      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
8142        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
8143        var stableNodeList;
8144
8145
8146        if (nodeLinkFnFound) {
8147          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
8148          // offsets don't get screwed up
8149          var nodeListLength = nodeList.length;
8150          stableNodeList = new Array(nodeListLength);
8151
8152          // create a sparse array by only copying the elements which have a linkFn
8153          for (i = 0; i < linkFns.length; i+=3) {
8154            idx = linkFns[i];
8155            stableNodeList[idx] = nodeList[idx];
8156          }
8157        } else {
8158          stableNodeList = nodeList;
8159        }
8160
8161        for (i = 0, ii = linkFns.length; i < ii;) {
8162          node = stableNodeList[linkFns[i++]];
8163          nodeLinkFn = linkFns[i++];
8164          childLinkFn = linkFns[i++];
8165
8166          if (nodeLinkFn) {
8167            if (nodeLinkFn.scope) {
8168              childScope = scope.$new();
8169              compile.$$addScopeInfo(jqLite(node), childScope);
8170            } else {
8171              childScope = scope;
8172            }
8173
8174            if (nodeLinkFn.transcludeOnThisElement) {
8175              childBoundTranscludeFn = createBoundTranscludeFn(
8176                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
8177
8178            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
8179              childBoundTranscludeFn = parentBoundTranscludeFn;
8180
8181            } else if (!parentBoundTranscludeFn && transcludeFn) {
8182              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
8183
8184            } else {
8185              childBoundTranscludeFn = null;
8186            }
8187
8188            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
8189
8190          } else if (childLinkFn) {
8191            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
8192          }
8193        }
8194      }
8195    }
8196
8197    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
8198
8199      var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
8200
8201        if (!transcludedScope) {
8202          transcludedScope = scope.$new(false, containingScope);
8203          transcludedScope.$$transcluded = true;
8204        }
8205
8206        return transcludeFn(transcludedScope, cloneFn, {
8207          parentBoundTranscludeFn: previousBoundTranscludeFn,
8208          transcludeControllers: controllers,
8209          futureParentElement: futureParentElement
8210        });
8211      };
8212
8213      // We need  to attach the transclusion slots onto the `boundTranscludeFn`
8214      // so that they are available inside the `controllersBoundTransclude` function
8215      var boundSlots = boundTranscludeFn.$$slots = createMap();
8216      for (var slotName in transcludeFn.$$slots) {
8217        if (transcludeFn.$$slots[slotName]) {
8218          boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
8219        } else {
8220          boundSlots[slotName] = null;
8221        }
8222      }
8223
8224      return boundTranscludeFn;
8225    }
8226
8227    /**
8228     * Looks for directives on the given node and adds them to the directive collection which is
8229     * sorted.
8230     *
8231     * @param node Node to search.
8232     * @param directives An array to which the directives are added to. This array is sorted before
8233     *        the function returns.
8234     * @param attrs The shared attrs object which is used to populate the normalized attributes.
8235     * @param {number=} maxPriority Max directive priority.
8236     */
8237    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
8238      var nodeType = node.nodeType,
8239          attrsMap = attrs.$attr,
8240          match,
8241          className;
8242
8243      switch (nodeType) {
8244        case NODE_TYPE_ELEMENT: /* Element */
8245          // use the node name: <directive>
8246          addDirective(directives,
8247              directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
8248
8249          // iterate over the attributes
8250          for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
8251                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
8252            var attrStartName = false;
8253            var attrEndName = false;
8254
8255            attr = nAttrs[j];
8256            name = attr.name;
8257            value = trim(attr.value);
8258
8259            // support ngAttr attribute binding
8260            ngAttrName = directiveNormalize(name);
8261            if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
8262              name = name.replace(PREFIX_REGEXP, '')
8263                .substr(8).replace(/_(.)/g, function(match, letter) {
8264                  return letter.toUpperCase();
8265                });
8266            }
8267
8268            var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
8269            if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
8270              attrStartName = name;
8271              attrEndName = name.substr(0, name.length - 5) + 'end';
8272              name = name.substr(0, name.length - 6);
8273            }
8274
8275            nName = directiveNormalize(name.toLowerCase());
8276            attrsMap[nName] = name;
8277            if (isNgAttr || !attrs.hasOwnProperty(nName)) {
8278                attrs[nName] = value;
8279                if (getBooleanAttrName(node, nName)) {
8280                  attrs[nName] = true; // presence means true
8281                }
8282            }
8283            addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
8284            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
8285                          attrEndName);
8286          }
8287
8288          // use class as directive
8289          className = node.className;
8290          if (isObject(className)) {
8291              // Maybe SVGAnimatedString
8292              className = className.animVal;
8293          }
8294          if (isString(className) && className !== '') {
8295            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
8296              nName = directiveNormalize(match[2]);
8297              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
8298                attrs[nName] = trim(match[3]);
8299              }
8300              className = className.substr(match.index + match[0].length);
8301            }
8302          }
8303          break;
8304        case NODE_TYPE_TEXT: /* Text Node */
8305          if (msie === 11) {
8306            // Workaround for #11781
8307            while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
8308              node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
8309              node.parentNode.removeChild(node.nextSibling);
8310            }
8311          }
8312          addTextInterpolateDirective(directives, node.nodeValue);
8313          break;
8314        case NODE_TYPE_COMMENT: /* Comment */
8315          try {
8316            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
8317            if (match) {
8318              nName = directiveNormalize(match[1]);
8319              if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
8320                attrs[nName] = trim(match[2]);
8321              }
8322            }
8323          } catch (e) {
8324            // turns out that under some circumstances IE9 throws errors when one attempts to read
8325            // comment's node value.
8326            // Just ignore it and continue. (Can't seem to reproduce in test case.)
8327          }
8328          break;
8329      }
8330
8331      directives.sort(byPriority);
8332      return directives;
8333    }
8334
8335    /**
8336     * Given a node with an directive-start it collects all of the siblings until it finds
8337     * directive-end.
8338     * @param node
8339     * @param attrStart
8340     * @param attrEnd
8341     * @returns {*}
8342     */
8343    function groupScan(node, attrStart, attrEnd) {
8344      var nodes = [];
8345      var depth = 0;
8346      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
8347        do {
8348          if (!node) {
8349            throw $compileMinErr('uterdir',
8350                      "Unterminated attribute, found '{0}' but no matching '{1}' found.",
8351                      attrStart, attrEnd);
8352          }
8353          if (node.nodeType == NODE_TYPE_ELEMENT) {
8354            if (node.hasAttribute(attrStart)) depth++;
8355            if (node.hasAttribute(attrEnd)) depth--;
8356          }
8357          nodes.push(node);
8358          node = node.nextSibling;
8359        } while (depth > 0);
8360      } else {
8361        nodes.push(node);
8362      }
8363
8364      return jqLite(nodes);
8365    }
8366
8367    /**
8368     * Wrapper for linking function which converts normal linking function into a grouped
8369     * linking function.
8370     * @param linkFn
8371     * @param attrStart
8372     * @param attrEnd
8373     * @returns {Function}
8374     */
8375    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
8376      return function(scope, element, attrs, controllers, transcludeFn) {
8377        element = groupScan(element[0], attrStart, attrEnd);
8378        return linkFn(scope, element, attrs, controllers, transcludeFn);
8379      };
8380    }
8381
8382    /**
8383     * A function generator that is used to support both eager and lazy compilation
8384     * linking function.
8385     * @param eager
8386     * @param $compileNodes
8387     * @param transcludeFn
8388     * @param maxPriority
8389     * @param ignoreDirective
8390     * @param previousCompileContext
8391     * @returns {Function}
8392     */
8393    function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
8394        if (eager) {
8395            return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
8396        }
8397
8398        var compiled;
8399
8400        return function() {
8401            if (!compiled) {
8402                compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
8403
8404                // Null out all of these references in order to make them eligible for garbage collection
8405                // since this is a potentially long lived closure
8406                $compileNodes = transcludeFn = previousCompileContext = null;
8407            }
8408
8409            return compiled.apply(this, arguments);
8410        };
8411    }
8412
8413    /**
8414     * Once the directives have been collected, their compile functions are executed. This method
8415     * is responsible for inlining directive templates as well as terminating the application
8416     * of the directives if the terminal directive has been reached.
8417     *
8418     * @param {Array} directives Array of collected directives to execute their compile function.
8419     *        this needs to be pre-sorted by priority order.
8420     * @param {Node} compileNode The raw DOM node to apply the compile functions to
8421     * @param {Object} templateAttrs The shared attribute function
8422     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8423     *                                                  scope argument is auto-generated to the new
8424     *                                                  child of the transcluded parent scope.
8425     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8426     *                              argument has the root jqLite array so that we can replace nodes
8427     *                              on it.
8428     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8429     *                                           compiling the transclusion.
8430     * @param {Array.<Function>} preLinkFns
8431     * @param {Array.<Function>} postLinkFns
8432     * @param {Object} previousCompileContext Context used for previous compilation of the current
8433     *                                        node
8434     * @returns {Function} linkFn
8435     */
8436    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8437                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8438                                   previousCompileContext) {
8439      previousCompileContext = previousCompileContext || {};
8440
8441      var terminalPriority = -Number.MAX_VALUE,
8442          newScopeDirective = previousCompileContext.newScopeDirective,
8443          controllerDirectives = previousCompileContext.controllerDirectives,
8444          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8445          templateDirective = previousCompileContext.templateDirective,
8446          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8447          hasTranscludeDirective = false,
8448          hasTemplate = false,
8449          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8450          $compileNode = templateAttrs.$$element = jqLite(compileNode),
8451          directive,
8452          directiveName,
8453          $template,
8454          replaceDirective = originalReplaceDirective,
8455          childTranscludeFn = transcludeFn,
8456          linkFn,
8457          didScanForMultipleTransclusion = false,
8458          mightHaveMultipleTransclusionError = false,
8459          directiveValue;
8460
8461      // executes all directives on the current element
8462      for (var i = 0, ii = directives.length; i < ii; i++) {
8463        directive = directives[i];
8464        var attrStart = directive.$$start;
8465        var attrEnd = directive.$$end;
8466
8467        // collect multiblock sections
8468        if (attrStart) {
8469          $compileNode = groupScan(compileNode, attrStart, attrEnd);
8470        }
8471        $template = undefined;
8472
8473        if (terminalPriority > directive.priority) {
8474          break; // prevent further processing of directives
8475        }
8476
8477        if (directiveValue = directive.scope) {
8478
8479          // skip the check for directives with async templates, we'll check the derived sync
8480          // directive when the template arrives
8481          if (!directive.templateUrl) {
8482            if (isObject(directiveValue)) {
8483              // This directive is trying to add an isolated scope.
8484              // Check that there is no scope of any kind already
8485              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8486                                directive, $compileNode);
8487              newIsolateScopeDirective = directive;
8488            } else {
8489              // This directive is trying to add a child scope.
8490              // Check that there is no isolated scope already
8491              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8492                                $compileNode);
8493            }
8494          }
8495
8496          newScopeDirective = newScopeDirective || directive;
8497        }
8498
8499        directiveName = directive.name;
8500
8501        // If we encounter a condition that can result in transclusion on the directive,
8502        // then scan ahead in the remaining directives for others that may cause a multiple
8503        // transclusion error to be thrown during the compilation process.  If a matching directive
8504        // is found, then we know that when we encounter a transcluded directive, we need to eagerly
8505        // compile the `transclude` function rather than doing it lazily in order to throw
8506        // exceptions at the correct time
8507        if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
8508            || (directive.transclude && !directive.$$tlb))) {
8509                var candidateDirective;
8510
8511                for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) {
8512                    if ((candidateDirective.transclude && !candidateDirective.$$tlb)
8513                        || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
8514                        mightHaveMultipleTransclusionError = true;
8515                        break;
8516                    }
8517                }
8518
8519                didScanForMultipleTransclusion = true;
8520        }
8521
8522        if (!directive.templateUrl && directive.controller) {
8523          directiveValue = directive.controller;
8524          controllerDirectives = controllerDirectives || createMap();
8525          assertNoDuplicate("'" + directiveName + "' controller",
8526              controllerDirectives[directiveName], directive, $compileNode);
8527          controllerDirectives[directiveName] = directive;
8528        }
8529
8530        if (directiveValue = directive.transclude) {
8531          hasTranscludeDirective = true;
8532
8533          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8534          // This option should only be used by directives that know how to safely handle element transclusion,
8535          // where the transcluded nodes are added or replaced after linking.
8536          if (!directive.$$tlb) {
8537            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8538            nonTlbTranscludeDirective = directive;
8539          }
8540
8541          if (directiveValue == 'element') {
8542            hasElementTranscludeDirective = true;
8543            terminalPriority = directive.priority;
8544            $template = $compileNode;
8545            $compileNode = templateAttrs.$$element =
8546                jqLite(document.createComment(' ' + directiveName + ': ' +
8547                                              templateAttrs[directiveName] + ' '));
8548            compileNode = $compileNode[0];
8549            replaceWith(jqCollection, sliceArgs($template), compileNode);
8550
8551            childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
8552                                        replaceDirective && replaceDirective.name, {
8553                                          // Don't pass in:
8554                                          // - controllerDirectives - otherwise we'll create duplicates controllers
8555                                          // - newIsolateScopeDirective or templateDirective - comb
vendor: 9,807 bytes, lines 8555-8773
8555ining templates with
8556                                          //   element transclusion doesn't make sense.
8557                                          //
8558                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8559                                          // on the same element more than once.
8560                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective
8561                                        });
8562          } else {
8563
8564            var slots = createMap();
8565
8566            $template = jqLite(jqLiteClone(compileNode)).contents();
8567
8568            if (isObject(directiveValue)) {
8569
8570              // We have transclusion slots,
8571              // collect them up, compile them and store their transclusion functions
8572              $template = [];
8573
8574              var slotMap = createMap();
8575              var filledSlots = createMap();
8576
8577              // Parse the element selectors
8578              forEach(directiveValue, function(elementSelector, slotName) {
8579                // If an element selector starts with a ? then it is optional
8580                var optional = (elementSelector.charAt(0) === '?');
8581                elementSelector = optional ? elementSelector.substring(1) : elementSelector;
8582
8583                slotMap[elementSelector] = slotName;
8584
8585                // We explicitly assign `null` since this implies that a slot was defined but not filled.
8586                // Later when calling boundTransclusion functions with a slot name we only error if the
8587                // slot is `undefined`
8588                slots[slotName] = null;
8589
8590                // filledSlots contains `true` for all slots that are either optional or have been
8591                // filled. This is used to check that we have not missed any required slots
8592                filledSlots[slotName] = optional;
8593              });
8594
8595              // Add the matching elements into their slot
8596              forEach($compileNode.contents(), function(node) {
8597                var slotName = slotMap[directiveNormalize(nodeName_(node))];
8598                if (slotName) {
8599                  filledSlots[slotName] = true;
8600                  slots[slotName] = slots[slotName] || [];
8601                  slots[slotName].push(node);
8602                } else {
8603                  $template.push(node);
8604                }
8605              });
8606
8607              // Check for required slots that were not filled
8608              forEach(filledSlots, function(filled, slotName) {
8609                if (!filled) {
8610                  throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
8611                }
8612              });
8613
8614              for (var slotName in slots) {
8615                if (slots[slotName]) {
8616                  // Only define a transclusion function if the slot was filled
8617                  slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
8618                }
8619              }
8620            }
8621
8622            $compileNode.empty(); // clear contents
8623            childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
8624                undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8625            childTranscludeFn.$$slots = slots;
8626          }
8627        }
8628
8629        if (directive.template) {
8630          hasTemplate = true;
8631          assertNoDuplicate('template', templateDirective, directive, $compileNode);
8632          templateDirective = directive;
8633
8634          directiveValue = (isFunction(directive.template))
8635              ? directive.template($compileNode, templateAttrs)
8636              : directive.template;
8637
8638          directiveValue = denormalizeTemplate(directiveValue);
8639
8640          if (directive.replace) {
8641            replaceDirective = directive;
8642            if (jqLiteIsTextNode(directiveValue)) {
8643              $template = [];
8644            } else {
8645              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8646            }
8647            compileNode = $template[0];
8648
8649            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8650              throw $compileMinErr('tplrt',
8651                  "Template for directive '{0}' must have exactly one root element. {1}",
8652                  directiveName, '');
8653            }
8654
8655            replaceWith(jqCollection, $compileNode, compileNode);
8656
8657            var newTemplateAttrs = {$attr: {}};
8658
8659            // combine directives from the original node and from the template:
8660            // - take the array of directives for this element
8661            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8662            // - collect directives from the template and sort them by priority
8663            // - combine directives as: processed + template + unprocessed
8664            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8665            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8666
8667            if (newIsolateScopeDirective || newScopeDirective) {
8668              // The original directive caused the current element to be replaced but this element
8669              // also needs to have a new scope, so we need to tell the template directives
8670              // that they would need to get their scope from further up, if they require transclusion
8671              markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8672            }
8673            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8674            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8675
8676            ii = directives.length;
8677          } else {
8678            $compileNode.html(directiveValue);
8679          }
8680        }
8681
8682        if (directive.templateUrl) {
8683          hasTemplate = true;
8684          assertNoDuplicate('template', templateDirective, directive, $compileNode);
8685          templateDirective = directive;
8686
8687          if (directive.replace) {
8688            replaceDirective = directive;
8689          }
8690
8691          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8692              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8693                controllerDirectives: controllerDirectives,
8694                newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8695                newIsolateScopeDirective: newIsolateScopeDirective,
8696                templateDirective: templateDirective,
8697                nonTlbTranscludeDirective: nonTlbTranscludeDirective
8698              });
8699          ii = directives.length;
8700        } else if (directive.compile) {
8701          try {
8702            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8703            if (isFunction(linkFn)) {
8704              addLinkFns(null, linkFn, attrStart, attrEnd);
8705            } else if (linkFn) {
8706              addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8707            }
8708          } catch (e) {
8709            $exceptionHandler(e, startingTag($compileNode));
8710          }
8711        }
8712
8713        if (directive.terminal) {
8714          nodeLinkFn.terminal = true;
8715          terminalPriority = Math.max(terminalPriority, directive.priority);
8716        }
8717
8718      }
8719
8720      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8721      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8722      nodeLinkFn.templateOnThisElement = hasTemplate;
8723      nodeLinkFn.transclude = childTranscludeFn;
8724
8725      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8726
8727      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8728      return nodeLinkFn;
8729
8730      ////////////////////
8731
8732      function addLinkFns(pre, post, attrStart, attrEnd) {
8733        if (pre) {
8734          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8735          pre.require = directive.require;
8736          pre.directiveName = directiveName;
8737          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8738            pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8739          }
8740          preLinkFns.push(pre);
8741        }
8742        if (post) {
8743          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8744          post.require = directive.require;
8745          post.directiveName = directiveName;
8746          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8747            post = cloneAndAnnotateFn(post, {isolateScope: true});
8748          }
8749          postLinkFns.push(post);
8750        }
8751      }
8752
8753
8754      function getControllers(directiveName, require, $element, elementControllers) {
8755        var value;
8756
8757        if (isString(require)) {
8758          var match = require.match(REQUIRE_PREFIX_REGEXP);
8759          var name = require.substring(match[0].length);
8760          var inheritType = match[1] || match[3];
8761          var optional = match[2] === '?';
8762
8763          //If only parents then start at the parent element
8764          if (inheritType === '^^') {
8765            $element = $element.parent();
8766          //Otherwise attempt getting the controller from elementControllers in case
8767          //the element is transcluded (and has no data) and to avoid .data if possible
8768          } else {
8769            value = elementControllers && elementControllers[name];
8770            value = value && value.instance;
8771          }
8772
8773          if (!value) {
8774            var dataName = '$' + name + 'Controller';
8775            value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8776          }
8777
8778          if (!value && !optional) {
8779            throw $compileMinErr('ctreq',
8780                "Controller '{0}', required by directive '{1}', can't be found!",
8781                name, directiveName);
8782          }
8783        } else if (isArray(require)) {
8784          value = [];
8785          for (var i = 0, ii = require.length; i < ii; i++) {
8786            value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8787          }
8788        }
8789
8790        return value || null;
8791      }
8792
8793      function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8794        var elementControllers = createMap();
8795        for (var controllerKey in controllerDirectives) {
8796          var directive = controllerDirectives[controllerKey];
8797          var locals = {
8798            $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8799            $element: $element,
8800            $attrs: attrs,
8801            $transclude: transcludeFn
8802          };
8803
8804          var controller = directive.controller;
8805          if (controller == '@') {
8806            controller = attrs[directive.name];
8807          }
8808
8809          var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8810
8811          // For directives with element transclusion the element is a comment,
8812          // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
8813          // clean up (http://bugs.jquery.com/ticket/8335).
8814          // Instead, we save the controllers for the element in a local hash and attach to .data
8815          // later, once we have the actual element.
8816          elementControllers[directive.name] = controllerInstance;
8817          if (!hasElementTranscludeDirective) {
8818            $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8819          }
8820        }
8821        return elementControllers;
8822      }
8823
8824      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8825        var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8826            attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8827
8828        if (compileNode === linkNode) {
8829          attrs = templateAttrs;
8830          $element = templateAttrs.$$element;
8831        } else {
8832          $element = jqLite(linkNode);
8833          attrs = new Attributes($element, templateAttrs);
8834        }
8835
8836        controllerScope = scope;
8837        if (newIsolateScopeDirective) {
8838          isolateScope = scope.$new(true);
8839        } else if (newScopeDirective) {
8840          controllerScope = scope.$parent;
8841        }
8842
8843        if (boundTranscludeFn) {
8844          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8845          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8846          transcludeFn = controllersBoundTransclude;
8847          transcludeFn.$$boundTransclude = boundTranscludeFn;
8848          // expose the slots on the `$transclude` function
8849          transcludeFn.isSlotFilled = function(slotName) {
8850            return !!boundTranscludeFn.$$slots[slotName];
8851          };
8852        }
8853
8854        if (controllerDirectives) {
8855          elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8856        }
8857
8858        if (newIsolateScopeDirective) {
8859          // Initialize isolate scope bindings for new isolate scope directive.
8860          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8861              templateDirective === newIsolateScopeDirective.$$originalDirective)));
8862          compile.$$addScopeClass($element, true);
8863          isolateScope.$$isolateBindings =
8864              newIsolateScopeDirective.$$isolateBindings;
8865          removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8866                                        isolateScope.$$isolateBindings,
8867                                        newIsolateScopeDirective);
8868          if (removeScopeBindingWatches) {
8869            isolateScope.$on('$destroy', removeScopeBindingWatches);
8870          }
8871        }
8872
8873        // Initialize bindToController bindings
8874        for (var name in elementControllers) {
8875          var controllerDirective = controllerDirectives[name];
8876          var controller = elementControllers[name];
8877          var bindings = controllerDirective.$$bindings.bindToController;
8878
8879          if (controller.identifier && bindings) {
8880            removeControllerBindingWatches =
8881              initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8882          }
8883
8884          var controllerResult = controller();
8885          if (controllerResult !== controller.instance) {
8886            // If the controller constructor has a return value, overwrite the instance
8887            // from setupControllers
8888            controller.instance = controllerResult;
8889            $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8890            removeControllerBindingWatches && removeControllerBindingWatches();
8891            removeControllerBindingWatches =
8892              initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8893          }
8894        }
8895
8896        // PRELINKING
8897        for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8898          linkFn = preLinkFns[i];
8899          invokeLinkFn(linkFn,
8900              linkFn.isolateScope ? isolateScope : scope,
8901              $element,
8902              attrs,
8903              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8904              transcludeFn
8905          );
8906        }
8907
8908        // RECURSION
8909        // We only pass the isolate scope, if the isolate directive has a template,
8910        // otherwise the child elements do not belong to the isolate directive.
8911        var scopeToChild = scope;
8912        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8913          scopeToChild = isolateScope;
8914        }
8915        childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
vendor: 5,754 bytes, lines 8916-9052
8916
8917        // POSTLINKING
8918        for (i = postLinkFns.length - 1; i >= 0; i--) {
8919          linkFn = postLinkFns[i];
8920          invokeLinkFn(linkFn,
8921              linkFn.isolateScope ? isolateScope : scope,
8922              $element,
8923              attrs,
8924              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8925              transcludeFn
8926          );
8927        }
8928
8929        // This is the function that is injected as `$transclude`.
8930        // Note: all arguments are optional!
8931        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
8932          var transcludeControllers;
8933          // No scope passed in:
8934          if (!isScope(scope)) {
8935            slotName = futureParentElement;
8936            futureParentElement = cloneAttachFn;
8937            cloneAttachFn = scope;
8938            scope = undefined;
8939          }
8940
8941          if (hasElementTranscludeDirective) {
8942            transcludeControllers = elementControllers;
8943          }
8944          if (!futureParentElement) {
8945            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8946          }
8947          if (slotName) {
8948            // slotTranscludeFn can be one of three things:
8949            //  * a transclude function - a filled slot
8950            //  * `null` - an optional slot that was not filled
8951            //  * `undefined` - a slot that was not declared (i.e. invalid)
8952            var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
8953            if (slotTranscludeFn) {
8954              return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8955            } else if (isUndefined(slotTranscludeFn)) {
8956              throw $compileMinErr('noslot',
8957               'No parent directive that requires a transclusion with slot name "{0}". ' +
8958               'Element: {1}',
8959               slotName, startingTag($element));
8960            }
8961          } else {
8962            return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8963          }
8964        }
8965      }
8966    }
8967
8968    // Depending upon the context in which a directive finds itself it might need to have a new isolated
8969    // or child scope created. For instance:
8970    // * if the directive has been pulled into a template because another directive with a higher priority
8971    // asked for element transclusion
8972    // * if the directive itself asks for transclusion but it is at the root of a template and the original
8973    // element was replaced. See https://github.com/angular/angular.js/issues/12936
8974    function markDirectiveScope(directives, isolateScope, newScope) {
8975      for (var j = 0, jj = directives.length; j < jj; j++) {
8976        directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
8977      }
8978    }
8979
8980    /**
8981     * looks up the directive and decorates it with exception handling and proper parameters. We
8982     * call this the boundDirective.
8983     *
8984     * @param {string} name name of the directive to look up.
8985     * @param {string} location The directive must be found in specific format.
8986     *   String containing any of theses characters:
8987     *
8988     *   * `E`: element name
8989     *   * `A': attribute
8990     *   * `C`: class
8991     *   * `M`: comment
8992     * @returns {boolean} true if directive was added.
8993     */
8994    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8995                          endAttrName) {
8996      if (name === ignoreDirective) return null;
8997      var match = null;
8998      if (hasDirectives.hasOwnProperty(name)) {
8999        for (var directive, directives = $injector.get(name + Suffix),
9000            i = 0, ii = directives.length; i < ii; i++) {
9001          try {
9002            directive = directives[i];
9003            if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
9004                 directive.restrict.indexOf(location) != -1) {
9005              if (startAttrName) {
9006                directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
9007              }
9008              tDirectives.push(directive);
9009              match = directive;
9010            }
9011          } catch (e) { $exceptionHandler(e); }
9012        }
9013      }
9014      return match;
9015    }
9016
9017
9018    /**
9019     * looks up the directive and returns true if it is a multi-element directive,
9020     * and therefore requires DOM nodes between -start and -end markers to be grouped
9021     * together.
9022     *
9023     * @param {string} name name of the directive to look up.
9024     * @returns true if directive was registered as multi-element.
9025     */
9026    function directiveIsMultiElement(name) {
9027      if (hasDirectives.hasOwnProperty(name)) {
9028        for (var directive, directives = $injector.get(name + Suffix),
9029            i = 0, ii = directives.length; i < ii; i++) {
9030          directive = directives[i];
9031          if (directive.multiElement) {
9032            return true;
9033          }
9034        }
9035      }
9036      return false;
9037    }
9038
9039    /**
9040     * When the element is replaced with HTML template then the new attributes
9041     * on the template need to be merged with the existing attributes in the DOM.
9042     * The desired effect is to have both of the attributes present.
9043     *
9044     * @param {object} dst destination attributes (original DOM)
9045     * @param {object} src source attributes (from the directive template)
9046     */
9047    function mergeTemplateAttributes(dst, src) {
9048      var srcAttr = src.$attr,
9049          dstAttr = dst.$attr,
9050          $element = dst.$$element;
9051
9052      // reapply the old attributes to the new element
9053      forEach(dst, function(value, key) {
9054        if (key.charAt(0) != '$') {
9055          if (src[key] && src[key] !== value) {
9056            value += (key === 'style' ? ';' : ' ') + src[key];
9057          }
9058          dst.$set(key, value, true, srcAttr[key]);
9059        }
9060      });
9061
9062      // copy the new attributes on the old attrs object
9063      forEach(src, function(value, key) {
9064        if (key == 'class') {
9065          safeAddClass($element, value);
9066          dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
9067        } else if (key == 'style') {
9068          $element.attr('style', $element.attr('style') + ';' + value);
9069          dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
9070          // `dst` will never contain hasOwnProperty as DOM parser won't let it.
9071          // You will get an "InvalidCharacterError: DOM Exception 5" error if you
9072          // have an attribute like "has-own-property" or "data-has-own-property", etc.
9073        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
9074          dst[key] = value;
9075          dstAttr[key] = srcAttr[key];
9076        }
9077      });
9078    }
9079
9080
9081    function compileTemplateUrl(directives, $compileNode, tAttrs,
9082        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
9083      var linkQueue = [],
9084          afterTemplateNodeLinkFn,
9085          afterTemplateChildLinkFn,
9086          beforeTemplateCompileNode = $compileNode[0],
9087          origAsyncDirective = directives.shift(),
9088          derivedSyncDirective = inherit(origAsyncDirective, {
9089            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
9090          }),
9091          templateUrl = (isFunction(origAsyncDirective.templateUrl))
9092              ? origAsyncDirective.templateUrl($compileNode, tAttrs)
9093              : origAsyncDirective.templateUrl,
9094          templateNamespace = origAsyncDirective.templateNamespace;
9095
9096      $compileNode.empty();
9097
9098      $templateRequest(templateUrl)
9099        .then(function(content) {
9100          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
9101
9102          content = denormalizeTemplate(content);
9103
9104          if (origAsyncDirective.replace) {
9105            if (jqLiteIsTextNode(content)) {
9106              $template = [];
9107            } else {
9108              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
9109            }
9110            compileNode = $template[0];
9111
9112            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
9113              throw $compileMinErr('tplrt',
9114                  "Template for directive '{0}' must have exactly one root element. {1}",
9115                  origAsyncDirective.name, templateUrl);
9116            }
9117
9118            tempTemplateAttrs = {$attr: {}};
9119            replaceWith($rootElement, $compileNode, compileNode);
9120            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
9121
9122            if (isObject(origAsyncDirective.scope)) {
9123              // the original directive that caused the template to be loaded async required
9124              // an isolate scope
9125              markDirectiveScope(templateDirectives, true);
9126            }
9127            directives = templateDirectives.concat(directives);
9128            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
9129          } else {
9130            compileNode = beforeTemplateCompileNode;
9131            $compileNode.html(content);
9132          }
9133
9134          directives.unshift(derivedSyncDirective);
9135
9136          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
9137              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
9138              previousCompileContext);
9139          forEach($rootElement, function(node, i) {
9140            if (node == compileNode) {
9141              $rootElement[i] = $compileNode[0];
9142            }
9143          });
9144          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
9145
9146          while (linkQueue.length) {
9147            var scope = linkQueue.shift(),
9148                beforeTemplateLinkNode = linkQueue.shift(),
9149                linkRootElement = linkQueue.shift(),
9150                boundTranscludeFn = linkQueue.shift(),
9151                linkNode = $compileNode[0];
9152
9153            if (scope.$$destroyed) continue;
9154
9155            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
vendor: 11,577 bytes, lines 9156-9432
9156              var oldClasses = beforeTemplateLinkNode.className;
9157
9158              if (!(previousCompileContext.hasElementTranscludeDirective &&
9159                  origAsyncDirective.replace)) {
9160                // it was cloned therefore we have to clone as well.
9161                linkNode = jqLiteClone(compileNode);
9162              }
9163              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
9164
9165              // Copy in CSS classes from original node
9166              safeAddClass(jqLite(linkNode), oldClasses);
9167            }
9168            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
9169              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
9170            } else {
9171              childBoundTranscludeFn = boundTranscludeFn;
9172            }
9173            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
9174              childBoundTranscludeFn);
9175          }
9176          linkQueue = null;
9177        });
9178
9179      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
9180        var childBoundTranscludeFn = boundTranscludeFn;
9181        if (scope.$$destroyed) return;
9182        if (linkQueue) {
9183          linkQueue.push(scope,
9184                         node,
9185                         rootElement,
9186                         childBoundTranscludeFn);
9187        } else {
9188          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
9189            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
9190          }
9191          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
9192        }
9193      };
9194    }
9195
9196
9197    /**
9198     * Sorting function for bound directives.
9199     */
9200    function byPriority(a, b) {
9201      var diff = b.priority - a.priority;
9202      if (diff !== 0) return diff;
9203      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
9204      return a.index - b.index;
9205    }
9206
9207    function assertNoDuplicate(what, previousDirective, directive, element) {
9208
9209      function wrapModuleNameIfDefined(moduleName) {
9210        return moduleName ?
9211          (' (module: ' + moduleName + ')') :
9212          '';
9213      }
9214
9215      if (previousDirective) {
9216        throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
9217            previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
9218            directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
9219      }
9220    }
9221
9222
9223    function addTextInterpolateDirective(directives, text) {
9224      var interpolateFn = $interpolate(text, true);
9225      if (interpolateFn) {
9226        directives.push({
9227          priority: 0,
9228          compile: function textInterpolateCompileFn(templateNode) {
9229            var templateNodeParent = templateNode.parent(),
9230                hasCompileParent = !!templateNodeParent.length;
9231
9232            // When transcluding a template that has bindings in the root
9233            // we don't have a parent and thus need to add the class during linking fn.
9234            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
9235
9236            return function textInterpolateLinkFn(scope, node) {
9237              var parent = node.parent();
9238              if (!hasCompileParent) compile.$$addBindingClass(parent);
9239              compile.$$addBindingInfo(parent, interpolateFn.expressions);
9240              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
9241                node[0].nodeValue = value;
9242              });
9243            };
9244          }
9245        });
9246      }
9247    }
9248
9249
9250    function wrapTemplate(type, template) {
9251      type = lowercase(type || 'html');
9252      switch (type) {
9253      case 'svg':
9254      case 'math':
9255        var wrapper = document.createElement('div');
9256        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
9257        return wrapper.childNodes[0].childNodes;
9258      default:
9259        return template;
9260      }
9261    }
9262
9263
9264    function getTrustedContext(node, attrNormalizedName) {
9265      if (attrNormalizedName == "srcdoc") {
9266        return $sce.HTML;
9267      }
9268      var tag = nodeName_(node);
9269      // maction[xlink:href] can source SVG.  It's not limited to <maction>.
9270      if (attrNormalizedName == "xlinkHref" ||
9271          (tag == "form" && attrNormalizedName == "action") ||
9272          (tag != "img" && (attrNormalizedName == "src" ||
9273                            attrNormalizedName == "ngSrc"))) {
9274        return $sce.RESOURCE_URL;
9275      }
9276    }
9277
9278
9279    function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
9280      var trustedContext = getTrustedContext(node, name);
9281      allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
9282
9283      var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
9284
9285      // no interpolation found -> ignore
9286      if (!interpolateFn) return;
9287
9288
9289      if (name === "multiple" && nodeName_(node) === "select") {
9290        throw $compileMinErr("selmulti",
9291            "Binding to the 'multiple' attribute is not supported. Element: {0}",
9292            startingTag(node));
9293      }
9294
9295      directives.push({
9296        priority: 100,
9297        compile: function() {
9298            return {
9299              pre: function attrInterpolatePreLinkFn(scope, element, attr) {
9300                var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
9301
9302                if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
9303                  throw $compileMinErr('nodomevents',
9304                      "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
9305                          "ng- versions (such as ng-click instead of onclick) instead.");
9306                }
9307
9308                // If the attribute has changed since last $interpolate()ed
9309                var newValue = attr[name];
9310                if (newValue !== value) {
9311                  // we need to interpolate again since the attribute value has been updated
9312                  // (e.g. by another directive's compile function)
9313                  // ensure unset/empty values make interpolateFn falsy
9314                  interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
9315                  value = newValue;
9316                }
9317
9318                // if attribute was updated so that there is no interpolation going on we don't want to
9319                // register any observers
9320                if (!interpolateFn) return;
9321
9322                // initialize attr object so that it's ready in case we need the value for isolate
9323                // scope initialization, otherwise the value would not be available from isolate
9324                // directive's linking fn during linking phase
9325                attr[name] = interpolateFn(scope);
9326
9327                ($$observers[name] || ($$observers[name] = [])).$$inter = true;
9328                (attr.$$observers && attr.$$observers[name].$$scope || scope).
9329                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
9330                    //special case for class attribute addition + removal
9331                    //so that class changes can tap into the animation
9332                    //hooks provided by the $animate service. Be sure to
9333                    //skip animations when the first digest occurs (when
9334                    //both the new and the old values are the same) since
9335                    //the CSS classes are the non-interpolated values
9336                    if (name === 'class' && newValue != oldValue) {
9337                      attr.$updateClass(newValue, oldValue);
9338                    } else {
9339                      attr.$set(name, newValue);
9340                    }
9341                  });
9342              }
9343            };
9344          }
9345      });
9346    }
9347
9348
9349    /**
9350     * This is a special jqLite.replaceWith, which can replace items which
9351     * have no parents, provided that the containing jqLite collection is provided.
9352     *
9353     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
9354     *                               in the root of the tree.
9355     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
9356     *                                  the shell, but replace its DOM node reference.
9357     * @param {Node} newNode The new DOM node.
9358     */
9359    function replaceWith($rootElement, elementsToRemove, newNode) {
9360      var firstElementToRemove = elementsToRemove[0],
9361          removeCount = elementsToRemove.length,
9362          parent = firstElementToRemove.parentNode,
9363          i, ii;
9364
9365      if ($rootElement) {
9366        for (i = 0, ii = $rootElement.length; i < ii; i++) {
9367          if ($rootElement[i] == firstElementToRemove) {
9368            $rootElement[i++] = newNode;
9369            for (var j = i, j2 = j + removeCount - 1,
9370                     jj = $rootElement.length;
9371                 j < jj; j++, j2++) {
9372              if (j2 < jj) {
9373                $rootElement[j] = $rootElement[j2];
9374              } else {
9375                delete $rootElement[j];
9376              }
9377            }
9378            $rootElement.length -= removeCount - 1;
9379
9380            // If the replaced element is also the jQuery .context then replace it
9381            // .context is a deprecated jQuery api, so we should set it only when jQuery set it
9382            // http://api.jquery.com/context/
9383            if ($rootElement.context === firstElementToRemove) {
9384              $rootElement.context = newNode;
9385            }
9386            break;
9387          }
9388        }
9389      }
9390
9391      if (parent) {
9392        parent.replaceChild(newNode, firstElementToRemove);
9393      }
9394
9395      // Append all the `elementsToRemove` to a fragment. This will...
9396      // - remove them from the DOM
9397      // - allow them to still be traversed with .nextSibling
9398      // - allow a single fragment.qSA to fetch all elements being removed
9399      var fragment = document.createDocumentFragment();
9400      for (i = 0; i < removeCount; i++) {
9401        fragment.appendChild(elementsToRemove[i]);
9402      }
9403
9404      if (jqLite.hasData(firstElementToRemove)) {
9405        // Copy over user data (that includes Angular's $scope etc.). Don't copy private
9406        // data here because there's no public interface in jQuery to do that and copying over
9407        // event listeners (which is the main use of private data) wouldn't work anyway.
9408        jqLite.data(newNode, jqLite.data(firstElementToRemove));
9409
9410        // Remove $destroy event listeners from `firstElementToRemove`
9411        jqLite(firstElementToRemove).off('$destroy');
9412      }
9413
9414      // Cleanup any data/listeners on the elements and children.
9415      // This includes invoking the $destroy event on any elements with listeners.
9416      jqLite.cleanData(fragment.querySelectorAll('*'));
9417
9418      // Update the jqLite collection to only contain the `newNode`
9419      for (i = 1; i < removeCount; i++) {
9420        delete elementsToRemove[i];
9421      }
9422      elementsToRemove[0] = newNode;
9423      elementsToRemove.length = 1;
9424    }
9425
9426
9427    function cloneAndAnnotateFn(fn, annotation) {
9428      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
9429    }
9430
9431
9432    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
9433      try {
9434        linkFn(scope, $element, attrs, controllers, transcludeFn);
9435      } catch (e) {
9436        $exceptionHandler(e, startingTag($element));
9437      }
9438    }
9439
9440
9441    // Set up $watches for isolate scope and controller bindings. This process
9442    // only occurs for isolate scopes and new scopes with controllerAs.
9443    function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
9444      var removeWatchCollection = [];
9445      forEach(bindings, function(definition, scopeName) {
9446        var attrName = definition.attrName,
9447        optional = definition.optional,
9448        mode = definition.mode, // @, =, or &
9449        lastValue,
9450        parentGet, parentSet, compare;
9451
9452        switch (mode) {
9453
9454          case '@':
9455            if (!optional && !hasOwnProperty.call(attrs, attrName)) {
9456              destination[scopeName] = attrs[attrName] = void 0;
9457            }
9458            attrs.$observe(attrName, function(value) {
9459              if (isString(value)) {
9460                destination[scopeName] = value;
9461              }
9462            });
9463            attrs.$$observers[attrName].$$scope = scope;
9464            if (isString(attrs[attrName])) {
9465              // If the attribute has been provided then we trigger an interpolation to ensure
9466              // the value is there for use in the link fn
9467              destination[scopeName] = $interpolate(attrs[attrName])(scope);
9468            }
9469            break;
9470
9471          case '=':
9472            if (!hasOwnProperty.call(attrs, attrName)) {
9473              if (optional) break;
9474              attrs[attrName] = void 0;
9475            }
9476            if (optional && !attrs[attrName]) break;
9477
9478            parentGet = $parse(attrs[attrName]);
9479            if (parentGet.literal) {
9480              compare = equals;
9481            } else {
9482              compare = function(a, b) { return a === b || (a !== a && b !== b); };
9483            }
9484            parentSet = parentGet.assign || function() {
9485              // reset the change, or we will throw this exception on every $digest
9486              lastValue = destination[scopeName] = parentGet(scope);
9487              throw $compileMinErr('nonassign',
9488                  "Expression '{0}' used with directive '{1}' is non-assignable!",
9489                  attrs[attrName], directive.name);
9490            };
9491            lastValue = destination[scopeName] = parentGet(scope);
9492            var parentValueWatch = function parentValueWatch(parentValue) {
9493              if (!compare(parentValue, destination[scopeName])) {
9494                // we are out of sync and need to copy
9495                if (!compare(parentValue, lastValue)) {
9496                  // parent changed and it has precedence
9497                  destination[scopeName] = parentValue;
9498                } else {
9499                  // if the parent can be assigned then do so
9500                  parentSet(scope, parentValue = destination[scopeName]);
9501                }
9502              }
9503              return lastValue = parentValue;
9504            };
9505            parentValueWatch.$stateful = true;
9506            var removeWatch;
9507            if (definition.collection) {
9508              removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
9509            } else {
9510              removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
9511            }
9512            removeWatchCollection.push(removeWatch);
9513            break;
9514
9515          case '&':
9516            // Don't assign Object.prototype method to scope
9517            parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
9518
9519            // Don't assign noop to destination if expression is not valid
9520            if (parentGet === noop && optional) break;
9521
9522            destination[scopeName] = function(locals) {
9523              return parentGet(scope, locals);
9524            };
9525            break;
9526        }
9527      });
9528
9529      return removeWatchCollection.length && function removeWatches() {
9530        for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
9531          removeWatchCollection[i]();
vendor: 9,465 bytes, lines 9532-9821
9532        }
9533      };
9534    }
9535  }];
9536}
9537
9538var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9539/**
9540 * Converts all accepted directives format into proper directive name.
9541 * @param name Name to normalize
9542 */
9543function directiveNormalize(name) {
9544  return camelCase(name.replace(PREFIX_REGEXP, ''));
9545}
9546
9547/**
9548 * @ngdoc type
9549 * @name $compile.directive.Attributes
9550 *
9551 * @description
9552 * A shared object between directive compile / linking functions which contains normalized DOM
9553 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9554 * needed since all of these are treated as equivalent in Angular:
9555 *
9556 * ```
9557 *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9558 * ```
9559 */
9560
9561/**
9562 * @ngdoc property
9563 * @name $compile.directive.Attributes#$attr
9564 *
9565 * @description
9566 * A map of DOM element attribute names to the normalized name. This is
9567 * needed to do reverse lookup from normalized name back to actual name.
9568 */
9569
9570
9571/**
9572 * @ngdoc method
9573 * @name $compile.directive.Attributes#$set
9574 * @kind function
9575 *
9576 * @description
9577 * Set DOM element attribute value.
9578 *
9579 *
9580 * @param {string} name Normalized element attribute name of the property to modify. The name is
9581 *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9582 *          property to the original name.
9583 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9584 */
9585
9586
9587
9588/**
9589 * Closure compiler type information
9590 */
9591
9592function nodesetLinkingFn(
9593  /* angular.Scope */ scope,
9594  /* NodeList */ nodeList,
9595  /* Element */ rootElement,
9596  /* function(Function) */ boundTranscludeFn
9597) {}
9598
9599function directiveLinkingFn(
9600  /* nodesetLinkingFn */ nodesetLinkingFn,
9601  /* angular.Scope */ scope,
9602  /* Node */ node,
9603  /* Element */ rootElement,
9604  /* function(Function) */ boundTranscludeFn
9605) {}
9606
9607function tokenDifference(str1, str2) {
9608  var values = '',
9609      tokens1 = str1.split(/\s+/),
9610      tokens2 = str2.split(/\s+/);
9611
9612  outer:
9613  for (var i = 0; i < tokens1.length; i++) {
9614    var token = tokens1[i];
9615    for (var j = 0; j < tokens2.length; j++) {
9616      if (token == tokens2[j]) continue outer;
9617    }
9618    values += (values.length > 0 ? ' ' : '') + token;
9619  }
9620  return values;
9621}
9622
9623function removeComments(jqNodes) {
9624  jqNodes = jqLite(jqNodes);
9625  var i = jqNodes.length;
9626
9627  if (i <= 1) {
9628    return jqNodes;
9629  }
9630
9631  while (i--) {
9632    var node = jqNodes[i];
9633    if (node.nodeType === NODE_TYPE_COMMENT) {
9634      splice.call(jqNodes, i, 1);
9635    }
9636  }
9637  return jqNodes;
9638}
9639
9640var $controllerMinErr = minErr('$controller');
9641
9642
9643var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
9644function identifierForController(controller, ident) {
9645  if (ident && isString(ident)) return ident;
9646  if (isString(controller)) {
9647    var match = CNTRL_REG.exec(controller);
9648    if (match) return match[3];
9649  }
9650}
9651
9652
9653/**
9654 * @ngdoc provider
9655 * @name $controllerProvider
9656 * @description
9657 * The {@link ng.$controller $controller service} is used by Angular to create new
9658 * controllers.
9659 *
9660 * This provider allows controller registration via the
9661 * {@link ng.$controllerProvider#register register} method.
9662 */
9663function $ControllerProvider() {
9664  var controllers = {},
9665      globals = false;
9666
9667  /**
9668   * @ngdoc method
9669   * @name $controllerProvider#register
9670   * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9671   *    the names and the values are the constructors.
9672   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9673   *    annotations in the array notation).
9674   */
9675  this.register = function(name, constructor) {
9676    assertNotHasOwnProperty(name, 'controller');
9677    if (isObject(name)) {
9678      extend(controllers, name);
9679    } else {
9680      controllers[name] = constructor;
9681    }
9682  };
9683
9684  /**
9685   * @ngdoc method
9686   * @name $controllerProvider#allowGlobals
9687   * @description If called, allows `$controller` to find controller constructors on `window`
9688   */
9689  this.allowGlobals = function() {
9690    globals = true;
9691  };
9692
9693
9694  this.$get = ['$injector', '$window', function($injector, $window) {
9695
9696    /**
9697     * @ngdoc service
9698     * @name $controller
9699     * @requires $injector
9700     *
9701     * @param {Function|string} constructor If called with a function then it's considered to be the
9702     *    controller constructor function. Otherwise it's considered to be a string which is used
9703     *    to retrieve the controller constructor using the following steps:
9704     *
9705     *    * check if a controller with given name is registered via `$controllerProvider`
9706     *    * check if evaluating the string on the current scope returns a constructor
9707     *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9708     *      `window` object (not recommended)
9709     *
9710     *    The string can use the `controller as property` syntax, where the controller instance is published
9711     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9712     *    to work correctly.
9713     *
9714     * @param {Object} locals Injection locals for Controller.
9715     * @return {Object} Instance of given controller.
9716     *
9717     * @description
9718     * `$controller` service is responsible for instantiating controllers.
9719     *
9720     * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9721     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9722     */
9723    return function(expression, locals, later, ident) {
9724      // PRIVATE API:
9725      //   param `later` --- indicates that the controller's constructor is invoked at a later time.
9726      //                     If true, $controller will allocate the object with the correct
9727      //                     prototype chain, but will not invoke the controller until a returned
9728      //                     callback is invoked.
9729      //   param `ident` --- An optional label which overrides the label parsed from the controller
9730      //                     expression, if any.
9731      var instance, match, constructor, identifier;
9732      later = later === true;
9733      if (ident && isString(ident)) {
9734        identifier = ident;
9735      }
9736
9737      if (isString(expression)) {
9738        match = expression.match(CNTRL_REG);
9739        if (!match) {
9740          throw $controllerMinErr('ctrlfmt',
9741            "Badly formed controller string '{0}'. " +
9742            "Must match `__name__ as __id__` or `__name__`.", expression);
9743        }
9744        constructor = match[1],
9745        identifier = identifier || match[3];
9746        expression = controllers.hasOwnProperty(constructor)
9747            ? controllers[constructor]
9748            : getter(locals.$scope, constructor, true) ||
9749                (globals ? getter($window, constructor, true) : undefined);
9750
9751        assertArgFn(expression, constructor, true);
9752      }
9753
9754      if (later) {
9755        // Instantiate controller later:
9756        // This machinery is used to create an instance of the object before calling the
9757        // controller's constructor itself.
9758        //
9759        // This allows properties to be added to the controller before the constructor is
9760        // invoked. Primarily, this is used for isolate scope bindings in $compile.
9761        //
9762        // This feature is not intended for use by applications, and is thus not documented
9763        // publicly.
9764        // Object creation: http://jsperf.com/create-constructor/2
9765        var controllerPrototype = (isArray(expression) ?
9766          expression[expression.length - 1] : expression).prototype;
9767        instance = Object.create(controllerPrototype || null);
9768
9769        if (identifier) {
9770          addIdentifier(locals, identifier, instance, constructor || expression.name);
9771        }
9772
9773        var instantiate;
9774        return instantiate = extend(function() {
9775          var result = $injector.invoke(expression, instance, locals, constructor);
9776          if (result !== instance && (isObject(result) || isFunction(result))) {
9777            instance = result;
9778            if (identifier) {
9779              // If result changed, re-assign controllerAs value to scope.
9780              addIdentifier(locals, identifier, instance, constructor || expression.name);
9781            }
9782          }
9783          return instance;
9784        }, {
9785          instance: instance,
9786          identifier: identifier
9787        });
9788      }
9789
9790      instance = $injector.instantiate(expression, locals, constructor);
9791
9792      if (identifier) {
9793        addIdentifier(locals, identifier, instance, constructor || expression.name);
9794      }
9795
9796      return instance;
9797    };
9798
9799    function addIdentifier(locals, identifier, instance, name) {
9800      if (!(locals && isObject(locals.$scope))) {
9801        throw minErr('$controller')('noscp',
9802          "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9803          name, identifier);
9804      }
9805
9806      locals.$scope[identifier] = instance;
9807    }
9808  }];
9809}
9810
9811/**
9812 * @ngdoc service
9813 * @name $document
9814 * @requires $window
9815 *
9816 * @description
9817 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9818 *
9819 * @example
9820   <example module="documentExample">
9821     <file name="index.html">
9822       <div ng-controller="ExampleController">
9823         <p>$document title: <b ng-bind="title"></b></p>
9824         <p>window.document title: <b ng-bind="windowTitle"></b></p>
9825       </div>
9826     </file>
9827     <file name="script.js">
9828       angular.module('documentExample', [])
9829         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9830           $scope.title = $document[0].title;
9831           $scope.windowTitle = angular.element(window.document)[0].title;
9832         }]);
9833     </file>
9834   </example>
9835 */
9836function $DocumentProvider() {
9837  this.$get = ['$window', function(window) {
9838    return jqLite(window.document);
9839  }];
9840}
9841
9842/**
9843 * @ngdoc service
9844 * @name $exceptionHandler
9845 * @requires ng.$log
9846 *
9847 * @description
9848 * Any uncaught exception in angular expressions is delegated to this service.
9849 * The default implementation simply delegates to `$log.error` which logs it into
9850 * the browser console.
9851 *
9852 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9853 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9854 *
9855 * ## Example:
9856 *
9857 * ```js
9858 *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9859 *     return function(exception, cause) {
9860 *       exception.message += ' (caused by "' + cause + '")';
9861 *       throw exception;
9862 *     };
9863 *   });
9864 * ```
9865 *
9866 * This example will override the normal action of `$exceptionHandler`, to make angular
9867 * exceptions fail hard when they happen, instead of just logging to the console.
9868 *
9869 * <hr />
9870 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9871 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9872 * (unless executed during a digest).
9873 *
9874 * If you wish, you can manually delegate exceptions, e.g.
9875 * `try { ... } catch(e) { $exceptionHandler(e); }`
9876 *
9877 * @param {Error} exception Exception associated with the error.
9878 * @param {string=} cause optional information about the context in which
9879 *       the error was thrown.
9880 *
9881 */
9882function $ExceptionHandlerProvider() {
9883  this.$get = ['$log', function($log) {
9884    return function(exception, cause) {
9885      $log.error.apply($log, arguments);
9886    };
9887  }];
9888}
9889
9890var $$ForceReflowProvider = function() {
9891  this.$get = ['$document', function($document) {
9892    return function(domNode) {
9893      //the line below will force the browser to perform a repaint so
9894      //that all the animated elements within the animation frame will
9895      //be properly updated and drawn on screen. This is required to
9896      //ensure that the preparation animation is properly flushed so that
9897      //the active state picks up from there. DO NOT REMOVE THIS LINE.
9898      //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9899      //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9900      //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9901      if (domNode) {
9902        if (!domNode.nodeType && domNode instanceof jqLite) {
9903          domNode = domNode[0];
9904        }
9905      } else {
9906        domNode = $document[0].body;
9907      }
9908      return domNode.offsetWidth + 1;
9909    };
9910  }];
9911};
9912
9913var APPLICATION_JSON = 'application/json';
9914var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9915var JSON_START = /^\[|^\{(?!\{)/;
9916var JSON_ENDS = {
9917  '[': /]$/,
9918  '{': /}$/
9919};
9920var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9921var $httpMinErr = minErr('$http');
9922var $httpMinErrLegacyFn = function(method) {
9923  return function() {
9924    throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9925  };
9926};
9927
9928function serializeValue(v) {
9929  if (isObject(v)) {
9930    return isDate(v) ? v.toISOString() : toJson(v);
9931  }
9932  return v;
9933}
9934
9935
9936function $HttpParamSerializerProvider() {
9937  /**
9938   * @ngdoc service
9939   * @name $httpParamSerializer
9940   * @description
9941   *
9942   * Default {@link $http `$http`} params serializer that converts objects to strings
9943   * according to the following rules:
9944   *
9945   * * `{'foo': 'bar'}` results in `foo=bar`
9946   * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9947   * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9948   * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9949   *
9950   * Note that serializer will sort the request parameters alphabetically.
9951   * */
9952
9953  this.$get = function() {
9954    return function ngParamSerializer(params) {
9955      if (!params) return '';
9956      var parts = [];
vendor: 3,809 bytes, lines 9957-10083
9957      forEachSorted(params, function(value, key) {
9958        if (value === null || isUndefined(value)) return;
9959        if (isArray(value)) {
9960          forEach(value, function(v, k) {
9961            parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
9962          });
9963        } else {
9964          parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9965        }
9966      });
9967
9968      return parts.join('&');
9969    };
9970  };
9971}
9972
9973function $HttpParamSerializerJQLikeProvider() {
9974  /**
9975   * @ngdoc service
9976   * @name $httpParamSerializerJQLike
9977   * @description
9978   *
9979   * Alternative {@link $http `$http`} params serializer that follows
9980   * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9981   * The serializer will also sort the params alphabetically.
9982   *
9983   * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9984   *
9985   * ```js
9986   * $http({
9987   *   url: myUrl,
9988   *   method: 'GET',
9989   *   params: myParams,
9990   *   paramSerializer: '$httpParamSerializerJQLike'
9991   * });
9992   * ```
9993   *
9994   * It is also possible to set it as the default `paramSerializer` in the
9995   * {@link $httpProvider#defaults `$httpProvider`}.
9996   *
9997   * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9998   * form data for submission:
9999   *
10000   * ```js
10001   * .controller(function($http, $httpParamSerializerJQLike) {
10002   *   //...
10003   *
10004   *   $http({
10005   *     url: myUrl,
10006   *     method: 'POST',
10007   *     data: $httpParamSerializerJQLike(myData),
10008   *     headers: {
10009   *       'Content-Type': 'application/x-www-form-urlencoded'
10010   *     }
10011   *   });
10012   *
10013   * });
10014   * ```
10015   *
10016   * */
10017  this.$get = function() {
10018    return function jQueryLikeParamSerializer(params) {
10019      if (!params) return '';
10020      var parts = [];
10021      serialize(params, '', true);
10022      return parts.join('&');
10023
10024      function serialize(toSerialize, prefix, topLevel) {
10025        if (toSerialize === null || isUndefined(toSerialize)) return;
10026        if (isArray(toSerialize)) {
10027          forEach(toSerialize, function(value, index) {
10028            serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
10029          });
10030        } else if (isObject(toSerialize) && !isDate(toSerialize)) {
10031          forEachSorted(toSerialize, function(value, key) {
10032            serialize(value, prefix +
10033                (topLevel ? '' : '[') +
10034                key +
10035                (topLevel ? '' : ']'));
10036          });
10037        } else {
10038          parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
10039        }
10040      }
10041    };
10042  };
10043}
10044
10045function defaultHttpResponseTransform(data, headers) {
10046  if (isString(data)) {
10047    // Strip json vulnerability protection prefix and trim whitespace
10048    var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
10049
10050    if (tempData) {
10051      var contentType = headers('Content-Type');
10052      if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
10053        data = fromJson(tempData);
10054      }
10055    }
10056  }
10057
10058  return data;
10059}
10060
10061function isJsonLike(str) {
10062    var jsonStart = str.match(JSON_START);
10063    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
10064}
10065
10066/**
10067 * Parse headers into key value object
10068 *
10069 * @param {string} headers Raw headers as a string
10070 * @returns {Object} Parsed headers as key value object
10071 */
10072function parseHeaders(headers) {
10073  var parsed = createMap(), i;
10074
10075  function fillInParsed(key, val) {
10076    if (key) {
10077      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
10078    }
10079  }
10080
10081  if (isString(headers)) {
10082    forEach(headers.split('\n'), function(line) {
10083      i = line.indexOf(':');
vendor: 5,581 bytes, lines 10084-10251
10084      fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
10085    });
10086  } else if (isObject(headers)) {
10087    forEach(headers, function(headerVal, headerKey) {
10088      fillInParsed(lowercase(headerKey), trim(headerVal));
10089    });
10090  }
10091
10092  return parsed;
10093}
10094
10095
10096/**
10097 * Returns a function that provides access to parsed headers.
10098 *
10099 * Headers are lazy parsed when first requested.
10100 * @see parseHeaders
10101 *
10102 * @param {(string|Object)} headers Headers to provide access to.
10103 * @returns {function(string=)} Returns a getter function which if called with:
10104 *
10105 *   - if called with single an argument returns a single header value or null
10106 *   - if called with no arguments returns an object containing all headers.
10107 */
10108function headersGetter(headers) {
10109  var headersObj;
10110
10111  return function(name) {
10112    if (!headersObj) headersObj =  parseHeaders(headers);
10113
10114    if (name) {
10115      var value = headersObj[lowercase(name)];
10116      if (value === void 0) {
10117        value = null;
10118      }
10119      return value;
10120    }
10121
10122    return headersObj;
10123  };
10124}
10125
10126
10127/**
10128 * Chain all given functions
10129 *
10130 * This function is used for both request and response transforming
10131 *
10132 * @param {*} data Data to transform.
10133 * @param {function(string=)} headers HTTP headers getter fn.
10134 * @param {number} status HTTP status code of the response.
10135 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
10136 * @returns {*} Transformed data.
10137 */
10138function transformData(data, headers, status, fns) {
10139  if (isFunction(fns)) {
10140    return fns(data, headers, status);
10141  }
10142
10143  forEach(fns, function(fn) {
10144    data = fn(data, headers, status);
10145  });
10146
10147  return data;
10148}
10149
10150
10151function isSuccess(status) {
10152  return 200 <= status && status < 300;
10153}
10154
10155
10156/**
10157 * @ngdoc provider
10158 * @name $httpProvider
10159 * @description
10160 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
10161 * */
10162function $HttpProvider() {
10163  /**
10164   * @ngdoc property
10165   * @name $httpProvider#defaults
10166   * @description
10167   *
10168   * Object containing default values for all {@link ng.$http $http} requests.
10169   *
10170   * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
10171   * that will provide the cache for all requests who set their `cache` property to `true`.
10172   * If you set the `defaults.cache = false` then only requests that specify their own custom
10173   * cache object will be cached. See {@link $http#caching $http Caching} for more information.
10174   *
10175   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
10176   * Defaults value is `'XSRF-TOKEN'`.
10177   *
10178   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
10179   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
10180   *
10181   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
10182   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
10183   * setting default headers.
10184   *     - **`defaults.headers.common`**
10185   *     - **`defaults.headers.post`**
10186   *     - **`defaults.headers.put`**
10187   *     - **`defaults.headers.patch`**
10188   *
10189   *
10190   * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
10191   *  used to the prepare string representation of request parameters (specified as an object).
10192   *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
10193   *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
10194   *
10195   **/
10196  var defaults = this.defaults = {
10197    // transform incoming response data
10198    transformResponse: [defaultHttpResponseTransform],
10199
10200    // transform outgoing request data
10201    transformRequest: [function(d) {
10202      return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
10203    }],
10204
10205    // default headers
10206    headers: {
10207      common: {
10208        'Accept': 'application/json, text/plain, */*'
10209      },
10210      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
10211      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
10212      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
10213    },
10214
10215    xsrfCookieName: 'XSRF-TOKEN',
10216    xsrfHeaderName: 'X-XSRF-TOKEN',
10217
10218    paramSerializer: '$httpParamSerializer'
10219  };
10220
10221  var useApplyAsync = false;
10222  /**
10223   * @ngdoc method
10224   * @name $httpProvider#useApplyAsync
10225   * @description
10226   *
10227   * Configure $http service to combine processing of multiple http responses received at around
10228   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
10229   * significant performance improvement for bigger applications that make many HTTP requests
10230   * concurrently (common during application bootstrap).
10231   *
10232   * Defaults to false. If no value is specified, returns the current configured value.
10233   *
10234   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
10235   *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
10236   *    to load and share the same digest cycle.
10237   *
10238   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
10239   *    otherwise, returns the current configured value.
10240   **/
10241  this.useApplyAsync = function(value) {
10242    if (isDefined(value)) {
10243      useApplyAsync = !!value;
10244      return this;
10245    }
10246    return useApplyAsync;
10247  };
10248
10249  var useLegacyPromise = true;
10250  /**
10251   * @ngdoc method
10252   * @name $httpProvider#useLegacyPromiseExtensions
10253   * @description
10254   *
10255   * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
10256   * This should be used to make sure that applications work without these methods.
10257   *
10258   * Defaults to true. If no value is specified, returns the current configured value.
10259   *
10260   * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
10261   *
10262   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
10263   *    otherwise, returns the current configured value.
10264   **/
10265  this.useLegacyPromiseExtensions = function(value) {
10266    if (isDefined(value)) {
10267      useLegacyPromise = !!value;
10268      return this;
10269    }
10270    return useLegacyPromise;
10271  };
10272
10273  /**
10274   * @ngdoc property
10275   * @name $httpProvider#interceptors
10276   * @description
10277   *
10278   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
10279   * pre-processing of request or postprocessing of responses.
10280   *
10281   * These service factories are ordered by request, i.e. they are applied in the same order as the
10282   * array, on request, but reverse order, on response.
10283   *
10284   * {@link ng.$http#interceptors Interceptors detailed info}
10285   **/
10286  var interceptorFactories = this.interceptors = [];
10287
10288  this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
10289      function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
10290
10291    var defaultCache = $cacheFactory('$http');
10292
10293    /**
10294     * Make sure that default param serializer is exposed as a function
10295     */
10296    defaults.paramSerializer = isString(defaults.paramSerializer) ?
10297      $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
10298
10299    /**
10300     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
10301     * The reversal is needed so that we can build up the interception chain around the
10302     * server request.
10303     */
10304    var reversedInterceptors = [];
10305
10306    forEach(interceptorFactories, function(interceptorFactory) {
10307      reversedInterceptors.unshift(isString(interceptorFactory)
10308          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
10309    });
10310
10311    /**
10312     * @ngdoc service
10313     * @kind function
10314     * @name $http
10315     * @requires ng.$httpBackend
10316     * @requires $cacheFactory
10317     * @requires $rootScope
10318     * @requires $q
10319     * @requires $injector
10320     *
10321     * @description
10322     * The `$http` service is a core Angular service that facilitates communication with the remote
10323     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
10324     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
10325     *
10326     * For unit testing applications that use `$http` service, see
10327     * {@link ngMock.$httpBackend $httpBackend mock}.
10328     *
10329     * For a higher level of abstraction, please check out the {@link ngResource.$resource
10330     * $resource} service.
10331     *
10332     * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
10333     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
10334     * it is important to familiarize yourself with these APIs and the guarantees they provide.
10335     *
10336     *
10337     * ## General usage
10338     * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
10339     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
10340     *
10341     * ```js
10342     *   // Simple GET request example:
10343     *   $http({
10344     *     method: 'GET',
10345     *     url: '/someUrl'
10346     *   }).then(function successCallback(response) {
10347     *       // this callback will be called asynchronously
10348     *       // when the response is available
10349     *     }, function errorCallback(response) {
10350     *       // called asynchronously if an error occurs
10351     *       // or server returns response with an error status.
10352     *     });
10353     * ```
10354     *
10355     * The response object has these properties:
10356     *
10357     *   - **data** – `{string|Object}` – The response body transformed with the transform
10358     *     functions.
10359     *   - **status** – `{number}` – HTTP status code of the response.
10360     *   - **headers** – `{function([headerName])}` – Header getter function.
10361     *   - **config** – `{Object}` – The configuration object that was used to generate the request.
10362     *   - **statusText** – `{string}` – HTTP status text of the response.
10363     *
10364     * A response status code between 200 and 299 is considered a success status and
10365     * will result in the success callback being called. Note that if the response is a redirect,
10366     * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
10367     * called for such responses.
10368     *
10369     *
10370     * ## Shortcut methods
10371     *
10372     * Shortcut methods are also available. All shortcut methods require passing in the URL, and
10373     * request data must be passed in for POST/PUT requests. An optional config can be passed as the
10374     * last argument.
10375     *
10376     * ```js
10377     *   $http.get('/someUrl', config).then(successCallback, errorCallback);
10378     *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
10379     * ```
10380     *
10381     * Complete list of shortcut methods:
10382     *
10383     * - {@link ng.$http#get $http.get}
10384     * - {@link ng.$http#head $http.head}
10385     * - {@link ng.$http#post $http.post}
10386     * - {@link ng.$http#put $http.put}
10387     * - {@link ng.$http#delete $http.delete}
10388     * - {@link ng.$http#jsonp $http.jsonp}
10389     * - {@link ng.$http#patch $http.patch}
10390     *
10391     *
10392     * ## Writing Unit Tests that use $http
10393     * When unit testing (using {@link ngMock ngMock}), it is necessary to call
10394     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
10395     * request using trained responses.
10396     *
10397     * ```
10398     * $httpBackend.expectGET(...);
10399     * $http.get(...);
10400     * $httpBackend.flush();
10401     * ```
10402     *
10403     * ## Deprecation Notice
10404     * <div class="alert alert-danger">
10405     *   The `$http` legacy promise methods `success` and `error` have been deprecated.
10406     *   Use the standard `then` method instead.
10407     *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
10408     *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
10409     * </div>
10410     *
10411     * ## Setting HTTP Headers
10412     *
10413     * The $http service will automatically add certain HTTP headers to all requests. These defaults
10414     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
10415     * object, which currently contains this default configuration:
10416     *
10417     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
10418     *   - `Accept: application/json, text/plain, * / *`
10419     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
10420     *   - `Content-Type: application/json`
10421     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
10422     *   - `Content-Type: application/json`
10423     *
10424     * To add or overwrite these defaults, simply add or remove a property from these configuration
10425     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
10426     * with the lowercased HTTP method name as the key, e.g.
10427     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
10428     *
10429     * The defaults can also be set at runtime via the `$http.defaults` object in the same
10430     * fashion. For example:
10431     *
10432     * ```
10433     * module.run(function($http) {
10434     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
10435     * });
10436     * ```
10437     *
10438     * In addition, you can supply a `headers` property in the config object passed when
10439     * calling `$http(config)`, which overrides the defaults without changing them globally.
10440     *
10441     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
10442     * Use the `headers` property, setting the desired header to `undefined`. For example:
10443     *
10444     * ```js
10445     * var req = {
10446     *  method: 'POST',
10447     *  url: 'http://example.com',
10448     *  headers: {
10449     *    'Content-Type': undefined
10450     *  },
10451     *  data: { test: 'test' }
10452     * }
10453     *
10454     * $http(req).then(function(){...}, function(){...});
10455     * ```
10456     *
10457     * ## Transforming Requests and Responses
10458     *
10459     * Both requests and responses can be transformed using transformation functions: `transformRequest`
10460     * and `transformResponse`. These properties can be a single function that returns
10461     * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
10462     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
10463     *
10464     * ### Default Transformations
10465     *
10466     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
10467     * `defaults.transformResponse` properties. If a request does not provide its own transformations
10468     * then these will be applied.
10469     *
10470     * You can augment or replace the default transformations by modifying these properties by adding to or
10471     * replacing the array.
10472     *
10473     * Angular provides the following default transformations:
10474     *
10475     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
10476     *
10477     * - If the `data` property of the request configuration object contains an object, serialize it
10478     *   into JSON format.
10479     *
10480     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
10481     *
10482     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
10483     *  - If JSON response is detected, deserialize it using a JSON parser.
10484     *
10485     *
10486     * ### Overriding the Default Transformations Per Request
10487     *
10488     * If you wish override the request/response transformations only for a single request then provide
10489     * `transformRequest` and/or `transformResponse` properties on the configuration object passed
10490     * into `$http`.
10491     *
10492     * Note that if you provide these properties on the config object the default transformations will be
10493     * overwritten. If you wish to augment the default transformations then you must include them in your
10494     * local transformation array.
10495     *
10496     * The following code demonstrates adding a new response transformation to be run after the default response
10497     * transformations have been run.
10498     *
10499     * ```js
10500     * function appendTransform(defaults, transform) {
10501     *
10502     *   // We can't guarantee that the default transformation is an array
10503     *   defaults = angular.isArray(defaults) ? defaults : [defaults];
10504     *
10505     *   // Append the new transformation to the defaults
10506     *   return defaults.concat(transform);
10507     * }
10508     *
10509     * $http({
10510     *   url: '...',
10511     *   method: 'GET',
10512     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
10513     *     return doTransform(value);
10514     *   })
10515     * });
10516     * ```
10517     *
10518     *
10519     * ## Caching
10520     *
10521     * To enable caching, set the request configuration `cache` property to `true` (to use default
10522     * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
10523     * When the cache is enabled, `$http` stores the response from the server in the specified
10524     * cache. The next time the same request is made, the response is served from the cache without
10525     * sending a request to the server.
10526     *
10527     * Note that even if the response is served from cache, delivery of the data is asynchronous in
10528     * the same way that real requests are.
10529     *
10530     * If there are multiple GET requests for the same URL that should be cached using the same
10531     * cache, but the cache is not populated yet, only one request to the server will be made and
10532     * the remaining requests will be fulfilled using the response from the first request.
10533     *
10534     * You can change the default cache to a new object (built with
10535     * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
10536     * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
10537     * their `cache` property to `true` will now use this cache object.
10538     *
10539     * If you set the default cache to `false` then only requests that specify their own custom
10540     * cache object will be cached.
10541     *
10542     * ## Interceptors
10543     *
10544     * Before you start creating interceptors, be sure to understand the
10545     * {@link ng.$q $q and deferred/promise APIs}.
10546     *
10547     * For purposes of global error handling, authentication, or any kind of synchronous or
10548     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10549     * able to intercept requests before they are handed to the server and
10550     * responses before they are handed over to the application code that
10551     * initiated these requests. The interceptors leverage the {@link ng.$q
10552     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10553     *
10554     * The interceptors are service factories that are registered with the `$httpProvider` by
10555     * adding them to the `$httpProvider.interceptors` array. The factory is called and
10556     * injected with dependencies (if specified) and returns the interceptor.
10557     *
10558     * There are two kinds of interceptors (and two kinds of rejection interceptors):
10559     *
10560     *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10561     *     modify the `config` object or create a new one. The function needs to return the `config`
10562     *     object directly, or a promise containing the `config` or a new `config` object.
10563     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
10564     *     resolved with a rejection.
10565     *   * `response`: interceptors get called with http `response` object. The function is free to
10566     *     modify the `response` object or create a new one. The function needs to return the `response`
10567     *     object directly, or as a promise containing the `response` or a new `response` object.
10568     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
10569     *     resolved with a rejection.
10570     *
10571     *
10572     * ```js
10573     *   // register the interceptor as a service
10574     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10575     *     return {
10576     *       // optional method
10577     *       'request': function(config) {
10578     *         // do something on success
10579     *         return config;
10580     *       },
10581     *
10582     *       // optional method
10583     *      'requestError': function(rejection) {
10584     *         // do something on error
10585     *         if (canRecover(rejection)) {
10586     *           return responseOrNewPromise
10587     *         }
10588     *         return $q.reject(rejection);
10589     *       },
10590     *
10591     *
10592     *
10593     *       // optional method
10594     *       'response': function(response) {
10595     *         // do something on success
10596     *         return response;
10597     *       },
10598     *
10599     *       // optional method
10600     *      'responseError': function(rejection) {
10601     *         // do something on error
10602     *         if (canRecover(rejection)) {
10603     *           return responseOrNewPromise
10604     *         }
10605     *         return $q.reject(rejection);
10606     *       }
10607     *     };
10608     *   });
10609     *
10610     *   $httpProvider.interceptors.push('myHttpInterceptor');
10611     *
10612     *
10613     *   // alternatively, register the interceptor via an anonymous factory
10614     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10615     *     return {
10616     *      'request': function(config) {
10617     *          // same as above
10618     *       },
10619     *
10620     *       'response': function(response) {
10621     *          // same as above
10622     *       }
10623     *     };
10624     *   });
10625     * ```
10626     *
10627     * ## Security Considerations
10628     *
10629     * When designing web applications, consider security threats from:
10630     *
10631     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10632     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10633     *
10634     * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10635     * pre-configured with strategies that address these issues, but for this to work backend server
10636     * cooperation is required.
10637     *
10638     * ### JSON Vulnerability Protection
10639     *
10640     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10641     * allows third party website to turn your JSON resource URL into
10642     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10643     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10644     * Angular will automatically strip the prefix before processing it as JSON.
10645     *
10646     * For example if your server needs to return:
10647     * ```js
10648     * ['one','two']
10649     * ```
10650     *
10651     * which is vulnerable to attack, your server can return:
10652     * ```js
10653     * )]}',
10654     * ['one','two']
10655     * ```
10656     *
10657     * Angular will strip the prefix, before processing the JSON.
10658     *
10659     *
10660     * ### Cross Site Request Forgery (XSRF) Protection
10661     *
10662     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
10663     * an unauthorized site can gain your user's private data. Angular provides a mechanism
10664     * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
10665     * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
10666     * JavaScript that runs on your domain could read the cookie, your server can be assured that
10667     * the XHR came from JavaScript running on your domain. The header will not be set for
10668     * cross-domain requests.
10669     *
10670     * To take advantage of this, your server needs to set a token in a JavaScript readable session
10671     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10672     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10673     * that only JavaScript running on your domain could have sent the request. The token must be
10674     * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10675     * making up its own tokens). We recommend that the token is a digest of your site's
10676     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
10677     * for added security.
10678     *
10679     * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10680     * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10681     * or the per-request config object.
10682     *
10683     * In order to prevent collisions in environments where multiple Angular apps share the
10684     * same domain or subdomain, we recommend that each application uses unique cookie name.
10685     *
10686     * @param {object} config Object describing the request to be made and how it should be
10687     *    processed. The object has following properties:
10688     *
10689     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10690     *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10691     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10692     *      with the `paramSerializer` and appended as GET parameters.
10693     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
10694     *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
10695     *      HTTP headers to send to the server. If the return value of a function is null, the
10696     *      header will not be sent. Functions accept a config object as an argument.
10697     *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10698     *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10699     *    - **transformRequest** –
10700     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>
10700}` –
10701     *      transform function or an array of such functions. The transform function takes the http
10702     *      request body and headers and returns its transformed (typically serialized) version.
10703     *      See {@link ng.$http#overriding-the-default-transformations-per-request
10704     *      Overriding the Default Transformations}
10705     *    - **transformResponse** –
10706     *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10707     *      transform function or an array of such functions. The transform function takes the http
10708     *      response body, headers and status and returns its transformed (typically deserialized) version.
10709     *      See {@link ng.$http#overriding-the-default-transformations-per-request
10710     *      Overriding the Default TransformationjqLiks}
10711     *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10712     *      prepare the string representation of request parameters (specified as an object).
10713     *      If specified as string, it is interpreted as function registered with the
10714     *      {@link $injector $injector}, which means you can create your own serializer
10715     *      by registering it as a {@link auto.$provide#service service}.
10716     *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10717     *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10718     *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
10719     *      GET request, otherwise if a cache instance built with
10720     *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
10721     *      caching.
10722     *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10723     *      that should abort the request when resolved.
10724     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10725     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10726     *      for more information.
10727     *    - **responseType** - `{string}` - see
10728     *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10729     *
10730     * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10731     *                        when the request succeeds or fails.
10732     *
10733     *
10734     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10735     *   requests. This is primarily meant to be used for debugging purposes.
10736     *
10737     *
10738     * @example
10739<example module="httpExample">
10740<file name="index.html">
10741  <div ng-controller="FetchController">
10742    <select ng-model="method" aria-label="Request method">
10743      <option>GET</option>
10744      <option>JSONP</option>
10745    </select>
10746    <input type="text" ng-model="url" size="80" aria-label="URL" />
10747    <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10748    <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10749    <button id="samplejsonpbtn"
10750      ng-click="updateModel('JSONP',
10751                    'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10752      Sample JSONP
10753    </button>
10754    <button id="invalidjsonpbtn"
10755      ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10756        Invalid JSONP
10757      </button>
10758    <pre>http status code: {{status}}</pre>
10759    <pre>http response data: {{data}}</pre>
10760  </div>
10761</file>
10762<file name="script.js">
10763  angular.module('httpExample', [])
10764    .controller('FetchController', ['$scope', '$http', '$templateCache',
10765      function($scope, $http, $templateCache) {
10766        $scope.method = 'GET';
10767        $scope.url = 'http-hello.html';
10768
10769        $scope.fetch = function() {
10770          $scope.code = null;
10771          $scope.response = null;
10772
10773          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10774            then(function(response) {
10775              $scope.status = response.status;
10776              $scope.data = response.data;
10777            }, function(response) {
10778              $scope.data = response.data || "Request failed";
10779              $scope.status = response.status;
10780          });
10781        };
10782
10783        $scope.updateModel = function(method, url) {
10784          $scope.method = method;
10785          $scope.url = url;
10786        };
10787      }]);
10788</file>
10789<file name="http-hello.html">
10790  Hello, $http!
10791</file>
10792<file name="protractor.js" type="protractor">
10793  var status = element(by.binding('status'));
10794  var data = element(by.binding('data'));
10795  var fetchBtn = element(by.id('fetchbtn'));
10796  var sampleGetBtn = element(by.id('samplegetbtn'));
10797  var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10798  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10799
10800  it('should make an xhr GET request', function() {
10801    sampleGetBtn.click();
10802    fetchBtn.click();
10803    expect(status.getText()).toMatch('200');
10804    expect(data.getText()).toMatch(/Hello, \$http!/);
10805  });
10806
10807// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10808// it('should make a JSONP request to angularjs.org', function() {
10809//   sampleJsonpBtn.click();
10810//   fetchBtn.click();
10811//   expect(status.getText()).toMatch('200');
10812//   expect(data.getText()).toMatch(/Super Hero!/);
10813// });
10814
10815  it('should make JSONP request to invalid URL and invoke the error handler',
10816      function() {
10817    invalidJsonpBtn.click();
10818    fetchBtn.click();
10819    expect(status.getText()).toMatch('0');
10820    expect(data.getText()).toMatch('Request failed');
10821  });
10822</file>
10823</example>
10824     */
10825    function $http(requestConfig) {
10826
10827      if (!isObject(requestConfig)) {
10828        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
10829      }
10830
10831      if (!isString(requestConfig.url)) {
10832        throw minErr('$http')('badreq', 'Http request configuration url must be a string.  Received: {0}', requestConfig.url);
10833      }
10834
10835      var config = extend({
10836        method: 'get',
10837        transformRequest: defaults.transformRequest,
10838        transformResponse: defaults.transformResponse,
10839        paramSerializer: defaults.paramSerializer
10840      }, requestConfig);
10841
10842      config.headers = mergeHeaders(requestConfig);
10843      config.method = uppercase(config.method);
10844      config.paramSerializer = isString(config.paramSerializer) ?
10845        $injector.get(config.paramSerializer) : config.paramSerializer;
10846
10847      var serverRequest = function(config) {
10848        var headers = config.headers;
10849        var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10850
10851        // strip content-type if data is undefined
10852        if (isUndefined(reqData)) {
10853          forEach(headers, function(value, header) {
10854            if (lowercase(header) === 'content-type') {
10855                delete headers[header];
10856            }
10857          });
10858        }
10859
10860        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10861          config.withCredentials = defaults.withCredentials;
10862        }
10863
10864        // send request
10865        return sendReq(config, reqData).then(transformResponse, transformResponse);
10866      };
10867
10868      var chain = [serverRequest, undefined];
10869      var promise = $q.when(config);
10870
10871      // apply interceptors
10872      forEach(reversedInterceptors, function(interceptor) {
10873        if (interceptor.request || interceptor.requestError) {
10874          chain.unshift(interceptor.request, interceptor.requestError);
10875        }
10876        if (interceptor.response || interceptor.responseError) {
10877          chain.push(interceptor.response, interceptor.responseError);
10878        }
10879      });
10880
10881      while (chain.length) {
10882        var thenFn = chain.shift();
10883        var rejectFn = chain.shift();
10884
10885        promise = promise.then(thenFn, rejectFn);
10886      }
10887
10888      if (useLegacyPromise) {
10889        promise.success = function(fn) {
10890          assertArgFn(fn, 'fn');
10891
10892          promise.then(function(response) {
10893            fn(response.data, response.status, response.headers, config);
10894          });
10895          return promise;
10896        };
10897
10898        promise.error = function(fn) {
10899          assertArgFn(fn, 'fn');
10900
10901          promise.then(null, function(response) {
10902            fn(response.data, response.status, response.headers, config);
10903          });
10904          return promise;
10905        };
10906      } else {
10907        promise.success = $httpMinErrLegacyFn('success');
10908        promise.error = $httpMinErrLegacyFn('error');
10909      }
10910
10911      return promise;
10912
10913      function transformResponse(response) {
10914        // make a copy since the response must be cacheable
10915        var resp = extend({}, response);
10916        resp.data = transformData(response.data, response.headers, response.status,
10917                                  config.transformResponse);
10918        return (isSuccess(response.status))
10919          ? resp
10920          : $q.reject(resp);
10921      }
10922
10923      function executeHeaderFns(headers, config) {
10924        var headerContent, processedHeaders = {};
10925
10926        forEach(headers, function(headerFn, header) {
10927          if (isFunction(headerFn)) {
10928            headerContent = headerFn(config);
10929            if (headerContent != null) {
10930              processedHeaders[header] = headerContent;
10931            }
10932          } else {
10933            processedHeaders[header] = headerFn;
10934          }
10935        });
10936
10937        return processedHeaders;
10938      }
10939
10940      function mergeHeaders(config) {
10941        var defHeaders = defaults.headers,
10942            reqHeaders = extend({}, config.headers),
10943            defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10944
10945        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10946
vendor: 9,400 bytes, lines 10947-11247
10947        // using for-in instead of forEach to avoid unnecessary iteration after header has been found
10948        defaultHeadersIteration:
10949        for (defHeaderName in defHeaders) {
10950          lowercaseDefHeaderName = lowercase(defHeaderName);
10951
10952          for (reqHeaderName in reqHeaders) {
10953            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10954              continue defaultHeadersIteration;
10955            }
10956          }
10957
10958          reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10959        }
10960
10961        // execute if header value is a function for merged headers
10962        return executeHeaderFns(reqHeaders, shallowCopy(config));
10963      }
10964    }
10965
10966    $http.pendingRequests = [];
10967
10968    /**
10969     * @ngdoc method
10970     * @name $http#get
10971     *
10972     * @description
10973     * Shortcut method to perform `GET` request.
10974     *
10975     * @param {string} url Relative or absolute URL specifying the destination of the request
10976     * @param {Object=} config Optional configuration object
10977     * @returns {HttpPromise} Future object
10978     */
10979
10980    /**
10981     * @ngdoc method
10982     * @name $http#delete
10983     *
10984     * @description
10985     * Shortcut method to perform `DELETE` request.
10986     *
10987     * @param {string} url Relative or absolute URL specifying the destination of the request
10988     * @param {Object=} config Optional configuration object
10989     * @returns {HttpPromise} Future object
10990     */
10991
10992    /**
10993     * @ngdoc method
10994     * @name $http#head
10995     *
10996     * @description
10997     * Shortcut method to perform `HEAD` request.
10998     *
10999     * @param {string} url Relative or absolute URL specifying the destination of the request
11000     * @param {Object=} config Optional configuration object
11001     * @returns {HttpPromise} Future object
11002     */
11003
11004    /**
11005     * @ngdoc method
11006     * @name $http#jsonp
11007     *
11008     * @description
11009     * Shortcut method to perform `JSONP` request.
11010     *
11011     * @param {string} url Relative or absolute URL specifying the destination of the request.
11012     *                     The name of the callback should be the string `JSON_CALLBACK`.
11013     * @param {Object=} config Optional configuration object
11014     * @returns {HttpPromise} Future object
11015     */
11016    createShortMethods('get', 'delete', 'head', 'jsonp');
11017
11018    /**
11019     * @ngdoc method
11020     * @name $http#post
11021     *
11022     * @description
11023     * Shortcut method to perform `POST` request.
11024     *
11025     * @param {string} url Relative or absolute URL specifying the destination of the request
11026     * @param {*} data Request content
11027     * @param {Object=} config Optional configuration object
11028     * @returns {HttpPromise} Future object
11029     */
11030
11031    /**
11032     * @ngdoc method
11033     * @name $http#put
11034     *
11035     * @description
11036     * Shortcut method to perform `PUT` request.
11037     *
11038     * @param {string} url Relative or absolute URL specifying the destination of the request
11039     * @param {*} data Request content
11040     * @param {Object=} config Optional configuration object
11041     * @returns {HttpPromise} Future object
11042     */
11043
11044     /**
11045      * @ngdoc method
11046      * @name $http#patch
11047      *
11048      * @description
11049      * Shortcut method to perform `PATCH` request.
11050      *
11051      * @param {string} url Relative or absolute URL specifying the destination of the request
11052      * @param {*} data Request content
11053      * @param {Object=} config Optional configuration object
11054      * @returns {HttpPromise} Future object
11055      */
11056    createShortMethodsWithData('post', 'put', 'patch');
11057
11058        /**
11059         * @ngdoc property
11060         * @name $http#defaults
11061         *
11062         * @description
11063         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
11064         * default headers, withCredentials as well as request and response transformations.
11065         *
11066         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
11067         */
11068    $http.defaults = defaults;
11069
11070
11071    return $http;
11072
11073
11074    function createShortMethods(names) {
11075      forEach(arguments, function(name) {
11076        $http[name] = function(url, config) {
11077          return $http(extend({}, config || {}, {
11078            method: name,
11079            url: url
11080          }));
11081        };
11082      });
11083    }
11084
11085
11086    function createShortMethodsWithData(name) {
11087      forEach(arguments, function(name) {
11088        $http[name] = function(url, data, config) {
11089          return $http(extend({}, config || {}, {
11090            method: name,
11091            url: url,
11092            data: data
11093          }));
11094        };
11095      });
11096    }
11097
11098
11099    /**
11100     * Makes the request.
11101     *
11102     * !!! ACCESSES CLOSURE VARS:
11103     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
11104     */
11105    function sendReq(config, reqData) {
11106      var deferred = $q.defer(),
11107          promise = deferred.promise,
11108          cache,
11109          cachedResp,
11110          reqHeaders = config.headers,
11111          url = buildUrl(config.url, config.paramSerializer(config.params));
11112
11113      $http.pendingRequests.push(config);
11114      promise.then(removePendingReq, removePendingReq);
11115
11116
11117      if ((config.cache || defaults.cache) && config.cache !== false &&
11118          (config.method === 'GET' || config.method === 'JSONP')) {
11119        cache = isObject(config.cache) ? config.cache
11120              : isObject(defaults.cache) ? defaults.cache
11121              : defaultCache;
11122      }
11123
11124      if (cache) {
11125        cachedResp = cache.get(url);
11126        if (isDefined(cachedResp)) {
11127          if (isPromiseLike(cachedResp)) {
11128            // cached request has already been sent, but there is no response yet
11129            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
11130          } else {
11131            // serving from cache
11132            if (isArray(cachedResp)) {
11133              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
11134            } else {
11135              resolvePromise(cachedResp, 200, {}, 'OK');
11136            }
11137          }
11138        } else {
11139          // put the promise for the non-transformed response into cache as a placeholder
11140          cache.put(url, promise);
11141        }
11142      }
11143
11144
11145      // if we won't have the response in cache, set the xsrf headers and
11146      // send the request to the backend
11147      if (isUndefined(cachedResp)) {
11148        var xsrfValue = urlIsSameOrigin(config.url)
11149            ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
11150            : undefined;
11151        if (xsrfValue) {
11152          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
11153        }
11154
11155        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
11156            config.withCredentials, config.responseType);
11157      }
11158
11159      return promise;
11160
11161
11162      /**
11163       * Callback registered to $httpBackend():
11164       *  - caches the response if desired
11165       *  - resolves the raw $http promise
11166       *  - calls $apply
11167       */
11168      function done(status, response, headersString, statusText) {
11169        if (cache) {
11170          if (isSuccess(status)) {
11171            cache.put(url, [status, response, parseHeaders(headersString), statusText]);
11172          } else {
11173            // remove promise from the cache
11174            cache.remove(url);
11175          }
11176        }
11177
11178        function resolveHttpPromise() {
11179          resolvePromise(response, status, headersString, statusText);
11180        }
11181
11182        if (useApplyAsync) {
11183          $rootScope.$applyAsync(resolveHttpPromise);
11184        } else {
11185          resolveHttpPromise();
11186          if (!$rootScope.$$phase) $rootScope.$apply();
11187        }
11188      }
11189
11190
11191      /**
11192       * Resolves the raw $http promise.
11193       */
11194      function resolvePromise(response, status, headers, statusText) {
11195        //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
11196        status = status >= -1 ? status : 0;
11197
11198        (isSuccess(status) ? deferred.resolve : deferred.reject)({
11199          data: response,
11200          status: status,
11201          headers: headersGetter(headers),
11202          config: config,
11203          statusText: statusText
11204        });
11205      }
11206
11207      function resolvePromiseWithResult(result) {
11208        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
11209      }
11210
11211      function removePendingReq() {
11212        var idx = $http.pendingRequests.indexOf(config);
11213        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
11214      }
11215    }
11216
11217
11218    function buildUrl(url, serializedParams) {
11219      if (serializedParams.length > 0) {
11220        url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
11221      }
11222      return url;
11223    }
11224  }];
11225}
11226
11227/**
11228 * @ngdoc service
11229 * @name $xhrFactory
11230 *
11231 * @description
11232 * Factory function used to create XMLHttpRequest objects.
11233 *
11234 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
11235 *
11236 * ```
11237 * angular.module('myApp', [])
11238 * .factory('$xhrFactory', function() {
11239 *   return function createXhr(method, url) {
11240 *     return new window.XMLHttpRequest({mozSystem: true});
11241 *   };
11242 * });
11243 * ```
11244 *
11245 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
11246 * @param {string} url URL of the request.
11247 */
11248function $xhrFactoryProvider() {
11249  this.$get = function() {
11250    return function createXhr() {
11251      return new window.XMLHttpRequest();
11252    };
11253  };
11254}
11255
11256/**
11257 * @ngdoc service
11258 * @name $httpBackend
11259 * @requires $window
11260 * @requires $document
11261 * @requires $xhrFactory
11262 *
11263 * @description
11264 * HTTP backend used by the {@link ng.$http service} that delegates to
11265 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
11266 *
11267 * You should never need to use this service directly, instead use the higher-level abstractions:
11268 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
11269 *
11270 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
11271 * $httpBackend} which can be trained with responses.
11272 */
11273function $HttpBackendProvider() {
11274  this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
11275    return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
11276  }];
11277}
11278
11279function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
11280  // TODO(vojta): fix the signature
11281  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
11282    $browser.$$incOutstandingRequestCount();
11283    url = url || $browser.url();
11284
11285    if (lowercase(method) == 'jsonp') {
11286      var callbackId = '_' + (callbacks.counter++).toString(36);
11287      callbacks[callbackId] = function(data) {
11288        callbacks[callbackId].data = data;
11289        callbacks[callbackId].called = true;
11290      };
11291
11292      var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
11293          callbackId, function(status, text) {
11294        completeRequest(callback, status, callbacks[callbackId].data, "", text);
11295        callbacks[callbackId] = noop;
11296      });
11297    } else {
11298
11299      var xhr = createXhr(method, url);
11300
11301      xhr.open(method, url, true);
11302      forEach(headers, function(value, key) {
11303        if (isDefined(value)) {
11304            xhr.setRequestHeader(key, value);
11305        }
11306      });
11307
11308      xhr.onload = function requestLoaded() {
11309        var statusText = xhr.statusText || '';
11310
11311        // responseText is the old-school way of retrieving response (supported by IE9)
11312        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
11313        var response = ('response' in xhr) ? xhr.response : xhr.responseText;
11314
11315        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
11316        var status = xhr.status === 1223 ? 204 : xhr.status;
11317
11318        // fix status code when it is 0 (0 status is undocumented).
11319        // Occurs when accessing file resources or on Android 4.1 stock browser
11320        // while retrieving files from application cache.
11321        if (status === 0) {
11322          status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
11323        }
11324
11325        completeRequest(callback,
11326            status,
11327            response,
11328            xhr.getAllResponseHeaders(),
11329            statusText);
11330      };
11331
11332      var requestError = function() {
11333        // The response is always empty
11334        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
11335        completeRequest(callback, -1, null, null, '');
11336      };
11337
11338      xhr.onerror = requestError;
11339      xhr.onabort = requestError;
11340
11341      if (withCredentials) {
11342        xhr.withCredentials = true;
11343      }
11344
11345      if (responseType) {
11346        try {
11347          xhr.responseType = responseType;
11348        } catch (e) {
11349          // WebKit added support for the json responseType value on 09/03/2013
11350          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
11351          // known to throw when setting the value "json" as the response type. Other older
11352          // browsers implementing the responseType
11353          //
11354          // The json response type can be ignored if not supported, because JSON payloads are
11355          // parsed on the client-side regardless.
11356          if (responseType !== 'json') {
11357            throw e;
11358          }
11359        }
11360      }
11361
11362      xhr.send(isUndefined(post) ? null : post);
11363    }
11364
11365    if (timeout > 0) {
11366      var timeoutId = $browserDefer(timeoutRequest, timeout);
11367    } else if (isPromiseLike(timeout)) {
11368      timeout.then(timeoutRequest);
11369    }
11370
11371
11372    function timeoutRequest() {
11373      jsonpDone && jsonpDone();
11374      xhr && xhr.abort();
11375    }
11376
11377    function completeRequest(callback, status, response, headersString, statusText) {
11378      // cancel timeout and subsequent timeout promise resolution
11379      if (isDefined(timeoutId)) {
11380        $browserDefer.cancel(timeoutId);
11381      }
11382      jsonpDone = xhr = null;
11383
11384      callback(status, response, headersString, statusText);
11385      $browser.$$completeOutstandingRequest(noop);
11386    }
11387  };
11388
11389  function jsonpReq(url, callbackId, done) {
11390    // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
11391    // - fetches local scripts via XHR and evals them
11392    // - adds and immediately removes script elements from the document
11393    var script = rawDocument.createElement('script'), callback = null;
11394    script.type = "text/javascript";
11395    script.src = url;
11396    script.async = true;
11397
11398    callback = function(event) {
11399      removeEventListenerFn(script, "load", callback);
11400      removeEventListenerFn(script, "error", callback);
11401      rawDocument.body.removeChild(script);
11402      script = null;
11403      var status = -1;
11404      var text = "unknown";
11405
11406      if (event) {
11407        if (event.type === "load" && !callbacks[callbackId].called) {
11408          event = { type: "error" };
11409        }
11410        text = event.type;
11411        status = event.type === "error" ? 404 : 200;
11412      }
11413
11414      if (done) {
11415        done(status, text);
11416      }
11417    };
11418
11419    addEventListenerFn(script, "load", callback);
11420    addEventListenerFn(script, "error", callback);
11421    rawDocument.body.appendChild(script);
11422    return callback;
11423  }
11424}
11425
11426var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
11427$interpolateMinErr.throwNoconcat = function(text) {
11428  throw $interpolateMinErr('noconcat',
11429      "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
11430      "interpolations that concatenate multiple expressions when a trusted value is " +
11431      "required.  See http://docs.angularjs.org/api/ng.$sce", text);
11432};
11433
11434$interpolateMinErr.interr = function(text, err) {
11435  return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
11436};
11437
11438/**
11439 * @ngdoc provider
11440 * @name $interpolateProvider
11441 *
11442 * @description
11443 *
11444 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
11445 *
11446 * <div class="alert alert-danger">
11447 * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
11448 * template within a Python Jinja template (or any other template language). Mixing templating
11449 * languages is **very dangerous**. The embedding template language will not safely escape Angular
11450 * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
11451 * security bugs!
11452 * </div>
11453 *
11454 * @example
11455<example name="custom-interpolation-markup" module="customInterpolationApp">
11456<file name="index.html">
11457<script>
11458  var customInterpolationApp = angular.module('customInterpolationApp', []);
11459
11460  customInterpolationApp.config(function($interpolateProvider) {
11461    $interpolateProvider.startSymbol('//');
11462    $interpolateProvider.endSymbol('//');
11463  });
11464
11465
11466  customInterpolationApp.controller('DemoController', function() {
11467      this.label = "This binding is brought you by // interpolation symbols.";
11468  });
11469</script>
11470<div ng-controller="DemoController as demo">
11471    //demo.label//
11472</div>
11473</file>
11474<file name="protractor.js" type="protractor">
11475  it('should interpolate binding with custom symbols', function() {
11476    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
11477  });
11478</file>
11479</example>
11480 */
11481function $InterpolateProvider() {
11482  var startSymbol = '{{';
11483  var endSymbol = '}}';
11484
11485  /**
11486   * @ngdoc method
11487   * @name $interpolateProvider#startSymbol
11488   * @description
11489   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
11490   *
11491   * @param {string=} value new value to set the starting symbol to.
11492   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11493   */
11494  this.startSymbol = function(value) {
11495    if (value) {
11496      startSymbol = value;
11497      return this;
11498    } else {
11499      return startSymbol;
11500    }
11501  };
11502
11503  /**
11504   * @ngdoc method
11505   * @name $interpolateProvider#endSymbol
11506   * @description
11507   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11508   *
11509   * @param {string=} value new value to set the ending symbol to.
11510   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11511   */
11512  this.endSymbol = function(value) {
11513    if (value) {
11514      endSymbol = value;
11515      return this;
11516    } else {
11517      return endSymbol;
11518    }
11519  };
11520
11521
11522  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
11523    var startSymbolLength = startSymbol.length,
11524        endSymbolLength = endSymbol.length,
11525        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
11526        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
11527
11528    function escape(ch) {
11529      return '\\\\\\' + ch;
11530    }
11531
11532    function unescapeText(text) {
11533      return text.replace(escapedStartRegexp, startSymbol).
11534        replace(escapedEndRegexp, endSymbol);
11535    }
11536
11537    function stringify(value) {
11538      if (value == null) { // null || undefined
11539        return '';
11540      }
11541      switch (typeof value) {
11542        case 'string':
11543          break;
11544        case 'number':
11545          value = '' + value;
11546          break;
11547        default:
11548          value = toJson(value);
11549      }
11550
11551      return value;
11552    }
11553
11554    //TODO: this is the same as the constantWatchDelegate in parse.js
11555    function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
11556      var unwatch;
11557      return unwatch = scope.$watch(function constantInterpolateWatch(scope) {
11558        unwatch();
11559        return constantInterp(scope);
11560      }, listener, objectEquality);
11561    }
11562
11563    /**
11564     * @ngdoc service
11565     * @name $interpolate
11566     * @kind function
11567     *
11568     * @requires $parse
11569     * @requires $sce
11570     *
11571     * @description
11572     *
11573     * Compiles a string with markup into an interpolation function. This service is used by the
11574     * HTML {@link ng.$compile $compile} service for data binding. See
11575     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11576     * interpolation markup.
11577     *
11578     *
11579     * ```js
11580     *   var $interpolate = ...; // injected
11581     *   var exp = $interpolate('Hello {{name | uppercase}}!');
11582     *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11583     * ```
11584     *
11585     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11586     * `true`, the interpolation function will return `undefined` unless all embedded expressions
11587     * evaluate to a value other than `undefined`.
11588     *
11589     * ```js
11590     *   var $interpolate = ...; // injected
11591     *   var context = {greeting: 'Hello', name: undefined };
11592     *
11593     *   // default "forgiving" mode
11594     *   var exp = $interpolate('{{greeting}} {{name}}!');
11595     *   expect(exp(context)).toEqual('Hello !');
11596     *
11597     *   // "allOrNothing" mode
11598     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11599     *   expect(exp(context)).toBeUndefined();
11600     *   context.name = 'Angular';
11601     *   expect(exp(context)).toEqual('Hello Angular!');
11602     * ```
11603     *
11604     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
11605     *
11606     * ####Escaped Interpolation
11607     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
11608     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
11609     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
11610     * or binding.
11611     *
11612     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
11613     * degree, while also enabling code examples to work without relying on the
11614     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11615     *
11616     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11617     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
11618     * interpolation start/end markers with their escaped counterparts.**
11619     *
11620     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11621     * output when the $interpolate service processes the text. So, for HTML elements interpolated
11622     * by {@link ng.$compile $compile}
11622, or otherwise interpolated with the `mustHaveExpression` parameter
11623     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11624     * this is typically useful only when user-data is used in rendering a template from the server, or
11625     * when otherwise untrusted data is used by a directive.
11626     *
11627     * <example>
11628     *  <file name="index.html">
11629     *    <div ng-init="username='A user'">
11630     *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11631     *        </p>
11632     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
11633     *        application, but fails to accomplish their task, because the server has correctly
11634     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11635     *        characters.</p>
11636     *      <p>Instead, the result of the attempted script injection is visible, and can be removed
11637     *        from the database by an administrator.</p>
11638     *    </div>
11639     *  </file>
11640     * </example>
11641     *
11642     * @param {string} text The text with markup to interpolate.
11643     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11644     *    embedded expression in order to return an interpolation function. Strings with no
11645     *    embedded expression will return null for the interpolation function.
11646     * @param {string=} trustedContext when provided, the returned function passes the interpolated
11647     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11648     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
11649     *    provides Strict Contextual Escaping for details.
11650     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11651     *    unless all embedded expressions evaluate to a value other than `undefined`.
11652     * @returns {function(context)} an interpolation function which is used to compute the
11653     *    interpolated string. The function has these parameters:
11654     *
11655     * - `context`: evaluation context for all expressions embedded in the interpolated text
11656     */
11657    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11658      // Provide a quick exit and simplified result function for text with no interpolation
11659      if (!text.length || text.indexOf(startSymbol) === -1) {
11660        var constantInterp;
11661        if (!mustHaveExpression) {
11662          var unescapedText = unescapeText(text);
11663          constantInterp = valueFn(unescapedText);
11664          constantInterp.exp = text;
11665          constantInterp.expressions = [];
11666          constantInterp.$$watchDelegate = constantWatchDelegate;
11667        }
11668        return constantInterp;
11669      }
11670
11671      allOrNothing = !!allOrNothing;
11672      var startIndex,
11673          endIndex,
11674          index = 0,
11675          expressions = [],
11676          parseFns = [],
11677          textLength = text.length,
11678          exp,
11679          concat = [],
11680          expressionPositions = [];
11681
11682      while (index < textLength) {
11683        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11684             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11685          if (index !== startIndex) {
11686            concat.push(unescapeText(text.substring(index, startIndex)));
11687          }
11688          exp = text.substring(startIndex + startSymbolLength, endIndex);
11689          expressions.push(exp);
11690          parseFns.push($parse(exp, parseStringifyInterceptor));
11691          index = endIndex + endSymbolLength;
11692          expressionPositions.push(concat.length);
11693          concat.push('');
11694        } else {
11695          // we did not find an interpolation, so we have to add the remainder to the separators array
11696          if (index !== textLength) {
11697            concat.push(unescapeText(text.substring(index)));
11698          }
11699          break;
11700        }
11701      }
11702
11703      // Concatenating expressions makes it hard to reason about whether some combination of
11704      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
11705      // single expression be used for iframe[src], object[src], etc., we ensure that the value
11706      // that's used is assigned or constructed by some JS code somewhere that is more testable or
11707      // make it obvious that you bound the value to some user controlled value.  This helps reduce
11708      // the load when auditing for XSS issues.
11709      if (trustedContext && concat.length > 1) {
11710          $interpolateMinErr.throwNoconcat(text);
11711      }
11712
11713      if (!mustHaveExpression || expressions.length) {
11714        var compute = function(values) {
11715          for (var i = 0, ii = expressions.length; i < ii; i++) {
11716            if (allOrNothing && isUndefined(values[i])) return;
11717            concat[expressionPositions[i]] = values[i];
11718          }
11719          return concat.join('');
11720        };
11721
11722        var getValue = function(value) {
11723          return trustedContext ?
11724            $sce.getTrusted(trustedContext, value) :
11725            $sce.valueOf(value);
11726        };
11727
11728        return extend(function interpolationFn(context) {
11729            var i = 0;
11730            var ii = expressions.length;
11731            var values = new Array(ii);
11732
11733            try {
11734              for (; i < ii; i++) {
11735                values[i] = parseFns[i](context);
11736              }
11737
11738              return compute(values);
11739            } catch (err) {
11740              $exceptionHandler($interpolateMinErr.interr(text, err));
vendor: 4,299 bytes, lines 11741-11851
11741            }
11742
11743          }, {
11744          // all of these properties are undocumented for now
11745          exp: text, //just for compatibility with regular watchers created via $watch
11746          expressions: expressions,
11747          $$watchDelegate: function(scope, listener) {
11748            var lastValue;
11749            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11750              var currValue = compute(values);
11751              if (isFunction(listener)) {
11752                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11753              }
11754              lastValue = currValue;
11755            });
11756          }
11757        });
11758      }
11759
11760      function parseStringifyInterceptor(value) {
11761        try {
11762          value = getValue(value);
11763          return allOrNothing && !isDefined(value) ? value : stringify(value);
11764        } catch (err) {
11765          $exceptionHandler($interpolateMinErr.interr(text, err));
11766        }
11767      }
11768    }
11769
11770
11771    /**
11772     * @ngdoc method
11773     * @name $interpolate#startSymbol
11774     * @description
11775     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11776     *
11777     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11778     * the symbol.
11779     *
11780     * @returns {string} start symbol.
11781     */
11782    $interpolate.startSymbol = function() {
11783      return startSymbol;
11784    };
11785
11786
11787    /**
11788     * @ngdoc method
11789     * @name $interpolate#endSymbol
11790     * @description
11791     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11792     *
11793     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11794     * the symbol.
11795     *
11796     * @returns {string} end symbol.
11797     */
11798    $interpolate.endSymbol = function() {
11799      return endSymbol;
11800    };
11801
11802    return $interpolate;
11803  }];
11804}
11805
11806function $IntervalProvider() {
11807  this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
11808       function($rootScope,   $window,   $q,   $$q,   $browser) {
11809    var intervals = {};
11810
11811
11812     /**
11813      * @ngdoc service
11814      * @name $interval
11815      *
11816      * @description
11817      * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11818      * milliseconds.
11819      *
11820      * The return value of registering an interval function is a promise. This promise will be
11821      * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11822      * run indefinitely if `count` is not defined. The value of the notification will be the
11823      * number of iterations that have run.
11824      * To cancel an interval, call `$interval.cancel(promise)`.
11825      *
11826      * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11827      * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11828      * time.
11829      *
11830      * <div class="alert alert-warning">
11831      * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11832      * with them.  In particular they are not automatically destroyed when a controller's scope or a
11833      * directive's element are destroyed.
11834      * You should take this into consideration and make sure to always cancel the interval at the
11835      * appropriate moment.  See the example below for more details on how and when to do this.
11836      * </div>
11837      *
11838      * @param {function()} fn A function that should be called repeatedly.
11839      * @param {number} delay Number of milliseconds between each function call.
11840      * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11841      *   indefinitely.
11842      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11843      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11844      * @param {...*=} Pass additional parameters to the executed function.
11845      * @returns {promise} A promise which will be notified on each iteration.
11846      *
11847      * @example
11848      * <example module="intervalExample">
11849      * <file name="index.html">
11850      *   <script>
11851      *     angular.module('intervalExample', [])
11852      *       .controller('ExampleController', ['$scope', '$interval',
11853      *         function($scope, $interval) {
11854      *           $scope.format = 'M/d/yy h:mm:ss a';
11855      *           $scope.blood_1 = 100;
11856      *           $scope.blood_2 = 120;
11857      *
11858      *           var stop;
11859      *           $scope.fight = function() {
11860      *             // Don't start a new fight if we are already fighting
11861      *             if ( angular.isDefined(stop) ) return;
11862      *
11863      *             stop = $interval(function() {
11864      *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11865      *                 $scope.blood_1 = $scope.blood_1 - 3;
11866      *                 $scope.blood_2 = $scope.blood_2 - 4;
11867      *               } else {
11868      *                 $scope.stopFight();
11869      *               }
11870      *             }, 100);
11871      *           };
11872      *
11873      *           $scope.stopFight = function() {
11874      *             if (angular.isDefined(stop)) {
11875      *               $interval.cancel(stop);
11876      *               stop = undefined;
11877      *             }
11878      *           };
11879      *
11880      *           $scope.resetFight = function() {
11881      *             $scope.blood_1 = 100;
11882      *             $scope.blood_2 = 120;
11883      *           };
11884      *
11885      *           $scope.$on('$destroy', function() {
11886      *             // Make sure that the interval is destroyed too
11887      *             $scope.stopFight();
11888      *           });
11889      *         }])
11890      *       // Register the 'myCurrentTime' directive factory method.
11891      *       // We inject $interval and dateFilter service since the factory method is DI.
11892      *       .directive('myCurrentTime', ['$interval', 'dateFilter',
11893      *         function($interval, dateFilter) {
11894      *           // return the directive link function. (compile function not needed)
11895      *           return function(scope, element, attrs) {
11896      *             var format,  // date format
11897      *                 stopTime; // so that we can cancel the time updates
11898      *
11899      *             // used to update the UI
11900      *             function updateTime() {
11901      *               element.text(dateFilter(new Date(), format));
11902      *             }
11903      *
11904      *             // watch the expression, and update the UI on change.
11905      *             scope.$watch(attrs.myCurrentTime, function(value) {
11906      *               format = value;
11907      *               updateTime();
11908      *             });
11909      *
11910      *             stopTime = $interval(updateTime, 1000);
11911      *
11912      *             // listen on DOM destroy (removal) event, and cancel the next UI update
11913      *             // to prevent updating time after the DOM element was removed.
11914      *             element.on('$destroy', function() {
11915      *               $interval.cancel(stopTime);
11916      *             });
11917      *           }
11918      *         }]);
11919      *   </script>
11920      *
11921      *   <div>
11922      *     <div ng-controller="ExampleController">
11923      *       <label>Date format: <input ng-model="format"></label> <hr/>
11924      *       Current time is: <span my-current-time="format"></span>
11925      *       <hr/>
11926      *       Blood 1 : <font color='red'>{{blood_1}}</font>
11927      *       Blood 2 : <font color='red'>{{blood_2}}</font>
11928      *       <button type="button" data-ng-click="fight()">Fight</button>
11929      *       <button type="button" data-ng-click="stopFight()">StopFight</button>
11930      *       <button type="button" data-ng-click="resetFight()">resetFight</button>
11931      *     </div>
11932      *   </div>
11933      *
11934      * </file>
11935      * </example>
11936      */
11937    function interval(fn, delay, count, invokeApply) {
11938      var hasParams = arguments.length > 4,
11939          args = hasParams ? sliceArgs(arguments, 4) : [],
11940          setInterval = $window.setInterval,
11941          clearInterval = $window.clearInterval,
11942          iteration = 0,
11943          skipApply = (isDefined(invokeApply) && !invokeApply),
11944          deferred = (skipApply ? $$q : $q).defer(),
11945          promise = deferred.promise;
11946
11947      count = isDefined(count) ? count : 0;
11948
11949      promise.$$intervalId = setInterval(function tick() {
11950        if (skipApply) {
11951          $browser.defer(callback);
11952        } else {
11953          $rootScope.$evalAsync(callback);
11954        }
11955        deferred.notify(iteration++);
11956
11957        if (count > 0 && iteration >= count) {
11958          deferred.resolve(iteration);
11959          clearInterval(promise.$$intervalId);
11960          delete intervals[promise.$$intervalId];
11961        }
11962
11963        if (!skipApply) $rootScope.$apply();
11964
11965      }, delay);
11966
11967      intervals[promise.$$intervalId] = deferred;
11968
11969      return promise;
11970
11971      function callback() {
11972        if (!hasParams) {
11973          fn(iteration);
11974        } else {
11975          fn.apply(null, args);
11976        }
11977      }
11978    }
11979
11980
11981     /**
11982      * @ngdoc method
11983      * @name $interval#cancel
11984      *
11985      * @description
11986      * Cancels a task associated with the `promise`.
11987      *
11988      * @param {Promise=} promise returned by the `$interval` function.
11989      * @returns {boolean} Returns `true` if the task was successfully canceled.
11990      */
11991    interval.cancel = function(promise) {
11992      if (promise && promise.$$intervalId in intervals) {
11993        intervals[promise.$$intervalId].reject('canceled');
11994        $window.clearInterval(promise.$$intervalId);
11995        delete intervals[promise.$$intervalId];
11996        return true;
11997      }
11998      return false;
11999    };
12000
12001    return interval;
12002  }];
12003}
12004
12005/**
12006 * @ngdoc service
12007 * @name $locale
12008 *
12009 * @description
12010 * $locale service provides localization rules for various Angular components. As of right now the
12011 * only public api is:
12012 *
12013 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
12014 */
12015
12016var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
12017    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
12018var $locationMinErr = minErr('$location');
12019
12020
12021/**
12022 * Encode path using encodeUriSegment, ignoring forward slashes
12023 *
12024 * @param {string} path Path to encode
12025 * @returns {string}
12026 */
12027function encodePath(path) {
12028  var segments = path.split('/'),
12029      i = segments.length;
12030
12031  while (i--) {
12032    segments[i] = encodeUriSegment(segments[i]);
12033  }
12034
12035  return segments.join('/');
12036}
12037
12038function parseAbsoluteUrl(absoluteUrl, locationObj) {
12039  var parsedUrl = urlResolve(absoluteUrl);
12040
12041  locationObj.$$protocol = parsedUrl.protocol;
12042  locationObj.$$host = parsedUrl.hostname;
12043  locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
12044}
12045
12046
12047function parseAppUrl(relativeUrl, locationObj) {
12048  var prefixed = (relativeUrl.charAt(0) !== '/');
12049  if (prefixed) {
12050    relativeUrl = '/' + relativeUrl;
12051  }
12052  var match = urlResolve(relativeUrl);
12053  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
12054      match.pathname.substring(1) : match.pathname);
12055  locationObj.$$search = parseKeyValue(match.search);
12056  locationObj.$$hash = decodeURIComponent(match.hash);
12057
12058  // make sure path starts with '/';
12059  if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
12060    locationObj.$$path = '/' + locationObj.$$path;
12061  }
12062}
12063
12064
12065/**
12066 *
12067 * @param {string} begin
12068 * @param {string} whole
12069 * @returns {string} returns text from whole after begin or undefined if it does not begin with
12070 *                   expected string.
12071 */
12072function beginsWith(begin, whole) {
12073  if (whole.indexOf(begin) === 0) {
12074    return whole.substr(begin.length);
12075  }
12076}
12077
12078
12079function stripHash(url) {
12080  var index = url.indexOf('#');
12081  return index == -1 ? url : url.substr(0, index);
12082}
12083
12084function trimEmptyHash(url) {
12085  return url.replace(/(#.+)|#$/, '$1');
12086}
12087
12088
12089function stripFile(url) {
12090  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
12091}
12092
12093/* return the server only (scheme://host:port) */
12094function serverBase(url) {
12095  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
12096}
12097
12098
12099/**
12100 * LocationHtml5Url represents an url
12101 * This object is exposed as $location service when HTML5 mode is enabled and supported
12102 *
12103 * @constructor
12104 * @param {string} appBase application base URL
12105 * @param {string} appBaseNoFile application base URL stripped of any filename
12106 * @param {string} basePrefix url path prefix
12107 */
12108function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
12109  this.$$html5 = true;
12110  basePrefix = basePrefix || '';
12111  parseAbsoluteUrl(appBase, this);
12112
12113
12114  /**
12115   * Parse given html5 (regular) url string into properties
12116   * @param {string} url HTML5 url
12117   * @private
12118   */
12119  this.$$parse = function(url) {
12120    var pathUrl = beginsWith(appBaseNoFile, url);
12121    if (!isString(pathUrl)) {
12122      throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
12123          appBaseNoFile);
12124    }
12125
12126    parseAppUrl(pathUrl, this);
12127
12128    if (!this.$$path) {
12129      this.$$path = '/';
12130    }
12131
12132    this.$$compose();
12133  };
12134
12135  /**
12136   * Compose url and update `absUrl` property
12137   * @private
12138   */
12139  this.$$compose = function() {
vendor: 4,105 bytes, lines 12140-12262
12140    var search = toKeyValue(this.$$search),
12141        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
12142
12143    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
12144    this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
12145  };
12146
12147  this.$$parseLinkUrl = function(url, relHref) {
12148    if (relHref && relHref[0] === '#') {
12149      // special case for links to hash fragments:
12150      // keep the old url and only replace the hash fragment
12151      this.hash(relHref.slice(1));
12152      return true;
12153    }
12154    var appUrl, prevAppUrl;
12155    var rewrittenUrl;
12156
12157    if (isDefined(appUrl = beginsWith(appBase, url))) {
12158      prevAppUrl = appUrl;
12159      if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
12160        rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
12161      } else {
12162        rewrittenUrl = appBase + prevAppUrl;
12163      }
12164    } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
12165      rewrittenUrl = appBaseNoFile + appUrl;
12166    } else if (appBaseNoFile == url + '/') {
12167      rewrittenUrl = appBaseNoFile;
12168    }
12169    if (rewrittenUrl) {
12170      this.$$parse(rewrittenUrl);
12171    }
12172    return !!rewrittenUrl;
12173  };
12174}
12175
12176
12177/**
12178 * LocationHashbangUrl represents url
12179 * This object is exposed as $location service when developer doesn't opt into html5 mode.
12180 * It also serves as the base class for html5 mode fallback on legacy browsers.
12181 *
12182 * @constructor
12183 * @param {string} appBase application base URL
12184 * @param {string} appBaseNoFile application base URL stripped of any filename
12185 * @param {string} hashPrefix hashbang prefix
12186 */
12187function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
12188
12189  parseAbsoluteUrl(appBase, this);
12190
12191
12192  /**
12193   * Parse given hashbang url into properties
12194   * @param {string} url Hashbang url
12195   * @private
12196   */
12197  this.$$parse = function(url) {
12198    var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
12199    var withoutHashUrl;
12200
12201    if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
12202
12203      // The rest of the url starts with a hash so we have
12204      // got either a hashbang path or a plain hash fragment
12205      withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
12206      if (isUndefined(withoutHashUrl)) {
12207        // There was no hashbang prefix so we just have a hash fragment
12208        withoutHashUrl = withoutBaseUrl;
12209      }
12210
12211    } else {
12212      // There was no hashbang path nor hash fragment:
12213      // If we are in HTML5 mode we use what is left as the path;
12214      // Otherwise we ignore what is left
12215      if (this.$$html5) {
12216        withoutHashUrl = withoutBaseUrl;
12217      } else {
12218        withoutHashUrl = '';
12219        if (isUndefined(withoutBaseUrl)) {
12220          appBase = url;
12221          this.replace();
12222        }
12223      }
12224    }
12225
12226    parseAppUrl(withoutHashUrl, this);
12227
12228    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
12229
12230    this.$$compose();
12231
12232    /*
12233     * In Windows, on an anchor node on documents loaded from
12234     * the filesystem, the browser will return a pathname
12235     * prefixed with the drive name ('/C:/path') when a
12236     * pathname without a drive is set:
12237     *  * a.setAttribute('href', '/foo')
12238     *   * a.pathname === '/C:/foo' //true
12239     *
12240     * Inside of Angular, we're always using pathnames that
12241     * do not include drive names for routing.
12242     */
12243    function removeWindowsDriveName(path, url, base) {
12244      /*
12245      Matches paths for file protocol on windows,
12246      such as /C:/foo/bar, and captures only /foo/bar.
12247      */
12248      var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
12249
12250      var firstPathSegmentMatch;
12251
12252      //Get the relative path from the input URL.
12253      if (url.indexOf(base) === 0) {
12254        url = url.replace(base, '');
12255      }
12256
12257      // The input URL intentionally contains a first path segment that ends with a colon.
12258      if (windowsFilePathExp.exec(url)) {
12259        return path;
12260      }
12261
12262      firstPathSegmentMatch = windowsFilePathExp.exec(path);
12263      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
12264    }
12265  };
12266
12267  /**
12268   * Compose hashbang url and update `absUrl` property
12269   * @private
12270   */
12271  this.$$compose = function() {
12272    var search = toKeyValue(this.$$search),
12273        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
12274
12275    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
12276    this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
12277  };
12278
12279  this.$$parseLinkUrl = function(url, relHref) {
12280    if (stripHash(appBase) == stripHash(url)) {
12281      this.$$parse(url);
12282      return true;
12283    }
12284    return false;
12285  };
12286}
12287
12288
12289/**
12290 * LocationHashbangUrl represents url
12291 * This object is exposed as $location service when html5 history api is enabled but the browser
12292 * does not support it.
12293 *
12294 * @constructor
12295 * @param {string} appBase application base URL
12296 * @param {string} appBaseNoFile application base URL stripped of any filename
12297 * @param {string} hashPrefix hashbang prefix
12298 */
12299function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
12300  this.$$html5 = true;
12301  LocationHashbangUrl.apply(this, arguments);
12302
12303  this.$$parseLinkUrl = function(url, relHref) {
12304    if (relHref && relHref[0] === '#') {
12305      // special case for links to hash fragments:
12306      // keep the old url and only replace the hash fragment
12307      this.hash(relHref.slice(1));
12308      return true;
12309    }
12310
12311    var rewrittenUrl;
12312    var appUrl;
12313
12314    if (appBase == stripHash(url)) {
12315      rewrittenUrl = url;
12316    } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
12317      rewrittenUrl = appBase + hashPrefix + appUrl;
12318    } else if (appBaseNoFile === url + '/') {
12319      rewrittenUrl = appBaseNoFile;
12320    }
12321    if (rewrittenUrl) {
12322      this.$$parse(rewrittenUrl);
12323    }
12324    return !!rewrittenUrl;
12325  };
12326
12327  this.$$compose = function() {
12328    var search = toKeyValue(this.$$search),
12329        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
12330
12331    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
12332    // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
12333    this.$$absUrl = appBase + hashPrefix + this.$$url;
12334  };
12335
12336}
12337
12338
12339var locationPrototype = {
12340
12341  /**
12342   * Are we in html5 mode?
12343   * @private
12344   */
12345  $$html5: false,
12346
12347  /**
12348   * Has any change been replacing?
12349   * @private
12350   */
12351  $$replace: false,
12352
12353  /**
12354   * @ngdoc method
12355   * @name $location#absUrl
12356   *
12357   * @description
12358   * This method is getter only.
12359   *
12360   * Return full url representation with all segments encoded according to rules specified in
12361   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
12362   *
12363   *
12364   * ```js
12365   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12366   * var absUrl = $location.absUrl();
12367   * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
12368   * ```
12369   *
12370   * @return {string} full url
12371   */
12372  absUrl: locationGetter('$$absUrl'),
12373
12374  /**
12375   * @ngdoc method
12376   * @name $location#url
12377   *
12378   * @description
12379   * This method is getter / setter.
12380   *
12381   * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
12382   *
12383   * Change path, search and hash, when called with parameter and return `$location`.
12384   *
12385   *
12386   * ```js
12387   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12388   * var url = $location.url();
12389   * // => "/some/path?foo=bar&baz=xoxo"
12390   * ```
12391   *
12392   * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
12393   * @return {string} url
12394   */
12395  url: function(url) {
12396    if (isUndefined(url)) {
12397      return this.$$url;
12398    }
12399
12400    var match = PATH_MATCH.exec(url);
12401    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
12402    if (match[2] || match[1] || url === '') this.search(match[3] || '');
12403    this.hash(match[5] || '');
12404
12405    return this;
12406  },
12407
12408  /**
12409   * @ngdoc method
12410   * @name $location#protocol
12411   *
12412   * @description
12413   * This method is getter only.
12414   *
12415   * Return protocol of current url.
12416   *
12417   *
12418   * ```js
12419   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12420   * var protocol = $location.protocol();
12421   * // => "http"
12422   * ```
12423   *
12424   * @return {string} protocol of current url
12425   */
12426  protocol: locationGetter('$$protocol'),
12427
12428  /**
12429   * @ngdoc method
12430   * @name $location#host
12431   *
12432   * @description
12433   * This method is getter only.
12434   *
12435   * Return host of current url.
12436   *
12437   * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
12438   *
12439   *
12440   * ```js
12441   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12442   * var host = $location.host();
12443   * // => "example.com"
12444   *
12445   * // given url http://user:[email protected]:8080/#/some/path?foo=bar&baz=xoxo
12446   * host = $location.host();
12447   * // => "example.com"
12448   * host = location.host;
12449   * // => "example.com:8080"
12450   * ```
12451   *
12452   * @return {string} host of current url.
12453   */
12454  host: locationGetter('$$host'),
12455
12456  /**
12457   * @ngdoc method
12458   * @name $location#port
12459   *
12460   * @description
12461   * This method is getter only.
12462   *
12463   * Return port of current url.
12464   *
12465   *
12466   * ```js
12467   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12468   * var port = $location.port();
12469   * // => 80
12470   * ```
12471   *
12472   * @return {Number} port
12473   */
12474  port: locationGetter('$$port'),
12475
12476  /**
12477   * @ngdoc method
12478   * @name $location#path
12479   *
12480   * @description
12481   * This method is getter / setter.
12482   *
12483   * Return path of current url when called without any parameter.
12484   *
12485   * Change path when called with parameter and return `$location`.
12486   *
12487   * Note: Path should always begin with forward slash (/), this method will add the forward slash
12488   * if it is missing.
12489   *
12490   *
12491   * ```js
12492   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12493   * var path = $location.path();
12494   * // => "/some/path"
12495   * ```
12496   *
12497   * @param {(string|number)=} path New path
12498   * @return {string} path
12499   */
12500  path: locationGetterSetter('$$path', function(path) {
12501    path = path !== null ? path.toString() : '';
12502    return path.charAt(0) == '/' ? path : '/' + path;
12503  }),
12504
12505  /**
12506   * @ngdoc method
12507   * @name $location#search
12508   *
12509   * @description
12510   * This method is getter / setter.
12511   *
12512   * Return search part (as object) of current url when called without any parameter.
12513   *
12514   * Change search part when called with parameter and return `$location`.
12515   *
12516   *
12517   * ```js
12518   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12519   * var searchObject = $location.search();
12520   * // => {foo: 'bar', baz: 'xoxo'}
12521   *
12522   * // set foo to 'yipee'
12523   * $location.search('foo', 'yipee');
12524   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
12525   * ```
12526   *
12527   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
12528   * hash object.
12529   *
12530   * When called with a single argument the method acts as a setter, setting the `search` component
12531   * of `$location` to the specified value.
12532   *
12533   * If the argument is a hash object containing an array of values, these values will be encoded
12534   * as duplicate search parameters in the url.
12535   *
12536   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
12537   * will override only a single search property.
12538   *
12539   * If `paramValue` is an array, it will override the property of the `search` component of
12540   * `$location` specified via the first argument.
12541   *
12542   * If `paramValue` is `null`, the property specified via the first argument will be deleted.
12543   *
12544   * If `paramValue` is `true`, the property specified via the first argument will be added with no
12545   * value nor trailing equal sign.
12546   *
12547   * @return {Object} If called with no arguments returns the parsed `search` object. If called with
12548   * one or more arguments returns `$location` object itself.
12549   */
12550  search: function(search, paramValue) {
12551    switch (arguments.length) {
12552      case 0:
12553        return this.$$search;
12554      case 1:
12555        if (isString(search) || isNumber(search)) {
12556          search = search.toString();
12557          this.$$search = parseKeyValue(search);
12558        } else if (isObject(search)) {
12559          search = copy(search, {});
12560          // remove object undefined or null properties
vendor: 9,382 bytes, lines 12561-12842
12561          forEach(search, function(value, key) {
12562            if (value == null) delete search[key];
12563          });
12564
12565          this.$$search = search;
12566        } else {
12567          throw $locationMinErr('isrcharg',
12568              'The first argument of the `$location#search()` call must be a string or an object.');
12569        }
12570        break;
12571      default:
12572        if (isUndefined(paramValue) || paramValue === null) {
12573          delete this.$$search[search];
12574        } else {
12575          this.$$search[search] = paramValue;
12576        }
12577    }
12578
12579    this.$$compose();
12580    return this;
12581  },
12582
12583  /**
12584   * @ngdoc method
12585   * @name $location#hash
12586   *
12587   * @description
12588   * This method is getter / setter.
12589   *
12590   * Returns the hash fragment when called without any parameters.
12591   *
12592   * Changes the hash fragment when called with a parameter and returns `$location`.
12593   *
12594   *
12595   * ```js
12596   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12597   * var hash = $location.hash();
12598   * // => "hashValue"
12599   * ```
12600   *
12601   * @param {(string|number)=} hash New hash fragment
12602   * @return {string} hash
12603   */
12604  hash: locationGetterSetter('$$hash', function(hash) {
12605    return hash !== null ? hash.toString() : '';
12606  }),
12607
12608  /**
12609   * @ngdoc method
12610   * @name $location#replace
12611   *
12612   * @description
12613   * If called, all changes to $location during the current `$digest` will replace the current history
12614   * record, instead of adding a new one.
12615   */
12616  replace: function() {
12617    this.$$replace = true;
12618    return this;
12619  }
12620};
12621
12622forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
12623  Location.prototype = Object.create(locationPrototype);
12624
12625  /**
12626   * @ngdoc method
12627   * @name $location#state
12628   *
12629   * @description
12630   * This method is getter / setter.
12631   *
12632   * Return the history state object when called without any parameter.
12633   *
12634   * Change the history state object when called with one parameter and return `$location`.
12635   * The state object is later passed to `pushState` or `replaceState`.
12636   *
12637   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12638   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12639   * older browsers (like IE9 or Android < 4.0), don't use this method.
12640   *
12641   * @param {object=} state State object for pushState or replaceState
12642   * @return {object} state
12643   */
12644  Location.prototype.state = function(state) {
12645    if (!arguments.length) {
12646      return this.$$state;
12647    }
12648
12649    if (Location !== LocationHtml5Url || !this.$$html5) {
12650      throw $locationMinErr('nostate', 'History API state support is available only ' +
12651        'in HTML5 mode and only in browsers supporting HTML5 History API');
12652    }
12653    // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12654    // but we're changing the $$state reference to $browser.state() during the $digest
12655    // so the modification window is narrow.
12656    this.$$state = isUndefined(state) ? null : state;
12657
12658    return this;
12659  };
12660});
12661
12662
12663function locationGetter(property) {
12664  return function() {
12665    return this[property];
12666  };
12667}
12668
12669
12670function locationGetterSetter(property, preprocess) {
12671  return function(value) {
12672    if (isUndefined(value)) {
12673      return this[property];
12674    }
12675
12676    this[property] = preprocess(value);
12677    this.$$compose();
12678
12679    return this;
12680  };
12681}
12682
12683
12684/**
12685 * @ngdoc service
12686 * @name $location
12687 *
12688 * @requires $rootElement
12689 *
12690 * @description
12691 * The $location service parses the URL in the browser address bar (based on the
12692 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12693 * available to your application. Changes to the URL in the address bar are reflected into
12694 * $location service and changes to $location are reflected into the browser address bar.
12695 *
12696 * **The $location service:**
12697 *
12698 * - Exposes the current URL in the browser address bar, so you can
12699 *   - Watch and observe the URL.
12700 *   - Change the URL.
12701 * - Synchronizes the URL with the browser when the user
12702 *   - Changes the address bar.
12703 *   - Clicks the back or forward button (or clicks a History link).
12704 *   - Clicks on a link.
12705 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12706 *
12707 * For more information see {@link guide/$location Developer Guide: Using $location}
12708 */
12709
12710/**
12711 * @ngdoc provider
12712 * @name $locationProvider
12713 * @description
12714 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12715 */
12716function $LocationProvider() {
12717  var hashPrefix = '',
12718      html5Mode = {
12719        enabled: false,
12720        requireBase: true,
12721        rewriteLinks: true
12722      };
12723
12724  /**
12725   * @ngdoc method
12726   * @name $locationProvider#hashPrefix
12727   * @description
12728   * @param {string=} prefix Prefix for hash part (containing path and search)
12729   * @returns {*} current value if used as getter or itself (chaining) if used as setter
12730   */
12731  this.hashPrefix = function(prefix) {
12732    if (isDefined(prefix)) {
12733      hashPrefix = prefix;
12734      return this;
12735    } else {
12736      return hashPrefix;
12737    }
12738  };
12739
12740  /**
12741   * @ngdoc method
12742   * @name $locationProvider#html5Mode
12743   * @description
12744   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12745   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12746   *   properties:
12747   *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12748   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12749   *     support `pushState`.
12750   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12751   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12752   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
12753   *     See the {@link guide/$location $location guide for more information}
12754   *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12755   *     enables/disables url rewriting for relative links.
12756   *
12757   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12758   */
12759  this.html5Mode = function(mode) {
12760    if (isBoolean(mode)) {
12761      html5Mode.enabled = mode;
12762      return this;
12763    } else if (isObject(mode)) {
12764
12765      if (isBoolean(mode.enabled)) {
12766        html5Mode.enabled = mode.enabled;
12767      }
12768
12769      if (isBoolean(mode.requireBase)) {
12770        html5Mode.requireBase = mode.requireBase;
12771      }
12772
12773      if (isBoolean(mode.rewriteLinks)) {
12774        html5Mode.rewriteLinks = mode.rewriteLinks;
12775      }
12776
12777      return this;
12778    } else {
12779      return html5Mode;
12780    }
12781  };
12782
12783  /**
12784   * @ngdoc event
12785   * @name $location#$locationChangeStart
12786   * @eventType broadcast on root scope
12787   * @description
12788   * Broadcasted before a URL will change.
12789   *
12790   * This change can be prevented by calling
12791   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12792   * details about event object. Upon successful change
12793   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12794   *
12795   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12796   * the browser supports the HTML5 History API.
12797   *
12798   * @param {Object} angularEvent Synthetic event object.
12799   * @param {string} newUrl New URL
12800   * @param {string=} oldUrl URL that was before it was changed.
12801   * @param {string=} newState New history state object
12802   * @param {string=} oldState History state object that was before it was changed.
12803   */
12804
12805  /**
12806   * @ngdoc event
12807   * @name $location#$locationChangeSuccess
12808   * @eventType broadcast on root scope
12809   * @description
12810   * Broadcasted after a URL was changed.
12811   *
12812   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12813   * the browser supports the HTML5 History API.
12814   *
12815   * @param {Object} angularEvent Synthetic event object.
12816   * @param {string} newUrl New URL
12817   * @param {string=} oldUrl URL that was before it was changed.
12818   * @param {string=} newState New history state object
12819   * @param {string=} oldState History state object that was before it was changed.
12820   */
12821
12822  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12823      function($rootScope, $browser, $sniffer, $rootElement, $window) {
12824    var $location,
12825        LocationMode,
12826        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12827        initialUrl = $browser.url(),
12828        appBase;
12829
12830    if (html5Mode.enabled) {
12831      if (!baseHref && html5Mode.requireBase) {
12832        throw $locationMinErr('nobase',
12833          "$location in HTML5 mode requires a <base> tag to be present!");
12834      }
12835      appBase = serverBase(initialUrl) + (baseHref || '/');
12836      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12837    } else {
12838      appBase = stripHash(initialUrl);
12839      LocationMode = LocationHashbangUrl;
12840    }
12841    var appBaseNoFile = stripFile(appBase);
12842
12843    $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12844    $location.$$parseLinkUrl(initialUrl, initialUrl);
12845
12846    $location.$$state = $browser.state();
12847
12848    var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12849
12850    function setBrowserUrlWithFallback(url, replace, state) {
12851      var oldUrl = $location.url();
12852      var oldState = $location.$$state;
12853      try {
12854        $browser.url(url, replace, state);
12855
12856        // Make sure $location.state() returns referentially identical (not just deeply equal)
12857        // state object; this makes possible quick checking if the state changed in the digest
12858        // loop. Checking deep equality would be too expensive.
12859        $location.$$state = $browser.state();
12860      } catch (e) {
12861        // Restore old values if pushState fails
12862        $location.url(oldUrl);
12863        $location.$$state = oldState;
12864
12865        throw e;
12866      }
12867    }
12868
12869    $rootElement.on('click', function(event) {
12870      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12871      // currently we open nice url link and redirect then
12872
12873      if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12874
12875      var elm = jqLite(event.target);
12876
12877      // traverse the DOM up to find first A tag
12878      while (nodeName_(elm[0]) !== 'a') {
12879        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12880        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12881      }
12882
12883      var absHref = elm.prop('href');
12884      // get the actual href attribute - see
12885      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12886      var relHref = elm.attr('href') || elm.attr('xlink:href');
12887
12888      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12889        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12890        // an animation.
12891        absHref = urlResolve(absHref.animVal).href;
12892      }
12893
12894      // Ignore when url is started with javascript: or mailto:
12895      if (IGNORE_URI_REGEXP.test(absHref)) return;
12896
12897      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12898        if ($location.$$parseLinkUrl(absHref, relHref)) {
12899          // We do a preventDefault for all urls that are part of the angular application,
12900          // in html5mode and also without, so that we are able to abort navigation without
12901          // getting double entries in the location history.
12902          event.preventDefault();
12903          // update location manually
12904          if ($location.absUrl() != $browser.url()) {
12905            $rootScope.$apply();
12906            // hack to work around FF6 bug 684208 when scenario runner clicks on links
12907            $window.angular['ff-684208-preventDefault'] = true;
12908          }
12909        }
12910      }
12911    });
12912
12913
12914    // rewrite hashbang url <> html5 url
12915    if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12916      $browser.url($location.absUrl(), true);
12917    }
12918
12919    var initializing = true;
12920
12921    // update $location when $browser url changes
12922    $browser.onUrlChange(function(newUrl, newState) {
12923
12924      if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12925        // If we are navigating outside of the app then force a reload
12926        $window.location.href = newUrl;
12927        return;
12928      }
12929
12930      $rootScope.$evalAsync(function() {
12931        var oldUrl = $location.absUrl();
12932        var oldState = $location.$$state;
12933        var defaultPrevented;
12934        newUrl = trimEmptyHash(newUrl);
12935        $location.$$parse(newUrl);
12936        $location.$$state = newState;
12937
12938        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12939            newState, oldState).defaultPrevented;
12940
12941        // if the location was changed by a `$locationChangeStart` handler then stop
12942        // processing this location change
12943        if ($location.absUrl() !== newUrl) return;
12944
12945        if (defaultPrevented) {
12946          $location.$$parse(oldUrl);
12947          $location.$$state = oldState;
12948          setBrowserUrlWithFallback(oldUrl, false, oldState);
12949        } else {
12950          initializing = false;
12951          afterLocationChange(oldUrl, oldState);
12952        }
12953      });
12954      if (!$rootScope.$$phase) $rootScope.$digest();
12955    });
vendor: 4,218 bytes, lines 12956-13093
12956
12957    // update browser
12958    $rootScope.$watch(function $locationWatch() {
12959      var oldUrl = trimEmptyHash($browser.url());
12960      var newUrl = trimEmptyHash($location.absUrl());
12961      var oldState = $browser.state();
12962      var currentReplace = $location.$$replace;
12963      var urlOrStateChanged = oldUrl !== newUrl ||
12964        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12965
12966      if (initializing || urlOrStateChanged) {
12967        initializing = false;
12968
12969        $rootScope.$evalAsync(function() {
12970          var newUrl = $location.absUrl();
12971          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12972              $location.$$state, oldState).defaultPrevented;
12973
12974          // if the location was changed by a `$locationChangeStart` handler then stop
12975          // processing this location change
12976          if ($location.absUrl() !== newUrl) return;
12977
12978          if (defaultPrevented) {
12979            $location.$$parse(oldUrl);
12980            $location.$$state = oldState;
12981          } else {
12982            if (urlOrStateChanged) {
12983              setBrowserUrlWithFallback(newUrl, currentReplace,
12984                                        oldState === $location.$$state ? null : $location.$$state);
12985            }
12986            afterLocationChange(oldUrl, oldState);
12987          }
12988        });
12989      }
12990
12991      $location.$$replace = false;
12992
12993      // we don't need to return anything because $evalAsync will make the digest loop dirty when
12994      // there is a change
12995    });
12996
12997    return $location;
12998
12999    function afterLocationChange(oldUrl, oldState) {
13000      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
13001        $location.$$state, oldState);
13002    }
13003}];
13004}
13005
13006/**
13007 * @ngdoc service
13008 * @name $log
13009 * @requires $window
13010 *
13011 * @description
13012 * Simple service for logging. Default implementation safely writes the message
13013 * into the browser's console (if present).
13014 *
13015 * The main purpose of this service is to simplify debugging and troubleshooting.
13016 *
13017 * The default is to log `debug` messages. You can use
13018 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
13019 *
13020 * @example
13021   <example module="logExample">
13022     <file name="script.js">
13023       angular.module('logExample', [])
13024         .controller('LogController', ['$scope', '$log', function($scope, $log) {
13025           $scope.$log = $log;
13026           $scope.message = 'Hello World!';
13027         }]);
13028     </file>
13029     <file name="index.html">
13030       <div ng-controller="LogController">
13031         <p>Reload this page with open console, enter text and hit the log button...</p>
13032         <label>Message:
13033         <input type="text" ng-model="message" /></label>
13034         <button ng-click="$log.log(message)">log</button>
13035         <button ng-click="$log.warn(message)">warn</button>
13036         <button ng-click="$log.info(message)">info</button>
13037         <button ng-click="$log.error(message)">error</button>
13038         <button ng-click="$log.debug(message)">debug</button>
13039       </div>
13040     </file>
13041   </example>
13042 */
13043
13044/**
13045 * @ngdoc provider
13046 * @name $logProvider
13047 * @description
13048 * Use the `$logProvider` to configure how the application logs messages
13049 */
13050function $LogProvider() {
13051  var debug = true,
13052      self = this;
13053
13054  /**
13055   * @ngdoc method
13056   * @name $logProvider#debugEnabled
13057   * @description
13058   * @param {boolean=} flag enable or disable debug level messages
13059   * @returns {*} current value if used as getter or itself (chaining) if used as setter
13060   */
13061  this.debugEnabled = function(flag) {
13062    if (isDefined(flag)) {
13063      debug = flag;
13064    return this;
13065    } else {
13066      return debug;
13067    }
13068  };
13069
13070  this.$get = ['$window', function($window) {
13071    return {
13072      /**
13073       * @ngdoc method
13074       * @name $log#log
13075       *
13076       * @description
13077       * Write a log message
13078       */
13079      log: consoleLog('log'),
13080
13081      /**
13082       * @ngdoc method
13083       * @name $log#info
13084       *
13085       * @description
13086       * Write an information message
13087       */
13088      info: consoleLog('info'),
13089
13090      /**
13091       * @ngdoc method
13092       * @name $log#warn
13093       *
13094       * @description
13095       * Write a warning message
13096       */
13097      warn: consoleLog('warn'),
13098
13099      /**
13100       * @ngdoc method
13101       * @name $log#error
13102       *
13103       * @description
13104       * Write an error message
13105       */
13106      error: consoleLog('error'),
13107
13108      /**
13109       * @ngdoc method
13110       * @name $log#debug
13111       *
13112       * @description
13113       * Write a debug message
13114       */
13115      debug: (function() {
13116        var fn = consoleLog('debug');
13117
13118        return function() {
13119          if (debug) {
13120            fn.apply(self, arguments);
13121          }
13122        };
13123      }())
13124    };
13125
13126    function formatError(arg) {
13127      if (arg instanceof Error) {
13128        if (arg.stack) {
13129          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
13130              ? 'Error: ' + arg.message + '\n' + arg.stack
13131              : arg.stack;
13132        } else if (arg.sourceURL) {
13133          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
13134        }
13135      }
13136      return arg;
13137    }
13138
13139    function consoleLog(type) {
13140      var console = $window.console || {},
13141          logFn = console[type] || console.log || noop,
13142          hasApply = false;
13143
13144      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
13145      // The reason behind this is that console.log has type "object" in IE8...
13146      try {
13147        hasApply = !!logFn.apply;
13148      } catch (e) {}
13149
13150      if (hasApply) {
13151        return function() {
13152          var args = [];
13153          forEach(arguments, function(arg) {
13154            args.push(formatError(arg));
13155          });
13156          return logFn.apply(console, args);
13157        };
13158      }
13159
13160      // we are IE which either doesn't have window.console => this is noop and we do nothing,
13161      // or we are IE where console.log doesn't have apply so we log at least first 2 args
13162      return function(arg1, arg2) {
13163        logFn(arg1, arg2 == null ? '' : arg2);
13164      };
13165    }
13166  }];
13167}
13168
13169/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
13170 *     Any commits to this file should be reviewed with security in mind.  *
13171 *   Changes to this file can potentially create security vulnerabilities. *
13172 *          An approval from 2 Core members with history of modifying      *
13173 *                         this file is required.                          *
13174 *                                                                         *
13175 *  Does the change somehow allow for arbitrary javascript to be executed? *
13176 *    Or allows for someone to change the prototype of built-in objects?   *
13177 *     Or gives undesired access to variables likes document or window?    *
13178 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13179
13180var $parseMinErr = minErr('$parse');
13181
13182// Sandboxing Angular Expressions
13183// ------------------------------
13184// Angular expressions are generally considered safe because these expressions only have direct
13185// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
13186// obtaining a reference to native JS functions such as the Function constructor.
13187//
13188// As an example, consider the following Angular expression:
13189//
13190//   {}.toString.constructor('alert("evil JS code")')
13191//
13192// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
13193// against the expression language, but not to prevent exploits that were enabled by exposing
13194// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
13195// practice and therefore we are not even trying to protect against interaction with an object
13196// explicitly exposed in this way.
13197//
13198// In general, it is not possible to access a Window object from an angular expression unless a
13199// window or some DOM object that has a reference to window is published onto a Scope.
13200// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
13201// native objects.
13202//
13203// See https://docs.angularjs.org/guide/security
13204
13205
13206function ensureSafeMemberName(name, fullExpression) {
13207  if (name === "__defineGetter__" || name === "__defineSetter__"
13208      || name === "__lookupGetter__" || name === "__lookupSetter__"
13209      || name === "__proto__") {
13210    throw $parseMinErr('isecfld',
13211        'Attempting to access a disallowed field in Angular expressions! '
13212        + 'Expression: {0}', fullExpression);
13213  }
13214  return name;
13215}
13216
13217function getStringValue(name) {
13218  // Property names must be strings. This means that non-string objects cannot be used
13219  // as keys in an object. Any non-string object, including a number, is typecasted
13220  // into a string via the toString method.
13221  // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
13222  //
13223  // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
13224  // to a string. It's not always possible. If `name` is an object and its `toString` method is
13225  // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
13226  //
13227  // TypeError: Cannot convert object to primitive value
13228  //
13229  // For performance reasons, we don't catch this error here and allow it to propagate up the call
13230  // stack. Note that you'll get the same error in JavaScript if you try to access a property using
13231  // such a 'broken' object as a key.
13232  return name + '';
13233}
13234
13235function ensureSafeObject(obj, fullExpression) {
13236  // nifty check if obj is Function that is fast and works across iframes and other contexts
13237  if (obj) {
13238    if (obj.constructor === obj) {
13239      throw $parseMinErr('isecfn',
13240          'Referencing Function in Angular expressions is disallowed! Expression: {0}',
13241          fullExpression);
13242    } else if (// isWindow(obj)
13243        obj.window === obj) {
13244      throw $parseMinErr('isecwindow',
13245          'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
13246          fullExpression);
13247    } else if (// isElement(obj)
13248        obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
13249      throw $parseMinErr('isecdom',
13250          'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
13251          fullExpression);
13252    } else if (// block Object so that we can't get hold of dangerous Object.* methods
13253        obj === Object) {
13254      throw $parseMinErr('isecobj',
13255          'Referencing Object in Angular expressions is disallowed! Expression: {0}',
13256          fullExpression);
13257    }
13258  }
13259  return obj;
13260}
13261
13262var CALL = Function.prototype.call;
13263var APPLY = Function.prototype.apply;
13264var BIND = Function.prototype.bind;
13265
13266function ensureSafeFunction(obj, fullExpression) {
13267  if (obj) {
13268    if (obj.constructor === obj) {
13269      throw $parseMinErr('isecfn',
13270        'Referencing Function in Angular expressions is disallowed! Expression: {0}',
13271        fullExpression);
13272    } else if (obj === CALL || obj === APPLY || obj === BIND) {
13273      throw $parseMinErr('isecff',
13274        'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
13275        fullExpression);
13276    }
13277  }
13278}
13279
13280function ensureSafeAssignContext(obj, fullExpression) {
13281  if (obj) {
13282    if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
13283        obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
13284      throw $parseMinErr('isecaf',
13285        'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
13286    }
13287  }
13288}
13289
13290var OPERATORS = createMap();
13291forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
13292var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
13293
13294
13295/////////////////////////////////////////
13296
13297
13298/**
13299 * @constructor
13300 */
13301var Lexer = function(options) {
13302  this.options = options;
13303};
13304
13305Lexer.prototype = {
13306  constructor: Lexer,
13307
13308  lex: function(text) {
13309    this.text = text;
13310    this.index = 0;
13311    this.tokens = [];
13312
13313    while (this.index < this.text.length) {
13314      var ch = this.text.charAt(this.index);
13315      if (ch === '"' || ch === "'") {
13316        this.readString(ch);
13317      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
13318        this.readNumber();
13319      } else if (this.isIdent(ch)) {
13320        this.readIdent();
13321      } else if (this.is(ch, '(){}[].,;:?')) {
13322        this.tokens.push({index: this.index, text: ch});
13323        this.index++;
13324      } else if (this.isWhitespace(ch)) {
13325        this.index++;
13326      } else {
13327        var ch2 = ch + this.peek();
13328        var ch3 = ch2 + this.peek(2);
13329        var op1 = OPERATORS[ch];
vendor: 47,995 bytes, lines 13330-14788
13330        var op2 = OPERATORS[ch2];
13331        var op3 = OPERATORS[ch3];
13332        if (op1 || op2 || op3) {
13333          var token = op3 ? ch3 : (op2 ? ch2 : ch);
13334          this.tokens.push({index: this.index, text: token, operator: true});
13335          this.index += token.length;
13336        } else {
13337          this.throwError('Unexpected next character ', this.index, this.index + 1);
13338        }
13339      }
13340    }
13341    return this.tokens;
13342  },
13343
13344  is: function(ch, chars) {
13345    return chars.indexOf(ch) !== -1;
13346  },
13347
13348  peek: function(i) {
13349    var num = i || 1;
13350    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
13351  },
13352
13353  isNumber: function(ch) {
13354    return ('0' <= ch && ch <= '9') && typeof ch === "string";
13355  },
13356
13357  isWhitespace: function(ch) {
13358    // IE treats non-breaking space as \u00A0
13359    return (ch === ' ' || ch === '\r' || ch === '\t' ||
13360            ch === '\n' || ch === '\v' || ch === '\u00A0');
13361  },
13362
13363  isIdent: function(ch) {
13364    return ('a' <= ch && ch <= 'z' ||
13365            'A' <= ch && ch <= 'Z' ||
13366            '_' === ch || ch === '$');
13367  },
13368
13369  isExpOperator: function(ch) {
13370    return (ch === '-' || ch === '+' || this.isNumber(ch));
13371  },
13372
13373  throwError: function(error, start, end) {
13374    end = end || this.index;
13375    var colStr = (isDefined(start)
13376            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
13377            : ' ' + end);
13378    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
13379        error, colStr, this.text);
13380  },
13381
13382  readNumber: function() {
13383    var number = '';
13384    var start = this.index;
13385    while (this.index < this.text.length) {
13386      var ch = lowercase(this.text.charAt(this.index));
13387      if (ch == '.' || this.isNumber(ch)) {
13388        number += ch;
13389      } else {
13390        var peekCh = this.peek();
13391        if (ch == 'e' && this.isExpOperator(peekCh)) {
13392          number += ch;
13393        } else if (this.isExpOperator(ch) &&
13394            peekCh && this.isNumber(peekCh) &&
13395            number.charAt(number.length - 1) == 'e') {
13396          number += ch;
13397        } else if (this.isExpOperator(ch) &&
13398            (!peekCh || !this.isNumber(peekCh)) &&
13399            number.charAt(number.length - 1) == 'e') {
13400          this.throwError('Invalid exponent');
13401        } else {
13402          break;
13403        }
13404      }
13405      this.index++;
13406    }
13407    this.tokens.push({
13408      index: start,
13409      text: number,
13410      constant: true,
13411      value: Number(number)
13412    });
13413  },
13414
13415  readIdent: function() {
13416    var start = this.index;
13417    while (this.index < this.text.length) {
13418      var ch = this.text.charAt(this.index);
13419      if (!(this.isIdent(ch) || this.isNumber(ch))) {
13420        break;
13421      }
13422      this.index++;
13423    }
13424    this.tokens.push({
13425      index: start,
13426      text: this.text.slice(start, this.index),
13427      identifier: true
13428    });
13429  },
13430
13431  readString: function(quote) {
13432    var start = this.index;
13433    this.index++;
13434    var string = '';
13435    var rawString = quote;
13436    var escape = false;
13437    while (this.index < this.text.length) {
13438      var ch = this.text.charAt(this.index);
13439      rawString += ch;
13440      if (escape) {
13441        if (ch === 'u') {
13442          var hex = this.text.substring(this.index + 1, this.index + 5);
13443          if (!hex.match(/[\da-f]{4}/i)) {
13444            this.throwError('Invalid unicode escape [\\u' + hex + ']');
13445          }
13446          this.index += 4;
13447          string += String.fromCharCode(parseInt(hex, 16));
13448        } else {
13449          var rep = ESCAPE[ch];
13450          string = string + (rep || ch);
13451        }
13452        escape = false;
13453      } else if (ch === '\\') {
13454        escape = true;
13455      } else if (ch === quote) {
13456        this.index++;
13457        this.tokens.push({
13458          index: start,
13459          text: rawString,
13460          constant: true,
13461          value: string
13462        });
13463        return;
13464      } else {
13465        string += ch;
13466      }
13467      this.index++;
13468    }
13469    this.throwError('Unterminated quote', start);
13470  }
13471};
13472
13473var AST = function(lexer, options) {
13474  this.lexer = lexer;
13475  this.options = options;
13476};
13477
13478AST.Program = 'Program';
13479AST.ExpressionStatement = 'ExpressionStatement';
13480AST.AssignmentExpression = 'AssignmentExpression';
13481AST.ConditionalExpression = 'ConditionalExpression';
13482AST.LogicalExpression = 'LogicalExpression';
13483AST.BinaryExpression = 'BinaryExpression';
13484AST.UnaryExpression = 'UnaryExpression';
13485AST.CallExpression = 'CallExpression';
13486AST.MemberExpression = 'MemberExpression';
13487AST.Identifier = 'Identifier';
13488AST.Literal = 'Literal';
13489AST.ArrayExpression = 'ArrayExpression';
13490AST.Property = 'Property';
13491AST.ObjectExpression = 'ObjectExpression';
13492AST.ThisExpression = 'ThisExpression';
13493AST.LocalsExpression = 'LocalsExpression';
13494
13495// Internal use only
13496AST.NGValueParameter = 'NGValueParameter';
13497
13498AST.prototype = {
13499  ast: function(text) {
13500    this.text = text;
13501    this.tokens = this.lexer.lex(text);
13502
13503    var value = this.program();
13504
13505    if (this.tokens.length !== 0) {
13506      this.throwError('is an unexpected token', this.tokens[0]);
13507    }
13508
13509    return value;
13510  },
13511
13512  program: function() {
13513    var body = [];
13514    while (true) {
13515      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
13516        body.push(this.expressionStatement());
13517      if (!this.expect(';')) {
13518        return { type: AST.Program, body: body};
13519      }
13520    }
13521  },
13522
13523  expressionStatement: function() {
13524    return { type: AST.ExpressionStatement, expression: this.filterChain() };
13525  },
13526
13527  filterChain: function() {
13528    var left = this.expression();
13529    var token;
13530    while ((token = this.expect('|'))) {
13531      left = this.filter(left);
13532    }
13533    return left;
13534  },
13535
13536  expression: function() {
13537    return this.assignment();
13538  },
13539
13540  assignment: function() {
13541    var result = this.ternary();
13542    if (this.expect('=')) {
13543      result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
13544    }
13545    return result;
13546  },
13547
13548  ternary: function() {
13549    var test = this.logicalOR();
13550    var alternate;
13551    var consequent;
13552    if (this.expect('?')) {
13553      alternate = this.expression();
13554      if (this.consume(':')) {
13555        consequent = this.expression();
13556        return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
13557      }
13558    }
13559    return test;
13560  },
13561
13562  logicalOR: function() {
13563    var left = this.logicalAND();
13564    while (this.expect('||')) {
13565      left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
13566    }
13567    return left;
13568  },
13569
13570  logicalAND: function() {
13571    var left = this.equality();
13572    while (this.expect('&&')) {
13573      left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
13574    }
13575    return left;
13576  },
13577
13578  equality: function() {
13579    var left = this.relational();
13580    var token;
13581    while ((token = this.expect('==','!=','===','!=='))) {
13582      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
13583    }
13584    return left;
13585  },
13586
13587  relational: function() {
13588    var left = this.additive();
13589    var token;
13590    while ((token = this.expect('<', '>', '<=', '>='))) {
13591      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
13592    }
13593    return left;
13594  },
13595
13596  additive: function() {
13597    var left = this.multiplicative();
13598    var token;
13599    while ((token = this.expect('+','-'))) {
13600      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
13601    }
13602    return left;
13603  },
13604
13605  multiplicative: function() {
13606    var left = this.unary();
13607    var token;
13608    while ((token = this.expect('*','/','%'))) {
13609      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
13610    }
13611    return left;
13612  },
13613
13614  unary: function() {
13615    var token;
13616    if ((token = this.expect('+', '-', '!'))) {
13617      return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
13618    } else {
13619      return this.primary();
13620    }
13621  },
13622
13623  primary: function() {
13624    var primary;
13625    if (this.expect('(')) {
13626      primary = this.filterChain();
13627      this.consume(')');
13628    } else if (this.expect('[')) {
13629      primary = this.arrayDeclaration();
13630    } else if (this.expect('{')) {
13631      primary = this.object();
13632    } else if (this.constants.hasOwnProperty(this.peek().text)) {
13633      primary = copy(this.constants[this.consume().text]);
13634    } else if (this.peek().identifier) {
13635      primary = this.identifier();
13636    } else if (this.peek().constant) {
13637      primary = this.constant();
13638    } else {
13639      this.throwError('not a primary expression', this.peek());
13640    }
13641
13642    var next;
13643    while ((next = this.expect('(', '[', '.'))) {
13644      if (next.text === '(') {
13645        primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13646        this.consume(')');
13647      } else if (next.text === '[') {
13648        primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13649        this.consume(']');
13650      } else if (next.text === '.') {
13651        primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13652      } else {
13653        this.throwError('IMPOSSIBLE');
13654      }
13655    }
13656    return primary;
13657  },
13658
13659  filter: function(baseExpression) {
13660    var args = [baseExpression];
13661    var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13662
13663    while (this.expect(':')) {
13664      args.push(this.expression());
13665    }
13666
13667    return result;
13668  },
13669
13670  parseArguments: function() {
13671    var args = [];
13672    if (this.peekToken().text !== ')') {
13673      do {
13674        args.push(this.expression());
13675      } while (this.expect(','));
13676    }
13677    return args;
13678  },
13679
13680  identifier: function() {
13681    var token = this.consume();
13682    if (!token.identifier) {
13683      this.throwError('is not a valid identifier', token);
13684    }
13685    return { type: AST.Identifier, name: token.text };
13686  },
13687
13688  constant: function() {
13689    // TODO check that it is a constant
13690    return { type: AST.Literal, value: this.consume().value };
13691  },
13692
13693  arrayDeclaration: function() {
13694    var elements = [];
13695    if (this.peekToken().text !== ']') {
13696      do {
13697        if (this.peek(']')) {
13698          // Support trailing commas per ES5.1.
13699          break;
13700        }
13701        elements.push(this.expression());
13702      } while (this.expect(','));
13703    }
13704    this.consume(']');
13705
13706    return { type: AST.ArrayExpression, elements: elements };
13707  },
13708
13709  object: function() {
13710    var properties = [], property;
13711    if (this.peekToken().text !== '}') {
13712      do {
13713        if (this.peek('}')) {
13714          // Support trailing commas per ES5.1.
13715          break;
13716        }
13717        property = {type: AST.Property, kind: 'init'};
13718        if (this.peek().constant) {
13719          property.key = this.constant();
13720        } else if (this.peek().identifier) {
13721          property.key = this.identifier();
13722        } else {
13723          this.throwError("invalid key", this.peek());
13724        }
13725        this.consume(':');
13726        property.value = this.expression();
13727        properties.push(property);
13728      } while (this.expect(','));
13729    }
13730    this.consume('}');
13731
13732    return {type: AST.ObjectExpression, properties: properties };
13733  },
13734
13735  throwError: function(msg, token) {
13736    throw $parseMinErr('syntax',
13737        'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13738          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13739  },
13740
13741  consume: function(e1) {
13742    if (this.tokens.length === 0) {
13743      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13744    }
13745
13746    var token = this.expect(e1);
13747    if (!token) {
13748      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13749    }
13750    return token;
13751  },
13752
13753  peekToken: function() {
13754    if (this.tokens.length === 0) {
13755      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13756    }
13757    return this.tokens[0];
13758  },
13759
13760  peek: function(e1, e2, e3, e4) {
13761    return this.peekAhead(0, e1, e2, e3, e4);
13762  },
13763
13764  peekAhead: function(i, e1, e2, e3, e4) {
13765    if (this.tokens.length > i) {
13766      var token = this.tokens[i];
13767      var t = token.text;
13768      if (t === e1 || t === e2 || t === e3 || t === e4 ||
13769          (!e1 && !e2 && !e3 && !e4)) {
13770        return token;
13771      }
13772    }
13773    return false;
13774  },
13775
13776  expect: function(e1, e2, e3, e4) {
13777    var token = this.peek(e1, e2, e3, e4);
13778    if (token) {
13779      this.tokens.shift();
13780      return token;
13781    }
13782    return false;
13783  },
13784
13785
13786  /* `undefined` is not a constant, it is an identifier,
13787   * but using it as an identifier is not supported
13788   */
13789  constants: {
13790    'true': { type: AST.Literal, value: true },
13791    'false': { type: AST.Literal, value: false },
13792    'null': { type: AST.Literal, value: null },
13793    'undefined': {type: AST.Literal, value: undefined },
13794    'this': {type: AST.ThisExpression },
13795    '$locals': {type: AST.LocalsExpression }
13796  }
13797};
13798
13799function ifDefined(v, d) {
13800  return typeof v !== 'undefined' ? v : d;
13801}
13802
13803function plusFn(l, r) {
13804  if (typeof l === 'undefined') return r;
13805  if (typeof r === 'undefined') return l;
13806  return l + r;
13807}
13808
13809function isStateless($filter, filterName) {
13810  var fn = $filter(filterName);
13811  return !fn.$stateful;
13812}
13813
13814function findConstantAndWatchExpressions(ast, $filter) {
13815  var allConstants;
13816  var argsToWatch;
13817  switch (ast.type) {
13818  case AST.Program:
13819    allConstants = true;
13820    forEach(ast.body, function(expr) {
13821      findConstantAndWatchExpressions(expr.expression, $filter);
13822      allConstants = allConstants && expr.expression.constant;
13823    });
13824    ast.constant = allConstants;
13825    break;
13826  case AST.Literal:
13827    ast.constant = true;
13828    ast.toWatch = [];
13829    break;
13830  case AST.UnaryExpression:
13831    findConstantAndWatchExpressions(ast.argument, $filter);
13832    ast.constant = ast.argument.constant;
13833    ast.toWatch = ast.argument.toWatch;
13834    break;
13835  case AST.BinaryExpression:
13836    findConstantAndWatchExpressions(ast.left, $filter);
13837    findConstantAndWatchExpressions(ast.right, $filter);
13838    ast.constant = ast.left.constant && ast.right.constant;
13839    ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13840    break;
13841  case AST.LogicalExpression:
13842    findConstantAndWatchExpressions(ast.left, $filter);
13843    findConstantAndWatchExpressions(ast.right, $filter);
13844    ast.constant = ast.left.constant && ast.right.constant;
13845    ast.toWatch = ast.constant ? [] : [ast];
13846    break;
13847  case AST.ConditionalExpression:
13848    findConstantAndWatchExpressions(ast.test, $filter);
13849    findConstantAndWatchExpressions(ast.alternate, $filter);
13850    findConstantAndWatchExpressions(ast.consequent, $filter);
13851    ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13852    ast.toWatch = ast.constant ? [] : [ast];
13853    break;
13854  case AST.Identifier:
13855    ast.constant = false;
13856    ast.toWatch = [ast];
13857    break;
13858  case AST.MemberExpression:
13859    findConstantAndWatchExpressions(ast.object, $filter);
13860    if (ast.computed) {
13861      findConstantAndWatchExpressions(ast.property, $filter);
13862    }
13863    ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13864    ast.toWatch = [ast];
13865    break;
13866  case AST.CallExpression:
13867    allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13868    argsToWatch = [];
13869    forEach(ast.arguments, function(expr) {
13870      findConstantAndWatchExpressions(expr, $filter);
13871      allConstants = allConstants && expr.constant;
13872      if (!expr.constant) {
13873        argsToWatch.push.apply(argsToWatch, expr.toWatch);
13874      }
13875    });
13876    ast.constant = allConstants;
13877    ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13878    break;
13879  case AST.AssignmentExpression:
13880    findConstantAndWatchExpressions(ast.left, $filter);
13881    findConstantAndWatchExpressions(ast.right, $filter);
13882    ast.constant = ast.left.constant && ast.right.constant;
13883    ast.toWatch = [ast];
13884    break;
13885  case AST.ArrayExpression:
13886    allConstants = true;
13887    argsToWatch = [];
13888    forEach(ast.elements, function(expr) {
13889      findConstantAndWatchExpressions(expr, $filter);
13890      allConstants = allConstants && expr.constant;
13891      if (!expr.constant) {
13892        argsToWatch.push.apply(argsToWatch, expr.toWatch);
13893      }
13894    });
13895    ast.constant = allConstants;
13896    ast.toWatch = argsToWatch;
13897    break;
13898  case AST.ObjectExpression:
13899    allConstants = true;
13900    argsToWatch = [];
13901    forEach(ast.properties, function(property) {
13902      findConstantAndWatchExpressions(property.value, $filter);
13903      allConstants = allConstants && property.value.constant;
13904      if (!property.value.constant) {
13905        argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13906      }
13907    });
13908    ast.constant = allConstants;
13909    ast.toWatch = argsToWatch;
13910    break;
13911  case AST.ThisExpression:
13912    ast.constant = false;
13913    ast.toWatch = [];
13914    break;
13915  case AST.LocalsExpression:
13916    ast.constant = false;
13917    ast.toWatch = [];
13918    break;
13919  }
13920}
13921
13922function getInputs(body) {
13923  if (body.length != 1) return;
13924  var lastExpression = body[0].expression;
13925  var candidate = lastExpression.toWatch;
13926  if (candidate.length !== 1) return candidate;
13927  return candidate[0] !== lastExpression ? candidate : undefined;
13928}
13929
13930function isAssignable(ast) {
13931  return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13932}
13933
13934function assignableAST(ast) {
13935  if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13936    return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13937  }
13938}
13939
13940function isLiteral(ast) {
13941  return ast.body.length === 0 ||
13942      ast.body.length === 1 && (
13943      ast.body[0].expression.type === AST.Literal ||
13944      ast.body[0].expression.type === AST.ArrayExpression ||
13945      ast.body[0].expression.type === AST.ObjectExpression);
13946}
13947
13948function isConstant(ast) {
13949  return ast.constant;
13950}
13951
13952function ASTCompiler(astBuilder, $filter) {
13953  this.astBuilder = astBuilder;
13954  this.$filter = $filter;
13955}
13956
13957ASTCompiler.prototype = {
13958  compile: function(expression, expensiveChecks) {
13959    var self = this;
13960    var ast = this.astBuilder.ast(expression);
13961    this.state = {
13962      nextId: 0,
13963      filters: {},
13964      expensiveChecks: expensiveChecks,
13965      fn: {vars: [], body: [], own: {}},
13966      assign: {vars: [], body: [], own: {}},
13967      inputs: []
13968    };
13969    findConstantAndWatchExpressions(ast, self.$filter);
13970    var extra = '';
13971    var assignable;
13972    this.stage = 'assign';
13973    if ((assignable = assignableAST(ast))) {
13974      this.state.computing = 'assign';
13975      var result = this.nextId();
13976      this.recurse(assignable, result);
13977      this.return_(result);
13978      extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13979    }
13980    var toWatch = getInputs(ast.body);
13981    self.stage = 'inputs';
13982    forEach(toWatch, function(watch, key) {
13983      var fnKey = 'fn' + key;
13984      self.state[fnKey] = {vars: [], body: [], own: {}};
13985      self.state.computing = fnKey;
13986      var intoId = self.nextId();
13987      self.recurse(watch, intoId);
13988      self.return_(intoId);
13989      self.state.inputs.push(fnKey);
13990      watch.watchId = key;
13991    });
13992    this.state.computing = 'fn';
13993    this.stage = 'main';
13994    this.recurse(ast);
13995    var fnString =
13996      // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13997      // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13998      '"' + this.USE + ' ' + this.STRICT + '";\n' +
13999      this.filterPrefix() +
14000      'var fn=' + this.generateFunction('fn', 's,l,a,i') +
14001      extra +
14002      this.watchFns() +
14003      'return fn;';
14004
14005    /* jshint -W054 */
14006    var fn = (new Function('$filter',
14007        'ensureSafeMemberName',
14008        'ensureSafeObject',
14009        'ensureSafeFunction',
14010        'getStringValue',
14011        'ensureSafeAssignContext',
14012        'ifDefined',
14013        'plus',
14014        'text',
14015        fnString))(
14016          this.$filter,
14017          ensureSafeMemberName,
14018          ensureSafeObject,
14019          ensureSafeFunction,
14020          getStringValue,
14021          ensureSafeAssignContext,
14022          ifDefined,
14023          plusFn,
14024          expression);
14025    /* jshint +W054 */
14026    this.state = this.stage = undefined;
14027    fn.literal = isLiteral(ast);
14028    fn.constant = isConstant(ast);
14029    return fn;
14030  },
14031
14032  USE: 'use',
14033
14034  STRICT: 'strict',
14035
14036  watchFns: function() {
14037    var result = [];
14038    var fns = this.state.inputs;
14039    var self = this;
14040    forEach(fns, function(name) {
14041      result.push('var ' + name + '=' + self.generateFunction(name, 's'));
14042    });
14043    if (fns.length) {
14044      result.push('fn.inputs=[' + fns.join(',') + '];');
14045    }
14046    return result.join('');
14047  },
14048
14049  generateFunction: function(name, params) {
14050    return 'function(' + params + '){' +
14051        this.varsPrefix(name) +
14052        this.body(name) +
14053        '};';
14054  },
14055
14056  filterPrefix: function() {
14057    var parts = [];
14058    var self = this;
14059    forEach(this.state.filters, function(id, filter) {
14060      parts.push(id + '=$filter(' + self.escape(filter) + ')');
14061    });
14062    if (parts.length) return 'var ' + parts.join(',') + ';';
14063    return '';
14064  },
14065
14066  varsPrefix: function(section) {
14067    return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
14068  },
14069
14070  body: function(section) {
14071    return this.state[section].body.join('');
14072  },
14073
14074  recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
14075    var left, right, self = this, args, expression;
14076    recursionFn = recursionFn || noop;
14077    if (!skipWatchIdCheck && isDefined(ast.watchId)) {
14078      intoId = intoId || this.nextId();
14079      this.if_('i',
14080        this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
14081        this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
14082      );
14083      return;
14084    }
14085    switch (ast.type) {
14086    case AST.Program:
14087      forEach(ast.body, function(expression, pos) {
14088        self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
14089        if (pos !== ast.body.length - 1) {
14090          self.current().body.push(right, ';');
14091        } else {
14092          self.return_(right);
14093        }
14094      });
14095      break;
14096    case AST.Literal:
14097      expression = this.escape(ast.value);
14098      this.assign(intoId, expression);
14099      recursionFn(expression);
14100      break;
14101    case AST.UnaryExpression:
14102      this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
14103      expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
14104      this.assign(intoId, expression);
14105      recursionFn(expression);
14106      break;
14107    case AST.BinaryExpression:
14108      this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
14109      this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
14110      if (ast.operator === '+') {
14111        expression = this.plus(left, right);
14112      } else if (ast.operator === '-') {
14113        expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
14114      } else {
14115        expression = '(' + left + ')' + ast.operator + '(' + right + ')';
14116      }
14117      this.assign(intoId, expression);
14118      recursionFn(expression);
14119      break;
14120    case AST.LogicalExpression:
14121      intoId = intoId || this.nextId();
14122      self.recurse(ast.left, intoId);
14123      self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
14124      recursionFn(intoId);
14125      break;
14126    case AST.ConditionalExpression:
14127      intoId = intoId || this.nextId();
14128      self.recurse(ast.test, intoId);
14129      self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
14130      recursionFn(intoId);
14131      break;
14132    case AST.Identifier:
14133      intoId = intoId || this.nextId();
14134      if (nameId) {
14135        nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
14136        nameId.computed = false;
14137        nameId.name = ast.name;
14138      }
14139      ensureSafeMemberName(ast.name);
14140      self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
14141        function() {
14142          self.if_(self.stage === 'inputs' || 's', function() {
14143            if (create && create !== 1) {
14144              self.if_(
14145                self.not(self.nonComputedMember('s', ast.name)),
14146                self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
14147            }
14148            self.assign(intoId, self.nonComputedMember('s', ast.name));
14149          });
14150        }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
14151        );
14152      if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
14153        self.addEnsureSafeObject(intoId);
14154      }
14155      recursionFn(intoId);
14156      break;
14157    case AST.MemberExpression:
14158      left = nameId && (nameId.context = this.nextId()) || this.nextId();
14159      intoId = intoId || this.nextId();
14160      self.recurse(ast.object, left, undefined, function() {
14161        self.if_(self.notNull(left), function() {
14162          if (create && create !== 1) {
14163            self.addEnsureSafeAssignContext(left);
14164          }
14165          if (ast.computed) {
14166            right = self.nextId();
14167            self.recurse(ast.property, right);
14168            self.getStringValue(right);
14169            self.addEnsureSafeMemberName(right);
14170            if (create && create !== 1) {
14171              self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
14172            }
14173            expression = self.ensureSafeObject(self.computedMember(left, right));
14174            self.assign(intoId, expression);
14175            if (nameId) {
14176              nameId.computed = true;
14177              nameId.name = right;
14178            }
14179          } else {
14180            ensureSafeMemberName(ast.property.name);
14181            if (create && create !== 1) {
14182              self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
14183            }
14184            expression = self.nonComputedMember(left, ast.property.name);
14185            if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
14186              expression = self.ensureSafeObject(expression);
14187            }
14188            self.assign(intoId, expression);
14189            if (nameId) {
14190              nameId.computed = false;
14191              nameId.name = ast.property.name;
14192            }
14193          }
14194        }, function() {
14195          self.assign(intoId, 'undefined');
14196        });
14197        recursionFn(intoId);
14198      }, !!create);
14199      break;
14200    case AST.CallExpression:
14201      intoId = intoId || this.nextId();
14202      if (ast.filter) {
14203        right = self.filter(ast.callee.name);
14204        args = [];
14205        forEach(ast.arguments, function(expr) {
14206          var argument = self.nextId();
14207          self.recurse(expr, argument);
14208          args.push(argument);
14209        });
14210        expression = right + '(' + args.join(',') + ')';
14211        self.assign(intoId, expression);
14212        recursionFn(intoId);
14213      } else {
14214        right = self.nextId();
14215        left = {};
14216        args = [];
14217        self.recurse(ast.callee, right, left, function() {
14218          self.if_(self.notNull(right), function() {
14219            self.addEnsureSafeFunction(right);
14220            forEach(ast.arguments, function(expr) {
14221              self.recurse(expr, self.nextId(), undefined, function(argument) {
14222                args.push(self.ensureSafeObject(argument));
14223              });
14224            });
14225            if (left.name) {
14226              if (!self.state.expensiveChecks) {
14227                self.addEnsureSafeObject(left.context);
14228              }
14229              expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
14230            } else {
14231              expression = right + '(' + args.join(',') + ')';
14232            }
14233            expression = self.ensureSafeObject(expression);
14234            self.assign(intoId, expression);
14235          }, function() {
14236            self.assign(intoId, 'undefined');
14237          });
14238          recursionFn(intoId);
14239        });
14240      }
14241      break;
14242    case AST.AssignmentExpression:
14243      right = this.nextId();
14244      left = {};
14245      if (!isAssignable(ast.left)) {
14246        throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
14247      }
14248      this.recurse(ast.left, undefined, left, function() {
14249        self.if_(self.notNull(left.context), function() {
14250          self.recurse(ast.right, right);
14251          self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
14252          self.addEnsureSafeAssignContext(left.context);
14253          expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
14254          self.assign(intoId, expression);
14255          recursionFn(intoId || expression);
14256        });
14257      }, 1);
14258      break;
14259    case AST.ArrayExpression:
14260      args = [];
14261      forEach(ast.elements, function(expr) {
14262        self.recurse(expr, self.nextId(), undefined, function(argument) {
14263          args.push(argument);
14264        });
14265      });
14266      expression = '[' + args.join(',') + ']';
14267      this.assign(intoId, expression);
14268      recursionFn(expression);
14269      break;
14270    case AST.ObjectExpression:
14271      args = [];
14272      forEach(ast.properties, function(property) {
14273        self.recurse(property.value, self.nextId(), undefined, function(expr) {
14274          args.push(self.escape(
14275              property.key.type === AST.Identifier ? property.key.name :
14276                ('' + property.key.value)) +
14277              ':' + expr);
14278        });
14279      });
14280      expression = '{' + args.join(',') + '}';
14281      this.assign(intoId, expression);
14282      recursionFn(expression);
14283      break;
14284    case AST.ThisExpression:
14285      this.assign(intoId, 's');
14286      recursionFn('s');
14287      break;
14288    case AST.LocalsExpression:
14289      this.assign(intoId, 'l');
14290      recursionFn('l');
14291      break;
14292    case AST.NGValueParameter:
14293      this.assign(intoId, 'v');
14294      recursionFn('v');
14295      break;
14296    }
14297  },
14298
14299  getHasOwnProperty: function(element, property) {
14300    var key = element + '.' + property;
14301    var own = this.current().own;
14302    if (!own.hasOwnProperty(key)) {
14303      own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
14304    }
14305    return own[key];
14306  },
14307
14308  assign: function(id, value) {
14309    if (!id) return;
14310    this.current().body.push(id, '=', value, ';');
14311    return id;
14312  },
14313
14314  filter: function(filterName) {
14315    if (!this.state.filters.hasOwnProperty(filterName)) {
14316      this.state.filters[filterName] = this.nextId(true);
14317    }
14318    return this.state.filters[filterName];
14319  },
14320
14321  ifDefined: function(id, defaultValue) {
14322    return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
14323  },
14324
14325  plus: function(left, right) {
14326    return 'plus(' + left + ',' + right + ')';
14327  },
14328
14329  return_: function(id) {
14330    this.current().body.push('return ', id, ';');
14331  },
14332
14333  if_: function(test, alternate, consequent) {
14334    if (test === true) {
14335      alternate();
14336    } else {
14337      var body = this.current().body;
14338      body.push('if(', test, '){');
14339      alternate();
14340      body.push('}');
14341      if (consequent) {
14342        body.push('else{');
14343        consequent();
14344        body.push('}');
14345      }
14346    }
14347  },
14348
14349  not: function(expression) {
14350    return '!(' + expression + ')';
14351  },
14352
14353  notNull: function(expression) {
14354    return expression + '!=null';
14355  },
14356
14357  nonComputedMember: function(left, right) {
14358    return left + '.' + right;
14359  },
14360
14361  computedMember: function(left, right) {
14362    return left + '[' + right + ']';
14363  },
14364
14365  member: function(left, right, computed) {
14366    if (computed) return this.computedMember(left, right);
14367    return this.nonComputedMember(left, right);
14368  },
14369
14370  addEnsureSafeObject: function(item) {
14371    this.current().body.push(this.ensureSafeObject(item), ';');
14372  },
14373
14374  addEnsureSafeMemberName: function(item) {
14375    this.current().body.push(this.ensureSafeMemberName(item), ';');
14376  },
14377
14378  addEnsureSafeFunction: function(item) {
14379    this.current().body.push(this.ensureSafeFunction(item), ';');
14380  },
14381
14382  addEnsureSafeAssignContext: function(item) {
14383    this.current().body.push(this.ensureSafeAssignContext(item), ';');
14384  },
14385
14386  ensureSafeObject: function(item) {
14387    return 'ensureSafeObject(' + item + ',text)';
14388  },
14389
14390  ensureSafeMemberName: function(item) {
14391    return 'ensureSafeMemberName(' + item + ',text)';
14392  },
14393
14394  ensureSafeFunction: function(item) {
14395    return 'ensureSafeFunction(' + item + ',text)';
14396  },
14397
14398  getStringValue: function(item) {
14399    this.assign(item, 'getStringValue(' + item + ')');
14400  },
14401
14402  ensureSafeAssignContext: function(item) {
14403    return 'ensureSafeAssignContext(' + item + ',text)';
14404  },
14405
14406  lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
14407    var self = this;
14408    return function() {
14409      self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
14410    };
14411  },
14412
14413  lazyAssign: function(id, value) {
14414    var self = this;
14415    return function() {
14416      self.assign(id, value);
14417    };
14418  },
14419
14420  stringEscapeRegex: /[^ a-zA-Z0-9]/g,
14421
14422  stringEscapeFn: function(c) {
14423    return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
14424  },
14425
14426  escape: function(value) {
14427    if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
14428    if (isNumber(value)) return value.toString();
14429    if (value === true) return 'true';
14430    if (value === false) return 'false';
14431    if (value === null) return 'null';
14432    if (typeof value === 'undefined') return 'undefined';
14433
14434    throw $parseMinErr('esc', 'IMPOSSIBLE');
14435  },
14436
14437  nextId: function(skip, init) {
14438    var id = 'v' + (this.state.nextId++);
14439    if (!skip) {
14440      this.current().vars.push(id + (init ? '=' + init : ''));
14441    }
14442    return id;
14443  },
14444
14445  current: function() {
14446    return this.state[this.state.computing];
14447  }
14448};
14449
14450
14451function ASTInterpreter(astBuilder, $filter) {
14452  this.astBuilder = astBuilder;
14453  this.$filter = $filter;
14454}
14455
14456ASTInterpreter.prototype = {
14457  compile: function(expression, expensiveChecks) {
14458    var self = this;
14459    var ast = this.astBuilder.ast(expression);
14460    this.expression = expression;
14461    this.expensiveChecks = expensiveChecks;
14462    findConstantAndWatchExpressions(ast, self.$filter);
14463    var assignable;
14464    var assign;
14465    if ((assignable = assignableAST(ast))) {
14466      assign = this.recurse(assignable);
14467    }
14468    var toWatch = getInputs(ast.body);
14469    var inputs;
14470    if (toWatch) {
14471      inputs = [];
14472      forEach(toWatch, function(watch, key) {
14473        var input = self.recurse(watch);
14474        watch.input = input;
14475        inputs.push(input);
14476        watch.watchId = key;
14477      });
14478    }
14479    var expressions = [];
14480    forEach(ast.body, function(expression) {
14481      expressions.push(self.recurse(expression.expression));
14482    });
14483    var fn = ast.body.length === 0 ? function() {} :
14484             ast.body.length === 1 ? expressions[0] :
14485             function(scope, locals) {
14486               var lastValue;
14487               forEach(expressions, function(exp) {
14488                 lastValue = exp(scope, locals);
14489               });
14490               return lastValue;
14491             };
14492    if (assign) {
14493      fn.assign = function(scope, value, locals) {
14494        return assign(scope, locals, value);
14495      };
14496    }
14497    if (inputs) {
14498      fn.inputs = inputs;
14499    }
14500    fn.literal = isLiteral(ast);
14501    fn.constant = isConstant(ast);
14502    return fn;
14503  },
14504
14505  recurse: function(ast, context, create) {
14506    var left, right, self = this, args, expression;
14507    if (ast.input) {
14508      return this.inputs(ast.input, ast.watchId);
14509    }
14510    switch (ast.type) {
14511    case AST.Literal:
14512      return this.value(ast.value, context);
14513    case AST.UnaryExpression:
14514      right = this.recurse(ast.argument);
14515      return this['unary' + ast.operator](right, context);
14516    case AST.BinaryExpression:
14517      left = this.recurse(ast.left);
14518      right = this.recurse(ast.right);
14519      return this['binary' + ast.operator](left, right, context);
14520    case AST.LogicalExpression:
14521      left = this.recurse(ast.left);
14522      right = this.recurse(ast.right);
14523      return this['binary' + ast.operator](left, right, context);
14524    case AST.ConditionalExpression:
14525      return this['ternary?:'](
14526        this.recurse(ast.test),
14527        this.recurse(ast.alternate),
14528        this.recurse(ast.consequent),
14529        context
14530      );
14531    case AST.Identifier:
14532      ensureSafeMemberName(ast.name, self.expression);
14533      return self.identifier(ast.name,
14534                             self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
14535                             context, create, self.expression);
14536    case AST.MemberExpression:
14537      left = this.recurse(ast.object, false, !!create);
14538      if (!ast.computed) {
14539        ensureSafeMemberName(ast.property.name, self.expression);
14540        right = ast.property.name;
14541      }
14542      if (ast.computed) right = this.recurse(ast.property);
14543      return ast.computed ?
14544        this.computedMember(left, right, context, create, self.expression) :
14545        this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
14546    case AST.CallExpression:
14547      args = [];
14548      forEach(ast.arguments, function(expr) {
14549        args.push(self.recurse(expr));
14550      });
14551      if (ast.filter) right = this.$filter(ast.callee.name);
14552      if (!ast.filter) right = this.recurse(ast.callee, true);
14553      return ast.filter ?
14554        function(scope, locals, assign, inputs) {
14555          var values = [];
14556          for (var i = 0; i < args.length; ++i) {
14557            values.push(args[i](scope, locals, assign, inputs));
14558          }
14559          var value = right.apply(undefined, values, inputs);
14560          return context ? {context: undefined, name: undefined, value: value} : value;
14561        } :
14562        function(scope, locals, assign, inputs) {
14563          var rhs = right(scope, locals, assign, inputs);
14564          var value;
14565          if (rhs.value != null) {
14566            ensureSafeObject(rhs.context, self.expression);
14567            ensureSafeFunction(rhs.value, self.expression);
14568            var values = [];
14569            for (var i = 0; i < args.length; ++i) {
14570              values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
14571            }
14572            value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
14573          }
14574          return context ? {value: value} : value;
14575        };
14576    case AST.AssignmentExpression:
14577      left = this.recurse(ast.left, true, 1);
14578      right = this.recurse(ast.right);
14579      return function(scope, locals, assign, inputs) {
14580        var lhs = left(scope, locals, assign, inputs);
14581        var rhs = right(scope, locals, assign, inputs);
14582        ensureSafeObject(lhs.value, self.expression);
14583        ensureSafeAssignContext(lhs.context);
14584        lhs.context[lhs.name] = rhs;
14585        return context ? {value: rhs} : rhs;
14586      };
14587    case AST.ArrayExpression:
14588      args = [];
14589      forEach(ast.elements, function(expr) {
14590        args.push(self.recurse(expr));
14591      });
14592      return function(scope, locals, assign, inputs) {
14593        var value = [];
14594        for (var i = 0; i < args.length; ++i) {
14595          value.push(args[i](scope, locals, assign, inputs));
14596        }
14597        return context ? {value: value} : value;
14598      };
14599    case AST.ObjectExpression:
14600      args = [];
14601      forEach(ast.properties, function(property) {
14602        args.push({key: property.key.type === AST.Identifier ?
14603                        property.key.name :
14604                        ('' + property.key.value),
14605                   value: self.recurse(property.value)
14606        });
14607      });
14608      return function(scope, locals, assign, inputs) {
14609        var value = {};
14610        for (var i = 0; i < args.length; ++i) {
14611          value[args[i].key] = args[i].value(scope, locals, assign, inputs);
14612        }
14613        return context ? {value: value} : value;
14614      };
14615    case AST.ThisExpression:
14616      return function(scope) {
14617        return context ? {value: scope} : scope;
14618      };
14619    case AST.LocalsExpression:
14620      return function(scope, locals) {
14621        return context ? {value: locals} : locals;
14622      };
14623    case AST.NGValueParameter:
14624      return function(scope, locals, assign, inputs) {
14625        return context ? {value: assign} : assign;
14626      };
14627    }
14628  },
14629
14630  'unary+': function(argument, context) {
14631    return function(scope, locals, assign, inputs) {
14632      var arg = argument(scope, locals, assign, inputs);
14633      if (isDefined(arg)) {
14634        arg = +arg;
14635      } else {
14636        arg = 0;
14637      }
14638      return context ? {value: arg} : arg;
14639    };
14640  },
14641  'unary-': function(argument, context) {
14642    return function(scope, locals, assign, inputs) {
14643      var arg = argument(scope, locals, assign, inputs);
14644      if (isDefined(arg)) {
14645        arg = -arg;
14646      } else {
14647        arg = 0;
14648      }
14649      return context ? {value: arg} : arg;
14650    };
14651  },
14652  'unary!': function(argument, context) {
14653    return function(scope, locals, assign, inputs) {
14654      var arg = !argument(scope, locals, assign, inputs);
14655      return context ? {value: arg} : arg;
14656    };
14657  },
14658  'binary+': function(left, right, context) {
14659    return function(scope, locals, assign, inputs) {
14660      var lhs = left(scope, locals, assign, inputs);
14661      var rhs = right(scope, locals, assign, inputs);
14662      var arg = plusFn(lhs, rhs);
14663      return context ? {value: arg} : arg;
14664    };
14665  },
14666  'binary-': function(left, right, context) {
14667    return function(scope, locals, assign, inputs) {
14668      var lhs = left(scope, locals, assign, inputs);
14669      var rhs = right(scope, locals, assign, inputs);
14670      var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14671      return context ? {value: arg} : arg;
14672    };
14673  },
14674  'binary*': function(left, right, context) {
14675    return function(scope, locals, assign, inputs) {
14676      var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14677      return context ? {value: arg} : arg;
14678    };
14679  },
14680  'binary/': function(left, right, context) {
14681    return function(scope, locals, assign, inputs) {
14682      var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14683      return context ? {value: arg} : arg;
14684    };
14685  },
14686  'binary%': function(left, right, context) {
14687    return function(scope, locals, assign, inputs) {
14688      var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14689      return context ? {value: arg} : arg;
14690    };
14691  },
14692  'binary===': function(left, right, context) {
14693    return function(scope, locals, assign, inputs) {
14694      var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14695      return context ? {value: arg} : arg;
14696    };
14697  },
14698  'binary!==': function(left, right, context) {
14699    return function(scope, locals, assign, inputs) {
14700      var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14701      return context ? {value: arg} : arg;
14702    };
14703  },
14704  'binary==': function(left, right, context) {
14705    return function(scope, locals, assign, inputs) {
14706      var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14707      return context ? {value: arg} : arg;
14708    };
14709  },
14710  'binary!=': function(left, right, context) {
14711    return function(scope, locals, assign, inputs) {
14712      var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14713      return context ? {value: arg} : arg;
14714    };
14715  },
14716  'binary<': function(left, right, context) {
14717    return function(scope, locals, assign, inputs) {
14718      var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14719      return context ? {value: arg} : arg;
14720    };
14721  },
14722  'binary>': function(left, right, context) {
14723    return function(scope, locals, assign, inputs) {
14724      var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14725      return context ? {value: arg} : arg;
14726    };
14727  },
14728  'binary<=': function(left, right, context) {
14729    return function(scope, locals, assign, inputs) {
14730      var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14731      return context ? {value: arg} : arg;
14732    };
14733  },
14734  'binary>=': function(left, right, context) {
14735    return function(scope, locals, assign, inputs) {
14736      var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14737      return context ? {value: arg} : arg;
14738    };
14739  },
14740  'binary&&': function(left, right, context) {
14741    return function(scope, locals, assign, inputs) {
14742      var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14743      return context ? {value: arg} : arg;
14744    };
14745  },
14746  'binary||': function(left, right, context) {
14747    return function(scope, locals, assign, inputs) {
14748      var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14749      return context ? {value: arg} : arg;
14750    };
14751  },
14752  'ternary?:': function(test, alternate, consequent, context) {
14753    return function(scope, locals, assign, inputs) {
14754      var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14755      return context ? {value: arg} : arg;
14756    };
14757  },
14758  value: function(value, context) {
14759    return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14760  },
14761  identifier: function(name, expensiveChecks, context, create, expression) {
14762    return function(scope, locals, assign, inputs) {
14763      var base = locals && (name in locals) ? locals : scope;
14764      if (create && create !== 1 && base && !(base[name])) {
14765        base[name] = {};
14766      }
14767      var value = base ? base[name] : undefined;
14768      if (expensiveChecks) {
14769        ensureSafeObject(value, expression);
14770      }
14771      if (context) {
14772        return {context: base, name: name, value: value};
14773      } else {
14774        return value;
14775      }
14776    };
14777  },
14778  computedMember: function(left, right, context, create, expression) {
14779    return function(scope, locals, assign, inputs) {
14780      var lhs = left(scope, locals, assign, inputs);
14781      var rhs;
14782      var value;
14783      if (lhs != null) {
14784        rhs = right(scope, locals, assign, inputs);
14785        rhs = getStringValue(rhs);
14786        ensureSafeMemberName(rhs, expression);
14787        if (create && create !== 1) {
14788          ensureSafeAssignContext(lhs);
14789          if (lhs && !(lhs[rhs])) {
14790            lhs[rhs] = {};
14791          }
14792        }
14793        value = lhs[rhs];
14794        ensureSafeObject(value, expression);
14795      }
14796      if (context) {
14797        return {context: lhs, name: rhs, value: value};
14798      } else {
14799        return value;
14800      }
14801    };
14802  },
14803  nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14804    return function(scope, locals, assign, inputs) {
14805      var lhs = left(scope, locals, assign, inputs);
14806      if (create && create !== 1) {
14807        ensureSafeAssignContext(lhs);
14808        if (lhs && !(lhs[right])) {
14809          lhs[right] = {};
14810        }
14811      }
14812      var value = lhs != null ? lhs[right] : undefined;
14813      if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14814        ensureSafeObject(value, expression);
14815      }
14816      if (context) {
14817        return {context: lhs, name: right, value: value};
14818      } else {
14819        return value;
14820      }
14821    };
14822  },
14823  inputs: function(input, watchId) {
14824    return function(scope, value, locals, inputs) {
14825      if (inputs) return inputs[watchId];
14826      return input(scope, value, locals);
14827    };
14828  }
14829};
14830
14831/**
14832 * @constructor
14833 */
14834var Parser = function(lexer, $filter, options) {
14835  this.lexer = lexer;
14836  this.$filter = $filter;
14837  this.options = options;
14838  this.ast = new AST(this.lexer);
14839  this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14840                                   new ASTCompiler(this.ast, $filter);
14841};
14842
14843Parser.prototype = {
14844  constructor: Parser,
14845
14846  parse: function(text) {
14847    return this.astCompiler.compile(text, this.options.expensiveChecks);
14848  }
14849};
14850
14851function isPossiblyDangerousMemberName(name) {
14852  return name == 'constructor';
14853}
14854
14855var objectValueOf = Object.prototype.valueOf;
14856
14857function getValueOf(value) {
14858  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14859}
14860
14861///////////////////////////////////
14862
14863/**
14864 * @ngdoc service
14865 * @name $parse
14866 * @kind function
14867 *
14868 * @description
14869 *
14870 * Converts Angular {@link guide/expression expression} into a function.
14871 *
14872 * ```js
14873 *   var getter = $parse('user.name');
14874 *   var setter = getter.assign;
14875 *   var context = {user:{name:'angular'}};
14876 *   var locals = {user:{name:'local'}};
14877 *
14878 *   expect(getter(context)).toEqual('angular');
14879 *   setter(context, 'newValue');
14880 *   expect(context.user.name).toEqual('newValue');
14881 *   expect(getter(context, locals)).toEqual('local');
14882 * ```
14883 *
14884 *
14885 * @param {string} expression String expression to compile.
14886 * @returns {function(context, locals)} a function which represents the compiled expression:
14887 *
14888 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
14889 *      are evaluated against (typically a scope object).
14890 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
14891 *      `context`.
14892 *
14893 *    The returned function also has the following properties:
14894 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14895 *        literal.
14896 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14897 *        constant literals.
14898 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14899 *        set to a function to change its value on the given context.
14900 *
14901 */
14902
14903
14904/**
14905 * @ngdoc provider
14906 * @name $parseProvider
14907 *
14908 * @description
14909 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14910 *  service.
14911 */
14912function $ParseProvider() {
14913  var cacheDefault = createMap();
14914  var cacheExpensive = createMap();
14915
14916  this.$get = ['$filter', function($filter) {
14917    var noUnsafeEval = csp().noUnsafeEval;
14918    var $parseOptions = {
14919          csp: noUnsafeEval,
14920          expensiveChecks: false
14921        },
14922        $parseOptionsExpensive = {
14923          csp: noUnsafeEval,
14924          expensiveChecks: true
14925        };
14926
14927    return function $parse(exp, interceptorFn, expensiveChecks) {
14928      var parsedExpression, oneTime, cacheKey;
14929
14930      switch (typeof exp) {
14931        case 'string':
14932          exp = exp.trim();
14933          cacheKey = exp;
14934
14935          var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14936          parsedExpression = cache[cacheKey];
14937
14938          if (!parsedExpression) {
14939            if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14940              oneTime = true;
14941              exp = exp.substring(2);
14942            }
14943            var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14944            var lexer = new Lexer(parseOptions);
14945            var parser = new Parser(lexer, $filter, parseOptions);
14946            parsedExpression = parser.parse(exp);
14947            if (parsedExpression.constant) {
14948              parsedExpression.$$watchDelegate = constantWatchDelegate;
14949            } else if (oneTime) {
14950              parsedExpression.$$watchDelegate = parsedExpression.literal ?
14951                  oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14952            } else if (parsedExpression.inputs) {
14953              parsedExpression.$$watchDelegate = inputsWatchDelegate;
14954            }
14955            cache[cacheKey] = parsedExpression;
14956          }
14957          return addInterceptor(parsedExpression, interceptorFn);
14958
14959        case 'function':
14960          return addInterceptor(exp, interceptorFn);
14961
14962        default:
14963          return addInterceptor(noop, interceptorFn);
14964      }
14965    };
14966
14967    function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14968
14969      if (newValue == null || oldValueOfValue == null) { // null/undefined
14970        return newValue === oldValueOfValue;
14971      }
14972
14973      if (typeof newValue === 'object') {
14974
14975        // attempt to convert the value to a primitive type
14976        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14977        //             be cheaply dirty-checked
14978        newValue = getValueOf(newValue);
14979
14980        if (typeof newValue === 'object') {
14981          // objects/arrays are not supported - deep-watching them would be too expensive
14982          return false;
14983        }
14984
14985        // fall-through to the primitive equality check
14986      }
14987
14988      //Primitive or NaN
14989      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14990    }
14991
14992    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14993      var inputExpressions = parsedExpression.inputs;
14994      var lastResult;
14995
14996      if (inputExpressions.length === 1) {
14997        var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14998        inputExpressions = inputExpressions[0];
14999        return scope.$watch(function expressionInputWatch(scope) {
15000          var newInputValue = inputExpressions(scope);
15001          if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
15002            lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
15003            oldInputValueOf = newInputValue && getValueOf(newInputValue);
15004          }
15005          return lastResult;
15006        }, listener, objectEquality, prettyPrintExpression);
15007      }
15008
15009      var oldInputValueOfValues = [];
15010      var oldInputValues = [];
15011      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
15012        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
15013        oldInputValues[i] = null;
15014      }
15015
15016      return scope.$watch(function expressionInputsWatch(scope) {
15017        var changed = false;
15018
15019        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
15020          var newInputValue = inputExpressions[i](scope);
15021          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
15022            oldInputValues[i] = newInputValue;
15023            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
15024          }
15025        }
15026
15027        if (changed) {
15028          lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
15029        }
15030
15031        return lastResult;
15032      }, listener, objectEquality, prettyPrintExpression);
15033    }
15034
15035    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
15036      var unwatch, lastValue;
15037      return unwatch = scope.$watch(function oneTimeWatch(scope) {
15038        return parsedExpression(scope);
15039      }, function oneTimeListener(value, old, scope) {
15040        lastValue = value;
15041        if (isFunction(listener)) {
15042          listener.apply(this, arguments);
15043        }
15044        if (isDefined(value)) {
15045          scope.$$postDigest(function() {
15046            if (isDefined(lastValue)) {
15047              unwatch();
15048            }
15049          });
15050        }
15051      }, objectEquality);
15052    }
15053
15054    function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
15055      var unwatch, lastValue;
15056      return unwatch = scope.$watch(function oneTimeWatch(scope) {
15057        return parsedExpression(scope);
15058      }, function oneTimeListener(value, old, scope) {
15059        lastValue = value;
15060        if (isFunction(listener)) {
15061          listener.call(this, value, old, scope);
15062        }
15063        if (isAllDefined(value)) {
15064          scope.$$postDigest(function() {
15065            if (isAllDefined(lastValue)) unwatch();
15066          });
15067        }
15068      }, objectEquality);
15069
15070      function isAllDefined(value) {
15071        var allDefined = true;
15072        forEach(value, function(val) {
15073          if (!isDefined(val)) allDefined = false;
15074        });
15075        return allDefined;
15076      }
15077    }
15078
15079    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
15080      var unwatch;
15081      return unwatch = scope.$watch(function constantWatch(scope) {
15082        unwatch();
15083        return parsedExpression(scope);
15084      }, listener, objectEquality);
15085    }
15086
15087    function addInterceptor(parsedExpression, interceptorFn) {
15088      if (!interceptorFn) return parsedExpression;
15089      var watchDelegate = parsedExpression.$$watchDelegate;
15090      var useInputs = false;
15091
15092      var regularWatch =
15093          watchDelegate !== oneTimeLiteralWatchDelegate &&
15094          watchDelegate !== oneTimeWatchDelegate;
15095
15096      var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
15097        var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
15098        return interceptorFn(value, scope, locals);
15099      } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
15100        var value = parsedExpression(scope, locals, assign, inputs);
15101        var result = interceptorFn(value, scope, locals);
15102        // we only return the interceptor's result if the
15103        // initial value is defined (for bind-once)
15104        return isDefined(value) ? result : value;
15105      };
15106
15107      // Propagate $$watchDelegates other then inputsWatchDelegate
15108      if (parsedExpression.$$watchDelegate &&
15109          parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
15110        fn.$$watchDelegate = parsedExpression.$$watchDelegate;
15111      } else if (!interceptorFn.$stateful) {
15112        // If there is an interceptor, but no watchDelegate then treat the interceptor like
15113        // we treat filters - it is assumed to be a pure function unless flagged with $stateful
15114        fn.$$watchDelegate = inputsWatchDelegate;
15115        useInputs = !parsedExpression.inputs;
15116        fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
15117      }
15118
15119      return fn;
15120    }
15121  }];
15122}
15123
15124/**
15125 * @ngdoc service
15126 * @name $q
15127 * @requires $rootScope
15128 *
15129 * @description
15130 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
15131 * when they are done processing.
15132 *
15133 * This is an implementation of promises/deferred objects inspired by
15134 * [Kris Kowal's Q](https://github.com/kriskowal/q).
15135 *
15136 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
15137 * implementations, and the other which resembles ES6 promises to some degree.
15138 *
15139 * # $q constructor
15140 *
15141 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
15142 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
15143 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
15144 *
15145 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
15146 * available yet.
15147 *
15148 * It can be used like so:
15149 *
15150 * ```js
15151 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
15152 *   // are available in the current lexical scope (they could have been injected or passed in).
15153 *
15154 *   function asyncGreet(name) {
15155 *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
15156 *     return $q(function(resolve, reject) {
15157 *       setTimeout(function() {
15158 *         if (okToGreet(name)) {
15159 *           resolve('Hello, ' + name + '!');
15160 *         } else {
15161 *           reject('Greeting ' + name + ' is not allowed.');
15162 *         }
15163 *       }, 1000);
15164 *     });
15165 *   }
15166 *
15167 *   var promise = asyncGreet('Robin Hood');
15168 *   promise.then(function(greeting) {
15169 *     alert('Success: ' + greeting);
15170 *   }, function(reason) {
15171 *     alert('Failed: ' + reason);
15172 *   });
15173 * ```
15174 *
15175 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
15176 *
15177 * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
15178 *
15179 * However, the more traditional CommonJS-style usage is still available, and documented below.
15180 *
15181 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
15182 * interface for interacting with an object that represents the result of an action that is
15183 * performed asynchronously, and may or may not be finished at any given point in time.
15184 *
15185 * From the perspective of dealing with error handling, deferred and promise APIs are to
15186 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
15187 *
15188 * ```js
15189 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
15190 *   // are available in the current lexical scope (they could have been injected or passed in).
15191 *
15192 *   function asyncGreet(name) {
15193 *     var deferred = $q.defer();
15194 *
15195 *     setTimeout(function() {
15196 *       deferred.notify('About to greet ' + name + '.');
15197 *
15198 *       if (okToGreet(name)) {
15199 *         deferred.resolve('Hello, ' + name + '!');
15200 *       } else {
15201 *         deferred.reject('Greeting ' + name + ' is not allowed.');
15202 *       }
15203 *     }, 1000);
15204 *
15205 *     return deferred.promise;
15206 *   }
15207 *
15208 *   var promise = asyncGreet('Robin Hood');
15209 *   promise.then(function(greeting) {
15210 *     alert('Success: ' + greeting);
15211 *   }, function(reason) {
15212 *     alert('Failed: ' + reason);
15213 *   }, function(update) {
15214 *     alert('Got notification: ' + update);
15215 *   });
15216 * ```
15217 *
15218 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
15219 * comes in the way of guarantees that promise and deferred APIs make, see
15220 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
15221 *
15222 * Additionally the promise api allows for composition that is very hard to do with the
15223 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
15224 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
15225 * section on serial or parallel joining of promises.
15226 *
15227 * # The Deferred API
15228 *
15229 * A new instance of deferred is constructed by calling `$q.defer()`.
15230 *
15231 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
15232 * that can be used for signaling the successful or unsuccessful completion, as well as the status
15233 * of the task.
15234 *
15235 * **Methods**
15236 *
15237 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
15238 *   constructed via `$q.reject`, the promise will be rejected instead.
15239 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
15240 *   resolving it with a rejection constructed via `$q.reject`.
15241 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
15242 *   multiple times before the promise is either resolved or rejected.
15243 *
15244 * **Properties**
15245 *
15246 * - promise – `{Promise}` – promise object associated with this deferred.
15247 *
15248 *
15249 * # The Promise API
15250 *
15251 * A new promise instance is created when a deferred instance is created and can be retrieved by
15252 * calling `deferred.promise`.
15253 *
15254 * The purpose of the promise object is to allow for interested parties to get access to the result
15255 * of the deferred task when it completes.
15256 *
15257 * **Methods**
15258 *
15259 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
15260 *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
15261 *   as soon as the result is available. The callbacks are called with a single argument: the result
15262 *   or rejection reason. Additionally, the notify callback may be called zero or more times to
15263 *   provide a progress indication, before the promise is resolved or rejected.
15264 *
15265 *   This method *returns a new promise* which is resolved or rejected via the return value of the
15266 *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
15267 *   with the value which is resolved in that promise using
15268 *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
15269 *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
15270 *   resolved or rejected from the notifyCallback method.
15271 *
15272 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
15273 *
15274 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
15275 *   but to do so without modifying the final value. This is useful to release resources or do some
15276 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
15277 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
15278 *   more information.
15279 *
15280 * # Chaining promises
15281 *
15282 * Because calling the `then` method of a promise returns a new derived promise, it is easily
15283 * possible to create a chain of promises:
15284 *
15285 * ```js
15286 *   promiseB = promiseA.then(function(result) {
15287 *     return result + 1;
15288 *   });
15289 *
15290 *   // promiseB will be resolved immediately after promiseA is resolved and its value
15291 *   // will be the result of promiseA incremented by 1
15292 * ```
15293 *
15294 * It is possible to create chains of any length and since a promise can be resolved with another
15295 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
15296 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
15297 * $http's response interceptors.
15298 *
15299 *
15300 * # Differences between Kris Kowal's Q and $q
15301 *
15302 *  There are two main differences:
15303 *
15304 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
15305 *   mechanism in angular, which means faster propagation of resolution or rejection into your
15306 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
15307 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
15308 *   all the important functionality needed for common async tasks.
15309 *
15310 *  # Testing
15311 *
15312 *  ```js
15313 *    it('should simulate promise', inject(function($q, $rootScope) {
15314 *      var deferred = $q.defer();
15315 *      var promise = deferred.promise;
15316 *      var resolvedValue;
15317 *
15318 *      promise.then(function(value) { resolvedValue = value; });
15319 *      expect(resolvedValue).toBeUndefined();
15320 *
15321 *      // Simulate resolving of promise
15322 *      deferred.resolve(123);
15323 *      // Note that the 'then' function does not get called synchronously.
15324 *      // This is because we want the promise API to always be async, whether or not
15325 *      // it got called synchronously or asynchronously.
15326 *      expect(resolvedValue).toBeUndefined();
15327 *
15328 *      // Propagate promise resolution to 'then' functions using $apply().
15329 *      $rootScope.$apply();
15330 *      expect(resolvedValue).toEqual(123);
15331 *    }));
15332 *  ```
15333 *
15334 * @param {function(function, function)} resolver Function which is responsible for resolving or
15335 *   rejecting the newly created promise. The first parameter is a function which resolves the
15336 *   promise, the second parameter is a function which rejects the promise.
15337 *
15338 * @returns {Promise} The newly created promise.
15339 */
15340function $QProvider() {
15341
15342  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
15343    return qFactory(function(callback) {
15344      $rootScope.$evalAsync(callback);
15345    }, $exceptionHandler);
15346  }];
15347}
15348
15349function $$QProvider() {
15350  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
15351    return qFactory(function(callback) {
15352      $browser.defer(callback);
15353    }, $exceptionHandler);
15354  }];
15355}
15356
15357/**
15358 * Constructs a promise manager.
15359 *
15360 * @param {function(function)} nextTick Function for executing functions in the next turn.
15361 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
15362 *     debugging purposes.
15363 * @returns {object} Promise manager.
15364 */
15365function qFactory(nextTick, exceptionHandler) {
15366  var $qMinErr = minErr('$q', TypeError);
15367
15368  /**
15369   * @ngdoc method
15370   * @name ng.$q#defer
15371   * @kind function
15372   *
15373   * @description
15374   * Creates a `Deferred` object which represents a task which will finish in the future.
15375   *
15376   * @returns {Deferred} Returns a new instance of deferred.
15377   */
15378  var defer = function() {
15379    var d = new Deferred();
15380    //Necessary to support unbound execution :/
15381    d.resolve = simpleBind(d, d.resolve);
15382    d.reject = simpleBind(d, d.reject);
15383    d.notify = simpleBind(d, d.notify);
15384    return d;
15385  };
15386
15387  function Promise() {
15388    this.$$state = { status: 0 };
15389  }
15390
15391  extend(Promise.prototype, {
15392    then: function(onFulfilled, onRejected, progressBack) {
15393      if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
15394        return this;
15395      }
15396      var result = new Deferred();
15397
15398      this.$$state.pending = this.$$state.pending || [];
15399      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
15400      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
15401
15402      return result.promise;
15403    },
15404
15405    "catch": function(callback) {
15406      return this.then(null, callback);
15407    },
15408
15409    "finally": function(callback, progressBack) {
15410      return this.then(function(value) {
15411        return handleCallback(value, true, callback);
15412      }, function(error) {
15413        return handleCallback(error, false, callback);
15414      }, progressBack);
15415    }
15416  });
15417
15418  //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
15419  function simpleBind(context, fn) {
15420    return function(value) {
15421      fn.call(context, value);
15422    };
15423  }
15424
15425  function processQueue(state) {
15426    var fn, deferred, pending;
15427
15428    pending = state.pending;
15429    state.processScheduled = false;
15430    state.pending = undefined;
15431    for (var i = 0, ii = pending.length; i < ii; ++i) {
15432      deferred = pending[i][0];
15433      fn = pending[i][state.status];
15434      try {
15435        if (isFunction(fn)) {
15436          deferred.resolve(fn(state.value));
15437        } else if (state.status === 1) {
15438          deferred.resolve(state.value);
15439        } else {
15440          deferred.reject(state.value);
15441        }
15442      } catch (e) {
15443        deferred.reject(e);
15444        exceptionHandler(e);
15445      }
15446    }
15447  }
15448
15449  function scheduleProcessQueue(state) {
15450    if (state.processScheduled || !state.pending) return;
15451    state.processScheduled = true;
15452    nextTick(function() { processQueue(state); });
15453  }
15454
15455  function Deferred() {
15456    this.promise = new Promise();
15457  }
15458
15459  extend(Deferred.prototype, {
15460    resolve: function(val) {
15461      if (this.promise.$$state.status) return;
15462      if (val === this.promise) {
15463        this.$$reject($qMinErr(
15464          'qcycle',
15465          "Expected promise to be resolved with value other than itself '{0}'",
15466          val));
15467      } else {
15468        this.$$resolve(val);
15469      }
15470
15471    },
15472
15473    $$resolve: function(val) {
15474      var then;
15475      var that = this;
15476      var done = false;
15477      try {
15478        if ((isObject(val) || isFunction(val))) then = val && val.then;
15479        if (isFunction(then)) {
15480          this.promise.$$state.status = -1;
15481          then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
15482        } else {
15483          this.promise.$$state.value = val;
15484          this.promise.$$state.status = 1;
15485          scheduleProcessQueue(this.promise.$$state);
15486        }
15487      } catch (e) {
15488        rejectPromise(e);
15489        exceptionHandler(e);
15490      }
15491
15492      function resolvePromise(val) {
15493        if (done) return;
15494        done = true;
15495        that.$$resolve(val);
15496      }
15497      function rejectPromise(val) {
15498        if (done) return;
15499        done = true;
15500        that.$$reject(val);
15501      }
15502    },
15503
15504    reject: function(reason) {
15505      if (this.promise.$$state.status) return;
15506      this.$$reject(reason);
15507    },
15508
15509    $$reject: function(reason) {
15510      this.promise.$$state.value = reason;
15511      this.promise.$$state.status = 2;
15512      scheduleProcessQueue(this.promise.$$state);
15513    },
15514
15515    notify: function(progress) {
15516      var callbacks = this.promise.$$state.pending;
15517
15518      if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
15519        nextTick(function() {
15520          var callback, result;
15521          for (var i = 0, ii = callbacks.length; i < ii; i++) {
15522            result = callbacks[i][0];
15523            callback = callbacks[i][3];
15524            try {
15525              result.notify(isFunction(callback) ? callback(progress) : progress);
15526            } catch (e) {
15527              exceptionHandler(e);
15528            }
15529          }
15530        });
15531      }
15532    }
15533  });
15534
15535  /**
15536   * @ngdoc method
15537   * @name $q#reject
15538   * @kind function
15539   *
15540   * @description
15541   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
15542   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
15543   * a promise chain, you don't need to worry about it.
15544   *
15545   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
15546   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
15547   * a promise error callback and you want to forward the error to the promise derived from the
15548   * current promise, you have to "rethrow" the error by returning a rejection constructed via
15549   * `reject`.
15550   *
15551   * ```js
15552   *   promiseB = promiseA.then(function(result) {
15553   *     // success: do something and resolve promiseB
15554   *     //          with the old or a new result
15555   *     return result;
15556   *   }, function(reason) {
15557   *     // error: handle the error if possible and
15558   *     //        resolve promiseB with newPromiseOrValue,
15559   *     //        otherwise forward the rejection to promiseB
15560   *     if (canHandle(reason)) {
15561   *      // handle the error and recover
15562   *      return newPromiseOrValue;
15563   *     }
15564   *     return $q.reject(reason);
15565   *   });
15566   * ```
15567   *
15568   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
15569   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
15570   */
15571  var reject = function(reason) {
15572    var result = new Deferred();
15573    result.reject(reason);
15574    return result.promise;
15575  };
15576
15577  var makePromise = function makePromise(value, resolved) {
15578    var result = new Deferred();
15579    if (resolved) {
15580      result.resolve(value);
15581    } else {
15582      result.reject(value);
15583    }
15584    return result.promise;
15585  };
15586
15587  var handleCallback = function handleCallback(value, isResolved, callback) {
15588    var callbackOutput = null;
15589    try {
15590      if (isFunction(callback)) callbackOutput = callback();
15591    } catch (e) {
15592      return makePromise(e, false);
15593    }
15594    if (isPromiseLike(callbackOutput)) {
15595      return callbackOutput.then(function() {
15596        return makePromise(value, isResolved);
15597      }, function(error) {
15598        return makePromise(error, false);
15599      });
15600    } else {
15601      return makePromise(value, isResolved);
15602    }
15603  };
15604
15605  /**
15606   * @ngdoc method
15607   * @name $q#when
15608   * @kind function
15609   *
15610   * @description
15611   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
15612   * This is useful when you are dealing with an object that might or might not be a promise, or if
15613   * the promise comes from a source that can't be trusted.
15614   *
15615   * @param {*} value Value or a promise
15616   * @param {Function=} successCallback
15617   * @param {Function=} errorCallback
15618   * @param {Function=} progressCallback
15619   * @returns {Promise} Returns a promise of the passed value or promise
15620   */
15621
15622
15623  var when = function(value, callback, errback, progressBack) {
15624    var result = new Deferred();
15625    result.resolve(value);
15626    return result.promise.then(callback, errback, progressBack);
15627  };
15628
15629  /**
15630   * @ngdoc method
15631   * @name $q#resolve
15632   * @kind function
15633   *
15634   * @description
15635   * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
15636   *
15637   * @param {*} value Value or a promise
15638   * @param {Function=} successCallback
15639   * @param {Function=} errorCallback
15640   * @param {Function=} progressCallback
15641   * @returns {Promise} Returns a promise of the passed value or promise
15642   */
15643  var resolve = when;
15644
15645  /**
15646   * @ngdoc method
15647   * @name $q#all
15648   * @kind function
15649   *
15650   * @description
15651   * Combines multiple promises into a single promise that is resolved when all of the input
15652   * promises are resolved.
15653   *
15654   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15655   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15656   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
15657   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
15658   *   with the same rejection value.
15659   */
15660
15661  function all(promises) {
15662    var deferred = new Deferred(),
15663        counter = 0,
15664        results = isArray(promises) ? [] : {};
15665
15666    forEach(promises, function(promise, key) {
15667      counter++;
15668      when(promise).then(function(value) {
15669        if (results.hasOwnProperty(key)) return;
15670        results[key] = value;
15671        if (!(--counter)) deferred.resolve(results);
15672      }, function(reason) {
15673        if (results.hasOwnProperty(key)) return;
15674        deferred.reject(reason);
15675      });
15676    });
15677
15678    if (counter === 0) {
15679      deferred.resolve(results);
15680    }
15681
15682    return deferred.promise;
15683  }
15684
15685  var $Q = function Q(resolver) {
15686    if (!isFunction(resolver)) {
15687      throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
15688    }
15689
15690    var deferred = new Deferred();
15691
15692    function resolveFn(value) {
15693      deferred.resolve(value);
15694    }
15695
15696    function rejectFn(reason) {
15697      deferred.reject(reason);
15698    }
15699
15700    resolver(resolveFn, rejectFn);
15701
15702    return deferred.promise;
15703  };
15704
15705  // Let's make the instanceof operator work for promises, so that
15706  // `new $q(fn) instanceof $q` would evaluate to true.
15707  $Q.prototype = Promise.prototype;
15708
15709  $Q.defer = defer;
15710  $Q.reject = reject;
15711  $Q.when = when;
15712  $Q.resolve = resolve;
15713  $Q.all = all;
15714
15715  return $Q;
15716}
15717
15718function $$RAFProvider() { //rAF
15719  this.$get = ['$window', '$timeout', function($window, $timeout) {
15720    var requestAnimationFrame = $window.requestAnimationFrame ||
15721                                $window.webkitRequestAnimationFrame;
15722
15723    var cancelAnimationFrame = $window.cancelAnimationFrame ||
15724                               $window.webkitCancelAnimationFrame ||
15725                               $window.webkitCancelRequestAnimationFrame;
15726
15727    var rafSupported = !!requestAnimationFrame;
15728    var raf = rafSupported
15729      ? function(fn) {
15730          var id = requestAnimationFrame(fn);
15731          return function() {
15732            cancelAnimationFrame(id);
15733          };
15734        }
15735      : function(fn) {
15736          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15737          return function() {
15738            $timeout.cancel(timer);
15739          };
15740        };
15741
15742    raf.supported = rafSupported;
15743
15744    return raf;
15745  }];
15746}
15747
15748/**
15749 * DESIGN NOTES
15750 *
15751 * The design decisions behind the scope are heavily favored for speed and memory consumption.
15752 *
15753 * The typical use of scope is to watch the expressions, which most of the time return the same
15754 * value as last time so we optimize the operation.
15755 *
15756 * Closures construction is expensive in terms of speed as well as memory:
15757 *   - No closures, instead use prototypical inheritance for API
15758 *   - Internal state needs to be stored on scope directly, which means that private state is
15759 *     exposed as $$____ properties
15760 *
15761 * Loop operations are optimized by using while(count--) { ... }
15762 *   - This means that in order to keep the same order of execution as addition we have to add
15763 *     items to the array at the beginning (unshift) instead of at the end (push)
15764 *
15765 * Child scopes are created and removed often
15766 *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
15767 *
15768 * There are fewer watches than observers. This is why you don't want the observer to be implemented
15769 * in the same way as watch. Watch requires return of the initialization function which is expensive
15770 * to construct.
15771 */
15772
15773
15774/**
15775 * @ngdoc provider
15776 * @name $rootScopeProvider
15777 * @description
15778 *
15779 * Provider for the $rootScope service.
15780 */
15781
15782/**
15783 * @ngdoc method
15784 * @name $rootScopeProvider#digestTtl
15785 * @description
15786 *
15787 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15788 * assuming that the model is unstable.
15789 *
15790 * The current default is 10 iterations.
15791 *
15792 * In complex applications it's possible that the dependencies between `$watch`s will result in
15793 * several digest iterations. However if an application needs more than the default 10 digest
15794 * iterations for its model to stabilize then you should investigate what is causing the model to
15795 * continuously change during the digest.
15796 *
15797 * Increasing the TTL could have performance implications, so you should not change it without
15798 * proper justification.
15799 *
15800 * @param {number} limit The number of digest iterations.
15801 */
15802
15803
15804/**
15805 * @ngdoc service
15806 * @name $rootScope
15807 * @description
15808 *
15809 * Every application has a single root {@link ng.$rootScope.Scope scope}.
15810 * All other scopes are descendant scopes of the root scope. Scopes provide separation
15811 * between the model and the view, via a mechanism for watching the model for changes.
15812 * They also provide event emission/broadcast and subscription facility. See the
15813 * {@link guide/scope developer guide on scopes}.
15814 */
15815function $RootScopeProvider() {
15816  var TTL = 10;
15817  var $rootScopeMinErr = minErr('$rootScope');
15818  var lastDirtyWatch = null;
15819  var applyAsyncId = null;
15820
15821  this.digestTtl = function(value) {
15822    if (arguments.length) {
15823      TTL = value;
15824    }
15825    return TTL;
15826  };
15827
15828  function createChildScopeClass(parent) {
15829    function ChildScope() {
15830      this.$$watchers = this.$$nextSibling =
15831          this.$$childHead = this.$$childTail = null;
15832      this.$$listeners = {};
15833      this.$$listenerCount = {};
15834      this.$$watchersCount = 0;
15835      this.$id = nextUid();
15836      this.$$ChildScope = null;
15837    }
15838    ChildScope.prototype = parent;
15839    return ChildScope;
15840  }
15841
15842  this.$get = ['$exceptionHandler', '$parse', '$browser',
15843      function($exceptionHandler, $parse, $browser) {
15844
15845    function destroyChildScope($event) {
15846        $event.currentScope.$$destroyed = true;
15847    }
15848
15849    function cleanUpScope($scope) {
15850
15851      if (msie === 9) {
15852        // There is a memory leak in IE9 if all child scopes are not disconnected
15853        // completely when a scope is destroyed. So this code will recurse up through
15854        // all this scopes children
15855        //
15856        // See issue https://github.com/angular/angular.js/issues/10706
15857        $scope.$$childHead && cleanUpScope($scope.$$childHead);
15858        $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
15859      }
15860
15861      // The code below works around IE9 and V8's memory leaks
15862      //
15863      // See:
15864      // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15865      // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15866      // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15867
15868      $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
15869          $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
15870    }
15871
15872    /**
15873     * @ngdoc type
15874     * @name $rootScope.Scope
15875     *
15876     * @description
15877     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15878     * {@link auto.$injector $injector}. Child scopes are created using the
15879     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15880     * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15881     * an in-depth introduction and usage examples.
15882     *
15883     *
15884     * # Inheritance
15885     * A scope can inherit from a parent scope, as in this example:
15886     * ```js
15887         var parent = $rootScope;
15888         var child = parent.$new();
15889
15890         parent.salutation = "Hello";
15891         expect(child.salutation).toEqual('Hello');
15892
15893         child.salutation = "Welcome";
15894         expect(child.salutation).toEqual('Welcome');
15895         expect(parent.salutation).toEqual('Hello');
15896     * ```
15897     *
15898     * When interacting with `Scope` in tests, additional helper methods are available on the
15899     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15900     * details.
15901     *
15902     *
15903     * @param {Object.<string, function()>=} providers Map of service factory which need to be
15904     *                                       provided for the current scope. Defaults to {@link ng}.
15905     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15906     *                              append/override services provided by `providers`. This is handy
15907     *                              when unit-testing and having the need to override a default
15908     *                              service.
15909     * @returns {Object} Newly created scope.
15910     *
15911     */
15912    function Scope() {
15913      this.$id = nextUid();
15914      this.$$phase = this.$parent = this.$$watchers =
15915                     this.$$nextSibling = this.$$prevSibling =
15916                     this.$$childHead = this.$$childTail = null;
15917      this.$root = this;
15918      this.$$destroyed = false;
15919      this.$$listeners = {};
15920      this.$$listenerCount = {};
15921      this.$$watchersCount = 0;
15922      this.$$isolateBindings = null;
15923    }
15924
15925    /**
15926     * @ngdoc property
15927     * @name $rootScope.Scope#$id
15928     *
15929     * @description
15930     * Unique scope ID (monotonically increasing) useful for debugging.
15931     */
15932
15933     /**
15934      * @ngdoc property
15935      * @name $rootScope.Scope#$parent
15936      *
15937      * @description
15938      * Reference to the parent scope.
15939      */
15940
15941      /**
15942       * @ngdoc property
15943       * @name $rootScope.Scope#$root
15944       *
vendor: 5,748 bytes, lines 15945-16051
15945       * @description
15946       * Reference to the root scope.
15947       */
15948
15949    Scope.prototype = {
15950      constructor: Scope,
15951      /**
15952       * @ngdoc method
15953       * @name $rootScope.Scope#$new
15954       * @kind function
15955       *
15956       * @description
15957       * Creates a new child {@link ng.$rootScope.Scope scope}.
15958       *
15959       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15960       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15961       *
15962       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15963       * desired for the scope and its child scopes to be permanently detached from the parent and
15964       * thus stop participating in model change detection and listener notification by invoking.
15965       *
15966       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15967       *         parent scope. The scope is isolated, as it can not see parent scope properties.
15968       *         When creating widgets, it is useful for the widget to not accidentally read parent
15969       *         state.
15970       *
15971       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15972       *                              of the newly created scope. Defaults to `this` scope if not provided.
15973       *                              This is used when creating a transclude scope to correctly place it
15974       *                              in the scope hierarchy while maintaining the correct prototypical
15975       *                              inheritance.
15976       *
15977       * @returns {Object} The newly created child scope.
15978       *
15979       */
15980      $new: function(isolate, parent) {
15981        var child;
15982
15983        parent = parent || this;
15984
15985        if (isolate) {
15986          child = new Scope();
15987          child.$root = this.$root;
15988        } else {
15989          // Only create a child scope class if somebody asks for one,
15990          // but cache it to allow the VM to optimize lookups.
15991          if (!this.$$ChildScope) {
15992            this.$$ChildScope = createChildScopeClass(this);
15993          }
15994          child = new this.$$ChildScope();
15995        }
15996        child.$parent = parent;
15997        child.$$prevSibling = parent.$$childTail;
15998        if (parent.$$childHead) {
15999          parent.$$childTail.$$nextSibling = child;
16000          parent.$$childTail = child;
16001        } else {
16002          parent.$$childHead = parent.$$childTail = child;
16003        }
16004
16005        // When the new scope is not isolated or we inherit from `this`, and
16006        // the parent scope is destroyed, the property `$$destroyed` is inherited
16007        // prototypically. In all other cases, this property needs to be set
16008        // when the parent scope is destroyed.
16009        // The listener needs to be added after the parent is set
16010        if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
16011
16012        return child;
16013      },
16014
16015      /**
16016       * @ngdoc method
16017       * @name $rootScope.Scope#$watch
16018       * @kind function
16019       *
16020       * @description
16021       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
16022       *
16023       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
16024       *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
16025       *   its value when executed multiple times with the same input because it may be executed multiple
16026       *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
16027       *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
16028       * - The `listener` is called only when the value from the current `watchExpression` and the
16029       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
16030       *   see below). Inequality is determined according to reference inequality,
16031       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
16032       *    via the `!==` Javascript operator, unless `objectEquality == true`
16033       *   (see next point)
16034       * - When `objectEquality == true`, inequality of the `watchExpression` is determined
16035       *   according to the {@link angular.equals} function. To save the value of the object for
16036       *   later comparison, the {@link angular.copy} function is used. This therefore means that
16037       *   watching complex objects will have adverse memory and performance implications.
16038       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
16039       *   This is achieved by rerunning the watchers until no changes are detected. The rerun
16040       *   iteration limit is 10 to prevent an infinite loop deadlock.
16041       *
16042       *
16043       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
16044       * you can register a `watchExpression` function with no `listener`. (Be prepared for
16045       * multiple calls to your `watchExpression` because it will execute multiple times in a
16046       * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
16047       *
16048       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
16049       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
16050       * watcher. In rare cases, this is undesirable because the listener is called when the result
16051       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
16052       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
16053       * listener was called due to initialization.
16054       *
16055       *
16056       *
16057       * # Example
16058       * ```js
16059           // let's assume that scope was dependency injected as the $rootScope
16060           var scope = $rootScope;
16061           scope.name = 'misko';
16062           scope.counter = 0;
16063
16064           expect(scope.counter).toEqual(0);
16065           scope.$watch('name', function(newValue, oldValue) {
16066             scope.counter = scope.counter + 1;
16067           });
16068           expect(scope.counter).toEqual(0);
16069
16070           scope.$digest();
16071           // the listener is always called during the first $digest loop after it was registered
16072           expect(scope.counter).toEqual(1);
16073
16074           scope.$digest();
16075           // but now it will not be called unless the value changes
16076           expect(scope.counter).toEqual(1);
16077
16078           scope.name = 'adam';
16079           scope.$digest();
16080           expect(scope.counter).toEqual(2);
16081
16082
16083
16084           // Using a function as a watchExpression
16085           var food;
16086           scope.foodCounter = 0;
16087           expect(scope.foodCounter).toEqual(0);
16088           scope.$watch(
16089             // This function returns the value being watched. It is called for each turn of the $digest loop
16090             function() { return food; },
16091             // This is the change listener, called when the value returned from the above function changes
16092             function(newValue, oldValue) {
16093               if ( newValue !== oldValue ) {
16094                 // Only increment the counter if the value changed
16095                 scope.foodCounter = scope.foodCounter + 1;
16096               }
16097             }
16098           );
16099           // No digest has been run so the counter will be zero
16100           expect(scope.foodCounter).toEqual(0);
16101
16102           // Run the digest but since food has not changed count will still be zero
16103           scope.$digest();
16104           expect(scope.foodCounter).toEqual(0);
16105
16106           // Update food and run digest.  Now the counter will increment
16107           food = 'cheeseburger';
16108           scope.$digest();
16109           expect(scope.foodCounter).toEqual(1);
16110
16111       * ```
16112       *
16113       *
16114       *
16115       * @param {(function()|string)} watchExpression Expression that is evaluated on each
16116       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
16117       *    a call to the `listener`.
16118       *
16119       *    - `string`: Evaluated as {@link guide/expression expression}
16120       *    - `function(scope)`: called with current `scope` as a parameter.
16121       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
16122       *    of `watchExpression` changes.
16123       *
16124       *    - `newVal` contains the current value of the `watchExpression`
16125       *    - `oldVal` contains the previous value of the `watchExpression`
16126       *    - `scope` refers to the current scope
16127       * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
16128       *     comparing for reference equality.
16129       * @returns {function()} Returns a deregistration function for this listener.
16130       */
16131      $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
16132        var get = $parse(watchExp);
16133
16134        if (get.$$watchDelegate) {
16135          return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
16136        }
16137        var scope = this,
16138            array = scope.$$watchers,
16139            watcher = {
16140              fn: listener,
16141              last: initWatchVal,
16142              get: get,
16143              exp: prettyPrintExpression || watchExp,
16144              eq: !!objectEquality
16145            };
16146
16147        lastDirtyWatch = null;
16148
16149        if (!isFunction(listener)) {
16150          watcher.fn = noop;
16151        }
16152
16153        if (!array) {
16154          array = scope.$$watchers = [];
16155        }
16156        // we use unshift since we use a while loop in $digest for speed.
16157        // the while loop reads in reverse order.
16158        array.unshift(watcher);
16159        incrementWatchersCount(this, 1);
16160
16161        return function deregisterWatch() {
16162          if (arrayRemove(array, watcher) >= 0) {
16163            incrementWatchersCount(scope, -1);
16164          }
16165          lastDirtyWatch = null;
16166        };
16167      },
16168
16169      /**
16170       * @ngdoc method
16171       * @name $rootScope.Scope#$watchGroup
16172       * @kind function
16173       *
16174       * @description
16175       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
16176       * If any one expression in the collection changes the `listener` is executed.
16177       *
16178       * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
16179       *   call to $digest() to see if any items changes.
16180       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
16181       *
16182       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
16183       * watched using {@link ng.$rootScope.Scope#$watch $watch()}
16184       *
16185       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
16186       *    expression in `watchExpressions` changes
16187       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
16188       *    those of `watchExpression`
16189       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
16190       *    those of `watchExpression`
16191       *    The `scope` refers to the current scope.
16192       * @returns {function()} Returns a de-registration function for all listeners.
16193       */
16194      $watchGroup: function(watchExpressions, listener) {
16195        var oldValues = new Array(watchExpressions.length);
16196        var newValues = new Array(watchExpressions.length);
16197        var deregisterFns = [];
16198        var self = this;
16199        var changeReactionScheduled = false;
16200        var firstRun = true;
16201
16202        if (!watchExpressions.length) {
16203          // No expressions means we call the listener ASAP
16204          var shouldCall = true;
16205          self.$evalAsync(function() {
16206            if (shouldCall) listener(newValues, newValues, self);
16207          });
16208          return function deregisterWatchGroup() {
16209            shouldCall = false;
16210          };
16211        }
16212
16213        if (watchExpressions.length === 1) {
16214          // Special case size of one
16215          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
16216            newValues[0] = value;
16217            oldValues[0] = oldValue;
16218            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
16219          });
16220        }
16221
16222        forEach(watchExpressions, function(expr, i) {
16223          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
16224            newValues[i] = value;
16225            oldValues[i] = oldValue;
16226            if (!changeReactionScheduled) {
16227              changeReactionScheduled = true;
16228              self.$evalAsync(watchGroupAction);
16229            }
16230          });
16231          deregisterFns.push(unwatchFn);
16232        });
16233
16234        function watchGroupAction() {
16235          changeReactionScheduled = false;
16236
16237          if (firstRun) {
16238            firstRun = false;
16239            listener(newValues, newValues, self);
16240          } else {
16241            listener(newValues, oldValues, self);
16242          }
16243        }
16244
16245        return function deregisterWatchGroup() {
16246          while (deregisterFns.length) {
16247            deregisterFns.shift()();
16248          }
16249        };
16250      },
16251
16252
16253      /**
16254       * @ngdoc method
16255       * @name $rootScope.Scope#$watchCollection
16256       * @kind function
16257       *
16258       * @description
16259       * Shallow watches the properties of an object and fires whenever any of the properties change
16260       * (for arrays, this implies watching the array items; for object maps, this implies watching
16261       * the properties). If a change is detected, the `listener` callback is fired.
16262       *
16263       * - The `obj` collection is observed via standard $watch operation and is examined on every
16264       *   call to $digest() to see if any items have been added, removed, or moved.
16265       * - The `listener` is called whenever anything within the `obj` has changed. Examples include
16266       *   adding, removing, and moving items belonging to an object or array.
16267       *
16268       *
16269       * # Example
16270       * ```js
16271          $scope.names = ['igor', 'matias', 'misko', 'james'];
16272          $scope.dataCount = 4;
16273
16274          $scope.$watchCollection('names', function(newNames, oldNames) {
16275            $scope.dataCount = newNames.length;
16276          });
16277
16278          expect($scope.dataCount).toEqual(4);
16279          $scope.$digest();
16280
16281          //still at 4 ... no changes
16282          expect($scope.dataCount).toEqual(4);
16283
16284          $scope.names.pop();
16285          $scope.$digest();
16286
16287          //now there's been a change
16288          expect($scope.dataCount).toEqual(3);
16289       * ```
16290       *
16291       *
16292       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
16293       *    expression value should evaluate to an object or an array which is observed on each
16294       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
16295       *    collection will trigger a call to the `listener`.
16296       *
16297       * @param {function(newCollection, oldCollection, scope)} listener a callback function called
16298       *    when a change is detected.
16299       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
16300       *    - The `oldCollection` object is a copy of the former collection data.
16301       *      Due to performance considerations, the`oldCollection` value is computed only if the
16302       *      `listener` function declares two or more arguments.
16303       *    - The `scope` argument refers to the current scope.
16304       *
16305       * @returns {function()} Returns a de-registration function for this listener. When the
16306       *    de-registration function is executed, the internal watch operation is terminated.
16307       */
16308      $watchCollection: function(obj, listener) {
16309        $watchCollectionInterceptor.$stateful = true;
16310
16311        var self = this;
16312        // the current value, updated on each dirty-check run
16313        var newValue;
16314        // a shallow copy of the newValue from the last dirty-check run,
16315        // updated to match newValue during dirty-check run
16316        var oldValue;
16317        // a shallow copy of the newValue from when the last change happened
16318        var veryOldValue;
16319        // only track veryOldValue if the listener is asking for it
16320        var trackVeryOldValue = (listener.length > 1);
16321        var changeDetected = 0;
16322        var changeDetector = $parse(obj, $watchCollectionInterceptor);
16323        var internalArray = [];
16324        var internalObject = {};
16325        var initRun = true;
16326        var oldLength = 0;
16327
16328        function $watchCollectionInterceptor(_value) {
16329          newValue = _value;
16330          var newLength, key, bothNaN, newItem, oldItem;
16331
16332          // If the new value is undefined, then return undefined as the watch may be a one-time watch
16333          if (isUndefined(newValue)) return;
16334
16335          if (!isObject(newValue)) { // if primitive
16336            if (oldValue !== newValue) {
16337              oldValue = newValue;
16338              changeDetected++;
16339            }
16340          } else if (isArrayLike(newValue)) {
16341            if (oldValue !== internalArray) {
16342              // we are transitioning from something which was not an array into array.
16343              oldValue = internalArray;
16344              oldLength = oldValue.length = 0;
16345              changeDetected++;
16346            }
16347
16348            newLength = newValue.length;
16349
16350            if (oldLength !== newLength) {
16351              // if lengths do not match we need to trigger change notification
16352              changeDetected++;
16353              oldValue.length = oldLength = newLength;
16354            }
16355            // copy the items to oldValue and look for changes.
16356            for (var i = 0; i < newLength; i++) {
16357              oldItem = oldValue[i];
16358              newItem = newValue[i];
16359
16360              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
vendor: 5,713 bytes, lines 16361-16512
16361              if (!bothNaN && (oldItem !== newItem)) {
16362                changeDetected++;
16363                oldValue[i] = newItem;
16364              }
16365            }
16366          } else {
16367            if (oldValue !== internalObject) {
16368              // we are transitioning from something which was not an object into object.
16369              oldValue = internalObject = {};
16370              oldLength = 0;
16371              changeDetected++;
16372            }
16373            // copy the items to oldValue and look for changes.
16374            newLength = 0;
16375            for (key in newValue) {
16376              if (hasOwnProperty.call(newValue, key)) {
16377                newLength++;
16378                newItem = newValue[key];
16379                oldItem = oldValue[key];
16380
16381                if (key in oldValue) {
16382                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
16383                  if (!bothNaN && (oldItem !== newItem)) {
16384                    changeDetected++;
16385                    oldValue[key] = newItem;
16386                  }
16387                } else {
16388                  oldLength++;
16389                  oldValue[key] = newItem;
16390                  changeDetected++;
16391                }
16392              }
16393            }
16394            if (oldLength > newLength) {
16395              // we used to have more keys, need to find them and destroy them.
16396              changeDetected++;
16397              for (key in oldValue) {
16398                if (!hasOwnProperty.call(newValue, key)) {
16399                  oldLength--;
16400                  delete oldValue[key];
16401                }
16402              }
16403            }
16404          }
16405          return changeDetected;
16406        }
16407
16408        function $watchCollectionAction() {
16409          if (initRun) {
16410            initRun = false;
16411            listener(newValue, newValue, self);
16412          } else {
16413            listener(newValue, veryOldValue, self);
16414          }
16415
16416          // make a copy for the next time a collection is changed
16417          if (trackVeryOldValue) {
16418            if (!isObject(newValue)) {
16419              //primitive
16420              veryOldValue = newValue;
16421            } else if (isArrayLike(newValue)) {
16422              veryOldValue = new Array(newValue.length);
16423              for (var i = 0; i < newValue.length; i++) {
16424                veryOldValue[i] = newValue[i];
16425              }
16426            } else { // if object
16427              veryOldValue = {};
16428              for (var key in newValue) {
16429                if (hasOwnProperty.call(newValue, key)) {
16430                  veryOldValue[key] = newValue[key];
16431                }
16432              }
16433            }
16434          }
16435        }
16436
16437        return this.$watch(changeDetector, $watchCollectionAction);
16438      },
16439
16440      /**
16441       * @ngdoc method
16442       * @name $rootScope.Scope#$digest
16443       * @kind function
16444       *
16445       * @description
16446       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
16447       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
16448       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
16449       * until no more listeners are firing. This means that it is possible to get into an infinite
16450       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
16451       * iterations exceeds 10.
16452       *
16453       * Usually, you don't call `$digest()` directly in
16454       * {@link ng.directive:ngController controllers} or in
16455       * {@link ng.$compileProvider#directive directives}.
16456       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
16457       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
16458       *
16459       * If you want to be notified whenever `$digest()` is called,
16460       * you can register a `watchExpression` function with
16461       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
16462       *
16463       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
16464       *
16465       * # Example
16466       * ```js
16467           var scope = ...;
16468           scope.name = 'misko';
16469           scope.counter = 0;
16470
16471           expect(scope.counter).toEqual(0);
16472           scope.$watch('name', function(newValue, oldValue) {
16473             scope.counter = scope.counter + 1;
16474           });
16475           expect(scope.counter).toEqual(0);
16476
16477           scope.$digest();
16478           // the listener is always called during the first $digest loop after it was registered
16479           expect(scope.counter).toEqual(1);
16480
16481           scope.$digest();
16482           // but now it will not be called unless the value changes
16483           expect(scope.counter).toEqual(1);
16484
16485           scope.name = 'adam';
16486           scope.$digest();
16487           expect(scope.counter).toEqual(2);
16488       * ```
16489       *
16490       */
16491      $digest: function() {
16492        var watch, value, last,
16493            watchers,
16494            length,
16495            dirty, ttl = TTL,
16496            next, current, target = this,
16497            watchLog = [],
16498            logIdx, logMsg, asyncTask;
16499
16500        beginPhase('$digest');
16501        // Check for changes to browser url that happened in sync before the call to $digest
16502        $browser.$$checkUrlChange();
16503
16504        if (this === $rootScope && applyAsyncId !== null) {
16505          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
16506          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
16507          $browser.defer.cancel(applyAsyncId);
16508          flushApplyAsync();
16509        }
16510
16511        lastDirtyWatch = null;
16512
16513        do { // "while dirty" loop
16514          dirty = false;
16515          current = target;
16516
16517          while (asyncQueue.length) {
16518            try {
16519              asyncTask = asyncQueue.shift();
16520              asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
16521            } catch (e) {
16522              $exceptionHandler(e);
16523            }
16524            lastDirtyWatch = null;
16525          }
16526
16527          traverseScopesLoop:
16528          do { // "traverse the scopes" loop
16529            if ((watchers = current.$$watchers)) {
16530              // process our watches
16531              length = watchers.length;
16532              while (length--) {
16533                try {
16534                  watch = watchers[length];
16535                  // Most common watches are on primitives, in which case we can short
16536                  // circuit it with === operator, only when === fails do we use .equals
16537                  if (watch) {
16538                    if ((value = watch.get(current)) !== (last = watch.last) &&
16539                        !(watch.eq
16540                            ? equals(value, last)
16541                            : (typeof value === 'number' && typeof last === 'number'
16542                               && isNaN(value) && isNaN(last)))) {
16543                      dirty = true;
16544                      lastDirtyWatch = watch;
16545                      watch.last = watch.eq ? copy(value, null) : value;
16546                      watch.fn(value, ((last === initWatchVal) ? value : last), current);
16547                      if (ttl < 5) {
16548                        logIdx = 4 - ttl;
16549                        if (!watchLog[logIdx]) watchLog[logIdx] = [];
16550                        watchLog[logIdx].push({
16551                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
16552                          newVal: value,
16553                          oldVal: last
16554                        });
16555                      }
16556                    } else if (watch === lastDirtyWatch) {
16557                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
16558                      // have already been tested.
16559                      dirty = false;
16560                      break traverseScopesLoop;
16561                    }
16562                  }
16563                } catch (e) {
16564                  $exceptionHandler(e);
16565                }
16566              }
16567            }
16568
16569            // Insanity Warning: scope depth-first traversal
16570            // yes, this code is a bit crazy, but it works and we have tests to prove it!
16571            // this piece should be kept in sync with the traversal in $broadcast
16572            if (!(next = ((current.$$watchersCount && current.$$childHead) ||
16573                (current !== target && current.$$nextSibling)))) {
16574              while (current !== target && !(next = current.$$nextSibling)) {
16575                current = current.$parent;
16576              }
16577            }
16578          } while ((current = next));
16579
16580          // `break traverseScopesLoop;` takes us to here
16581
16582          if ((dirty || asyncQueue.length) && !(ttl--)) {
16583            clearPhase();
16584            throw $rootScopeMinErr('infdig',
16585                '{0} $digest() iterations reached. Aborting!\n' +
16586                'Watchers fired in the last 5 iterations: {1}',
16587                TTL, watchLog);
16588          }
16589
16590        } while (dirty || asyncQueue.length);
16591
16592        clearPhase();
16593
16594        while (postDigestQueue.length) {
16595          try {
16596            postDigestQueue.shift()();
16597          } catch (e) {
16598            $exceptionHandler(e);
16599          }
16600        }
16601      },
16602
16603
16604      /**
16605       * @ngdoc event
16606       * @name $rootScope.Scope#$destroy
16607       * @eventType broadcast on scope being destroyed
16608       *
16609       * @description
16610       * Broadcasted when a scope and its children are being destroyed.
16611       *
16612       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16613       * clean up DOM bindings before an element is removed from the DOM.
16614       */
16615
16616      /**
16617       * @ngdoc method
16618       * @name $rootScope.Scope#$destroy
16619       * @kind function
16620       *
16621       * @description
16622       * Removes the current scope (and all of its children) from the parent scope. Removal implies
16623       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
16624       * propagate to the current scope and its children. Removal also implies that the current
16625       * scope is eligible for garbage collection.
16626       *
16627       * The `$destroy()` is usually used by directives such as
16628       * {@link ng.directive:ngRepeat ngRepeat} for managing the
16629       * unrolling of the loop.
16630       *
16631       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
16632       * Application code can register a `$destroy` event handler that will give it a chance to
16633       * perform any necessary cleanup.
16634       *
16635       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16636       * clean up DOM bindings before an element is removed from the DOM.
16637       */
16638      $destroy: function() {
16639        // We can't destroy a scope that has been already destroyed.
16640        if (this.$$destroyed) return;
16641        var parent = this.$parent;
16642
16643        this.$broadcast('$destroy');
16644        this.$$destroyed = true;
16645
16646        if (this === $rootScope) {
16647          //Remove handlers attached to window when $rootScope is removed
16648          $browser.$$applicationDestroyed();
16649        }
16650
16651        incrementWatchersCount(this, -this.$$watchersCount);
16652        for (var eventName in this.$$listenerCount) {
16653          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16654        }
16655
16656        // sever all the references to parent scopes (after this cleanup, the current scope should
16657        // not be retained by any of our references and should be eligible for garbage collection)
16658        if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16659        if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16660        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16661        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16662
16663        // Disable listeners, watchers and apply/digest methods
16664        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16665        this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16666        this.$$listeners = {};
16667
16668        // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16669        this.$$nextSibling = null;
16670        cleanUpScope(this);
16671      },
16672
16673      /**
16674       * @ngdoc method
16675       * @name $rootScope.Scope#$eval
16676       * @kind function
16677       *
16678       * @description
16679       * Executes the `expression` on the current scope and returns the result. Any exceptions in
16680       * the expression are propagated (uncaught). This is useful when evaluating Angular
16681       * expressions.
16682       *
16683       * # Example
16684       * ```js
16685           var scope = ng.$rootScope.Scope();
16686           scope.a = 1;
16687           scope.b = 2;
16688
16689           expect(scope.$eval('a+b')).toEqual(3);
16690           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
16691       * ```
16692       *
16693       * @param {(string|function())=} expression An angular expression to be executed.
16694       *
16695       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
16696       *    - `function(scope)`: execute the function with the current `scope` parameter.
16697       *
16698       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16699       * @returns {*} The result of evaluating the expression.
16700       */
16701      $eval: function(expr, locals) {
16702        return $parse(expr)(this, locals);
16703      },
16704
16705      /**
16706       * @ngdoc method
16707       * @name $rootScope.Scope#$evalAsync
16708       * @kind function
16709       *
16710       * @description
16711       * Executes the expression on the current scope at a later point in time.
16712       *
16713       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16714       * that:
16715       *
16716       *   - it will execute after the function that scheduled the evaluation (preferably before DOM
16717       *     rendering).
16718       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16719       *     `expression` execution.
16720       *
16721       * Any exceptions from the execution of the expression are forwarded to the
16722       * {@link ng.$exceptionHandler $exceptionHandler} service.
16723       *
16724       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16725       * will be scheduled. However, it is encouraged to always call code that changes the model
16726       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16727       *
16728       * @param {(string|function())=} expression An angular expression to be executed.
16729       *
16730       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16731       *    - `function(scope)`: execute the function with the current `scope` parameter.
16732       *
16733       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16734       */
16735      $evalAsync: function(expr, locals) {
16736        // if we are outside of an $digest loop and this is the first time we are scheduling async
16737        // task also schedule async auto-flush
16738        if (!$rootScope.$$phase && !asyncQueue.length) {
16739          $browser.defer(function() {
16740            if (asyncQueue.length) {
16741              $rootScope.$digest();
16742            }
16743          });
16744        }
16745
16746        asyncQueue.push({scope: this, expression: expr, locals: locals});
16747      },
16748
16749      $$postDigest: function(fn) {
16750        postDigestQueue.push(fn);
16751      },
16752
16753      /**
16754       * @ngdoc method
16755       * @name $rootScope.Scope#$apply
16756       * @kind function
16757       *
16758       * @description
16759       * `$apply()` is used to execute an expression in angular from outside of the angular
16760       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16761       * Because we are calling into the angular framework we need to perform proper scope life
16762       * cycle of {@link ng.$exceptionHandler exception handling},
16763       * {@link ng.$rootScope.Scope#$digest executing watches}.
16764       *
16765       * ## Life cycle
16766       *
16767       * # Pseudo-Code of `$apply()`
16768       * ```js
16769           function $apply(expr) {
16770             try {
16771               return $eval(expr);
16772             } catch (e) {
16773               $exceptionHandler(e);
16774             } finally {
16775               $root.$digest();
16776             }
16777           }
16778       * ```
16779       *
16780       *
16781       * Scope's `$apply()` method transitions through the following stages:
16782       *
16783       * 1. The {@link guide/expression expression} is executed using the
16784       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
16785       * 2. Any exceptions from the execution of the expression are forwarded to the
16786       *    {@link ng.$exceptionHandler $exceptionHandler} service.
16787       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16788       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16789       *
16790       *
16791       * @param {(string|function())=} exp An angular expression to be executed.
16792       *
16793       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16794       *    - `function(scope)`: execute the function with current `scope` parameter.
16795       *
16796       * @returns {*} The result of evaluating the expression.
16797       */
16798      $apply: function(expr) {
16799        try {
16800          beginPhase('$apply');
16801          try {
16802            return this.$eval(expr);
16803          } finally {
16804            clearPhase();
16805          }
16806        } catch (e) {
16807          $exceptionHandler(e);
16808        } finally {
16809          try {
16810            $rootScope.$digest();
16811          } catch (e) {
16812            $exceptionHandler(e);
16813            throw e;
16814          }
16815        }
16816      },
16817
16818      /**
16819       * @ngdoc method
16820       * @name $rootScope.Scope#$applyAsync
16821       * @kind function
16822       *
16823       * @description
16824       * Schedule the invocation of $apply to occur at a later time. The actual time difference
16825       * varies across browsers, but is typically around ~10 milliseconds.
16826       *
16827       * This can be used to queue up multiple expressions which need to be evaluated in the same
16828       * digest.
16829       *
16830       * @param {(string|function())=} exp An angular expression to be executed.
16831       *
16832       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16833       *    - `function(scope)`: execute the function with current `scope` parameter.
16834       */
16835      $applyAsync: function(expr) {
16836        var scope = this;
16837        expr && applyAsyncQueue.push($applyAsyncExpression);
16838        scheduleApplyAsync();
16839
16840        function $applyAsyncExpression() {
16841          scope.$eval(expr);
16842        }
16843      },
16844
16845      /**
16846       * @ngdoc method
16847       * @name $rootScope.Scope#$on
16848       * @kind function
16849       *
16850       * @description
16851       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16852       * discussion of event life cycle.
16853       *
16854       * The event listener function format is: `function(event, args...)`. The `event` object
16855       * passed into the listener has the following attributes:
16856       *
16857       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16858       *     `$broadcast`-ed.
16859       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16860       *     event propagates through the scope hierarchy, this property is set to null.
16861       *   - `name` - `{string}`: name of the event.
16862       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16863       *     further event propagation (available only for events that were `$emit`-ed).
16864       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16865       *     to true.
16866       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16867       *
16868       * @param {string} name Event name to listen on.
16869       * @param {function(event, ...args)} listener Function to call when the event is emitted.
16870       * @returns {function()} Returns a deregistration function for this listener.
16871       */
16872      $on: function(name, listener) {
16873        var namedListeners = this.$$listeners[name];
16874        if (!namedListeners) {
16875          this.$$listeners[name] = namedListeners = [];
16876        }
16877        namedListeners.push(listener);
16878
16879        var current = this;
16880        do {
16881          if (!current.$$listenerCount[name]) {
16882            current.$$listenerCount[name] = 0;
16883          }
16884          current.$$listenerCount[name]++;
16885        } while ((current = current.$parent));
16886
16887        var self = this;
16888        return function() {
16889          var indexOfListener = namedListeners.indexOf(listener);
16890          if (indexOfListener !== -1) {
16891            namedListeners[indexOfListener] = null;
16892            decrementListenerCount(self, 1, name);
16893          }
16894        };
16895      },
16896
16897
16898      /**
16899       * @ngdoc method
16900       * @name $rootScope.Scope#$emit
16901       * @kind function
16902       *
16903       * @description
16904       * Dispatches an event `name` upwards through the scope hierarchy notifying the
16905       * registered {@link ng.$rootScope.Scope#$on} listeners.
16906       *
16907       * The event life cycle starts at the scope on which `$emit` was called. All
16908       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16909       * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16910       * registered listeners along the way. The event will stop propagating if one of the listeners
16911       * cancels it.
16912       *
16913       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16914       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16915       *
16916       * @param {string} name Event name to emit.
16917       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16918       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16919       */
16920      $emit: function(name, args) {
16921        var empty = [],
16922            namedListeners,
16923            scope = this,
16924            stopPropagation = false,
16925            event = {
16926              name: name,
16927              targetScope: scope,
16928              stopPropagation: function() {stopPropagation = true;},
16929              preventDefault: function() {
16930                event.defaultPrevented = true;
16931              },
16932              defaultPrevented: false
16933            },
16934            listenerArgs = concat([event], arguments, 1),
16935            i, length;
16936
16937        do {
16938          namedListeners = scope.$$listeners[name] || empty;
16939          event.currentScope = scope;
16940          for (i = 0, length = namedListeners.length; i < length; i++) {
16941
16942            // if listeners were deregistered, defragment the array
16943            if (!namedListeners[i]) {
16944              namedListeners.splice(i, 1);
16945              i--;
16946              length--;
16947              continue;
16948            }
16949            try {
16950              //allow all listeners attached to the current scope to run
16951              namedListeners[i].apply(null, listenerArgs);
16952            } catch (e) {
16953              $exceptionHandler(e);
16954            }
16955          }
16956          //if any listener on the current scope stops propagation, prevent bubbling
16957          if (stopPropagation) {
16958            event.currentScope = null;
16959            return event;
16960          }
16961          //traverse upwards
16962          scope = scope.$parent;
16963        } while (scope);
16964
16965        event.currentScope = null;
16966
16967        return event;
16968      },
16969
16970
16971      /**
16972       * @ngdoc method
16973       * @name $rootScope.Scope#$broadcast
16974       * @kind function
16975       *
16976       * @description
16977       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16978       * registered {@link ng.$rootScope.Scope#$on} listeners.
16979       *
16980       * The event life cycle starts at the scope on which `$broadcast` was called. All
16981       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16982       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16983       * scope and calls all registered listeners along the way. The event cannot be canceled.
16984       *
16985       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16986       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16987       *
16988       * @param {string} name Event name to broadcast.
16989       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16990       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16991       */
16992      $broadcast: function(name, args) {
16993        var target = this,
16994            current = target,
16995            next = target,
16996            event = {
16997              name: name,
16998              targetScope: target,
16999              preventDefault: function() {
17000                event.defaultPrevented = true;
17001              },
17002              defaultPrevented: false
17003            };
17004
17005        if (!target.$$listenerCount[name]) return event;
17006
17007        var listenerArgs = concat([event], arguments, 1),
17008            listeners, i, length;
17009
17010        //down while you can, then up and next sibling or up and next sibling until back at root
17011        while ((current = next)) {
17012          event.currentScope = current;
17013          listeners = current.$$listeners[name] || [];
17014          for (i = 0, length = listeners.length; i < length; i++) {
17015            // if listeners were deregistered, defragment the array
17016            if (!listeners[i]) {
17017              listeners.splice(i, 1);
17018              i--;
17019              length--;
17020              continue;
17021            }
17022
17023            try {
17024              listeners[i].apply(null, listenerArgs);
17025            } catch (e) {
17026              $exceptionHandler(e);
17027            }
17028          }
17029
17030          // Insanity Warning: scope depth-first traversal
17031          // yes, this code is a bit crazy, but it works and we have tests to prove it!
17032          // this piece should be kept in sync with the traversal in $digest
17033          // (though it differs due to having the extra check for $$listenerCount)
17034          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
17035              (current !== target && current.$$nextSibling)))) {
17036            while (current !== target && !(next = current.$$nextSibling)) {
17037              current = current.$parent;
17038            }
17039          }
17040        }
17041
17042        event.currentScope = null;
17043        return event;
17044      }
17045    };
17046
17047    var $rootScope = new Scope();
17048
17049    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
17050    var asyncQueue = $rootScope.$$asyncQueue = [];
17051    var postDigestQueue = $rootScope.$$postDigestQueue = [];
17052    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
17053
17054    return $rootScope;
17055
17056
17057    function beginPhase(phase) {
17058      if ($rootScope.$$phase) {
17059        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
17060      }
17061
17062      $rootScope.$$phase = phase;
17063    }
17064
17065    function clearPhase() {
17066      $rootScope.$$phase = null;
17067    }
17068
17069    function incrementWatchersCount(current, count) {
17070      do {
17071        current.$$watchersCount += count;
17072      } while ((current = current.$parent));
17073    }
17074
17075    function decrementListenerCount(current, count, name) {
17076      do {
17077        current.$$listenerCount[name] -= count;
17078
17079        if (current.$$listenerCount[name] === 0) {
17080          delete current.$$listenerCount[name];
17081        }
17082      } while ((current = current.$parent));
17083    }
17084
17085    /**
17086     * function used as an initial value for watchers.
17087     * because it's unique we can easily tell it apart from other values
17088     */
17089    function initWatchVal() {}
17090
17091    function flushApplyAsync() {
vendor: 6,136 bytes, lines 17092-17252
17092      while (applyAsyncQueue.length) {
17093        try {
17094          applyAsyncQueue.shift()();
17095        } catch (e) {
17096          $exceptionHandler(e);
17097        }
17098      }
17099      applyAsyncId = null;
17100    }
17101
17102    function scheduleApplyAsync() {
17103      if (applyAsyncId === null) {
17104        applyAsyncId = $browser.defer(function() {
17105          $rootScope.$apply(flushApplyAsync);
17106        });
17107      }
17108    }
17109  }];
17110}
17111
17112/**
17113 * @ngdoc service
17114 * @name $rootElement
17115 *
17116 * @description
17117 * The root element of Angular application. This is either the element where {@link
17118 * ng.directive:ngApp ngApp} was declared or the element passed into
17119 * {@link angular.bootstrap}. The element represents the root element of application. It is also the
17120 * location where the application's {@link auto.$injector $injector} service gets
17121 * published, and can be retrieved using `$rootElement.injector()`.
17122 */
17123
17124
17125// the implementation is in angular.bootstrap
17126
17127/**
17128 * @description
17129 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
17130 */
17131function $$SanitizeUriProvider() {
17132  var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
17133    imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
17134
17135  /**
17136   * @description
17137   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
17138   * urls during a[href] sanitization.
17139   *
17140   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
17141   *
17142   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
17143   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
17144   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
17145   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
17146   *
17147   * @param {RegExp=} regexp New regexp to whitelist urls with.
17148   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
17149   *    chaining otherwise.
17150   */
17151  this.aHrefSanitizationWhitelist = function(regexp) {
17152    if (isDefined(regexp)) {
17153      aHrefSanitizationWhitelist = regexp;
17154      return this;
17155    }
17156    return aHrefSanitizationWhitelist;
17157  };
17158
17159
17160  /**
17161   * @description
17162   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
17163   * urls during img[src] sanitization.
17164   *
17165   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
17166   *
17167   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
17168   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
17169   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
17170   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
17171   *
17172   * @param {RegExp=} regexp New regexp to whitelist urls with.
17173   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
17174   *    chaining otherwise.
17175   */
17176  this.imgSrcSanitizationWhitelist = function(regexp) {
17177    if (isDefined(regexp)) {
17178      imgSrcSanitizationWhitelist = regexp;
17179      return this;
17180    }
17181    return imgSrcSanitizationWhitelist;
17182  };
17183
17184  this.$get = function() {
17185    return function sanitizeUri(uri, isImage) {
17186      var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
17187      var normalizedVal;
17188      normalizedVal = urlResolve(uri).href;
17189      if (normalizedVal !== '' && !normalizedVal.match(regex)) {
17190        return 'unsafe:' + normalizedVal;
17191      }
17192      return uri;
17193    };
17194  };
17195}
17196
17197/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17198 *     Any commits to this file should be reviewed with security in mind.  *
17199 *   Changes to this file can potentially create security vulnerabilities. *
17200 *          An approval from 2 Core members with history of modifying      *
17201 *                         this file is required.                          *
17202 *                                                                         *
17203 *  Does the change somehow allow for arbitrary javascript to be executed? *
17204 *    Or allows for someone to change the prototype of built-in objects?   *
17205 *     Or gives undesired access to variables likes document or window?    *
17206 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
17207
17208var $sceMinErr = minErr('$sce');
17209
17210var SCE_CONTEXTS = {
17211  HTML: 'html',
17212  CSS: 'css',
17213  URL: 'url',
17214  // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
17215  // url.  (e.g. ng-include, script src, templateUrl)
17216  RESOURCE_URL: 'resourceUrl',
17217  JS: 'js'
17218};
17219
17220// Helper functions follow.
17221
17222function adjustMatcher(matcher) {
17223  if (matcher === 'self') {
17224    return matcher;
17225  } else if (isString(matcher)) {
17226    // Strings match exactly except for 2 wildcards - '*' and '**'.
17227    // '*' matches any character except those from the set ':/.?&'.
17228    // '**' matches any character (like .* in a RegExp).
17229    // More than 2 *'s raises an error as it's ill defined.
17230    if (matcher.indexOf('***') > -1) {
17231      throw $sceMinErr('iwcard',
17232          'Illegal sequence *** in string matcher.  String: {0}', matcher);
17233    }
17234    matcher = escapeForRegexp(matcher).
17235                  replace('\\*\\*', '.*').
17236                  replace('\\*', '[^:/.?&;]*');
17237    return new RegExp('^' + matcher + '$');
17238  } else if (isRegExp(matcher)) {
17239    // The only other type of matcher allowed is a Regexp.
17240    // Match entire URL / disallow partial matches.
17241    // Flags are reset (i.e. no global, ignoreCase or multiline)
17242    return new RegExp('^' + matcher.source + '$');
17243  } else {
17244    throw $sceMinErr('imatcher',
17245        'Matchers may only be "self", string patterns or RegExp objects');
17246  }
17247}
17248
17249
17250function adjustMatchers(matchers) {
17251  var adjustedMatchers = [];
17252  if (isDefined(matchers)) {
17253    forEach(matchers, function(matcher) {
17254      adjustedMatchers.push(adjustMatcher(matcher));
17255    });
17256  }
17257  return adjustedMatchers;
17258}
17259
17260
17261/**
17262 * @ngdoc service
17263 * @name $sceDelegate
17264 * @kind function
17265 *
17266 * @description
17267 *
17268 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
17269 * Contextual Escaping (SCE)} services to AngularJS.
17270 *
17271 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
17272 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
17273 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
17274 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
17275 * work because `$sce` delegates to `$sceDelegate` for these operations.
17276 *
17277 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
17278 *
17279 * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
17280 * can override it completely to change the behavior of `$sce`, the common case would
17281 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
17282 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
17283 * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
17284 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
17285 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
17286 */
17287
17288/**
17289 * @ngdoc provider
17290 * @name $sceDelegateProvider
17291 * @description
17292 *
17293 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
17294 * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
17295 * that the URLs used for sourcing Angular templates are safe.  Refer {@link
17296 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
17297 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
17298 *
17299 * For the general details about this service in Angular, read the main page for {@link ng.$sce
17300 * Strict Contextual Escaping (SCE)}.
17301 *
17302 * **Example**:  Consider the following case. <a name="example"></a>
17303 *
17304 * - your app is hosted at url `http://myapp.example.com/`
17305 * - but some of your templates are hosted on other domains you control such as
17306 *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
17307 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
17308 *
17309 * Here is what a secure configuration for this scenario might look like:
17310 *
17311 * ```
17312 *  angular.module('myApp', []).config(function($sceDelegateProvider) {
17313 *    $sceDelegateProvider.resourceUrlWhitelist([
17314 *      // Allow same origin resource loads.
17315 *      'self',
17316 *      // Allow loading from our assets domain.  Notice the difference between * and **.
17317 *      'http://srv*.assets.example.com/**'
17318 *    ]);
17319 *
17320 *    // The blacklist overrides the whitelist so the open redirect here is blocked.
17321 *    $sceDelegateProvider.resourceUrlBlacklist([
17322 *      'http://myapp.example.com/clickThru**'
17323 *    ]);
17324 *  });
17325 * ```
17326 */
17327
17328function $SceDelegateProvider() {
17329  this.SCE_CONTEXTS = SCE_CONTEXTS;
17330
17331  // Resource URLs can also be trusted by policy.
17332  var resourceUrlWhitelist = ['self'],
17333      resourceUrlBlacklist = [];
17334
17335  /**
17336   * @ngdoc method
17337   * @name $sceDelegateProvider#resourceUrlWhitelist
17338   * @kind function
17339   *
17340   * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
17341   *     provided.  This must be an array or null.  A snapshot of this array is used so further
17342   *     changes to the array are ignored.
17343   *
17344   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
17345   *     allowed in this array.
17346   *
17347   *     Note: **an empty whitelist array will block all URLs**!
17348   *
17349   * @return {Array} the currently set whitelist array.
17350   *
17351   * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
17352   * same origin resource requests.
17353   *
17354   * @description
17355   * Sets/Gets the whitelist of trusted resource URLs.
17356   */
17357  this.resourceUrlWhitelist = function(value) {
17358    if (arguments.length) {
17359      resourceUrlWhitelist = adjustMatchers(value);
17360    }
17361    return resourceUrlWhitelist;
17362  };
17363
17364  /**
17365   * @ngdoc method
17366   * @name $sceDelegateProvider#resourceUrlBlacklist
17367   * @kind function
17368   *
17369   * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
17370   *     provided.  This must be an array or null.  A snapshot of this array is used so further
17371   *     changes to the array are ignored.
17372   *
17373   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
17374   *     allowed in this array.
17375   *
17376   *     The typical usage for the blacklist is to **block
17377   *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
17378   *     these would otherwise be trusted but actually return content from the redirected domain.
17379   *
17380   *     Finally, **the blacklist overrides the whitelist** and has the final say.
17381   *
17382   * @return {Array} the currently set blacklist array.
17383   *
17384   * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
17385   * is no blacklist.)
17386   *
17387   * @description
17388   * Sets/Gets the blacklist of trusted resource URLs.
17389   */
17390
17391  this.resourceUrlBlacklist = function(value) {
17392    if (arguments.length) {
17393      resourceUrlBlacklist = adjustMatchers(value);
17394    }
17395    return resourceUrlBlacklist;
17396  };
17397
17398  this.$get = ['$injector', function($injector) {
17399
17400    var htmlSanitizer = function htmlSanitizer(html) {
17401      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
17402    };
17403
17404    if ($injector.has('$sanitize')) {
17405      htmlSanitizer = $injector.get('$sanitize');
17406    }
17407
17408
17409    function matchUrl(matcher, parsedUrl) {
17410      if (matcher === 'self') {
17411        return urlIsSameOrigin(parsedUrl);
17412      } else {
17413        // definitely a regex.  See adjustMatchers()
17414        return !!matcher.exec(parsedUrl.href);
17415      }
17416    }
17417
17418    function isResourceUrlAllowedByPolicy(url) {
17419      var parsedUrl = urlResolve(url.toString());
17420      var i, n, allowed = false;
17421      // Ensure that at least one item from the whitelist allows this url.
17422      for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
17423        if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
17424          allowed = true;
17425          break;
17426        }
17427      }
17428      if (allowed) {
17429        // Ensure that no item from the blacklist blocked this url.
17430        for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
17431          if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
17432            allowed = false;
17433            break;
17434          }
17435        }
17436      }
17437      return allowed;
17438    }
17439
17440    function generateHolderType(Base) {
17441      var holderType = function TrustedValueHolderType(trustedValue) {
17442        this.$$unwrapTrustedValue = function() {
17443          return trustedValue;
17444        };
17445      };
17446      if (Base) {
17447        holderType.prototype = new Base();
17448      }
17449      holderType.prototype.valueOf = function sceValueOf() {
17450        return this.$$unwrapTrustedValue();
17451      };
17452      holderType.prototype.toString = function sceToString() {
17453        return this.$$unwrapTrustedValue().toString();
17454      };
17455      return holderType;
17456    }
17457
17458    var trustedValueHolderBase = generateHolderType(),
17459        byType = {};
17460
17461    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
17462    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
17463    byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
17464    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
17465    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
17466
17467    /**
17468     * @ngdoc method
17469     * @name $sceDelegate#trustAs
17470     *
17471     * @description
17472     * Returns an object that is trusted by angular for use in specified strict
17473     * contextual escaping contexts (such as ng-bind-html, ng-include, any src
17474     * attribute interpolation, any dom event binding attribute interpolation
17475     * such as for onclick,  etc.) that uses the provided value.
17476     * See {@link ng.$sce $sce} for enabling strict contextual escaping.
17477     *
17478     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17479     *   resourceUrl, html, js and css.
17480     * @param {*} value The value that that should be considered trusted/safe.
17481     * @returns {*} A value that can be used to stand in for the provided `value` in places
17482     * where Angular expects a $sce.trustAs() return value.
17483     */
17484    function trustAs(type, trustedValue) {
17485      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
17486      if (!Constructor) {
17487        throw $sceMinErr('icontext',
vendor: 5,338 bytes, lines 17488-17616
17488            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
17489            type, trustedValue);
17490      }
17491      if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
17492        return trustedValue;
17493      }
17494      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
17495      // mutable objects, we ensure here that the value passed in is actually a string.
17496      if (typeof trustedValue !== 'string') {
17497        throw $sceMinErr('itype',
17498            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
17499            type);
17500      }
17501      return new Constructor(trustedValue);
17502    }
17503
17504    /**
17505     * @ngdoc method
17506     * @name $sceDelegate#valueOf
17507     *
17508     * @description
17509     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
17510     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
17511     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
17512     *
17513     * If the passed parameter is not a value that had been returned by {@link
17514     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
17515     *
17516     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
17517     *      call or anything else.
17518     * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
17519     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
17520     *     `value` unchanged.
17521     */
17522    function valueOf(maybeTrusted) {
17523      if (maybeTrusted instanceof trustedValueHolderBase) {
17524        return maybeTrusted.$$unwrapTrustedValue();
17525      } else {
17526        return maybeTrusted;
17527      }
17528    }
17529
17530    /**
17531     * @ngdoc method
17532     * @name $sceDelegate#getTrusted
17533     *
17534     * @description
17535     * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
17536     * returns the originally supplied value if the queried context type is a supertype of the
17537     * created type.  If this condition isn't satisfied, throws an exception.
17538     *
17539     * <div class="alert alert-danger">
17540     * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
17541     * (XSS) vulnerability in your application.
17542     * </div>
17543     *
17544     * @param {string} type The kind of context in which this value is to be used.
17545     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
17546     *     `$sceDelegate.trustAs`} call.
17547     * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
17548     *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
17549     */
17550    function getTrusted(type, maybeTrusted) {
17551      if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
17552        return maybeTrusted;
17553      }
17554      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
17555      if (constructor && maybeTrusted instanceof constructor) {
17556        return maybeTrusted.$$unwrapTrustedValue();
17557      }
17558      // If we get here, then we may only take one of two actions.
17559      // 1. sanitize the value for the requested type, or
17560      // 2. throw an exception.
17561      if (type === SCE_CONTEXTS.RESOURCE_URL) {
17562        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
17563          return maybeTrusted;
17564        } else {
17565          throw $sceMinErr('insecurl',
17566              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
17567              maybeTrusted.toString());
17568        }
17569      } else if (type === SCE_CONTEXTS.HTML) {
17570        return htmlSanitizer(maybeTrusted);
17571      }
17572      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
17573    }
17574
17575    return { trustAs: trustAs,
17576             getTrusted: getTrusted,
17577             valueOf: valueOf };
17578  }];
17579}
17580
17581
17582/**
17583 * @ngdoc provider
17584 * @name $sceProvider
17585 * @description
17586 *
17587 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
17588 * -   enable/disable Strict Contextual Escaping (SCE) in a module
17589 * -   override the default implementation with a custom delegate
17590 *
17591 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
17592 */
17593
17594/* jshint maxlen: false*/
17595
17596/**
17597 * @ngdoc service
17598 * @name $sce
17599 * @kind function
17600 *
17601 * @description
17602 *
17603 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
17604 *
17605 * # Strict Contextual Escaping
17606 *
17607 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
17608 * contexts to result in a value that is marked as safe to use for that context.  One example of
17609 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
17610 * to these contexts as privileged or SCE contexts.
17611 *
17612 * As of version 1.2, Angular ships with SCE enabled by default.
17613 *
17614 * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
17615 * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
17616 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
17617 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
17618 * to the top of your HTML document.
17619 *
17620 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
17621 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
17622 *
17623 * Here's an example of a binding in a privileged context:
17624 *
17625 * ```
17626 * <input ng-model="userHtml" aria-label="User input">
17627 * <div ng-bind-html="userHtml"></div>
17628 * ```
17629 *
17630 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
17631 * disabled, this application allows the user to render arbitrary HTML into the DIV.
17632 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
17633 * bindings.  (HTML is just one example of a context where rendering user controlled input creates
17634 * security vulnerabilities.)
17635 *
17636 * For the case of HTML, you might use a library, either on the client side, or on the server side,
17637 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
17638 *
17639 * How would you ensure that every place that used these types of bindings was bound to a value that
17640 * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
17641 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
17642 * properties/fields and forgot to update the binding to the sanitized value?
17643 *
17644 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
17645 * determine that something explicitly says it's safe to use a value for binding in that
17646 * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
17647 * for those values that you can easily tell are safe - because they were received from your server,
17648 * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
17649 * allowing only the files in a specific directory to do this.  Ensuring that the internal API
17650 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
17651 *
17652 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
17653 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
17654 * obtain values that will be accepted by SCE / privileged contexts.
17655 *
17656 *
17657 * ## How does it work?
17658 *
17659 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
17660 * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
17661 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
17662 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
17663 *
17664 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
17665 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
17666 * simplified):
17667 *
17668 * ```
17669 * var ngBindHtmlDirective = ['$sce', function($sce) {
17670 *   return function(scope, element, attr) {
17671 *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17672 *       element.html(value || '');
17673 *     });
17674 *   };
17675 * }];
17676 * ```
17677 *
17678 * ## Impact on loading templates
17679 *
17680 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
17681 * `templateUrl`'s specified by {@link guide/directive directives}.
17682 *
17683 * By default, Angular only loads templates from the same domain and protocol as the application
17684 * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
17685 * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
17686 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17687 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
17688 *
17689 * *Please note*:
17690 * The browser's
17691 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
17692 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
17693 * policy apply in addition to this and may further restrict whether the template is successfully
17694 * loaded.  This means that without the right CORS policy, loading templates from a different domain
17695 * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
17696 * browsers.
17697 *
17698 * ## This feels like too much overhead
17699 *
17700 * It's important to remember that SCE only applies to interpolation expressions.
17701 *
17702 * If your expressions are constant literals, they're automatically trusted and you don't need to
17703 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
17704 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
17705 *
17706 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
17707 * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
17708 *
17709 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
17710 * templates in `ng-include` from your application's domain without having to even know about SCE.
17711 * It blocks loading templates from other domains or loading templates over http from an https
17712 * served document.  You can change these by setting your own custom {@link
17713 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
17714 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
17715 *
17716 * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
17717 * application that's secure and can be audited to verify that with much more ease than bolting
17718 * security onto an application later.
17719 *
17720 * <a name="contexts"></a>
17721 * ## What trusted context types are supported?
17722 *
17723 * | Context             | Notes          |
17724 * |---------------------|----------------|
17725 * | `$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. |
17726 * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
17727 * | `$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. |
17728 * | `$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. |
17729 * | `$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. |
17730 *
17731 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
17732 *
17733 *  Each element in these arrays must be one of the following:
17734 *
17735 *  - **'self'**
17736 *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
17737 *      domain** as the application document using the **same protocol**.
17738 *  - **String** (except the special value `'self'`)
17739 *    - The string is matched against the full *normalized / absolute URL* of the resource
17740 *      being tested (substring matches are not good enough.)
17741 *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
17742 *      match themselves.
17743 *    - `*`: matches zero or more occurrences of any character other than one of the following 6
17744 *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
17745 *      in a whitelist.
17746 *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
17747 *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
17748 *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17749 *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
17750 *      http://foo.example.com/templates/**).
17751 *  - **RegExp** (*see caveat below*)
17752 *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
17753 *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
17754 *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17755 *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
17756 *      small number of cases.  A `.` character in the regex used when matching the scheme or a
17757 *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
17758 *      is highly recommended to use the string patterns and only fall back to regular expressions
17759 *      as a last resort.
17760 *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
17761 *      matched against the **entire** *normalized / absolute URL* of the resource being tested
17762 *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
17763 *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17764 *    - If you are generating your JavaScript from some other templating engine (not
17765 *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17766 *      remember to escape your regular expression (and be aware that you might need more than
17767 *      one level of escaping depending on your templating engine and the way you interpolated
17768 *      the value.)  Do make use of your platform's escaping mechanism as it might be good
17769 *      enough before coding your own.  E.g. Ruby has
17770 *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17771 *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17772 *      Javascript lacks a similar built in function for escaping.  Take a look at Google
17773 *      Closure library's [goog.string.regExpEscape(s)](
17774 *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17775 *
17776 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17777 *
17778 * ## Show me an example using SCE.
17779 *
17780 * <example module="mySceApp" deps="angular-sanitize.js">
17781 * <file name="index.html">
17782 *   <div ng-controller="AppController as myCtrl">
17783 *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17784 *     <b>User comments</b><br>
17785 *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17786 *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
17787 *     exploit.
17788 *     <div class="well">
17789 *       <div ng-repeat="userComment in myCtrl.userComments">
17790 *         <b>{{userComment.name}}</b>:
17791 *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17792 *         <br>
17793 *       </div>
17794 *     </div>
17795 *   </div>
17796 * </file>
17797 *
17798 * <file name="script.js">
17799 *   angular.module('mySceApp', ['ngSanitize'])
17800 *     .controller('AppController', ['$http', '$templateCache', '$sce',
17801 *       function($http, $templateCache, $sce) {
17802 *         var self = this;
17803 *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17804 *           self.userComments = userComments;
17805 *         });
17806 *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
17807 *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17808 *             'sanitization.&quot;">Hover over this text.</span>');
17809 *       }]);
17810 * </file>
17811 *
17812 * <file name="test_data.json">
17813 * [
17814 *   { "name": "Alice",
17815 *     "htmlComment":
17816 *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17817 *   },
17818 *   { "name": "Bob",
17819 *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
17820 *   }
17821 * ]
17822 * </file>
17823 *
17824 * <file name="protractor.js" type="protractor">
17825 *   describe('SCE doc demo', function() {
17826 *     it('should sanitize untrusted values', function() {
17827 *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17828 *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
17829 *     });
17830 *
17831 *     it('should NOT sanitize explicitly trusted values', function() {
17832 *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17833 *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17834 *           'sanitization.&quot;">Hover over this text.</span>');
17835 *     });
17836 *   });
17837 * </file>
17838 * </example>
17839 *
17840 *
17841 *
17842 * ## Can I disable SCE completely?
17843 *
17844 * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
17845 * for little coding overhead.  It will be much harder to take an SCE disabled application and
17846 * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
17847 * for cases where you have a lot of existing code that was written before SCE was introduced and
17848 * you're migrating them a module at a time.
17849 *
17850 * That said, here's how you can completely disable SCE:
17851 *
17852 * ```
17853 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17854 *   // Completely disable SCE.  For demonstration purposes only!
17855 *   // Do not use in new projects.
17856 *   $sceProvider.enabled(false);
17857 * });
17858 * ```
17859 *
17860 */
17861/* jshint maxlen: 100 */
17862
17863function $SceProvider() {
17864  var enabled = true;
17865
17866  /**
17867   * @ngdoc method
17868   * @name $sceProvider#enabled
17869   * @kind function
17870   *
17871   * @param {boolean=} value If provided, then enables/disables SCE.
17872   * @return {boolean} true if SCE is enabled, false otherwise.
17873   *
17874   * @description
17875   * Enables/disables SCE and returns the current value.
17876   */
17877  this.enabled = function(value) {
17878    if (arguments.length) {
17879      enabled = !!value;
17880    }
17881    return enabled;
17882  };
17883
17884
17885  /* Design notes on the default implementation for SCE.
17886   *
17887   * The API contract for the SCE delegate
17888   * -------------------------------------
17889   * The SCE delegate object must provide the following 3 methods:
17890   *
17891   * - trustAs(contextEnum, value)
17892   *     This method is used to tell the SCE service that the provided value is OK to use in the
17893   *     contexts specified by contextEnum.  It must return an object that will be accepted by
17894   *     getTrusted() for a compatible contextEnum and return this value.
17895   *
17896   * - valueOf(value)
17897   *     For values that were not produced by trustAs(), return them as is.  For values that were
17898   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
17899   *     trustAs is wrapping the given values into some type, this operation unwraps it when given
17900   *     such a value.
17901   *
17902   * - getTrusted(contextEnum, value)
17903   *     This function should return the a value that is safe to use in the context specified by
17904   *     contextEnum or throw and exception otherwise.
17905   *
17906   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17907   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
17908   * instance, an implementation could maintain a registry of all trusted objects by context.  In
17909   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
17910   * return the same object passed in if it was found in the registry under a compatible context or
17911   * throw an exception otherwise.  An implementation might only wrap values some of the time based
17912   * on some criteria.  getTrusted() might return a value and not throw an exception for special
17913   * constants or objects even if not wrapped.  All such implementations fulfill this contract.
17914   *
17915   *
17916   * A note on the inheritance model for SCE contexts
17917   * ------------------------------------------------
17918   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
17919   * is purely an implementation details.
17920   *
17921   * The contract is simply this:
17922   *
17923   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17924   *     will also succeed.
17925   *
17926   * Inheritance happens to capture this in a natural way.  In some future, we
17927   * may not use inheritance anymore.  That is OK because no code outside of
17928   * sce.js and sceSpecs.js would need to be aware of this detail.
17929   */
17930
17931  this.$get = ['$parse', '$sceDelegate', function(
17932                $parse,   $sceDelegate) {
17933    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
17934    // the "expression(javascript expression)" syntax which is insecure.
17935    if (enabled && msie < 8) {
17936      throw $sceMinErr('iequirks',
17937        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17938        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17939        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
17940    }
17941
17942    var sce = shallowCopy(SCE_CONTEXTS);
17943
17944    /**
17945     * @ngdoc method
17946     * @name $sce#isEnabled
17947     * @kind function
17948     *
17949     * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
17950     * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17951     *
17952     * @description
17953     * Returns a boolean indicating if SCE is enabled.
17954     */
17955    sce.isEnabled = function() {
17956      return enabled;
17957    };
17958    sce.trustAs = $sceDelegate.trustAs;
17959    sce.getTrusted = $sceDelegate.getTrusted;
17960    sce.valueOf = $sceDelegate.valueOf;
17961
17962    if (!enabled) {
17963      sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17964      sce.valueOf = identity;
17965    }
17966
17967    /**
17968     * @ngdoc method
17969     * @name $sce#parseAs
17970     *
17971     * @description
17972     * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
17973     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
17974     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17975     * *result*)}
17976     *
17977     * @param {string} type The kind of SCE context in which this result will be used.
17978     * @param {string} expression String expression to compile.
17979     * @returns {function(context, locals)} a function which represents the compiled expression:
17980     *
17981     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17982     *      are evaluated against (typically a scope object).
17983     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17984     *      `context`.
17985     */
17986    sce.parseAs = function sceParseAs(type, expr) {
17987      var parsed = $parse(expr);
17988      if (parsed.literal && parsed.constant) {
17989        return parsed;
17990      } else {
17991        return $parse(expr, function(value) {
17992          return sce.getTrusted(type, value);
17993        });
17994      }
17995    };
17996
17997    /**
17998     * @ngdoc method
17999     * @name $sce#trustAs
18000     *
18001     * @description
18002     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
18003     * returns an object that is trusted by angular for use in specified strict contextual
18004     * escaping contexts (such as ng-bind-html, ng-include, any src attribute
18005     * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
18006     * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
18007     * escaping.
18008     *
18009     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
18010     *   resourceUrl, html, js and css.
18011     * @param {*} value The value that that should be considered trusted/safe.
18012     * @returns {*} A value that can be used to stand in for the provided `value` in places
18013     * where Angular expects a $sce.trustAs() return value.
18014     */
18015
18016    /**
18017     * @ngdoc method
18018     * @name $sce#trustAsHtml
18019     *
18020     * @description
18021     * Shorthand method.  `$sce.trustAsHtml(value)` →
18022     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
18023     *
18024     * @param {*} value The value to trustAs.
18025     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
18026     *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
18027     *     only accept expressions that are either literal constants or are the
18028     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
18029     */
18030
18031    /**
18032     * @ngdoc method
18033     * @name $sce#trustAsUrl
18034     *
18035     * @description
18036     * Shorthand method.  `$sce.trustAsUrl(value)` →
18037     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
18038     *
18039     * @param {*} value The value to trustAs.
18040     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
18041     *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
18042     *     only accept expressions that are either literal constants or are the
18043     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
18044     */
18045
18046    /**
18047     * @ngdoc method
18048     * @name $sce#trustAsResourceUrl
18049     *
18050     * @description
18051     * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
18052     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
18053     *
18054     * @param {*} value The value to trustAs.
18055     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
18056     *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
18057     *     only accept expressions that are either literal constants or are the return
18058     *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
18059     */
18060
18061    /**
18062     * @ngdoc method
18063     * @name $sce#trustAsJs
18064     *
18065     * @description
18066     * Shorthand method.  `$sce.trustAsJs(value)` →
18067     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
18068     *
18069     * @param {*} value The value to trustAs.
18070     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
18071     *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
18072     *     only accept expressions that are either literal constants or are the
18073     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
18074     */
18075
18076    /**
18077     * @ngdoc method
18078     * @name $sce#getTrusted
18079     *
18080     * @description
18081     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
18082     * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
18083     * originally supplied value if the queried context type is a supertype of the created type.
18084     * If this condition isn't satisfied, throws an exception.
18085     *
18086     * @param {string} type The kind of context in which this value is to be used.
18087     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
18088     *                         call.
18089     * @returns {*} The value the was originally provided to
18090     *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
18091     *              Otherwise, throws an exception.
18092     */
18093
18094    /**
18095     * @ngdoc method
18096     * @name $sce#getTrustedHtml
18097     *
18098     * @description
18099     * Shorthand method.  `$sce.getTrustedHtml(value)` →
18100     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
18101     *
18102     * @param {*} value The value to pass to `$sce.getTrusted`.
18103     * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
18104     */
18105
18106    /**
18107     * @ngdoc method
18108     * @name $sce#getTrustedCss
18109     *
18110     * @description
18111     * Shorthand method.  `$sce.getTrustedCss(value)` →
18112     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
18113     *
18114     * @param {*} value The value to pass to `$sce.getTrusted`.
18115     * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
18116     */
18117
18118    /**
18119     * @ngdoc method
18120     * @name $sce#getTrustedUrl
18121     *
18122     * @description
18123     * Shorthand method.  `$sce.getTrustedUrl(value)` →
18124     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
18125     *
18126     * @param {*} value The value to pass to `$sce.getTrusted`.
18127     * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
18128     */
18129
18130    /**
18131     * @ngdoc method
18132     * @name $sce#getTrustedResourceUrl
18133     *
18134     * @description
18135     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
18136     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
18137     *
18138     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
18139     * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
18140     */
18141
18142    /**
18143     * @ngdoc method
18144     * @name $sce#getTrustedJs
18145     *
18146     * @description
18147     * Shorthand method.  `$sce.getTrustedJs(value)` →
18148     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
18149     *
18150     * @param {*} value The value to pass to `$sce.getTrusted`.
18151     * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
18152     */
18153
18154    /**
18155     * @ngdoc method
18156     * @name $sce#parseAsHtml
18157     *
18158     * @description
18159     * Shorthand method.  `$sce.parseAsHtml(expression string)` →
18160     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
18161     *
18162     * @param {string} expression String expression to compile.
18163     * @returns {function(context, locals)} a function which represents the compiled expression:
18164     *
18165     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
18166     *      are evaluated against (typically a scope object).
18167     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
18168     *      `context`.
18169     */
18170
18171    /**
18172     * @ngdoc method
18173     * @name $sce#parseAsCss
18174     *
18175     * @description
18176     * Shorthand method.  `$sce.parseAsCss(value)` →
18177     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
18178     *
18179     * @param {string} expression String expression to compile.
18180     * @returns {function(context, locals)} a function which represents the compiled expression:
18181     *
18182     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
18183     *      are evaluated against (typically a scope object).
18184     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
18185     *      `context`.
18186     */
18187
18188    /**
18189     * @ngdoc method
18190     * @name $sce#parseAsUrl
18191     *
18192     * @description
18193     * Shorthand method.  `$sce.parseAsUrl(value)` →
18194     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
18195     *
18196     * @param {string} expression String expression to compile.
18197     * @returns {function(context, locals)} a function which represents the compiled expression:
18198     *
18199     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
18200     *      are evaluated against (typically a scope object).
18201     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
18202     *      `context`.
18203     */
18204
18205    /**
18206     * @ngdoc method
18207     * @name $sce#parseAsResourceUrl
18208     *
18209     * @description
18210     * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
18211     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
18212     *
18213     * @param {string} expression String expression to compile.
18214     * @returns {function(context, locals)} a function which represents the compiled expression:
18215     *
18216     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
18217     *      are evaluated against (typically a scope object).
18218     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
18219     *      `context`.
18220     */
18221
18222    /**
18223     * @ngdoc method
18224     * @name $sce#parseAsJs
18225     *
18226     * @description
18227     * Shorthand method.  `$sce.parseAsJs(value)` →
18228     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
18229     *
18230     * @param {string} expression String expression to compile.
18231     * @returns {function(context, locals)} a function which represents the compiled expression:
18232     *
18233     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
18234     *      are evaluated against (typically a scope object).
18235     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
18236     *      `context`.
18237     */
18238
18239    // Shorthand delegations.
18240    var parse = sce.parseAs,
18241        getTrusted = sce.getTrusted,
18242        trustAs = sce.trustAs;
18243
18244    forEach(SCE_CONTEXTS, function(enumValue, name) {
18245      var lName = lowercase(name);
18246      sce[camelCase("parse_as_" + lName)] = function(expr) {
18247        return parse(enumValue, expr);
18248      };
18249      sce[camelCase("get_trusted_" + lName)] = function(value) {
18250        return getTrusted(enumValue, value);
18251      };
18252      sce[camelCase("trust_as_" + lName)] = function(value) {
18253        return trustAs(enumValue, value);
18254      };
18255    });
18256
18257    return sce;
18258  }];
18259}
18260
18261/**
18262 * !!! This is an undocumented "private" service !!!
18263 *
18264 * @name $sniffer
18265 * @requires $window
18266 * @requires $document
18267 *
18268 * @property {boolean} history Does the browser support html5 history api ?
18269 * @property {boolean} transitions Does the browser support CSS transition events ?
18270 * @property {boolean} animations Does the browser support CSS animation events ?
18271 *
18272 * @description
18273 * This is very simple implementation of testing browser's features.
18274 */
18275function $SnifferProvider() {
18276  this.$get = ['$window', '$document', function($window, $document) {
18277    var eventSupport = {},
18278        android =
18279          toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
18280        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
18281        document = $document[0] || {},
18282        vendorPrefix,
18283        vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
18284        bodyStyle = document.body && document.body.style,
18285        transitions = false,
18286        animations = false,
18287        match;
18288
18289    if (bodyStyle) {
18290      for (var prop in bodyStyle) {
18291        if (match = vendorRegex.exec(prop)) {
18292          vendorPrefix = match[0];
18293          vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
18294          break;
18295        }
18296      }
18297
18298      if (!vendorPrefix) {
18299        vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
18300      }
18301
18302      transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
18303      animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
18304
18305      if (android && (!transitions ||  !animations)) {
18306        transitions = isString(bodyStyle.webkitTransition);
18307        animations = isString(bodyStyle.webkitAnimation);
18308      }
18309    }
18310
18311
18312    return {
18313      // Android has history.pushState, but it does not update location correctly
18314      // so let's not use the history API at all.
18315      // http://code.google.com/p/android/issues/detail?id=17471
18316      // https://github.com/angular/angular.js/issues/904
18317
18318      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
18319      // so let's not use the history API also
18320      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
18321      // jshint -W018
18322      history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
18323      // jshint +W018
18324      hasEvent: function(event) {
18325        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
18326        // it. In particular the event is not fired when backspace or delete key are pressed or
18327        // when cut operation is performed.
18328        // IE10+ implements 'input' event but it erroneously fires under various situations,
18329        // e.g. when placeholder changes, or a form is focused.
18330        if (event === 'input' && msie <= 11) return false;
18331
18332        if (isUndefined(eventSupport[event])) {
18333          var divElm = document.createElement('div');
18334          eventSupport[event] = 'on' + event in divElm;
18335        }
18336
18337        return eventSupport[event];
18338      },
18339      csp: csp(),
18340      vendorPrefix: vendorPrefix,
18341      transitions: transitions,
18342      animations: animations,
18343      android: android
18344    };
18345  }];
18346}
18347
18348var $compileMinErr = minErr('$compile');
18349
18350/**
18351 * @ngdoc provider
18352 * @name $templateRequestProvider
18353 * @description
18354 * Used to configure the options passed to the {@link $http} service when making a template request.
18355 *
18356 * For example, it can be used for specifying the "Accept" header that is sent to the server, when
18357 * requesting a template.
18358 */
18359function $TemplateRequestProvider() {
18360
18361  var httpOptions;
18362
18363  /**
18364   * @ngdoc method
18365   * @name $templateRequestProvider#httpOptions
18366   * @description
18367   * The options to be passed to the {@link $http} service when making the request.
18368   * You can use this to override options such as the "Accept" header for template requests.
18369   *
18370   * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
18371   * options if not overridden here.
18372   *
18373   * @param {string=} value new value for the {@link $http} options.
18374   * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
18375   */
18376  this.httpOptions = function(val) {
18377    if (val) {
18378      httpOptions = val;
18379      return this;
18380    }
18381    return httpOptions;
18382  };
18383
18384  /**
18385   * @ngdoc service
18386   * @name $templateRequest
18387   *
18388   * @description
18389   * The `$templateRequest` service runs security checks then downloads the provided template using
18390   * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
18391   * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
18392   * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
18393   * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
18394   * when `tpl` is of type string and `$templateCache` has the matching entry.
18395   *
18396   * If you want to pass custom options to the `$http` service, such as setting the Accept header you
18397   * can configure this via {@link $templateRequestProvider#httpOptions}.
18398   *
18399   * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
18400   * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
18401   *
18402   * @return {Promise} a promise for the HTTP response data of the given URL.
18403   *
18404   * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
18405   */
18406  this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
18407
18408    function handleRequestFn(tpl, ignoreRequestError) {
18409      handleRequestFn.totalPendingRequests++;
18410
18411      // We consider the template cache holds only trusted templates, so
18412      // there's no need to go through whitelisting again for keys that already
18413      // are included in there. This also makes Angular accept any script
18414      // directive, no matter its name. However, we still need to unwrap trusted
18415      // types.
18416      if (!isString(tpl) || !$templateCache.get(tpl)) {
18417        tpl = $sce.getTrustedResourceUrl(tpl);
18418      }
18419
18420      var transformResponse = $http.defaults && $http.defaults.transformResponse;
18421
18422      if (isArray(transformResponse)) {
18423        transformResponse = transformResponse.filter(function(transformer) {
18424          return transformer !== defaultHttpResponseTransform;
18425        });
18426      } else if (transformResponse === defaultHttpResponseTransform) {
18427        transformResponse = null;
18428      }
18429
18430      return $http.get(tpl, extend({
18431          cache: $templateCache,
18432          transformResponse: transformResponse
18433        }, httpOptions))
18434        ['finally'](function() {
18435          handleRequestFn.totalPendingRequests--;
18436        })
18437        .then(function(response) {
18438          $templateCache.put(tpl, response.data);
18439          return response.data;
18440        }, handleError);
18441
18442      function handleError(resp) {
18443        if (!ignoreRequestError) {
18444          throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
18445            tpl, resp.status, resp.statusText);
18446        }
18447        return $q.reject(resp);
18448      }
18449    }
18450
18451    handleRequestFn.totalPendingRequests = 0;
18452
18453    return handleRequestFn;
18454  }];
18455}
18456
vendor: 5,580 bytes, lines 18457-18610
18457function $$TestabilityProvider() {
18458  this.$get = ['$rootScope', '$browser', '$location',
18459       function($rootScope,   $browser,   $location) {
18460
18461    /**
18462     * @name $testability
18463     *
18464     * @description
18465     * The private $$testability service provides a collection of methods for use when debugging
18466     * or by automated test and debugging tools.
18467     */
18468    var testability = {};
18469
18470    /**
18471     * @name $$testability#findBindings
18472     *
18473     * @description
18474     * Returns an array of elements that are bound (via ng-bind or {{}})
18475     * to expressions matching the input.
18476     *
18477     * @param {Element} element The element root to search from.
18478     * @param {string} expression The binding expression to match.
18479     * @param {boolean} opt_exactMatch If true, only returns exact matches
18480     *     for the expression. Filters and whitespace are ignored.
18481     */
18482    testability.findBindings = function(element, expression, opt_exactMatch) {
18483      var bindings = element.getElementsByClassName('ng-binding');
18484      var matches = [];
18485      forEach(bindings, function(binding) {
18486        var dataBinding = angular.element(binding).data('$binding');
18487        if (dataBinding) {
18488          forEach(dataBinding, function(bindingName) {
18489            if (opt_exactMatch) {
18490              var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
18491              if (matcher.test(bindingName)) {
18492                matches.push(binding);
18493              }
18494            } else {
18495              if (bindingName.indexOf(expression) != -1) {
18496                matches.push(binding);
18497              }
18498            }
18499          });
18500        }
18501      });
18502      return matches;
18503    };
18504
18505    /**
18506     * @name $$testability#findModels
18507     *
18508     * @description
18509     * Returns an array of elements that are two-way found via ng-model to
18510     * expressions matching the input.
18511     *
18512     * @param {Element} element The element root to search from.
18513     * @param {string} expression The model expression to match.
18514     * @param {boolean} opt_exactMatch If true, only returns exact matches
18515     *     for the expression.
18516     */
18517    testability.findModels = function(element, expression, opt_exactMatch) {
18518      var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
18519      for (var p = 0; p < prefixes.length; ++p) {
18520        var attributeEquals = opt_exactMatch ? '=' : '*=';
18521        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
18522        var elements = element.querySelectorAll(selector);
18523        if (elements.length) {
18524          return elements;
18525        }
18526      }
18527    };
18528
18529    /**
18530     * @name $$testability#getLocation
18531     *
18532     * @description
18533     * Shortcut for getting the location in a browser agnostic way. Returns
18534     *     the path, search, and hash. (e.g. /path?a=b#hash)
18535     */
18536    testability.getLocation = function() {
18537      return $location.url();
18538    };
18539
18540    /**
18541     * @name $$testability#setLocation
18542     *
18543     * @description
18544     * Shortcut for navigating to a location without doing a full page reload.
18545     *
18546     * @param {string} url The location url (path, search and hash,
18547     *     e.g. /path?a=b#hash) to go to.
18548     */
18549    testability.setLocation = function(url) {
18550      if (url !== $location.url()) {
18551        $location.url(url);
18552        $rootScope.$digest();
18553      }
18554    };
18555
18556    /**
18557     * @name $$testability#whenStable
18558     *
18559     * @description
18560     * Calls the callback when $timeout and $http requests are completed.
18561     *
18562     * @param {function} callback
18563     */
18564    testability.whenStable = function(callback) {
18565      $browser.notifyWhenNoOutstandingRequests(callback);
18566    };
18567
18568    return testability;
18569  }];
18570}
18571
18572function $TimeoutProvider() {
18573  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
18574       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
18575
18576    var deferreds = {};
18577
18578
18579     /**
18580      * @ngdoc service
18581      * @name $timeout
18582      *
18583      * @description
18584      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
18585      * block and delegates any exceptions to
18586      * {@link ng.$exceptionHandler $exceptionHandler} service.
18587      *
18588      * The return value of calling `$timeout` is a promise, which will be resolved when
18589      * the delay has passed and the timeout function, if provided, is executed.
18590      *
18591      * To cancel a timeout request, call `$timeout.cancel(promise)`.
18592      *
18593      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
18594      * synchronously flush the queue of deferred functions.
18595      *
18596      * If you only want a promise that will be resolved after some specified delay
18597      * then you can call `$timeout` without the `fn` function.
18598      *
18599      * @param {function()=} fn A function, whose execution should be delayed.
18600      * @param {number=} [delay=0] Delay in milliseconds.
18601      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
18602      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
18603      * @param {...*=} Pass additional parameters to the executed function.
18604      * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
18605      *   will be resolved with the return value of the `fn` function.
18606      *
18607      */
18608    function timeout(fn, delay, invokeApply) {
18609      if (!isFunction(fn)) {
18610        invokeApply = delay;
18611        delay = fn;
18612        fn = noop;
18613      }
18614
18615      var args = sliceArgs(arguments, 3),
18616          skipApply = (isDefined(invokeApply) && !invokeApply),
18617          deferred = (skipApply ? $$q : $q).defer(),
18618          promise = deferred.promise,
18619          timeoutId;
18620
18621      timeoutId = $browser.defer(function() {
18622        try {
18623          deferred.resolve(fn.apply(null, args));
18624        } catch (e) {
18625          deferred.reject(e);
18626          $exceptionHandler(e);
18627        }
18628        finally {
18629          delete deferreds[promise.$$timeoutId];
18630        }
18631
18632        if (!skipApply) $rootScope.$apply();
18633      }, delay);
18634
18635      promise.$$timeoutId = timeoutId;
18636      deferreds[timeoutId] = deferred;
18637
18638      return promise;
18639    }
18640
18641
18642     /**
18643      * @ngdoc method
18644      * @name $timeout#cancel
18645      *
18646      * @description
18647      * Cancels a task associated with the `promise`. As a result of this, the promise will be
18648      * resolved with a rejection.
18649      *
18650      * @param {Promise=} promise Promise returned by the `$timeout` function.
18651      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
18652      *   canceled.
18653      */
18654    timeout.cancel = function(promise) {
18655      if (promise && promise.$$timeoutId in deferreds) {
18656        deferreds[promise.$$timeoutId].reject('canceled');
18657        delete deferreds[promise.$$timeoutId];
18658        return $browser.defer.cancel(promise.$$timeoutId);
18659      }
18660      return false;
18661    };
18662
18663    return timeout;
18664  }];
18665}
18666
18667// NOTE:  The usage of window and document instead of $window and $document here is
18668// deliberate.  This service depends on the specific behavior of anchor nodes created by the
18669// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
18670// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
18671// doesn't know about mocked locations and resolves URLs to the real document - which is
18672// exactly the behavior needed here.  There is little value is mocking these out for this
18673// service.
18674var urlParsingNode = document.createElement("a");
18675var originUrl = urlResolve(window.location.href);
18676
18677
18678/**
18679 *
18680 * Implementation Notes for non-IE browsers
18681 * ----------------------------------------
18682 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
18683 * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
18684 * URL will be resolved into an absolute URL in the context of the application document.
18685 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
18686 * properties are all populated to reflect the normalized URL.  This approach has wide
18687 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
18688 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18689 *
18690 * Implementation Notes for IE
18691 * ---------------------------
18692 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
18693 * browsers.  However, the parsed components will not be set if the URL assigned did not specify
18694 * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
18695 * work around that by performing the parsing in a 2nd step by taking a previously normalized
18696 * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
18697 * properties such as protocol, hostname, port, etc.
18698 *
18699 * References:
18700 *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
18701 *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18702 *   http://url.spec.whatwg.org/#urlutils
18703 *   https://github.com/angular/angular.js/pull/2902
18704 *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
18705 *
18706 * @kind function
18707 * @param {string} url The URL to be parsed.
18708 * @description Normalizes and parses a URL.
18709 * @returns {object} Returns the normalized URL as a dictionary.
18710 *
18711 *   | member name   | Description    |
18712 *   |---------------|----------------|
18713 *   | href          | A normalized version of the provided URL if it was not an absolute URL |
18714 *   | protocol      | The protocol including the trailing colon                              |
18715 *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
18716 *   | search        | The search params, minus the question mark                             |
18717 *   | hash          | The hash string, minus the hash symbol
18718 *   | hostname      | The hostname
18719 *   | port          | The port, without ":"
18720 *   | pathname      | The pathname, beginning with "/"
18721 *
18722 */
18723function urlResolve(url) {
18724  var href = url;
18725
18726  if (msie) {
18727    // Normalize before parse.  Refer Implementation Notes on why this is
18728    // done in two steps on IE.
18729    urlParsingNode.setAttribute("href", href);
18730    href = urlParsingNode.href;
18731  }
18732
18733  urlParsingNode.setAttribute('href', href);
18734
18735  // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
18736  return {
18737    href: urlParsingNode.href,
18738    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
18739    host: urlParsingNode.host,
18740    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
18741    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
18742    hostname: urlParsingNode.hostname,
18743    port: urlParsingNode.port,
18744    pathname: (urlParsingNode.pathname.charAt(0) === '/')
18745      ? urlParsingNode.pathname
18746      : '/' + urlParsingNode.pathname
18747  };
18748}
18749
18750/**
18751 * Parse a request URL and determine whether this is a same-origin request as the application document.
18752 *
18753 * @param {string|object} requestUrl The url of the request as a string that will be resolved
18754 * or a parsed URL object.
18755 * @returns {boolean} Whether the request is for the same origin as the application document.
18756 */
18757function urlIsSameOrigin(requestUrl) {
18758  var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
18759  return (parsed.protocol === originUrl.protocol &&
18760          parsed.host === originUrl.host);
18761}
18762
18763/**
18764 * @ngdoc service
18765 * @name $window
18766 *
18767 * @description
18768 * A reference to the browser's `window` object. While `window`
18769 * is globally available in JavaScript, it causes testability problems, because
18770 * it is a global variable. In angular we always refer to it through the
18771 * `$window` service, so it may be overridden, removed or mocked for testing.
18772 *
18773 * Expressions, like the one defined for the `ngClick` directive in the example
18774 * below, are evaluated with respect to the current scope.  Therefore, there is
18775 * no risk of inadvertently coding in a dependency on a global value in such an
18776 * expression.
18777 *
18778 * @example
18779   <example module="windowExample">
18780     <file name="index.html">
18781       <script>
18782         angular.module('windowExample', [])
18783           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18784             $scope.greeting = 'Hello, World!';
18785             $scope.doGreeting = function(greeting) {
18786               $window.alert(greeting);
18787             };
18788           }]);
18789       </script>
18790       <div ng-controller="ExampleController">
18791         <input type="text" ng-model="greeting" aria-label="greeting" />
18792         <button ng-click="doGreeting(greeting)">ALERT</button>
18793       </div>
18794     </file>
18795     <file name="protractor.js" type="protractor">
18796      it('should display the greeting in the input box', function() {
18797       element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18798       // If we click the button it will block the test runner
18799       // element(':button').click();
18800      });
18801     </file>
18802   </example>
18803 */
18804function $WindowProvider() {
18805  this.$get = valueFn(window);
18806}
18807
18808/**
18809 * @name $$cookieReader
18810 * @requires $document
18811 *
18812 * @description
18813 * This is a private service for reading cookies used by $http and ngCookies
18814 *
18815 * @return {Object} a key/value map of the current cookies
18816 */
18817function $$CookieReader($document) {
18818  var rawDocument = $document[0] || {};
18819  var lastCookies = {};
18820  var lastCookieString = '';
18821
18822  function safeDecodeURIComponent(str) {
18823    try {
18824      return decodeURIComponent(str);
18825    } catch (e) {
18826      return str;
18827    }
18828  }
18829
18830  return function() {
18831    var cookieArray, cookie, i, index, name;
18832    var currentCookieString = rawDocument.cookie || '';
18833
18834    if (currentCookieString !== lastCookieString) {
18835      lastCookieString = currentCookieString;
18836      cookieArray = lastCookieString.split('; ');
18837      lastCookies = {};
18838
18839      for (i = 0; i < cookieArray.length; i++) {
18840        cookie = cookieArray[i];
18841        index = cookie.indexOf('=');
18842        if (index > 0) { //ignore nameless cookies
18843          name = safeDecodeURIComponent(cookie.substring(0, index));
18844          // the first value that is seen for a cookie is the most
18845          // specific one.  values for the same cookie name that
18846          // follow are for less specific paths.
18847          if (isUndefined(lastCookies[name])) {
18848            lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18849          }
18850        }
18851      }
18852    }
18853    return lastCookies;
18854  };
18855}
18856
18857$$CookieReader.$inject = ['$document'];
18858
18859function $$CookieReaderProvider() {
18860  this.$get = $$CookieReader;
18861}
18862
18863/* global currencyFilter: true,
18864 dateFilter: true,
18865 filterFilter: true,
18866 jsonFilter: true,
18867 limitToFilter: true,
18868 lowercaseFilter: true,
18869 numberFilter: true,
18870 orderByFilter: true,
18871 uppercaseFilter: true,
18872 */
18873
18874/**
18875 * @ngdoc provider
18876 * @name $filterProvider
18877 * @description
18878 *
18879 * Filters are just functions which transform input to an output. However filters need to be
18880 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18881 * annotated with dependencies and is responsible for creating a filter function.
18882 *
18883 * <div class="alert alert-warning">
18884 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18885 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18886 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18887 * (`myapp_subsection_filterx`).
18888 * </div>
18889 *
18890 * ```js
18891 *   // Filter registration
18892 *   function MyModule($provide, $filterProvider) {
18893 *     // create a service to demonstrate injection (not always needed)
18894 *     $provide.value('greet', function(name){
18895 *       return 'Hello ' + name + '!';
18896 *     });
18897 *
18898 *     // register a filter factory which uses the
18899 *     // greet service to demonstrate DI.
18900 *     $filterProvider.register('greet', function(greet){
18901 *       // return the filter function which uses the greet service
18902 *       // to generate salutation
18903 *       return function(text) {
18904 *         // filters need to be forgiving so check input validity
18905 *         return text && greet(text) || text;
18906 *       };
18907 *     });
18908 *   }
18909 * ```
18910 *
18911 * The filter function is registered with the `$injector` under the filter name suffix with
18912 * `Filter`.
18913 *
18914 * ```js
18915 *   it('should be the same instance', inject(
18916 *     function($filterProvider) {
18917 *       $filterProvider.register('reverse', function(){
18918 *         return ...;
18919 *       });
18920 *     },
18921 *     function($filter, reverseFilter) {
18922 *       expect($filter('reverse')).toBe(reverseFilter);
18923 *     });
18924 * ```
18925 *
18926 *
18927 * For more information about how angular filters work, and how to create your own filters, see
18928 * {@link guide/filter Filters} in the Angular Developer Guide.
18929 */
18930
18931/**
18932 * @ngdoc service
18933 * @name $filter
18934 * @kind function
18935 * @description
18936 * Filters are used for formatting data displayed to the user.
18937 *
18938 * The general syntax in templates is as follows:
18939 *
18940 *         {{ expression [| filter_name[:parameter_value] ... ] }}
18941 *
18942 * @param {String} name Name of the filter function to retrieve
18943 * @return {Function} the filter function
18944 * @example
18945   <example name="$filter" module="filterExample">
18946     <file name="index.html">
18947       <div ng-controller="MainCtrl">
18948        <h3>{{ originalText }}</h3>
18949        <h3>{{ filteredText }}</h3>
18950       </div>
18951     </file>
18952
18953     <file name="script.js">
18954      angular.module('filterExample', [])
18955      .controller('MainCtrl', function($scope, $filter) {
18956        $scope.originalText = 'hello';
18957        $scope.filteredText = $filter('uppercase')($scope.originalText);
18958      });
18959     </file>
18960   </example>
18961  */
18962$FilterProvider.$inject = ['$provide'];
18963function $FilterProvider($provide) {
18964  var suffix = 'Filter';
18965
18966  /**
18967   * @ngdoc method
18968   * @name $filterProvider#register
18969   * @param {string|Object} name Name of the filter function, or an object map of filters where
18970   *    the keys are the filter names and the values are the filter factories.
18971   *
18972   *    <div class="alert alert-warning">
18973   *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18974   *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18975   *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18976   *    (`myapp_subsection_filterx`).
18977   *    </div>
18978    * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18979   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18980   *    of the registered filter instances.
18981   */
18982  function register(name, factory) {
18983    if (isObject(name)) {
18984      var filters = {};
18985      forEach(name, function(filter, key) {
18986        filters[key] = register(key, filter);
18987      });
18988      return filters;
18989    } else {
18990      return $provide.factory(name + suffix, factory);
18991    }
18992  }
18993  this.register = register;
18994
18995  this.$get = ['$injector', function($injector) {
18996    return function(name) {
18997      return $injector.get(name + suffix);
18998    };
18999  }];
19000
19001  ////////////////////////////////////////
19002
19003  /* global
19004    currencyFilter: false,
19005    dateFilter: false,
19006    filterFilter: false,
19007    jsonFilter: false,
19008    limitToFilter: false,
19009    lowercaseFilter: false,
19010    numberFilter: false,
19011    orderByFilter: false,
19012    uppercaseFilter: false,
19013  */
19014
19015  register('currency', currencyFilter);
19016  register('date', dateFilter);
19017  register('filter', filterFilter);
19018  register('json', jsonFilter);
19019  register('limitTo', limitToFilter);
19020  register('lowercase', lowercaseFilter);
19021  register('number', numberFilter);
19022  register('orderBy', orderByFilter);
19023  register('uppercase', uppercaseFilter);
19024}
19025
19026/**
19027 * @ngdoc filter
19028 * @name filter
19029 * @kind function
19030 *
19031 * @description
19032 * Selects a subset of items from `array` and returns it as a new array.
19033 *
19034 * @param {Array} array The source array.
19035 * @param {string|Object|function()} expression The predicate to be used for selecting items from
19036 *   `array`.
19037 *
19038 *   Can be one of:
19039 *
19040 *   - `string`: The string is used for matching against the contents of the `array`. All strings or
19041 *     objects with string properties in `array` that match this string will be returned. This also
19042 *     applies to nested object properties.
19043 *     The predicate can be negated by prefixing the string with `!`.
19044 *
19045 *   - `Object`: A pattern object can be used to filter specific properties on objects contained
19046 *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
19047 *     which have property `name` containing "M" and property `phone` containing "1". A special
19048 *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
19049 *     property of the object or its nested object properties. That's equivalent to the simple
19050 *     substring match with a `string` as described above. The predicate can be negated by prefixing
19051 *     the string with `!`.
19052 *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
19053 *     not containing "M".
19054 *
19055 *     Note that a named property will match properties on the same level only, while the special
19056 *     `$` property will match properties on the same level or deeper. E.g. an array item like
19057 *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
19058 *     **will** be matched by `{$: 'John'}`.
19059 *
19060 *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
19061 *     The function is called for each element of the array, with the element, its index, and
19062 *     the entire array itself as arguments.
19063 *
19064 *     The final result is an array of those elements that the predicate returned true for.
19065 *
19066 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
19067 *     determining if the expected value (from the filter expression) and actual value (from
19068 *     the object in the array) should be considered a match.
19069 *
19070 *   Can be one of:
19071 *
19072 *   - `function(actual, expected)`:
19073 *     The function will be given the object value and the predicate value to compare and
19074 *     should return true if both values should be considered equal.
19075 *
19076 *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
19077 *     This is essentially strict comparison of expected and actual.
19078 *
19079 *   - `false|undefined`: A short hand for a function which will look for a substring match in case
19080 *     insensitive way.
19081 *
19082 *     Primitive values are converted to strings. Objects are not compared against primitives,
19083 *     unless they have a custom `toString` method (e.g. `Date` objects).
19084 *
19085 * @example
19086   <example>
19087     <file name="index.html">
19088       <div ng-init="friends = [{name:'John', phone:'555-1276'},
19089                                {name:'Mary', phone:'800-BIG-MARY'},
19090                                {name:'Mike', phone:'555-4321'},
19091                                {name:'Adam', phone:'555-5678'},
19092                                {name:'Julie', phone:'555-8765'},
19093                                {name:'Juliette', phone:'555-5678'}]"></div>
19094
19095       <label>Search: <input ng-model="searchText"></label>
19096       <table id="searchTextResults">
19097         <tr><th>
19097Name</th><th>Phone</th></tr>
19098         <tr ng-repeat="friend in friends | filter:searchText">
19099           <td>{{friend.name}}</td>
19100           <td>{{friend.phone}}</td>
19101         </tr>
19102       </table>
19103       <hr>
19104       <label>Any: <input ng-model="search.$"></label> <br>
19105       <label>Name only <input ng-model="search.name"></label><br>
19106       <label>Phone only <input ng-model="search.phone"></label><br>
19107       <label>Equality <input type="checkbox" ng-model="strict"></label><br>
19108       <table id="searchObjResults">
19109         <tr><th>Name</th><th>Phone</th></tr>
19110         <tr ng-repeat="friendObj in friends | filter:search:strict">
19111           <td>{{friendObj.name}}</td>
19112           <td>{{friendObj.phone}}</td>
19113         </tr>
19114       </table>
19115     </file>
19116     <file name="protractor.js" type="protractor">
19117       var expectFriendNames = function(expectedNames, key) {
19118         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
19119           arr.forEach(function(wd, i) {
19120             expect(wd.getText()).toMatch(expectedNames[i]);
19121           });
19122         });
19123       };
19124
19125       it('should search across all fields when filtering with a string', function() {
19126         var searchText = element(by.model('searchText'));
19127         searchText.clear();
19128         searchText.sendKeys('m');
19129         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
19130
19131         searchText.clear();
19132         searchText.sendKeys('76');
19133         expectFriendNames(['John', 'Julie'], 'friend');
19134       });
19135
19136       it('should search in specific fields when filtering with a predicate object', function() {
19137         var searchAny = element(by.model('search.$'));
19138         searchAny.clear();
19139         searchAny.sendKeys('i');
19140         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
19141       });
19142       it('should use a equal comparison when comparator is true', function() {
19143         var searchName = element(by.model('search.name'));
19144         var strict = element(by.model('strict'));
19145         searchName.clear();
19146         searchName.sendKeys('Julie');
19147         strict.click();
19148         expectFriendNames(['Julie'], 'friendObj');
19149       });
19150     </file>
19151   </example>
19152 */
19153function filterFilter() {
19154  return function(array, expression, comparator) {
19155    if (!isArrayLike(array)) {
19156      if (array == null) {
19157        return array;
19158      } else {
19159        throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
19160      }
19161    }
19162
19163    var expressionType = getTypeForFilter(expression);
19164    var predicateFn;
19165    var matchAgainstAnyProp;
19166
19167    switch (expressionType) {
19168      case 'function':
19169        predicateFn = expression;
19170        break;
19171      case 'boolean':
19172      case 'null':
19173      case 'number':
19174      case 'string':
19175        matchAgainstAnyProp = true;
19176        //jshint -W086
19177      case 'object':
19178        //jshint +W086
19179        predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
19180        break;
19181      default:
19182        return array;
19183    }
19184
19185    return Array.prototype.filter.call(array, predicateFn);
19186  };
19187}
19188
19189// Helper functions for `filterFilter`
19190function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
19191  var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
19192  var predicateFn;
19193
19194  if (comparator === true) {
19195    comparator = equals;
19196  } else if (!isFunction(comparator)) {
19197    comparator = function(actual, expected) {
19198      if (isUndefined(actual)) {
19199        // No substring matching against `undefined`
19200        return false;
19201      }
19202      if ((actual === null) || (expected === null)) {
19203        // No substring matching against `null`; only match against `null`
19204        return actual === expected;
19205      }
19206      if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
19207        // Should not compare primitives against objects, unless they have custom `toString` method
19208        return false;
19209      }
19210
19211      actual = lowercase('' + actual);
19212      expected = lowercase('' + expected);
19213      return actual.indexOf(expected) !== -1;
19214    };
19215  }
19216
19217  predicateFn = function(item) {
19218    if (shouldMatchPrimitives && !isObject(item)) {
19219      return deepCompare(item, expression.$, comparator, false);
19220    }
19221    return deepCompare(item, expression, comparator, matchAgainstAnyProp);
19222  };
19223
19224  return predicateFn;
19225}
19226
19227function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
19228  var actualType = getTypeForFilter(actual);
19229  var expectedType = getTypeForFilter(expected);
19230
19231  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
19232    return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
19233  } else if (isArray(actual)) {
19234    // In case `actual` is an array, consider it a match
19235    // if ANY of it's items matches `expected`
19236    return actual.some(function(item) {
19237      return deepCompare(item, expected, comparator, matchAgainstAnyProp);
19238    });
19239  }
19240
19241  switch (actualType) {
19242    case 'object':
19243      var key;
19244      if (matchAgainstAnyProp) {
19245        for (key in actual) {
19246          if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
19247            return true;
19248          }
19249        }
19250        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
19251      } else if (expectedType === 'object') {
19252        for (key in expected) {
19253          var expectedVal = expected[key];
19254          if (isFunction(expectedVal) || isUndefined(expectedVal)) {
19255            continue;
19256          }
19257
19258          var matchAnyProperty = key === '$';
19259          var actualVal = matchAnyProperty ? actual : actual[key];
19260          if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
19261            return false;
19262          }
19263        }
19264        return true;
19265      } else {
19266        return comparator(actual, expected);
19267      }
19268      break;
19269    case 'function':
19270      return false;
19271    default:
19272      return comparator(actual, expected);
19273  }
19274}
19275
19276// Used for easily differentiating between `null` and actual `object`
19277function getTypeForFilter(val) {
19278  return (val === null) ? 'null' : typeof val;
19279}
19280
19281var MAX_DIGITS = 22;
19282var DECIMAL_SEP = '.';
19283var ZERO_CHAR = '0';
19284
19285/**
19286 * @ngdoc filter
19287 * @name currency
19288 * @kind function
19289 *
19290 * @description
19291 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
19292 * symbol for current locale is used.
19293 *
19294 * @param {number} amount Input to filter.
19295 * @param {string=} symbol Currency symbol or identifier to be displayed.
19296 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
19297 * @returns {string} Formatted number.
19298 *
19299 *
19300 * @example
19301   <example module="currencyExample">
19302     <file name="index.html">
19303       <script>
19304         angular.module('currencyExample', [])
19305           .controller('ExampleController', ['$scope', function($scope) {
19306             $scope.amount = 1234.56;
19307           }]);
19308       </script>
19309       <div ng-controller="ExampleController">
19310         <input type="number" ng-model="amount" aria-label="amount"> <br>
19311         default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
19312         custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
19313         no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
19314       </div>
19315     </file>
19316     <file name="protractor.js" type="protractor">
19317       it('should init with 1234.56', function() {
19318         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
19319         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
19320         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
19321       });
19322       it('should update', function() {
19323         if (browser.params.browser == 'safari') {
19324           // Safari does not understand the minus key. See
19325           // https://github.com/angular/protractor/issues/481
19326           return;
19327         }
19328         element(by.model('amount')).clear();
19329         element(by.model('amount')).sendKeys('-1234');
19330         expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
19331         expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
19332         expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
19333       });
19334     </file>
19335   </example>
19336 */
19337currencyFilter.$inject = ['$locale'];
19338function currencyFilter($locale) {
19339  var formats = $locale.NUMBER_FORMATS;
19340  return function(amount, currencySymbol, fractionSize) {
19341    if (isUndefined(currencySymbol)) {
19342      currencySymbol = formats.CURRENCY_SYM;
19343    }
19344
19345    if (isUndefined(fractionSize)) {
19346      fractionSize = formats.PATTERNS[1].maxFrac;
19347    }
19348
19349    // if null or undefined pass it through
19350    return (amount == null)
19351        ? amount
19352        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
19353            replace(/\u00A4/g, currencySymbol);
19354  };
19355}
19356
19357/**
19358 * @ngdoc filter
19359 * @name number
19360 * @kind function
19361 *
19362 * @description
19363 * Formats a number as text.
19364 *
19365 * If the input is null or undefined, it will just be returned.
19366 * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
19367 * If the input is not a number an empty string is returned.
19368 *
19369 *
19370 * @param {number|string} number Number to format.
19371 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
19372 * If this is not provided then the fraction size is computed from the current locale's number
19373 * formatting pattern. In the case of the default locale, it will be 3.
19374 * @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
19375 *
19376 * @example
19377   <example module="numberFilterExample">
19378     <file name="index.html">
19379       <script>
19380         angular.module('numberFilterExample', [])
19381           .controller('ExampleController', ['$scope', function($scope) {
19382             $scope.val = 1234.56789;
19383           }]);
19384       </script>
19385       <div ng-controller="ExampleController">
19386         <label>Enter number: <input ng-model='val'></label><br>
19387         Default formatting: <span id='number-default'>{{val | number}}</span><br>
19388         No fractions: <span>{{val | number:0}}</span><br>
19389         Negative number: <span>{{-val | number:4}}</span>
19390       </div>
19391     </file>
19392     <file name="protractor.js" type="protractor">
19393       it('should format numbers', function() {
19394         expect(element(by.id('number-default')).getText()).toBe('1,234.568');
19395         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
19396         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
19397       });
19398
19399       it('should update', function() {
19400         element(by.model('val')).clear();
19401         element(by.model('val')).sendKeys('3374.333');
19402         expect(element(by.id('number-default')).getText()).toBe('3,374.333');
19403         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
19404         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
19405      });
19406     </file>
19407   </example>
19408 */
19409numberFilter.$inject = ['$locale'];
19410function numberFilter($locale) {
19411  var formats = $locale.NUMBER_FORMATS;
19412  return function(number, fractionSize) {
19413
19414    // if null or undefined pass it through
19415    return (number == null)
19416        ? number
19417        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
19418                       fractionSize);
19419  };
19420}
19421
19422/**
19423 * Parse a number (as a string) into three components that can be used
19424 * for formatting the number.
19425 *
19426 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
19427 *
19428 * @param  {string} numStr The number to parse
19429 * @return {object} An object describing this number, containing the following keys:
19430 *  - d : an array of digits containing leading zeros as necessary
19431 *  - i : the number of the digits in `d` that are to the left of the decimal point
19432 *  - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
19433 *
19434 */
19435function parse(numStr) {
19436  var exponent = 0, digits, numberOfIntegerDigits;
19437  var i, j, zeros;
19438
19439  // Decimal point?
19440  if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
19441    numStr = numStr.replace(DECIMAL_SEP, '');
19442  }
19443
19444  // Exponential form?
vendor: 10,817 bytes, lines 19445-19765
19445  if ((i = numStr.search(/e/i)) > 0) {
19446    // Work out the exponent.
19447    if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
19448    numberOfIntegerDigits += +numStr.slice(i + 1);
19449    numStr = numStr.substring(0, i);
19450  } else if (numberOfIntegerDigits < 0) {
19451    // There was no decimal point or exponent so it is an integer.
19452    numberOfIntegerDigits = numStr.length;
19453  }
19454
19455  // Count the number of leading zeros.
19456  for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++);
19457
19458  if (i == (zeros = numStr.length)) {
19459    // The digits are all zero.
19460    digits = [0];
19461    numberOfIntegerDigits = 1;
19462  } else {
19463    // Count the number of trailing zeros
19464    zeros--;
19465    while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
19466
19467    // Trailing zeros are insignificant so ignore them
19468    numberOfIntegerDigits -= i;
19469    digits = [];
19470    // Convert string to array of digits without leading/trailing zeros.
19471    for (j = 0; i <= zeros; i++, j++) {
19472      digits[j] = +numStr.charAt(i);
19473    }
19474  }
19475
19476  // If the number overflows the maximum allowed digits then use an exponent.
19477  if (numberOfIntegerDigits > MAX_DIGITS) {
19478    digits = digits.splice(0, MAX_DIGITS - 1);
19479    exponent = numberOfIntegerDigits - 1;
19480    numberOfIntegerDigits = 1;
19481  }
19482
19483  return { d: digits, e: exponent, i: numberOfIntegerDigits };
19484}
19485
19486/**
19487 * Round the parsed number to the specified number of decimal places
19488 * This function changed the parsedNumber in-place
19489 */
19490function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
19491    var digits = parsedNumber.d;
19492    var fractionLen = digits.length - parsedNumber.i;
19493
19494    // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
19495    fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
19496
19497    // The index of the digit to where rounding is to occur
19498    var roundAt = fractionSize + parsedNumber.i;
19499    var digit = digits[roundAt];
19500
19501    if (roundAt > 0) {
19502      digits.splice(roundAt);
19503    } else {
19504      // We rounded to zero so reset the parsedNumber
19505      parsedNumber.i = 1;
19506      digits.length = roundAt = fractionSize + 1;
19507      for (var i=0; i < roundAt; i++) digits[i] = 0;
19508    }
19509
19510    if (digit >= 5) digits[roundAt - 1]++;
19511
19512    // Pad out with zeros to get the required fraction length
19513    for (; fractionLen < fractionSize; fractionLen++) digits.push(0);
19514
19515
19516    // Do any carrying, e.g. a digit was rounded up to 10
19517    var carry = digits.reduceRight(function(carry, d, i, digits) {
19518      d = d + carry;
19519      digits[i] = d % 10;
19520      return Math.floor(d / 10);
19521    }, 0);
19522    if (carry) {
19523      digits.unshift(carry);
19524      parsedNumber.i++;
19525    }
19526}
19527
19528/**
19529 * Format a number into a string
19530 * @param  {number} number       The number to format
19531 * @param  {{
19532 *           minFrac, // the minimum number of digits required in the fraction part of the number
19533 *           maxFrac, // the maximum number of digits required in the fraction part of the number
19534 *           gSize,   // number of digits in each group of separated digits
19535 *           lgSize,  // number of digits in the last group of digits before the decimal separator
19536 *           negPre,  // the string to go in front of a negative number (e.g. `-` or `(`))
19537 *           posPre,  // the string to go in front of a positive number
19538 *           negSuf,  // the string to go after a negative number (e.g. `)`)
19539 *           posSuf   // the string to go after a positive number
19540 *         }} pattern
19541 * @param  {string} groupSep     The string to separate groups of number (e.g. `,`)
19542 * @param  {string} decimalSep   The string to act as the decimal separator (e.g. `.`)
19543 * @param  {[type]} fractionSize The size of the fractional part of the number
19544 * @return {string}              The number formatted as a string
19545 */
19546function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
19547
19548  if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
19549
19550  var isInfinity = !isFinite(number);
19551  var isZero = false;
19552  var numStr = Math.abs(number) + '',
19553      formattedText = '',
19554      parsedNumber;
19555
19556  if (isInfinity) {
19557    formattedText = '\u221e';
19558  } else {
19559    parsedNumber = parse(numStr);
19560
19561    roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
19562
19563    var digits = parsedNumber.d;
19564    var integerLen = parsedNumber.i;
19565    var exponent = parsedNumber.e;
19566    var decimals = [];
19567    isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
19568
19569    // pad zeros for small numbers
19570    while (integerLen < 0) {
19571      digits.unshift(0);
19572      integerLen++;
19573    }
19574
19575    // extract decimals digits
19576    if (integerLen > 0) {
19577      decimals = digits.splice(integerLen);
19578    } else {
19579      decimals = digits;
19580      digits = [0];
19581    }
19582
19583    // format the integer digits with grouping separators
19584    var groups = [];
19585    if (digits.length > pattern.lgSize) {
19586      groups.unshift(digits.splice(-pattern.lgSize).join(''));
19587    }
19588    while (digits.length > pattern.gSize) {
19589      groups.unshift(digits.splice(-pattern.gSize).join(''));
19590    }
19591    if (digits.length) {
19592      groups.unshift(digits.join(''));
19593    }
19594    formattedText = groups.join(groupSep);
19595
19596    // append the decimal digits
19597    if (decimals.length) {
19598      formattedText += decimalSep + decimals.join('');
19599    }
19600
19601    if (exponent) {
19602      formattedText += 'e+' + exponent;
19603    }
19604  }
19605  if (number < 0 && !isZero) {
19606    return pattern.negPre + formattedText + pattern.negSuf;
19607  } else {
19608    return pattern.posPre + formattedText + pattern.posSuf;
19609  }
19610}
19611
19612function padNumber(num, digits, trim) {
19613  var neg = '';
19614  if (num < 0) {
19615    neg =  '-';
19616    num = -num;
19617  }
19618  num = '' + num;
19619  while (num.length < digits) num = ZERO_CHAR + num;
19620  if (trim) {
19621    num = num.substr(num.length - digits);
19622  }
19623  return neg + num;
19624}
19625
19626
19627function dateGetter(name, size, offset, trim) {
19628  offset = offset || 0;
19629  return function(date) {
19630    var value = date['get' + name]();
19631    if (offset > 0 || value > -offset) {
19632      value += offset;
19633    }
19634    if (value === 0 && offset == -12) value = 12;
19635    return padNumber(value, size, trim);
19636  };
19637}
19638
19639function dateStrGetter(name, shortForm) {
19640  return function(date, formats) {
19641    var value = date['get' + name]();
19642    var get = uppercase(shortForm ? ('SHORT' + name) : name);
19643
19644    return formats[get][value];
19645  };
19646}
19647
19648function timeZoneGetter(date, formats, offset) {
19649  var zone = -1 * offset;
19650  var paddedZone = (zone >= 0) ? "+" : "";
19651
19652  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
19653                padNumber(Math.abs(zone % 60), 2);
19654
19655  return paddedZone;
19656}
19657
19658function getFirstThursdayOfYear(year) {
19659    // 0 = index of January
19660    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
19661    // 4 = index of Thursday (+1 to account for 1st = 5)
19662    // 11 = index of *next* Thursday (+1 account for 1st = 12)
19663    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
19664}
19665
19666function getThursdayThisWeek(datetime) {
19667    return new Date(datetime.getFullYear(), datetime.getMonth(),
19668      // 4 = index of Thursday
19669      datetime.getDate() + (4 - datetime.getDay()));
19670}
19671
19672function weekGetter(size) {
19673   return function(date) {
19674      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
19675         thisThurs = getThursdayThisWeek(date);
19676
19677      var diff = +thisThurs - +firstThurs,
19678         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
19679
19680      return padNumber(result, size);
19681   };
19682}
19683
19684function ampmGetter(date, formats) {
19685  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
19686}
19687
19688function eraGetter(date, formats) {
19689  return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
19690}
19691
19692function longEraGetter(date, formats) {
19693  return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
19694}
19695
19696var DATE_FORMATS = {
19697  yyyy: dateGetter('FullYear', 4),
19698    yy: dateGetter('FullYear', 2, 0, true),
19699     y: dateGetter('FullYear', 1),
19700  MMMM: dateStrGetter('Month'),
19701   MMM: dateStrGetter('Month', true),
19702    MM: dateGetter('Month', 2, 1),
19703     M: dateGetter('Month', 1, 1),
19704    dd: dateGetter('Date', 2),
19705     d: dateGetter('Date', 1),
19706    HH: dateGetter('Hours', 2),
19707     H: dateGetter('Hours', 1),
19708    hh: dateGetter('Hours', 2, -12),
19709     h: dateGetter('Hours', 1, -12),
19710    mm: dateGetter('Minutes', 2),
19711     m: dateGetter('Minutes', 1),
19712    ss: dateGetter('Seconds', 2),
19713     s: dateGetter('Seconds', 1),
19714     // while ISO 8601 requires fractions to be prefixed with `.` or `,`
19715     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
19716   sss: dateGetter('Milliseconds', 3),
19717  EEEE: dateStrGetter('Day'),
19718   EEE: dateStrGetter('Day', true),
19719     a: ampmGetter,
19720     Z: timeZoneGetter,
19721    ww: weekGetter(2),
19722     w: weekGetter(1),
19723     G: eraGetter,
19724     GG: eraGetter,
19725     GGG: eraGetter,
19726     GGGG: longEraGetter
19727};
19728
19729var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
19730    NUMBER_STRING = /^\-?\d+$/;
19731
19732/**
19733 * @ngdoc filter
19734 * @name date
19735 * @kind function
19736 *
19737 * @description
19738 *   Formats `date` to a string based on the requested `format`.
19739 *
19740 *   `format` string can be composed of the following elements:
19741 *
19742 *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
19743 *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
19744 *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
19745 *   * `'MMMM'`: Month in year (January-December)
19746 *   * `'MMM'`: Month in year (Jan-Dec)
19747 *   * `'MM'`: Month in year, padded (01-12)
19748 *   * `'M'`: Month in year (1-12)
19749 *   * `'dd'`: Day in month, padded (01-31)
19750 *   * `'d'`: Day in month (1-31)
19751 *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
19752 *   * `'EEE'`: Day in Week, (Sun-Sat)
19753 *   * `'HH'`: Hour in day, padded (00-23)
19754 *   * `'H'`: Hour in day (0-23)
19755 *   * `'hh'`: Hour in AM/PM, padded (01-12)
19756 *   * `'h'`: Hour in AM/PM, (1-12)
19757 *   * `'mm'`: Minute in hour, padded (00-59)
19758 *   * `'m'`: Minute in hour (0-59)
19759 *   * `'ss'`: Second in minute, padded (00-59)
19760 *   * `'s'`: Second in minute (0-59)
19761 *   * `'sss'`: Millisecond in second, padded (000-999)
19762 *   * `'a'`: AM/PM marker
19763 *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
19764 *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
19765 *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
19766 *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
19767 *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
19768 *
19769 *   `format` string can also be one of the following predefined
19770 *   {@link guide/i18n localizable formats}:
19771 *
19772 *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
19773 *     (e.g. Sep 3, 2010 12:05:08 PM)
19774 *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
19775 *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
19776 *     (e.g. Friday, September 3, 2010)
19777 *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
19778 *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
19779 *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
19780 *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
19781 *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
19782 *
19783 *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
19784 *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
19785 *   (e.g. `"h 'o''clock'"`).
19786 *
19787 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
19788 *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
19789 *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
19790 *    specified in the string input, the time is considered to be in the local timezone.
19791 * @param {string=} format Formatting rules (see Description). If not specified,
19792 *    `mediumDate` is used.
19793 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
19794 *    continental US time zone abbreviations, but for general use, use a time zone offset, for
19795 *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
19796 *    If not specified, the timezone of the browser will be used.
19797 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
19798 *
19799 * @example
19800   <example>
19801     <file name="index.html">
19802       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
19803           <span>{{1288323623006 | date:'medium'}}</span><br>
19804       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
19805          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
19806       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
19807          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
19808       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
19809          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
19810     </file>
19811     <file name="protractor.js" type="protractor">
19812       it('should format date', function() {
19813         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
19814            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19815         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
19816            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19817         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
19818            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19819         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
19820            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
19821       });
19822     </file>
19823   </example>
19824 */
19825dateFilter.$inject = ['$locale'];
19826function dateFilter($locale) {
19827
19828
19829  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
19830                     // 1        2       3         4          5          6          7          8  9     10      11
19831  function jsonStringToDate(string) {
19832    var match;
19833    if (match = string.match(R_ISO8601_STR)) {
19834      var date = new Date(0),
19835          tzHour = 0,
19836          tzMin  = 0,
19837          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
19838          timeSetter = match[8] ? date.setUTCHours : date.setHours;
19839
19840      if (match[9]) {
19841        tzHour = toInt(match[9] + match[10]);
19842        tzMin = toInt(match[9] + match[11]);
19843      }
19844      dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
19845      var h = toInt(match[4] || 0) - tzHour;
19846      var m = toInt(match[5] || 0) - tzMin;
19847      var s = toInt(match[6] || 0);
19848      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
19849      timeSetter.call(date, h, m, s, ms);
19850      return date;
19851    }
19852    return string;
19853  }
19854
19855
19856  return function(date, format, timezone) {
19857    var text = '',
19858        parts = [],
19859        fn, match;
19860
19861    format = format || 'mediumDate';
19862    format = $locale.DATETIME_FORMATS[format] || format;
19863    if (isString(date)) {
19864      date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
19865    }
19866
19867    if (isNumber(date)) {
19868      date = new Date(date);
19869    }
19870
19871    if (!isDate(date) || !isFinite(date.getTime())) {
19872      return date;
19873    }
19874
19875    while (format) {
19876      match = DATE_FORMATS_SPLIT.exec(format);
19877      if (match) {
19878        parts = concat(parts, match, 1);
19879        format = parts.pop();
19880      } else {
19881        parts.push(format);
19882        format = null;
19883      }
19884    }
19885
19886    var dateTimezoneOffset = date.getTimezoneOffset();
19887    if (timezone) {
19888      dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
19889      date = convertTimezoneToLocal(date, timezone, true);
19890    }
19891    forEach(parts, function(value) {
19892      fn = DATE_FORMATS[value];
19893      text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19894                 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19895    });
19896
19897    return text;
19898  };
19899}
19900
19901
19902/**
19903 * @ngdoc filter
19904 * @name json
19905 * @kind function
19906 *
19907 * @description
19908 *   Allows you to convert a JavaScript object into JSON string.
19909 *
19910 *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
19911 *   the binding is automatically converted to JSON.
19912 *
19913 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19914 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
19915 * @returns {string} JSON string.
19916 *
19917 *
19918 * @example
19919   <example>
19920     <file name="index.html">
19921       <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19922       <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19923     </file>
19924     <file name="protractor.js" type="protractor">
19925       it('should jsonify filtered objects', function() {
19926         expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
19927         expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
19928       });
19929     </file>
19930   </example>
19931 *
19932 */
19933function jsonFilter() {
19934  return function(object, spacing) {
19935    if (isUndefined(spacing)) {
19936        spacing = 2;
19937    }
19938    return toJson(object, spacing);
19939  };
19940}
19941
19942
19943/**
19944 * @ngdoc filter
19945 * @name lowercase
19946 * @kind function
19947 * @description
19948 * Converts string to lowercase.
19949 * @see angular.lowercase
19950 */
19951var lowercaseFilter = valueFn(lowercase);
19952
19953
19954/**
19955 * @ngdoc filter
19956 * @name uppercase
19957 * @kind function
19958 * @description
19959 * Converts string to uppercase.
19960 * @see angular.uppercase
19961 */
19962var uppercaseFilter = valueFn(uppercase);
19963
19964/**
19965 * @ngdoc filter
19966 * @name limitTo
19967 * @kind function
19968 *
19969 * @description
19970 * Creates a new array or string containing only a specified number of elements. The elements
19971 * are taken from either the beginning or the end of the source array, string or number, as specified by
19972 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19973 * converted to a string.
19974 *
19975 * @param {Array|string|number} input Source array, string or number to be limited.
19976 * @param {string|number} limit The length of the returned array or string. If the `limit` number
19977 *     is positive, `limit` number of items from the beginning of the source array/string are copied.
19978 *     If the number is negative, `limit` number  of items from the end of the source array/string
19979 *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19980 *     the input will be returned unchanged.
19981 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19982 *     indicates an offset from the end of `input`. Defaults to `0`.
19983 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19984 *     had less than `limit` elements.
19985 *
19986 * @example
19987   <example module="limitToExample">
19988     <file name="index.html">
19989       <script>
19990         angular.module('limitToExample', [])
19991           .controller('ExampleController', ['$scope', function($scope) {
19992             $scope.numbers = [1,2,3,4,5,6,7,8,9];
19993             $scope.letters = "abcdefghi";
19994             $scope.longNumber = 2345432342;
19995             $scope.numLimit = 3;
19996             $scope.letterLimit = 3;
19997             $scope.longNumberLimit = 3;
19998           }]);
19999       </script>
20000       <div ng-controller="ExampleController">
20001         <label>
20002            Limit {{numbers}} to:
20003            <input type="number" step="1" ng-model="numLimit">
20004         </label>
20005         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
20006         <label>
20007            Limit {{letters}} to:
20008            <input type="number" step="1" ng-model="letterLimit">
20009         </label>
20010         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
20011         <label>
20012            Limit {{longNumber}} to:
20013            <input type="number" step="1" ng-model="longNumberLimit">
20014         </label>
20015         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
20016       </div>
20017     </file>
20018     <file name="protractor.js" type="protractor">
20019       var numLimitInput = element(by.model('numLimit'));
20020       var letterLimitInput = element(by.model('letterLimit'));
20021       var longNumberLimitInput = element(by.model('longNumberLimit'));
20022       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
20023       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
20024       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
20025
20026       it('should limit the number array to first three items', function() {
20027         expect(numLimitInput.getAttribute('value')).toBe('3');
20028         expect(letterLimitInput.getAttribute('value')).toBe('3');
20029         expect(longNumberLimitInput.getAttribute('value')).toBe('3');
20030         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
20031         expect(limitedLetters.getText()).toEqual('Output letters: abc');
20032         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
20033       });
20034
20035       // There is a bug in safari and protractor that doesn't like the minus key
20036       // it('should update the output when -3 is entered', function() {
20037       //   numLimitInput.clear();
20038       //   numLimitInput.sendKeys('-3');
20039       //   letterLimitInput.clear();
20040       //   letterLimitInput.sendKeys('-3');
20041       //   longNumberLimitInput.clear();
20042       //   longNumberLimitInput.sendKeys('-3');
20043       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
20044       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
20045       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
20046       // });
20047
20048       it('should not exceed the maximum size of input array', function() {
20049         numLimitInput.clear();
20050         numLimitInput.sendKeys('100');
20051         letterLimitInput.clear();
20052         letterLimitInput.sendKeys('100');
20053         longNumberLimitInput.clear();
20054         longNumberLimitInput.sendKeys('100');
20055         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
20056         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
20057         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
20058       });
20059     </file>
20060   </example>
20061*/
20062function limitToFilter() {
20063  return function(input, limit, begin) {
20064    if (Math.abs(Number(limit)) === Infinity) {
20065      limit = Number(limit);
20066    } else {
20067      limit = toInt(limit);
20068    }
20069    if (isNaN(limit)) return input;
20070
20071    if (isNumber(input)) input = input.toString();
20072    if (!isArray(input) && !isString(input)) return input;
20073
20074    begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
20075    begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
20076
20077    if (limit >= 0) {
20078      return input.slice(begin, begin + limit);
20079    } else {
20080      if (begin === 0) {
20081        return input.slice(limit, input.length);
20082      } else {
20083        return input.slice(Math.max(0, begin + limit), begin);
20084      }
20085    }
20086  };
20087}
20088
20089/**
20090 * @ngdoc filter
20091 * @name orderBy
20092 * @kind function
20093 *
20094 * @description
20095 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
20096 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
20097 * as expected, make sure they are actually being saved as numbers and not strings.
20098 * Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
20099 *
20100 * @param {Array} array The array (or array-like object) to sort.
20101 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
20102 *    used by the comparator to determine the order of elements.
20103 *
20104 *    Can be one of:
20105 *
20106 *    - `function`: Getter function. The result of this function will be sorted using the
20107 *      `<`, `===`, `>` operator.
20108 *    - `string`: An Angular expression. The result of this expression is used to compare elements
20109 *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
20110 *      3 first characters of a property called `name`). The result of a constant expression
20111 *      is interpreted as a property name to be used in comparisons (for example `"special name"`
20112 *      to sort object by the value of their `special name` property). An expression can be
20113 *      optionally prefixed with `+` or `-` to control ascending or descending sort order
20114 *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
20115 *      element itself is used to compare where sorting.
20116 *    - `Array`: An array of function or string predicates. The first predicate in the array
20117 *      is used for sorting, but when two items are equivalent, the next predicate is used.
20118 *
20119 *    If the predicate is missing or empty then it defaults to `'+'`.
20120 *
20121 * @param {boolean=} reverse Reverse the order of the array.
20122 * @returns {Array} Sorted copy of the source array.
20123 *
20124 *
20125 * @example
20126 * The example below demonstrates a simple ngRepeat, where the data is sorted
20127 * by age in descending order (predicate is set to `'-age'`).
20128 * `reverse` is not set, which means it defaults to `false`.
20129   <example module="orderByExample">
20130     <file name="index.html">
20131       <div ng-controller="ExampleController">
20132         <table class="friend">
20133           <tr>
20134             <th>Name</th>
20135             <th>Phone Number</th>
20136             <th>Age</th>
20137           </tr>
20138           <tr ng-repeat="friend in friends | orderBy:'-age'">
20139             <td>{{friend.name}}</td>
20140             <td>{{friend.phone}}</td>
20141             <td>{{friend.age}}</td>
20142           </tr>
20143         </table>
20144       </div>
20145     </file>
20146     <file name="script.js">
20147       angular.module('orderByExample', [])
20148         .controller('ExampleController', ['$scope', function($scope) {
20149           $scope.friends =
20150               [{name:'John', phone:'555-1212', age:10},
20151                {name:'Mary', phone:'555-9876', age:19},
20152                {name:'Mike', phone:'555-4321', age:21},
20153                {name:'Adam', phone:'555-5678', age:35},
20154                {name:'Julie', phone:'555-8765', age:29}];
20155         }]);
20156     </file>
20157   </example>
20158 *
20159 * The predicate and reverse parameters can be controlled dynamically through scope properties,
20160 * as shown in the next example.
20161 * @example
20162   <example module="orderByExample">
20163     <file name="index.html">
20164       <div ng-controller="ExampleController">
20165         <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
20166         <hr/>
20167         <button ng-click="predicate=''">Set to unsorted</button>
20168         <table class="friend">
20169           <tr>
20170            <th>
20171                <button ng-click="order('name')">Name</button>
20172                <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
20173            </th>
20174            <th>
20175                <button ng-click="order('phone')">Phone Number</button>
20176                <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
20177            </th>
20178            <th>
20179                <button ng-click="order('age')">Age</button>
20180                <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
20181            </th>
20182           </tr>
20183           <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
20184             <td>{{friend.name}}</td>
20185             <td>{{friend.phone}}</td>
20186             <td>{{friend.age}}</td>
20187           </tr>
20188         </table>
20189       </div>
20190     </file>
20191     <file name="script.js">
20192       angular.module('orderByExample', [])
20193         .controller('ExampleController', ['$scope', function($scope) {
20194           $scope.friends =
20195               [{name:'John', phone:'555-1212', age:10},
20196                {name:'Mary', phone:'555-9876', age:19},
20197                {name:'Mike', phone:'555-4321', age:21},
20198                {name:'Adam', phone:'555-5678', age:35},
20199                {name:'Julie', phone:'555-8765', age:29}];
20200           $scope.predicate = 'age';
20201           $scope.reverse = true;
20202           $scope.order = function(predicate) {
20203             $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
20204             $scope.predicate = predicate;
20205           };
20206         }]);
20207      </file>
20208     <file name="style.css">
20209       .sortorder:after {
20210         content: '\25b2';
20211       }
20212       .sortorder.reverse:after {
20213         content: '\25bc';
20214       }
20215     </file>
20216   </example>
20217 *
20218 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
20219 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
20220 * desired parameters.
20221 *
20222 * Example:
20223 *
20224 * @example
20225  <example module="orderByExample">
20226    <file name="index.html">
20227    <div ng-controller="ExampleController">
20228      <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
20229      <table class="friend">
20230        <tr>
20231          <th>
20232              <button ng-click="order('name')">Name</button>
20233              <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
20234          </th>
20235          <th>
20236              <button ng-click="order('phone')">Phone Number</button>
20237              <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
20238          </th>
20239          <th>
20240              <button ng-click="order('age')">Age</button>
20241              <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
20242          </th>
20243        </tr>
20244        <tr ng-repeat="friend in friends">
20245          <td>{{friend.name}}</td>
20246          <td>{{friend.phone}}</td>
20247          <td>{{friend.age}}</td>
20248        </tr>
20249      </table>
20250    </div>
20251    </file>
20252
20253    <file name="script.js">
20254      angular.module('orderByExample', [])
20255        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
20256          var orderBy = $filter('orderBy');
20257          $scope.friends = [
20258            { name: 'John',    phone: '555-1212',    age: 10 },
20259            { name: 'Mary',    phone: '555-9876',    age: 19 },
20260            { name: 'Mike',    phone: '555-4321',    age: 21 },
20261            { name: 'Adam',    phone: '555-5678',    age: 35 },
20262            { name: 'Julie',   phone: '555-8765',    age: 29 }
20263          ];
20264          $scope.order = function(predicate) {
20265            $scope.predicate = predicate;
20266            $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
20267            $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
20268          };
20269          $scope.order('age', true);
20270        }]);
20271    </file>
20272
20273    <file name="style.css">
20274       .sortorder:after {
20275         content: '\25b2';
20276       }
20277       .sortorder.reverse:after {
20278         content: '\25bc';
20279       }
20280    </file>
20281</example>
20282 */
20283orderByFilter.$inject = ['$parse'];
20284function orderByFilter($parse) {
20285  return function(array, sortPredicate, reverseOrder) {
20286
20287    if (array == null) return array;
20288    if (!isArrayLike(array)) {
20289      throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
20290    }
20291
20292    if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
20293    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
20294
20295    var predicates = processPredicates(sortPredicate, reverseOrder);
20296    // Add a predicate at the end that evaluates to the element index. This makes the
20297    // sort stable as it works as a tie-breaker when all the input predicates cannot
20298    // distinguish between two elements.
20299    predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
20300
20301    // The next three lines are a version of a Swartzian Transform idiom from Perl
20302    // (sometimes called the Decorate-Sort-Undecorate idiom)
20303    // See https://en.wikipedia.org/wiki/Schwartzian_transform
20304    var compareValues = Array.prototype.map.call(array, getComparisonObject);
20305    compareValues.sort(doComparison);
20306    array = compareValues.map(function(item) { return item.value; });
20307
20308    return array;
20309
20310    function getComparisonObject(value, index) {
20311      return {
20312        value: value,
20313        predicateValues: predicates.map(function(predicate) {
20314          return getPredicateValue(predicate.get(value), index);
20315        })
20316      };
20317    }
20318
20319    function doComparison(v1, v2) {
20320      var result = 0;
20321      for (var index=0, length = predicates.length; index < length; ++index) {
20322        result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
20323        if (result) break;
20324      }
20325      return result;
20326    }
20327  };
20328
20329  function processPredicates(sortPredicate, reverseOrder) {
20330    reverseOrder = reverseOrder ? -1 : 1;
20331    return sortPredicate.map(function(predicate) {
20332      var descending = 1, get = identity;
20333
20334      if (isFunction(predicate)) {
20335        get = predicate;
20336      } else if (isString(predicate)) {
20337        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
20338          descending = predicate.charAt(0) == '-' ? -1 : 1;
20339          predicate = predicate.substring(1);
20340        }
20341        if (predicate !== '') {
20342          get = $parse(predicate);
20343          if (get.constant) {
20344            var key = get();
20345            get = function(value) { return value[key]; };
20346          }
20347        }
20348      }
20349      return { get: get, descending: descending * reverseOrder };
20350    });
20351  }
20352
20353  function isPrimitive(value) {
20354    switch (typeof value) {
20355      case 'number': /* falls through */
20356      case 'boolean': /* falls through */
20357      case 'string':
20358        return true;
20359      default:
20360        return false;
20361    }
20362  }
20363
20364  function objectValue(value, index) {
20365    // If `valueOf` is a valid function use that
20366    if (typeof value.valueOf === 'function') {
20367      value = value.valueOf();
20368      if (isPrimitive(value)) return value;
20369    }
20370    // If `toString` is a valid function and not the one from `Object.prototype` use that
20371    if (hasCustomToString(value)) {
20372      value = value.toString();
20373      if (isPrimitive(value)) return value;
20374    }
20375    // We have a basic object so we use the position of the object in the collection
20376    return index;
20377  }
20378
20379  function getPredicateValue(value, index) {
20380    var type = typeof value;
20381    if (value === null) {
20382      type = 'string';
20383      value = 'null';
20384    } else if (type === 'string') {
20385      value = value.toLowerCase();
20386    } else if (type === 'object') {
20387      value = objectValue(value, index);
20388    }
20389    return { value: value, type: type };
20390  }
20391
20392  function compare(v1, v2) {
20393    var result = 0;
20394    if (v1.type === v2.type) {
20395      if (v1.value !== v2.value) {
20396        result = v1.value < v2.value ? -1 : 1;
20397      }
20398    } else {
20399      result = v1.type < v2.type ? -1 : 1;
20400    }
20401    return result;
20402  }
20403}
20404
20405function ngDirective(directive) {
20406  if (isFunction(directive)) {
20407    directive = {
20408      link: directive
20409    };
20410  }
20411  directive.restrict = directive.restrict || 'AC';
20412  return valueFn(directive);
20413}
20414
20415/**
20416 * @ngdoc directive
20417 * @name a
20418 * @restrict E
20419 *
20420 * @description
20421 * Modifies the default behavior of the html A tag so that the default action is prevented when
20422 * the href attribute is empty.
20423 *
20424 * This change permits the easy creation of action links with the `ngClick` directive
20425 * without changing the location or causing page reloads, e.g.:
20426 * `<a href="" ng-click="list.addItem()">Add Item</a>`
20427 */
20428var htmlAnchorDirective = valueFn({
20429  restrict: 'E',
20430  compile: function(element, attr) {
20431    if (!attr.href && !attr.xlinkHref) {
20432      return function(scope, element) {
20433        // If the linked element is not an anchor tag anymore, do nothing
20434        if (element[0].nodeName.toLowerCase() !== 'a') return;
20435
20436        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
20437        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
20438                   'xlink:href' : 'href';
20439        element.on('click', function(event) {
20440          // if we have no href url, then don't navigate anywhere.
20441          if (!element.attr(href)) {
20442            event.preventDefault();
20443          }
20444        });
20445      };
20446    }
20447  }
20448});
20449
20450/**
20451 * @ngdoc directive
20452 * @name ngHref
20453 * @restrict A
20454 * @priority 99
20455 *
20456 * @description
20457 * Using Angular markup like `{{hash}}` in an href attribute will
20458 * make the link go to the wrong URL if the user clicks it before
20459 * Angular has a chance to replace the `{{hash}}` markup with its
20460 * value. Until Angular replaces the markup the link will be broken
20461 * and will most likely return a 404 error. The `ngHref` directive
20462 * solves this problem.
20463 *
20464 * The wrong way to write it:
20465 * ```html
20466 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
20467 * ```
20468 *
20469 * The correct way to write it:
20470 * ```html
20471 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
20472 * ```
20473 *
20474 * @element A
20475 * @param {template} ngHref any string which can contain `{{}}` markup.
20476 *
20477 * @example
20478 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
20479 * in links and their different behaviors:
20480    <example>
20481      <file name="index.html">
20482        <input ng-model="value" /><br />
20483        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
20484        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
20485        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
20486        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
20487        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
20488        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
20489      </file>
20490      <file name="protractor.js" type="protractor">
20491        it('should execute ng-click but not reload when href without value', function() {
20492          element(by.id('link-1')).click();
20493          expect(element(by.model('value')).getAttribute('value')).toEqual('1');
20494          expect(element(by.id('link-1')).getAttribute('href')).toBe('');
20495        });
20496
20497        it('should execute ng-click but not reload when href empty string', function() {
20498          element(by.id('link-2')).click();
20499          expect(element(by.model('value')).getAttribute('value')).toEqual('2');
20500          expect(element(by.id('link-2')).getAttribute('href')).toBe('');
20501        });
20502
20503        it('should execute ng-click and change url when ng-href specified', function() {
20504          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
20505
20506          element(by.id('link-3')).click();
20507
20508          // At this point, we navigate away from an Angular page, so we need
20509          // to use browser.driver to get the base webdriver.
20510
20511          browser.wait(function() {
20512            return browser.driver.getCurrentUrl().then(function(url) {
20513              return url.match(/\/123$/);
20514            });
20515          }, 5000, 'page should navigate to /123');
20516        });
20517
20518        it('should execute ng-click but not reload when href empty string and name specified', function() {
20519          element(by.id('link-4')).click();
20520          expect(element(by.model('value')).getAttribute('value')).toEqual('4');
20521          expect(element(by.id('link-4')).getAttribute('href')).toBe('');
20522        });
20523
20524        it('should execute ng-click but not reload when no href but name specified', function() {
20525          element(by.id('link-5')).click();
20526          expect(element(by.model('value')).getAttribute('value')).toEqual('5');
20527          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
20528        });
20529
20530        it('should only change url when only ng-href', function() {
20531          element(by.model('value')).clear();
20532          element(by.model('value')).sendKeys('6');
20533          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
20534
20535          element(by.id('link-6')).click();
20536
20537          // At this point, we navigate away from an Angular page, so we need
20538          // to use browser.driver to get the base webdriver.
20539          browser.wait(function() {
20540            return browser.driver.getCurrentUrl().then(function(url) {
20541              return url.match(/\/6$/);
20542            });
20543          }, 5000, 'page should navigate to /6');
20544        });
20545      </file>
20546    </example>
20547 */
20548
20549/**
20550 * @ngdoc directive
20551 * @name ngSrc
20552 * @restrict A
20553 * @priority 99
20554 *
20555 * @description
20556 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
20557 * work right: The browser will fetch from the URL with the literal
20558 * text `{{hash}}` until Angular replaces the expression inside
20559 * `{{hash}}`. The `ngSrc` directive solves this problem.
20560 *
20561 * The buggy way to write it:
20562 * ```html
20563 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
20564 * ```
20565 *
20566 * The correct way to write it:
20567 * ```html
20568 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
20569 * ```
20570 *
20571 * @element IMG
20572 * @param {template} ngSrc any string which can contain `{{}}` markup.
20573 */
20574
20575/**
20576 * @ngdoc directive
20577 * @name ngSrcset
20578 * @restrict A
20579 * @priority 99
20580 *
20581 * @description
20582 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
20583 * work right: The browser will fetch from the URL with the literal
20584 * text `{{hash}}` until Angular replaces the expression inside
20585 * `{{hash}}`. The `ngSrcset` directive solves this problem.
20586 *
20587 * The buggy way to write it:
20588 * ```html
20589 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
20590 * ```
20591 *
20592 * The correct way to write it:
20593 * ```html
20594 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
20595 * ```
20596 *
20597 * @element IMG
20598 * @param {template} ngSrcset any string which can contain `{{}}` markup.
20599 */
20600
20601/**
20602 * @ngdoc directive
20603 * @name ngDisabled
20604 * @restrict A
20605 * @priority 100
20606 *
20607 * @description
20608 *
20609 * This directive sets the `disabled` attribute on the element if the
20610 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
20611 *
20612 * A special directive is necessary because we cannot use interpolation inside the `disabled`
20613 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20614 *
20615 * @example
20616    <example>
20617      <file name="index.html">
20618        <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
20619        <button ng-model="button" ng-disabled="checked">Button</button>
20620      </file>
20621      <file name="protractor.js" type="protractor">
20622        it('should toggle button', function() {
20623          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
20624          element(by.model('checked')).click();
20625          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
20626        });
20627      </file>
20628    </example>
20629 *
20630 * @element INPUT
20631 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
20632 *     then the `disabled` attribute will be set on the element
20633 */
20634
20635
20636/**
20637 * @ngdoc directive
20638 * @name ngChecked
20639 * @restrict A
20640 * @priority 100
20641 *
20642 * @description
20643 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
20644 *
20645 * Note that this directive should not be used together with {@link ngModel `ngModel`},
20646 * as this can lead to unexpected behavior.
20647 *
20648 * A special directive is necessary because we cannot use interpolation inside the `checked`
20649 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20650 *
20651 * @example
20652    <example>
20653      <file name="index.html">
20654        <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
20655        <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
20656      </file>
20657      <file name="protractor.js" type="protractor">
20658        it('should check both checkBoxes', function() {
20659          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
20660          element(by.model('master')).click();
20661          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
20662        });
20663      </file>
20664    </example>
20665 *
20666 * @element INPUT
20667 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
20668 *     then the `checked` attribute will be set on the element
20669 */
20670
20671
20672/**
20673 * @ngdoc directive
20674 * @name ngReadonly
20675 * @restrict A
20676 * @priority 100
20677 *
20678 * @description
20679 *
20680 * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
20681 *
20682 * A special directive is necessary because we cannot use interpolation inside the `readOnly`
20683 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20684 *
20685 * @example
20686    <example>
20687      <file name="index.html">
20688        <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
20689        <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
20690      </file>
20691      <file name="protractor.js" type="protractor">
20692        it('should toggle readonly attr', function() {
20693          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
20694          element(by.model('checked')).click();
20695          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
20696        });
20697      </file>
20698    </example>
20699 *
20700 * @element INPUT
20701 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
20702 *     then special attribute "readonly" will be set on the element
20703 */
20704
20705
20706/**
20707 * @ngdoc directive
20708 * @name ngSelected
20709 * @restrict A
20710 * @priority 100
20711 *
20712 * @description
20713 *
20714 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
20715 *
20716 * A special directive is necessary because we cannot use interpolation inside the `selected`
20717 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20718 *
20719 * @example
20720    <example>
20721      <file name="index.html">
20722        <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
20723        <select aria-label="ngSelected demo">
20724          <option>Hello!</option>
20725          <option id="greet" ng-selected="selected">Greetings!</option>
20726        </select>
20727      </file>
20728      <file name="protractor.js" type="protractor">
20729        it('should select Greetings!', function() {
20730          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
20731          element(by.model('selected')).click();
20732          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
20733        });
20734      </file>
20735    </example>
20736 *
20737 * @element OPTION
20738 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
20739 *     then special attribute "selected" will be set on the element
20740 */
20741
20742/**
20743 * @ngdoc directive
20744 * @name ngOpen
20745 * @restrict A
20746 * @priority 100
20747 *
20748 * @description
20749 *
20750 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
20751 *
20752 * A special directive is necessary because we cannot use interpolation inside the `open`
20753 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20754 *
20755 * @example
20756     <example>
20757       <file name="index.html">
20758         <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
20759         <details id="details" ng-open="open">
20760            <summary>Show/Hide me</summary>
20761         </details>
20762       </file>
20763       <file name="protractor.js" type="protractor">
20764         it('should toggle open', function() {
20765           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
20766           element(by.model('open')).click();
20767           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
20768         });
20769       </file>
20770     </example>
20771 *
20772 * @element DETAILS
20773 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
20774 *     then special attribute "open" will be set on the element
20775 */
20776
20777var ngAttributeAliasDirectives = {};
20778
20779// boolean attrs are evaluated
20780forEach(BOOLEAN_ATTR, function(propName, attrName) {
20781  // binding to multiple is not supported
20782  if (propName == "multiple") return;
20783
20784  function defaultLinkFn(scope, element, attr) {
20785    scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
20786      attr.$set(attrName, !!value);
20787    });
20788  }
20789
20790  var normalized = directiveNormalize('ng-' + attrName);
20791  var linkFn = defaultLinkFn;
20792
20793  if (propName === 'checked') {
20794    linkFn = function(scope, element, attr) {
20795      // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
20796      if (attr.ngModel !== attr[normalized]) {
20797        defaultLinkFn(scope, element, attr);
20798      }
20799    };
20800  }
20801
20802  ngAttributeAliasDirectives[normalized] = function() {
20803    return {
20804      restrict: 'A',
20805      priority: 100,
20806      link: linkFn
20807    };
20808  };
20809});
20810
20811// aliased input attrs are evaluated
20812forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
20813  ngAttributeAliasDirectives[ngAttr] = function() {
20814    return {
20815      priority: 100,
20816      link: function(scope, element, attr) {
20817        //special case ngPattern when a literal regular expression value
20818        //is used as the expression (this way we don't have to watch anything).
20819        if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
20820          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
20821          if (match) {
20822            attr.$set("ngPattern", new RegExp(match[1], match[2]));
20823            return;
20824          }
20825        }
20826
20827        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
20828          attr.$set(ngAttr, value);
20829        });
20830      }
20831    };
20832  };
20833});
20834
20835// ng-src, ng-srcset, ng-href are interpolated
20836forEach(['src', 'srcset', 'href'], function(attrName) {
20837  var normalized = directiveNormalize('ng-' + attrName);
20838  ngAttributeAliasDirectives[normalized] = function() {
20839    return {
20840      priority: 99, // it needs to run after the attributes are interpolated
20841      link: function(scope, element, attr) {
20842        var propName = attrName,
20843            name = attrName;
20844
20845        if (attrName === 'href' &&
20846            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
20847          name = 'xlinkHref';
20848          attr.$attr[name] = 'xlink:href';
20849          propName = null;
20850        }
20851
20852        attr.$observe(normalized, function(value) {
20853          if (!value) {
20854            if (attrName === 'href') {
20855              attr.$set(name, null);
20856            }
20857            return;
20858          }
20859
20860          attr.$set(name, value);
20861
20862          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
20863          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
20864          // to set the property as well to achieve the desired effect.
20865          // we use attr[attrName] value since $set can sanitize the url.
20866          if (msie && propName) element.prop(propName, attr[name]);
20867        });
20868      }
20869    };
20870  };
20871});
20872
20873/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20874 */
20875var nullFormCtrl = {
20876  $addControl: noop,
20877  $$renameControl: nullFormRenameControl,
20878  $removeControl: noop,
20879  $setValidity: noop,
20880  $setDirty: noop,
20881  $setPristine: noop,
20882  $setSubmitted: noop
20883},
20884SUBMITTED_CLASS = 'ng-submitted';
20885
20886function nullFormRenameControl(control, name) {
20887  control.$name = name;
20888}
20889
20890/**
20891 * @ngdoc type
20892 * @name form.FormController
20893 *
20894 * @property {boolean} $pristine True if user has not interacted with the form yet.
20895 * @property {boolean} $dirty True if user has already interacted with the form.
20896 * @property {boolean} $valid True if all of the containing forms and controls are valid.
20897 * @property {boolean}
20897 $invalid True if at least one containing control or form is invalid.
20898 * @property {boolean} $pending True if at least one containing control or form is pending.
20899 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20900 *
20901 * @property {Object} $error Is an object hash, containing references to controls or
20902 *  forms with failing validators, where:
20903 *
20904 *  - keys are validation tokens (error names),
20905 *  - values are arrays of controls or forms that have a failing validator for given error name.
20906 *
20907 *  Built-in validation tokens:
20908 *
20909 *  - `email`
20910 *  - `max`
20911 *  - `maxlength`
20912 *  - `min`
20913 *  - `minlength`
20914 *  - `number`
20915 *  - `pattern`
20916 *  - `required`
20917 *  - `url`
20918 *  - `date`
20919 *  - `datetimelocal`
20920 *  - `time`
20921 *  - `week`
20922 *  - `month`
20923 *
20924 * @description
20925 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20926 * such as being valid/invalid or dirty/pristine.
20927 *
20928 * Each {@link ng.directive:form form} directive creates an instance
20929 * of `FormController`.
20930 *
20931 */
20932//asks for $scope to fool the BC controller module
20933FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20934function FormController(element, attrs, $scope, $animate, $interpolate) {
20935  var form = this,
20936      controls = [];
20937
20938  // init state
20939  form.$error = {};
20940  form.$$success = {};
20941  form.$pending = undefined;
20942  form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20943  form.$dirty = false;
20944  form.$pristine = true;
20945  form.$valid = true;
20946  form.$invalid = false;
20947  form.$submitted = false;
20948  form.$$parentForm = nullFormCtrl;
20949
20950  /**
20951   * @ngdoc method
20952   * @name form.FormController#$rollbackViewValue
20953   *
20954   * @description
20955   * Rollback all form controls pending updates to the `$modelValue`.
20956   *
20957   * Updates may be pending by a debounced event or because the input is waiting for a some future
20958   * event defined in `ng-model-options`. This method is typically needed by the reset button of
20959   * a form that uses `ng-model-options` to pend updates.
20960   */
20961  form.$rollbackViewValue = function() {
20962    forEach(controls, function(control) {
20963      control.$rollbackViewValue();
20964    });
20965  };
20966
20967  /**
20968   * @ngdoc method
20969   * @name form.FormController#$commitViewValue
20970   *
20971   * @description
20972   * Commit all form controls pending updates to the `$modelValue`.
20973   *
20974   * Updates may be pending by a debounced event or because the input is waiting for a some future
20975   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20976   * usually handles calling this in response to input events.
20977   */
20978  form.$commitViewValue = function() {
20979    forEach(controls, function(control) {
20980      control.$commitViewValue();
20981    });
20982  };
20983
20984  /**
20985   * @ngdoc method
20986   * @name form.FormController#$addControl
20987   * @param {object} control control object, either a {@link form.FormController} or an
20988   * {@link ngModel.NgModelController}
20989   *
20990   * @description
20991   * Register a control with the form. Input elements using ngModelController do this automatically
20992   * when they are linked.
20993   *
20994   * Note that the current state of the control will not be reflected on the new parent form. This
20995   * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20996   * state.
20997   *
20998   * However, if the method is used programmatically, for example by adding dynamically created controls,
20999   * or controls that have been previously removed without destroying their corresponding DOM element,
21000   * it's the developers responsibility to make sure the current state propagates to the parent form.
21001   *
21002   * For example, if an input control is added that is already `$dirty` and has `$error` properties,
21003   * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
21004   */
21005  form.$addControl = function(control) {
21006    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
21007    // and not added to the scope.  Now we throw an error.
21008    assertNotHasOwnProperty(control.$name, 'input');
21009    controls.push(control);
21010
21011    if (control.$name) {
21012      form[control.$name] = control;
21013    }
21014
21015    control.$$parentForm = form;
21016  };
21017
21018  // Private API: rename a form control
21019  form.$$renameControl = function(control, newName) {
21020    var oldName = control.$name;
21021
21022    if (form[oldName] === control) {
21023      delete form[oldName];
21024    }
21025    form[newName] = control;
21026    control.$name = newName;
21027  };
21028
21029  /**
21030   * @ngdoc method
21031   * @name form.FormController#$removeControl
21032   * @param {object} control control object, either a {@link form.FormController} or an
21033   * {@link ngModel.NgModelController}
21034   *
21035   * @description
21036   * Deregister a control from the form.
21037   *
21038   * Input elements using ngModelController do this automatically when they are destroyed.
21039   *
21040   * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
21041   * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
21042   * different from case to case. For example, removing the only `$dirty` control from a form may or
21043   * may not mean that the form is still `$dirty`.
21044   */
21045  form.$removeControl = function(control) {
21046    if (control.$name && form[control.$name] === control) {
21047      delete form[control.$name];
21048    }
21049    forEach(form.$pending, function(value, name) {
21050      form.$setValidity(name, null, control);
21051    });
21052    forEach(form.$error, function(value, name) {
21053      form.$setValidity(name, null, control);
21054    });
21055    forEach(form.$$success, function(value, name) {
21056      form.$setValidity(name, null, control);
21057    });
21058
21059    arrayRemove(controls, control);
21060    control.$$parentForm = nullFormCtrl;
21061  };
21062
21063
21064  /**
21065   * @ngdoc method
21066   * @name form.FormController#$setValidity
21067   *
21068   * @description
21069   * Sets the validity of a form control.
21070   *
21071   * This method will also propagate to parent forms.
21072   */
21073  addSetValidityMethod({
21074    ctrl: this,
21075    $element: element,
21076    set: function(object, property, controller) {
21077      var list = object[property];
21078      if (!list) {
21079        object[property] = [controller];
21080      } else {
21081        var index = list.indexOf(controller);
21082        if (index === -1) {
21083          list.push(controller);
21084        }
21085      }
21086    },
21087    unset: function(object, property, controller) {
21088      var list = object[property];
21089      if (!list) {
21090        return;
21091      }
21092      arrayRemove(list, controller);
21093      if (list.length === 0) {
21094        delete object[property];
21095      }
21096    },
21097    $animate: $animate
21098  });
21099
21100  /**
21101   * @ngdoc method
21102   * @name form.FormController#$setDirty
21103   *
21104   * @description
21105   * Sets the form to a dirty state.
21106   *
21107   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
21108   * state (ng-dirty class). This method will also propagate to parent forms.
21109   */
21110  form.$setDirty = function() {
21111    $animate.removeClass(element, PRISTINE_CLASS);
21112    $animate.addClass(element, DIRTY_CLASS);
21113    form.$dirty = true;
21114    form.$pristine = false;
21115    form.$$parentForm.$setDirty();
21116  };
21117
21118  /**
21119   * @ngdoc method
21120   * @name form.FormController#$setPristine
21121   *
21122   * @description
21123   * Sets the form to its pristine state.
21124   *
21125   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
21126   * state (ng-pristine class). This method will also propagate to all the controls contained
21127   * in this form.
21128   *
21129   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
21130   * saving or resetting it.
21131   */
21132  form.$setPristine = function() {
21133    $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
21134    form.$dirty = false;
21135    form.$pristine = true;
21136    form.$submitted = false;
21137    forEach(controls, function(control) {
21138      control.$setPristine();
21139    });
21140  };
21141
21142  /**
21143   * @ngdoc method
21144   * @name form.FormController#$setUntouched
21145   *
21146   * @description
21147   * Sets the form to its untouched state.
21148   *
21149   * This method can be called to remove the 'ng-touched' class and set the form controls to their
21150   * untouched state (ng-untouched class).
21151   *
21152   * Setting a form controls back to their untouched state is often useful when setting the form
21153   * back to its pristine state.
21154   */
21155  form.$setUntouched = function() {
21156    forEach(controls, function(control) {
21157      control.$setUntouched();
21158    });
21159  };
21160
21161  /**
21162   * @ngdoc method
21163   * @name form.FormController#$setSubmitted
21164   *
21165   * @description
21166   * Sets the form to its submitted state.
21167   */
21168  form.$setSubmitted = function() {
21169    $animate.addClass(element, SUBMITTED_CLASS);
21170    form.$submitted = true;
21171    form.$$parentForm.$setSubmitted();
21172  };
21173}
21174
21175/**
21176 * @ngdoc directive
21177 * @name ngForm
21178 * @restrict EAC
21179 *
21180 * @description
21181 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
21182 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
21183 * sub-group of controls needs to be determined.
21184 *
21185 * Note: the purpose of `ngForm` is to group controls,
21186 * but not to be a replacement for the `<form>` tag with all of its capabilities
21187 * (e.g. posting to the server, ...).
21188 *
21189 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
21190 *                       related scope, under this name.
21191 *
21192 */
21193
21194 /**
21195 * @ngdoc directive
21196 * @name form
21197 * @restrict E
21198 *
21199 * @description
21200 * Directive that instantiates
21201 * {@link form.FormController FormController}.
21202 *
21203 * If the `name` attribute is specified, the form controller is published onto the current scope under
21204 * this name.
21205 *
21206 * # Alias: {@link ng.directive:ngForm `ngForm`}
21207 *
21208 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
21209 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
21210 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
21211 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
21212 * of controls needs to be determined.
21213 *
21214 * # CSS classes
21215 *  - `ng-valid` is set if the form is valid.
21216 *  - `ng-invalid` is set if the form is invalid.
21217 *  - `ng-pending` is set if the form is pending.
21218 *  - `ng-pristine` is set if the form is pristine.
21219 *  - `ng-dirty` is set if the form is dirty.
21220 *  - `ng-submitted` is set if the form was submitted.
21221 *
21222 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
21223 *
21224 *
21225 * # Submitting a form and preventing the default action
21226 *
21227 * Since the role of forms in client-side Angular applications is different than in classical
21228 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
21229 * page reload that sends the data to the server. Instead some javascript logic should be triggered
21230 * to handle the form submission in an application-specific way.
21231 *
21232 * For this reason, Angular prevents the default action (form submission to the server) unless the
21233 * `<form>` element has an `action` attribute specified.
21234 *
21235 * You can use one of the following two ways to specify what javascript method should be called when
21236 * a form is submitted:
21237 *
21238 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
21239 * - {@link ng.directive:ngClick ngClick} directive on the first
21240  *  button or input field of type submit (input[type=submit])
21241 *
21242 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
21243 * or {@link ng.directive:ngClick ngClick} directives.
21244 * This is because of the following form submission rules in the HTML specification:
21245 *
21246 * - If a form has only one input field then hitting enter in this field triggers form submit
21247 * (`ngSubmit`)
21248 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
21249 * doesn't trigger submit
21250 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
21251 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
21252 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
21253 *
21254 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
21255 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
21256 * to have access to the updated model.
21257 *
21258 * ## Animation Hooks
21259 *
21260 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
21261 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
21262 * other validations that are performed within the form. Animations in ngForm are similar to how
21263 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
21264 * as JS animations.
21265 *
21266 * The following example shows a simple way to utilize CSS transitions to style a form element
21267 * that has been rendered as invalid after it has been validated:
21268 *
21269 * <pre>
21270 * //be sure to include ngAnimate as a module to hook into more
21271 * //advanced animations
21272 * .my-form {
21273 *   transition:0.5s linear all;
21274 *   background: white;
21275 * }
21276 * .my-form.ng-invalid {
21277 *   background: red;
21278 *   color:white;
21279 * }
21280 * </pre>
21281 *
21282 * @example
21283    <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
21284      <file name="index.html">
21285       <script>
21286         angular.module('formExample', [])
21287           .controller('FormController', ['$scope', function($scope) {
21288             $scope.userType = 'guest';
21289           }]);
21290       </script>
21291       <style>
21292        .my-form {
21293          transition:all linear 0.5s;
21294          background: transparent;
21295        }
21296        .my-form.ng-invalid {
21297          background: red;
21298        }
21299       </style>
21300       <form name="myForm" ng-controller="FormController" class="my-form">
21301         userType: <input name="input" ng-model="userType" required>
21302         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
21303         <code>userType = {{userType}}</code><br>
21304         <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
21305         <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
21306         <code>myForm.$valid = {{myForm.$valid}}</code><br>
21307         <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
21308        </form>
21309      </file>
21310      <file name="protractor.js" type="protractor">
21311        it('should initialize to model', function() {
21312          var userType = element(by.binding('userType'));
21313          var valid = element(by.binding('myForm.input.$valid'));
21314
21315          expect(userType.getText()).toContain('guest');
21316          expect(valid.getText()).toContain('true');
21317        });
21318
21319        it('should be invalid if empty', function() {
21320          var userType = element(by.binding('userType'));
21321          var valid = element(by.binding('myForm.input.$valid'));
21322          var userInput = element(by.model('userType'));
21323
21324          userInput.clear();
21325          userInput.sendKeys('');
21326
21327          expect(userType.getText()).toEqual('userType =');
21328          expect(valid.getText()).toContain('false');
21329        });
21330      </file>
21331    </example>
21332 *
21333 * @param {string=} name Name of the form. If specified, the form controller will be published into
21334 *                       related scope, under this name.
21335 */
21336var formDirectiveFactory = function(isNgForm) {
21337  return ['$timeout', '$parse', function($timeout, $parse) {
21338    var formDirective = {
21339      name: 'form',
21340      restrict: isNgForm ? 'EAC' : 'E',
21341      require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
21342      controller: FormController,
21343      compile: function ngFormCompile(formElement, attr) {
21344        // Setup initial state of the control
21345        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
21346
21347        var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
21348
21349        return {
21350          pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
21351            var controller = ctrls[0];
21352
21353            // if `action` attr is not present on the form, prevent the default action (submission)
21354            if (!('action' in attr)) {
21355              // we can't use jq events because if a form is destroyed during submission the default
21356              // action is not prevented. see #1238
21357              //
21358              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
21359              // page reload if the form was destroyed by submission of the form via a click handler
21360              // on a button in the form. Looks like an IE9 specific bug.
21361              var handleFormSubmission = function(event) {
21362                scope.$apply(function() {
21363                  controller.$commitViewValue();
21364                  controller.$setSubmitted();
21365                });
21366
21367                event.preventDefault();
21368              };
21369
21370              addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
21371
21372              // unregister the preventDefault listener so that we don't not leak memory but in a
21373              // way that will achieve the prevention of the default action.
21374              formElement.on('$destroy', function() {
21375                $timeout(function() {
21376                  removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
21377                }, 0, false);
21378              });
21379            }
21380
21381            var parentFormCtrl = ctrls[1] || controller.$$parentForm;
21382            parentFormCtrl.$addControl(controller);
21383
21384            var setter = nameAttr ? getSetter(controller.$name) : noop;
21385
21386            if (nameAttr) {
21387              setter(scope, controller);
21388              attr.$observe(nameAttr, function(newValue) {
21389                if (controller.$name === newValue) return;
21390                setter(scope, undefined);
21391                controller.$$parentForm.$$renameControl(controller, newValue);
21392                setter = getSetter(controller.$name);
21393                setter(scope, controller);
21394              });
21395            }
21396            formElement.on('$destroy', function() {
21397              controller.$$parentForm.$removeControl(controller);
21398              setter(scope, undefined);
21399              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
21400            });
21401          }
21402        };
21403      }
21404    };
21405
21406    return formDirective;
21407
21408    function getSetter(expression) {
21409      if (expression === '') {
21410        //create an assignable expression, so forms with an empty name can be renamed later
21411        return $parse('this[""]').assign;
21412      }
21413      return $parse(expression).assign || noop;
21414    }
21415  }];
21416};
21417
21418var formDirective = formDirectiveFactory();
21419var ngFormDirective = formDirectiveFactory(true);
21420
21421/* global VALID_CLASS: false,
21422  INVALID_CLASS: false,
21423  PRISTINE_CLASS: false,
21424  DIRTY_CLASS: false,
21425  UNTOUCHED_CLASS: false,
21426  TOUCHED_CLASS: false,
21427  ngModelMinErr: false,
21428*/
21429
21430// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
21431var 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)/;
21432// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
21433// Note: We are being more lenient, because browsers are too.
21434//   1. Scheme
21435//   2. Slashes
21436//   3. Username
21437//   4. Password
21438//   5. Hostname
21439//   6. Port
21440//   7. Path
21441//   8. Query
21442//   9. Fragment
21443//                 1111111111111111 222   333333    44444        555555555555555555555555    666     77777777     8888888     999
21444var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
21445var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
21446var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
21447var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
21448var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
21449var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
21450var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
21451var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
21452
21453var inputType = {
21454
21455  /**
21456   * @ngdoc input
21457   * @name input[text]
21458   *
21459   * @description
21460   * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
21461   *
21462   *
21463   * @param {string} ngModel Assignable angular expression to data-bind to.
21464   * @param {string=} name Property name of the form under which the control is published.
21465   * @param {string=} required Adds `required` validation error key if the value is not entered.
21466   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21467   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21468   *    `required` when you want to data-bind to the `required` attribute.
21469   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21470   *    minlength.
21471   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21472   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21473   *    any length.
21474   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21475   *    that contains the regular expression body that will be converted to a regular expression
21476   *    as in the ngPattern directive.
21477   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
21478   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
21479   *    If the expression evaluates to a RegExp object, then this is used directly.
21480   *    If the expression evaluates to a string, then it will be converted to a RegExp
21481   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21482   *    `new RegExp('^abc$')`.<br />
21483   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21484   *    start at the index of the last search's match, thus not taking the whole input value into
21485   *    account.
21486   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21487   *    interaction with the input element.
21488   * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
21489   *    This parameter is ignored for input[type=password] controls, which will never trim the
21490   *    input.
21491   *
21492   * @example
21493      <example name="text-input-directive" module="textInputExample">
21494        <file name="index.html">
21495         <script>
21496           angular.module('textInputExample', [])
21497             .controller('ExampleController', ['$scope', function($scope) {
21498               $scope.example = {
21499                 text: 'guest',
21500                 word: /^\s*\w*\s*$/
21501               };
21502             }]);
21503         </script>
21504         <form name="myForm" ng-controller="ExampleController">
21505           <label>Single word:
21506             <input type="text" name="input" ng-model="example.text"
21507                    ng-pattern="example.word" required ng-trim="false">
21508           </label>
21509           <div role="alert">
21510             <span class="error" ng-show="myForm.input.$error.required">
21511               Required!</span>
21512             <span class="error" ng-show="myForm.input.$error.pattern">
21513               Single word only!</span>
21514           </div>
21515           <tt>text = {{example.text}}</tt><br/>
21516           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21517           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21518           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21519           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21520          </form>
21521        </file>
21522        <file name="protractor.js" type="protractor">
21523          var text = element(by.binding('example.text'));
21524          var valid = element(by.binding('myForm.input.$valid'));
21525          var input = element(by.model('example.text'));
21526
21527          it('should initialize to model', function() {
21528            expect(text.getText()).toContain('guest');
21529            expect(valid.getText()).toContain('true');
21530          });
21531
21532          it('should be invalid if empty', function() {
21533            input.clear();
21534            input.sendKeys('');
21535
21536            expect(text.getText()).toEqual('text =');
21537            expect(valid.getText()).toContain('false');
21538          });
21539
21540          it('should be invalid if multi word', function() {
21541            input.clear();
21542            input.sendKeys('hello world');
21543
21544            expect(valid.getText()).toContain('false');
21545          });
21546        </file>
21547      </example>
21548   */
21549  'text': textInputType,
21550
21551    /**
21552     * @ngdoc input
21553     * @name input[date]
21554     *
21555     * @description
21556     * Input with date validation and transformation. In browsers that do not yet support
21557     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
21558     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
21559     * modern browsers do not yet support this input type, it is important to provide cues to users on the
21560     * expected input format via a placeholder or label.
21561     *
21562     * The model must always be a Date object, otherwise Angular will throw an error.
21563     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21564     *
21565     * The timezone to be used to read/write the `Date` instance in the model can be defined using
21566     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21567     *
21568     * @param {string} ngModel Assignable angular expression to data-bind to.
21569     * @param {string=} name Property name of the form under which the control is published.
21570     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
21571     *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
21572     *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
21573     *   constraint validation.
21574     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
21575     *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
21576     *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
21577     *   constraint validation.
21578     * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
21579     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21580     * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
21581     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21582     * @param {string=} required Sets `required` validation error key if the value is not entered.
21583     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21584     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21585     *    `required` when you want to data-bind to the `required` attribute.
21586     * @param {string=} ngChange Angular expression to be executed when input changes due to user
21587     *    interaction with the input element.
21588     *
21589     * @example
21590     <example name="date-input-directive" module="dateInputExample">
21591     <file name="index.html">
21592       <script>
21593          angular.module('dateInputExample', [])
21594            .controller('DateController', ['$scope', function($scope) {
21595              $scope.example = {
21596                value: new Date(2013, 9, 22)
21597              };
21598            }]);
21599       </script>
21600       <form name="myForm" ng-controller="DateController as dateCtrl">
21601          <label for="exampleInput">Pick a date in 2013:</label>
21602          <input type="date" id="exampleInput" name="input" ng-model="example.value"
21603              placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
21604          <div role="alert">
21605            <span class="error" ng-show="myForm.input.$error.required">
21606                Required!</span>
21607            <span class="error" ng-show="myForm.input.$error.date">
21608                Not a valid date!</span>
21609           </div>
21610           <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
21611           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21612           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21613           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21614           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21615       </form>
21616     </file>
21617     <file name="protractor.js" type="protractor">
21618        var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
21619        var valid = element(by.binding('myForm.input.$valid'));
21620        var input = element(by.model('example.value'));
21621
21622        // currently protractor/webdriver does not support
21623        // sending keys to all known HTML5 input controls
21624        // for various browsers (see https://github.com/angular/protractor/issues/562).
21625        function setInput(val) {
21626          // set the value of the element and force validation.
21627          var scr = "var ipt = document.getElementById('exampleInput'); " +
21628          "ipt.value = '" + val + "';" +
21629          "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21630          browser.executeScript(scr);
21631        }
21632
21633        it('should initialize to model', function() {
21634          expect(value.getText()).toContain('2013-10-22');
21635          expect(valid.getText()).toContain('myForm.input.$valid = true');
21636        });
21637
21638        it('should be invalid if empty', function() {
21639          setInput('');
21640          expect(value.getText()).toEqual('value =');
21641          expect(valid.getText()).toContain('myForm.input.$valid = false');
21642        });
21643
21644        it('should be invalid if over max', function() {
21645          setInput('2015-01-01');
21646          expect(value.getText()).toContain('');
21647          expect(valid.getText()).toContain('myForm.input.$valid = false');
21648        });
21649     </file>
21650     </example>
21651     */
21652  'date': createDateInputType('date', DATE_REGEXP,
21653         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
21654         'yyyy-MM-dd'),
21655
21656   /**
21657    * @ngdoc input
21658    * @name input[datetime-local]
21659    *
21660    * @description
21661    * Input with datetime validation and transformation. In browsers that do not yet support
21662    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21663    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
21664    *
21665    * The model must always be a Date object, otherwise Angular will throw an error.
21666    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21667    *
21668    * The timezone to be used to read/write the `Date` instance in the model can be defined using
21669    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21670    *
21671    * @param {string} ngModel Assignable angular expression to data-bind to.
21672    * @param {string=} name Property name of the form under which the control is published.
21673    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21674    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
21675    *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
21676    *   Note that `min` will also add native HTML5 constraint validation.
21677    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21678    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
21679    *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
21680    *   Note that `max` will also add native HTML5 constraint validation.
21681    * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
21682    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21683    * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
21684    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21685    * @param {string=} required Sets `required` validation error key if the value is not entered.
21686    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21687    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21688    *    `required` when you want to data-bind to the `required` attribute.
21689    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21690    *    interaction with the input element.
21691    *
21692    * @example
21693    <example name="datetimelocal-input-directive" module="dateExample">
21694    <file name="index.html">
21695      <script>
21696        angular.module('dateExample', [])
21697          .controller('DateController', ['$scope', function($scope) {
21698            $scope.example = {
21699              value: new Date(2010, 11, 28, 14, 57)
21700            };
21701          }]);
21702      </script>
21703      <form name="myForm" ng-controller="DateController as dateCtrl">
21704        <label for="exampleInput">Pick a date between in 2013:</label>
21705        <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
21706            placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
21707        <div role="alert">
21708          <span class="error" ng-show="myForm.input.$error.required">
21709              Required!</span>
21710          <span class="error" ng-show="myForm.input.$error.datetimelocal">
21711              Not a valid date!</span>
21712        </div>
21713        <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
21714        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21715        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21716        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21717        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21718      </form>
21719    </file>
21720    <file name="protractor.js" type="protractor">
21721      var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
21722      var valid = element(by.binding('myForm.input.$valid'));
21723      var input = element(by.model('example.value'));
21724
21725      // currently protractor/webdriver does not support
21726      // sending keys to all known HTML5 input controls
21727      // for various browsers (https://github.com/angular/protractor/issues/562).
21728      function setInput(val) {
21729        // set the value of the element and force validation.
21730        var scr = "var ipt = document.getElementById('exampleInput'); " +
21731        "ipt.value = '" + val + "';" +
21732        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21733        browser.executeScript(scr);
21734      }
21735
21736      it('should initialize to model', function() {
21737        expect(value.getText()).toContain('2010-12-28T14:57:00');
21738        expect(valid.getText()).toContain('myForm.input.$valid = true');
21739      });
21740
21741      it('should be invalid if empty', function() {
21742        setInput('');
21743        expect(value.getText()).toEqual('value =');
21744        expect(valid.getText()).toContain('myForm.input.$valid = false');
21745      });
21746
21747      it('should be invalid if over max', function() {
21748        setInput('2015-01-01T23:59:00');
21749        expect(value.getText()).toContain('');
21750        expect(valid.getText()).toContain('myForm.input.$valid = false');
21751      });
21752    </file>
21753    </example>
21754    */
21755  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
21756      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
21757      'yyyy-MM-ddTHH:mm:ss.sss'),
21758
21759  /**
21760   * @ngdoc input
21761   * @name input[time]
21762   *
21763   * @description
21764   * Input with time validation and transformation. In browsers that do not yet support
21765   * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21766   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
21767   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
21768   *
21769   * The model must always be a Date object, otherwise Angular will throw an error.
21770   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21771   *
21772   * The timezone to be used to read/write the `Date` instance in the model can be defined using
21773   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21774   *
21775   * @param {string} ngModel Assignable angular expression to data-bind to.
21776   * @param {string=} name Property name of the form under which the control is published.
21777   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21778   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21779   *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
21780   *   native HTML5 constraint validation.
21781   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21782   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21783   *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
21784   *   native HTML5 constraint validation.
21785   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
21786   *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21787   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
21788   *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21789   * @param {string=} required Sets `required` validation error key if the value is not entered.
21790   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21791   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21792   *    `required` when you want to data-bind to the `required` attribute.
21793   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21794   *    interaction with the input element.
21795   *
21796   * @example
21797   <example name="time-input-directive" module="timeExample">
21798   <file name="index.html">
21799     <script>
21800      angular.module('timeExample', [])
21801        .controller('DateController', ['$scope', function($scope) {
21802          $scope.example = {
21803            value: new Date(1970, 0, 1, 14, 57, 0)
21804          };
21805        }]);
21806     </script>
21807     <form name="myForm" ng-controller="DateController as dateCtrl">
21808        <label for="exampleInput">Pick a between 8am and 5pm:</label>
21809        <input type="time" id="exampleInput" name="input" ng-model="example.value"
21810            placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
21811        <div role="alert">
21812          <span class="error" ng-show="myForm.input.$error.required">
21813              Required!</span>
21814          <span class="error" ng-show="myForm.input.$error.time">
21815              Not a valid date!</span>
21816        </div>
21817        <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
21818        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21819        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21820        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21821        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21822     </form>
21823   </file>
21824   <file name="protractor.js" type="protractor">
21825      var value = element(by.binding('example.value | date: "HH:mm:ss"'));
21826      var valid = element(by.binding('myForm.input.$valid'));
21827      var input = element(by.model('example.value'));
21828
21829      // currently protractor/webdriver does not support
21830      // sending keys to all known HTML5 input controls
21831      // for various browsers (https://github.com/angular/protractor/issues/562).
21832      function setInput(val) {
21833        // set the value of the element and force validation.
21834        var scr = "var ipt = document.getElementById('exampleInput'); " +
21835        "ipt.value = '" + val + "';" +
21836        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21837        browser.executeScript(scr);
21838      }
21839
21840      it('should initialize to model', function() {
21841        expect(value.getText()).toContain('14:57:00');
21842        expect(valid.getText()).toContain('myForm.input.$valid = true');
21843      });
21844
21845      it('should be invalid if empty', function() {
21846        setInput('');
21847        expect(value.getText()).toEqual('value =');
21848        expect(valid.getText()).toContain('myForm.input.$valid = false');
21849      });
21850
21851      it('should be invalid if over max', function() {
21852        setInput('23:59:00');
21853        expect(value.getText()).toContain('');
21854        expect(valid.getText()).toContain('myForm.input.$valid = false');
21855      });
21856   </file>
21857   </example>
21858   */
21859  'time': createDateInputType('time', TIME_REGEXP,
21860      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
21861     'HH:mm:ss.sss'),
21862
21863   /**
21864    * @ngdoc input
21865    * @name input[week]
21866    *
21867    * @description
21868    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
21869    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21870    * week format (yyyy-W##), for example: `2013-W02`.
21871    *
21872    * The model must always be a Date object, otherwise Angular will throw an error.
21873    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21874    *
21875    * The timezone to be used to read/write the `Date` instance in the model can be defined using
21876    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21877    *
21878    * @param {string} ngModel Assignable angular expression to data-bind to.
21879    * @param {string=} name Property name of the form under which the control is published.
21880    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21881    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21882    *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21883    *   native HTML5 constraint validation.
21884    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21885    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21886    *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21887    *   native HTML5 constraint validation.
21888    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21889    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21890    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21891    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21892    * @param {string=} required Sets `required` validation error key if the value is not entered.
21893    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21894    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21895    *    `required` when you want to data-bind to the `required` attribute.
21896    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21897    *    interaction with the input element.
21898    *
21899    * @example
21900    <example name="week-input-directive" module="weekExample">
21901    <file name="index.html">
21902      <script>
21903      angular.module('weekExample', [])
21904        .controller('DateController', ['$scope', function($scope) {
21905          $scope.example = {
21906            value: new Date(2013, 0, 3)
21907          };
21908        }]);
21909      </script>
21910      <form name="myForm" ng-controller="DateController as dateCtrl">
21911        <label>Pick a date between in 2013:
21912          <input id="exampleInput" type="week" name="input" ng-model="example.value"
21913                 placeholder="YYYY-W##" min="2012-W32"
21914                 max="2013-W52" required />
21915        </label>
21916        <div role="alert">
21917          <span class="error" ng-show="myForm.input.$error.required">
21918              Required!</span>
21919          <span class="error" ng-show="myForm.input.$error.week">
21920              Not a valid date!</span>
21921        </div>
21922        <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21923        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21924        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21925        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21926        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21927      </form>
21928    </file>
21929    <file name="protractor.js" type="protractor">
21930      var value = element(by.binding('example.value | date: "yyyy-Www"'));
21931      var valid = element(by.binding('myForm.input.$valid'));
21932      var input = element(by.model('example.value'));
21933
21934      // currently protractor/webdriver does not support
21935      // sending keys to all known HTML5 input controls
21936      // for various browsers (https://github.com/angular/protractor/issues/562).
21937      function setInput(val) {
21938        // set the value of the element and force validation.
21939        var scr = "var ipt = document.getElementById('exampleInput'); " +
21940        "ipt.value = '" + val + "';" +
21941        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21942        browser.executeScript(scr);
21943      }
21944
21945      it('should initialize to model', function() {
21946        expect(value.getText()).toContain('2013-W01');
21947        expect(valid.getText()).toContain('myForm.input.$valid = true');
21948      });
21949
21950      it('should be invalid if empty', function() {
21951        setInput('');
21952        expect(value.getText()).toEqual('value =');
21953        expect(valid.getText()).toContain('myForm.input.$valid = false');
21954      });
21955
21956      it('should be invalid if over max', function() {
21957        setInput('2015-W01');
21958        expect(value.getText()).toContain('');
21959        expect(valid.getText()).toContain('myForm.input.$valid = false');
21960      });
21961    </file>
21962    </example>
21963    */
21964  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21965
21966  /**
21967   * @ngdoc input
21968   * @name input[month]
21969   *
21970   * @description
21971   * Input with month validation and transformation. In browsers that do not yet support
21972   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21973   * month format (yyyy-MM), for example: `2009-01`.
21974   *
21975   * The model must always be a Date object, otherwise Angular will throw an error.
21976   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21977   * If the model is not set to the first of the month, the next view to model update will set it
21978   * to the first of the month.
21979   *
21980   * The timezone to be used to read/write the `Date` instance in the model can be defined using
21981   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21982   *
21983   * @param {string} ngModel Assignable angular expression to data-bind to.
21984   * @param {string=} name Property name of the form under which the control is published.
21985   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21986   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21987   *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21988   *   native HTML5 constraint validation.
21989   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21990   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21991   *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21992   *   native HTML5 constraint validation.
21993   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21994   *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21995   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21996   *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21997
21998   * @param {string=} required Sets `required` validation error key if the value is not entered.
21999   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22000   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22001   *    `required` when you want to data-bind to the `required` attribute.
22002   * @param {string=} ngChange Angular expression to be executed when input changes due to user
22003   *    interaction with the input element.
22004   *
22005   * @example
22006   <example name="month-input-directive" module="monthExample">
22007   <file name="index.html">
22008     <script>
22009      angular.module('monthExample', [])
22010        .controller('DateController', ['$scope', function($scope) {
22011          $scope.example = {
22012            value: new Date(2013, 9, 1)
22013          };
22014        }]);
22015     </script>
22016     <form name="myForm" ng-controller="DateController as dateCtrl">
22017       <label for="exampleInput">Pick a month in 2013:</label>
22018       <input id="exampleInput" type="month" name="input" ng-model="example.value"
22019          placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
22020       <div role="alert">
22021         <span class="error" ng-show="myForm.input.$error.required">
22022            Required!</span>
22023         <span class="error" ng-show="myForm.input.$error.month">
22024            Not a valid month!</span>
22025       </div>
22026       <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
22027       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22028       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22029       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22030       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22031     </form>
22032   </file>
22033   <file name="protractor.js" type="protractor">
22034      var value = element(by.binding('example.value | date: "yyyy-MM"'));
22035      var valid = element(by.binding('myForm.input.$valid'));
22036      var input = element(by.model('example.value'));
22037
22038      // currently protractor/webdriver does not support
22039      // sending keys to all known HTML5 input controls
22040      // for various browsers (https://github.com/angular/protractor/issues/562).
22041      function setInput(val) {
22042        // set the value of the element and force validation.
22043        var scr = "var ipt = document.getElementById('exampleInput'); " +
22044        "ipt.value = '" + val + "';" +
22045        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
22046        browser.executeScript(scr);
22047      }
22048
22049      it('should initialize to model', function() {
22050        expect(value.getText()).toContain('2013-10');
22051        expect(valid.getText()).toContain('myForm.input.$valid = true');
22052      });
22053
22054      it('should be invalid if empty', function() {
22055        setInput('');
22056        expect(value.getText()).toEqual('value =');
22057        expect(valid.getText()).toContain('myForm.input.$valid = false');
22058      });
22059
22060      it('should be invalid if over max', function() {
22061        setInput('2015-01');
22062        expect(value.getText()).toContain('');
22063        expect(valid.getText()).toContain('myForm.input.$valid = false');
22064      });
22065   </file>
22066   </example>
22067   */
22068  'month': createDateInputType('month', MONTH_REGEXP,
22069     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
22070     'yyyy-MM'),
22071
22072  /**
22073   * @ngdoc input
22074   * @name input[number]
22075   *
22076   * @description
22077   * Text input with number validation and transformation. Sets the `number` validation
22078   * error if not a valid number.
22079   *
22080   * <div class="alert alert-warning">
22081   * The model must always be of type `number` otherwise Angular will throw an error.
22082   * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
22083   * error docs for more information and an example of how to convert your model if necessary.
22084   * </div>
22085   *
22086   * ## Issues with HTML5 constraint validation
22087   *
22088   * In browsers that follow the
22089   * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
22090   * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
22091   * If a non-number is entered in the input, the browser will report the value as an empty string,
22092   * which means the view / model values in `ngModel` and subsequently the scope value
22093   * will also be an empty string.
22094   *
22095   *
22096   * @param {string} ngModel Assignable angular expression to data-bind to.
22097   * @param {string=} name Property name of the form under which the control is published.
22098   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
22099   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
22100   * @param {string=} required Sets `required` validation error key if the value is not entered.
22101   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22102   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22103   *    `required` when you want to data-bind to the `required` attribute.
22104   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22105   *    minlength.
22106   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22107   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
22108   *    any length.
22109   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
22110   *    that contains the regular expression body that will be converted to a regular expression
22111   *    as in the ngPattern directive.
22112   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22113   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22114   *    If the expression evaluates to a RegExp object, then this is used directly.
22115   *    If the expression evaluates to a string, then it will be converted to a RegExp
22116   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22117   *    `new RegExp('^abc$')`.<br />
22118   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22119   *    start at the index of the last search's match, thus not taking the whole input value into
22120   *    account.
22121   * @param {string=} ngChange Angular expression to be executed when input changes due to user
22122   *    interaction with the input element.
22123   *
22124   * @example
22125      <example name="number-input-directive" module="numberExample">
22126        <file name="index.html">
22127         <script>
22128           angular.module('numberExample', [])
22129             .controller('ExampleController', ['$scope', function($scope) {
22130               $scope.example = {
22131                 value: 12
22132               };
22133             }]);
22134         </script>
22135         <form name="myForm" ng-controller="ExampleController">
22136           <label>Number:
22137             <input type="number" name="input" ng-model="example.value"
22138                    min="0" max="99" required>
22139          </label>
22140           <div role="alert">
22141             <span class="error" ng-show="myForm.input.$error.required">
22142               Required!</span>
22143             <span class="error" ng-show="myForm.input.$error.number">
22144               Not valid number!</span>
22145           </div>
22146           <tt>value = {{example.value}}</tt><br/>
22147           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22148           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22149           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22150           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22151          </form>
22152        </file>
22153        <file name="protractor.js" type="protractor">
22154          var value = element(by.binding('example.value'));
22155          var valid = element(by.binding('myForm.input.$valid'));
22156          var input = element(by.model('example.value'));
22157
22158          it('should initialize to model', function() {
22159            expect(value.getText()).toContain('12');
22160            expect(valid.getText()).toContain('true');
22161          });
22162
22163          it('should be invalid if empty', function() {
22164            input.clear();
22165            input.sendKeys('');
22166            expect(value.getText()).toEqual('value =');
22167            expect(valid.getText()).toContain('false');
22168          });
22169
22170          it('should be invalid if over max', function() {
22171            input.clear();
22172            input.sendKeys('123');
22173            expect(value.getText()).toEqual('value =');
22174            expect(valid.getText()).toContain('false');
22175          });
22176        </file>
22177      </example>
22178   */
22179  'number': numberInputType,
22180
22181
22182  /**
22183   * @ngdoc input
22184   * @name input[url]
22185   *
22186   * @description
22187   * Text input with URL validation. Sets the `url` validation error key if the content is not a
22188   * valid URL.
22189   *
22190   * <div class="alert alert-warning">
22191   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
22192   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
22193   * the built-in validators (see the {@link guide/forms Forms guide})
22194   * </div>
22195   *
22196   * @param {string} ngModel Assignable angular expression to data-bind to.
22197   * @param {string=} name Property name of the form under which the control is published.
22198   * @param {string=} required Sets `required` validation error key if the value is not entered.
22199   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22200   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22201   *    `required` when you want to data-bind to the `required` attribute.
22202   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22203   *    minlength.
22204   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22205   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
22206   *    any length.
22207   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
22208   *    that contains the regular expression body that will be converted to a regular expression
22209   *    as in the ngPattern directive.
22210   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22211   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22212   *    If the expression evaluates to a RegExp object, then this is used directly.
22213   *    If the expression evaluates to a string, then it will be converted to a RegExp
22214   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22215   *    `new RegExp('^abc$')`.<br />
22216   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22217   *    start at the index of the last search's match, thus not taking the whole input value into
22218   *    account.
22219   * @param {string=} ngChange Angular expression to be executed when input changes due to user
22220   *    interaction with the input element.
22221   *
22222   * @example
22223      <example name="url-input-directive" module="urlExample">
22224        <file name="index.html">
22225         <script>
22226           angular.module('urlExample', [])
22227             .controller('ExampleController', ['$scope', function($scope) {
22228               $scope.url = {
22229                 text: 'http://google.com'
22230               };
22231             }]);
22232         </script>
22233         <form name="myForm" ng-controller="ExampleController">
22234           <label>URL:
22235             <input type="url" name="input" ng-model="url.text" required>
22236           <label>
22237           <div role="alert">
22238             <span class="error" ng-show="myForm.input.$error.required">
22239               Required!</span>
22240             <span class="error" ng-show="myForm.input.$error.url">
22241               Not valid url!</span>
22242           </div>
22243           <tt>text = {{url.text}}</tt><br/>
22244           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22245           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22246           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22247           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22248           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
22249          </form>
22250        </file>
22251        <file name="protractor.js" type="protractor">
22252          var text = element(by.binding('url.text'));
22253          var valid = element(by.binding('myForm.input.$valid'));
22254          var input = element(by.model('url.text'));
22255
22256          it('should initialize to model', function() {
22257            expect(text.getText()).toContain('http://google.com');
22258            expect(valid.getText()).toContain('true');
22259          });
22260
22261          it('should be invalid if empty', function() {
22262            input.clear();
22263            input.sendKeys('');
22264
22265            expect(text.getText()).toEqual('text =');
22266            expect(valid.getText()).toContain('false');
22267          });
22268
22269          it('should be invalid if not url', function() {
22270            input.clear();
22271            input.sendKeys('box');
22272
22273            expect(valid.getText()).toContain('false');
22274          });
22275        </file>
22276      </example>
22277   */
22278  'url': urlInputType,
22279
22280
22281  /**
22282   * @ngdoc input
22283   * @name input[email]
22284   *
22285   * @description
22286   * Text input with email validation. Sets the `email` validation error key if not a valid email
22287   * address.
22288   *
22289   * <div class="alert alert-warning">
22290   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
22291   * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
22292   * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
22293   * </div>
22294   *
22295   * @param {string} ngModel Assignable angular expression to data-bind to.
22296   * @param {string=} name Property name of the form under which the control is published.
22297   * @param {string=} required Sets `required` validation error key if the value is not entered.
22298   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22299   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22300   *    `required` when you want to data-bind to the `required` attribute.
22301   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22302   *    minlength.
22303   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22304   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
22305   *    any length.
22306   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
22307   *    that contains the regular expression body that will be converted to a regular expression
22308   *    as in the ngPattern directive.
22309   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22310   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22311   *    If the expression evaluates to a RegExp object, then this is used directly.
22312   *    If the expression evaluates to a string, then it will be converted to a RegExp
22313   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22314   *    `new RegExp('^abc$')`.<br />
22315   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22316   *    start at the index of the last search's match, thus not taking the whole input value into
22317   *    account.
22318   * @param {string=} ngChange Angular expression to be executed when input changes due to user
22319   *    interaction with the input element.
22320   *
22321   * @example
22322      <example name="email-input-directive" module="emailExample">
22323        <file name="index.html">
22324         <script>
22325           angular.module('emailExample', [])
22326             .controller('ExampleController', ['$scope', function($scope) {
22327               $scope.email = {
22328                 text: '[email protected]'
22329               };
22330             }]);
22331         </script>
22332           <form name="myForm" ng-controller="ExampleController">
22333             <label>Email:
22334               <input type="email" name="input" ng-model="email.text" required>
22335             </label>
22336             <div role="alert">
22337               <span class="error" ng-show="myForm.input.$error.required">
22338                 Required!</span>
22339               <span class="error" ng-show="myForm.input.$error.email">
22340                 Not valid email!</span>
22341             </div>
22342             <tt>text = {{email.text}}</tt><br/>
22343             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22344             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22345             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22346             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22347             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
22348           </form>
22349         </file>
22350        <file name="protractor.js" type="protractor">
22351          var text = element(by.binding('email.text'));
22352          var valid = element(by.binding('myForm.input.$valid'));
22353          var input = element(by.model('email.text'));
22354
22355          it('should initialize to model', function() {
22356            expect(text.getText()).toContain('[email protected]');
22357            expect(valid.getText()).toContain('true');
22358          });
22359
22360          it('should be invalid if empty', function() {
22361            input.clear();
22362            input.sendKeys('');
22363            expect(text.getText()).toEqual('text =');
22364            expect(valid.getText()).toContain('false');
22365          });
22366
22367          it('should be invalid if not email', function() {
22368            input.clear();
22369            input.sendKeys('xxx');
22370
22371            expect(valid.getText()).toContain('false');
22372          });
22373        </file>
22374      </example>
22375   */
22376  'email': emailInputType,
22377
22378
22379  /**
22380   * @ngdoc input
22381   * @name input[radio]
22382   *
22383   * @description
22384   * HTML radio button.
22385   *
22386   * @param {string} ngModel Assignable angular expression to data-bind to.
22387   * @param {string} value The value to which the `ngModel` expression should be set when selected.
22388   *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
22389   *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
22390   * @param {string=} name Property name of the form under which the control is published.
22391   * @param {string=} ngChange Angular expression to be executed when input changes due to user
22392   *    interaction with the input element.
22393   * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
22394   *    is selected. Should be used instead of the `value` attribute if you need
22395   *    a non-string `ngModel` (`boolean`, `array`, ...).
22396   *
22397   * @example
22398      <example name="radio-input-directive" module="radioExample">
22399        <file name="index.html">
22400         <script>
22401           angular.module('radioExample', [])
22402             .controller('ExampleController', ['$scope', function($scope) {
22403               $scope.color = {
22404                 name: 'blue'
22405               };
22406               $scope.specialValue = {
22407                 "id": "12345",
22408                 "value": "green"
22409               };
22410             }]);
22411         </script>
22412         <form name="myForm" ng-controller="ExampleController">
22413           <label>
22414             <input type="radio" ng-model="color.name" value="red">
22415             Red
22416           </label><br/>
22417           <label>
22418             <input type="radio" ng-model="color.name" ng-value="specialValue">
22419             Green
22420           </label><br/>
22421           <label>
22422             <input type="radio" ng-model="color.name" value="blue">
22423             Blue
22424           </label><br/>
22425           <tt>color = {{color.name | json}}</tt><br/>
22426          </form>
22427          Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
22428        </file>
22429        <file name="protractor.js" type="protractor">
22430          it('should change state', function() {
22431            var color = element(by.binding('color.name'));
22432
22433            expect(color.getText()).toContain('blue');
22434
22435            element.all(by.model('color.name')).get(0).click();
22436
22437            expect(color.getText()).toContain('red');
22438          });
22439        </file>
22440      </example>
22441   */
22442  'radio': radioInputType,
22443
22444
22445  /**
22446   * @ngdoc input
22447   * @name input[checkbox]
22448   *
22449   * @description
22450   * HTML checkbox.
22451   *
22452   * @param {string} ngModel Assignable angular expression to data-bind to.
22453   * @param {string=} name Property name of the form under which the control is published.
22454   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
22455   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
22456   * @param {string=} ngChange Angular expression to be executed when input changes due to user
22457   *    interaction with the input element.
22458   *
22459   * @example
22460      <example name="checkbox-input-directive" module="checkboxExample">
22461        <file name="index.html">
22462         <script>
22463           angular.module('checkboxExample', [])
22464             .controller('ExampleController', ['$scope', function($scope) {
22465               $scope.checkboxModel = {
22466                value1 : true,
22467                value2 : 'YES'
22468              };
22469             }]);
22470         </script>
22471         <form name="myForm" ng-controller="ExampleController">
22472           <label>Value1:
22473             <input type="checkbox" ng-model="checkboxModel.value1">
22474           </label><br/>
22475           <label>Value2:
22476             <input type="checkbox" ng-model="checkboxModel.value2"
22477                    ng-true-value="'YES'" ng-false-value="'NO'">
22478            </label><br/>
22479           <tt>value1 = {{checkboxModel.value1}}</tt><br/>
22480           <tt>value2 = {{checkboxModel.value2}}</tt><br/>
22481          </form>
22482        </file>
22483        <file name="protractor.js" type="protractor">
22484          it('should change state', function() {
22485            var value1 = element(by.binding('checkboxModel.value1'));
22486            var value2 = element(by.binding('checkboxModel.value2'));
22487
22488            expect(value1.getText()).toContain('true');
22489            expect(value2.getText()).toContain('YES');
22490
22491            element(by.model('checkboxModel.value1')).click();
22492            element(by.model('checkboxModel.value2')).click();
22493
22494            expect(value1.getText()).toContain('false');
22495            expect(value2.getText()).toContain('NO');
22496          });
22497        </file>
22498      </example>
22499   */
22500  'checkbox': checkboxInputType,
22501
22502  'hidden': noop,
22503  'button': noop,
22504  'submit': noop,
22505  'reset': noop,
22506  'file': noop
22507};
22508
22509function stringBasedInputType(ctrl) {
22510  ctrl.$formatters.push(function(value) {
22511    return ctrl.$isEmpty(value) ? value : value.toString();
22512  });
22513}
22514
22515function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22516  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22517  stringBasedInputType(ctrl);
22518}
22519
22520function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22521  var type = lowercase(element[0].type);
22522
22523  // In composition mode, users are still inputing intermediate text buffer,
22524  // hold the listener until composition is done.
22525  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
22526  if (!$sniffer.android) {
22527    var composing = false;
22528
22529    element.on('compositionstart', function(data) {
22530      composing = true;
22531    });
22532
22533    element.on('compositionend', function() {
22534      composing = false;
22535      listener();
22536    });
22537  }
22538
22539  var listener = function(ev) {
22540    if (timeout) {
22541      $browser.defer.cancel(timeout);
22542      timeout = null;
22543    }
22544    if (composing) return;
22545    var value = element.val(),
22546        event = ev && ev.type;
22547
22548    // By default we will trim the value
22549    // If the attribute ng-trim exists we will avoid trimming
22550    // If input type is 'password', the value is never trimmed
22551    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
22552      value = trim(value);
22553    }
22554
22555    // If a control is suffering from bad input (due to native validators), browsers discard its
22556    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
22557    // control's value is the same empty value twice in a row.
22558    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
22559      ctrl.$setViewValue(value, event);
22560    }
22561  };
22562
22563  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
22564  // input event on backspace, delete or cut
22565  if ($sniffer.hasEvent('input')) {
22566    element.on('input', listener);
22567  } else {
22568    var timeout;
22569
22570    var deferListener = function(ev, input, origValue) {
22571      if (!timeout) {
22572        timeout = $browser.defer(function() {
22573          timeout = null;
22574          if (!input || input.value !== origValue) {
22575            listener(ev);
22576          }
22577        });
22578      }
22579    };
22580
22581    element.on('keydown', function(event) {
22582      var key = event.keyCode;
22583
22584      // ignore
22585      //    command            modifiers                   arrows
22586      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
22587
22588      deferListener(event, this, this.value);
22589    });
22590
22591    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
22592    if ($sniffer.hasEvent('paste')) {
22593      element.on('paste cut', deferListener);
22594    }
22595  }
22596
22597  // if user paste into input using mouse on older browser
22598  // or form autocomplete on newer browser, we need "change" event to catch it
22599  element.on('change', listener);
22600
22601  ctrl.$render = function() {
22602    // Workaround for Firefox validation #12102.
22603    var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
22604    if (element.val() !== value) {
22605      element.val(value);
22606    }
22607  };
22608}
22609
22610function weekParser(isoWeek, existingDate) {
22611  if (isDate(isoWeek)) {
22612    return isoWeek;
22613  }
22614
22615  if (isString(isoWeek)) {
22616    WEEK_REGEXP.lastIndex = 0;
22617    var parts = WEEK_REGEXP.exec(isoWeek);
22618    if (parts) {
22619      var year = +parts[1],
22620          week = +parts[2],
22621          hours = 0,
22622          minutes = 0,
22623          seconds = 0,
22624          milliseconds = 0,
22625          firstThurs = getFirstThursdayOfYear(year),
22626          addDays = (week - 1) * 7;
22627
22628      if (existingDate) {
22629        hours = existingDate.getHours();
22630        minutes = existingDate.getMinutes();
22631        seconds = existingDate.getSeconds();
22632        milliseconds = existingDate.getMilliseconds();
22633      }
22634
22635      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
22636    }
22637  }
22638
22639  return NaN;
22640}
22641
22642function createDateParser(regexp, mapping) {
22643  return function(iso, date) {
22644    var parts, map;
22645
22646    if (isDate(iso)) {
22647      return iso;
22648    }
22649
22650    if (isString(iso)) {
22651      // When a date is JSON'ified to wraps itself inside of an extra
22652      // set of double quotes. This makes the date parsing code unable
22653      // to match the date string and parse it as a date.
22654      if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
22655        iso = iso.substring(1, iso.length - 1);
22656      }
22657      if (ISO_DATE_REGEXP.test(iso)) {
22658        return new Date(iso);
22659      }
22660      regexp.lastIndex = 0;
22661      parts = regexp.exec(iso);
22662
22663      if (parts) {
22664        parts.shift();
22665        if (date) {
22666          map = {
22667            yyyy: date.getFullYear(),
22668            MM: date.getMonth() + 1,
22669            dd: date.getDate(),
22670            HH: date.getHours(),
22671            mm: date.getMinutes(),
22672            ss: date.getSeconds(),
22673            sss: date.getMilliseconds() / 1000
22674          };
22675        } else {
22676          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
22677        }
22678
22679        forEach(parts, function(part, index) {
22680          if (index < mapping.length) {
22681            map[mapping[index]] = +part;
22682          }
22683        });
22684        return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
22685      }
22686    }
22687
22688    return NaN;
22689  };
22690}
22691
22692function createDateInputType(type, regexp, parseDate, format) {
22693  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
22694    badInputChecker(scope, element, attr, ctrl);
22695    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22696    var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
22697    var previousDate;
22698
22699    ctrl.$$parserName = type;
22700    ctrl.$parsers.push(function(value) {
22701      if (ctrl.$isEmpty(value)) return null;
22702      if (regexp.test(value)) {
22703        // Note: We cannot read ctrl.$modelValue, as there might be a different
22704        // parser/formatter in the processing chain so that the model
22705        // contains some different data format!
22706        var parsedDate = parseDate(value, previousDate);
22707        if (timezone) {
22708          parsedDate = convertTimezoneToLocal(parsedDate, timezone);
22709        }
22710        return parsedDate;
22711      }
22712      return undefined;
22713    });
22714
22715    ctrl.$formatters.push(function(value) {
22716      if (value && !isDate(value)) {
22717        throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
22718      }
22719      if (isValidDate(value)) {
22720        previousDate = value;
22721        if (previousDate && timezone) {
22722          previousDate = convertTimezoneToLocal(previousDate, timezone, true);
22723        }
22724        return $filter('date')(value, format, timezone);
22725      } else {
22726        previousDate = null;
22727        return '';
22728      }
22729    });
22730
22731    if (isDefined(attr.min) || attr.ngMin) {
22732      var minVal;
22733      ctrl.$validators.min = function(value) {
22734        return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
22735      };
22736      attr.$observe('min', function(val) {
22737        minVal = parseObservedDateValue(val);
22738        ctrl.$validate();
22739      });
22740    }
22741
22742    if (isDefined(attr.max) || attr.ngMax) {
22743      var maxVal;
22744      ctrl.$validators.max = function(value) {
22745        return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
22746      };
22747      attr.$observe('max', function(val) {
22748        maxVal = parseObservedDateValue(val);
22749        ctrl.$validate();
22750      });
22751    }
22752
22753    function isValidDate(value) {
22754      // Invalid Date: getTime() returns NaN
22755      return value && !(value.getTime && value.getTime() !== value.getTime());
22756    }
22757
22758    function parseObservedDateValue(val) {
22759      return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
22760    }
22761  };
22762}
22763
22764function badInputChecker(scope, element, attr, ctrl) {
22765  var node = element[0];
22766  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
22767  if (nativeValidation) {
22768    ctrl.$parsers.push(function(value) {
22769      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
22770      return validity.badInput || validity.typeMismatch ? undefined : value;
22771    });
22772  }
22773}
22774
22775function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22776  badInputChecker(scope, element, attr, ctrl);
22777  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22778
22779  ctrl.$$parserName = 'number';
22780  ctrl.$parsers.push(function(value) {
22781    if (ctrl.$isEmpty(value))      return null;
22782    if (NUMBER_REGEXP.test(value)) return parseFloat(value);
22783    return undefined;
22784  });
22785
22786  ctrl.$formatters.push(function(value) {
22787    if (!ctrl.$isEmpty(value)) {
22788      if (!isNumber(value)) {
22789        throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
22790      }
22791      value = value.toString();
22792    }
22793    return value;
22794  });
22795
22796  if (isDefined(attr.min) || attr.ngMin) {
22797    var minVal;
22798    ctrl.$validators.min = function(value) {
22799      return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
22800    };
22801
22802    attr.$observe('min', function(val) {
22803      if (isDefined(val) && !isNumber(val)) {
22804        val = parseFloat(val, 10);
22805      }
22806      minVal = isNumber(val) && !isNaN(val) ? val : undefined;
22807      // TODO(matsko): implement validateLater to reduce number of validations
22808      ctrl.$validate();
22809    });
22810  }
22811
22812  if (isDefined(attr.max) || attr.ngMax) {
22813    var maxVal;
22814    ctrl.$validators.max = function(value) {
22815      return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
22816    };
22817
22818    attr.$observe('max', function(val) {
22819      if (isDefined(val) && !isNumber(val)) {
22820        val = parseFloat(val, 10);
22821      }
22822      maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
22823      // TODO(matsko): implement validateLater to reduce number of validations
22824      ctrl.$validate();
22825    });
22826  }
22827}
22828
22829function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22830  // Note: no badInputChecker here by purpose as `url` is only a validation
22831  // in browsers, i.e. we can always read out input.value even if it is not valid!
22832  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22833  stringBasedInputType(ctrl);
22834
22835  ctrl.$$parserName = 'url';
22836  ctrl.$validators.url = function(modelValue, viewValue) {
22837    var value = modelValue || viewValue;
22838    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
22839  };
22840}
22841
22842function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22843  // Note: no badInputChecker here by purpose as `url` is only a validation
22844  // in browsers, i.e. we can always read out input.value even if it is not valid!
22845  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22846  stringBasedInputType(ctrl);
22847
22848  ctrl.$$parserName = 'email';
22849  ctrl.$validators.email = function(modelValue, viewValue) {
22850    var value = modelValue || viewValue;
22851    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
22852  };
22853}
22854
22855function radioInputType(scope, element, attr, ctrl) {
22856  // make the name unique, if not defined
22857  if (isUndefined(attr.name)) {
22858    element.attr('name', nextUid());
22859  }
22860
22861  var listener = function(ev) {
22862    if (element[0].checked) {
22863      ctrl.$setViewValue(attr.value, ev && ev.type);
22864    }
22865  };
22866
22867  element.on('click', listener);
22868
22869  ctrl.$render = function() {
22870    var value = attr.value;
22871    element[0].checked = (value == ctrl.$viewValue);
22872  };
22873
22874  attr.$observe('value', ctrl.$render);
22875}
22876
22877function parseConstantExpr($parse, context, name, expression, fallback) {
22878  var parseFn;
22879  if (isDefined(expression)) {
22880    parseFn = $parse(expression);
22881    if (!parseFn.constant) {
22882      throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22883                                   '`{1}`.', name, expression);
22884    }
22885    return parseFn(context);
22886  }
22887  return fallback;
22888}
22889
22890function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22891  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22892  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22893
22894  var listener = function(ev) {
22895    ctrl.$setViewValue(element[0].checked, ev && ev.type);
22896  };
22897
22898  element.on('click', listener);
22899
22900  ctrl.$render = function() {
22901    element[0].checked = ctrl.$viewValue;
22902  };
22903
22904  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22905  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22906  // it to a boolean.
22907  ctrl.$isEmpty = function(value) {
22908    return value === false;
22909  };
22910
22911  ctrl.$formatters.push(function(value) {
22912    return equals(value, trueValue);
22913  });
22914
22915  ctrl.$parsers.push(function(value) {
22916    return value ? trueValue : falseValue;
22917  });
22918}
22919
22920
22921/**
22922 * @ngdoc directive
22923 * @name textarea
22924 * @restrict E
22925 *
22926 * @description
22927 * HTML textarea element control with angular data-binding. The data-binding and validation
22928 * properties of this element are exactly the same as those of the
22929 * {@link ng.directive:input input element}.
22930 *
22931 * @param {string} ngModel Assignable angular expression to data-bind to.
22932 * @param {string=} name Property name of the form under which the control is published.
22933 * @param {string=} required Sets `required` validation error key if the value is not entered.
22934 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22935 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22936 *    `required` when you want to data-bind to the `required` attribute.
22937 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22938 *    minlength.
22939 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22940 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22941 *    length.
22942 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22943 *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22944 *    If the expression evaluates to a RegExp object, then this is used directly.
22945 *    If the expression evaluates to a string, then it will be converted to a RegExp
22946 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22947 *    `new RegExp('^abc$')`.<br />
22948 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22949 *    start at the index of the last search's match, thus not taking the whole input value into
22950 *    account.
22951 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22952 *    interaction with the input element.
22953 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22954 */
22955
22956
22957/**
22958 * @ngdoc directive
22959 * @name input
22960 * @restrict E
22961 *
22962 * @description
22963 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22964 * input state control, and validation.
22965 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22966 *
22967 * <div class="alert alert-warning">
22968 * **Note:** Not every feature offered is available for all input types.
22969 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22970 * </div>
22971 *
22972 * @param {string} ngModel Assignable angular expression to data-bind to.
22973 * @param {string=} name Property name of the form under which the control is published.
22974 * @param {string=} required Sets `required` validation error key if the value is not entered.
22975 * @param {boolean=} ngRequired Sets `required` attribute if set to true
22976 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22977 *    minlength.
22978 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22979 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22980 *    length.
22981 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22982 *    value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22983 *    If the expression evaluates to a RegExp object, then this is used directly.
22984 *    If the expression evaluates to a string, then it will be converted to a RegExp
22985 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22986 *    `new RegExp('^abc$')`.<br />
22987 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22988 *    start at the index of the last search's match, thus not taking the whole input value into
22989 *    account.
22990 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22991 *    interaction with the input element.
22992 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22993 *    This parameter is ignored for input[type=password] controls, which will never trim the
22994 *    input.
22995 *
22996 * @example
22997    <example name="input-directive" module="inputExample">
22998      <file name="index.html">
22999       <script>
23000          angular.module('inputExample', [])
23001            .controller('ExampleController', ['$scope', function($scope) {
23002              $scope.user = {name: 'guest', last: 'visitor'};
23003            }]);
23004       </script>
23005       <div ng-controller="ExampleController">
23006         <form name="myForm">
23007           <label>
23008              User name:
23009              <input type="text" name="userName" ng-model="user.name" required>
23010           </label>
23011           <div role="alert">
23012             <span class="error" ng-show="myForm.userName.$error.required">
23013              Required!</span>
23014           </div>
23015           <label>
23016              Last name:
23017              <input type="text" name="lastName" ng-model="user.last"
23018              ng-minlength="3" ng-maxlength="10">
23019           </label>
23020           <div role="alert">
23021             <span class="error" ng-show="myForm.lastName.$error.minlength">
23022               Too short!</span>
23023             <span class="error" ng-show="myForm.lastName.$error.maxlength">
23024               Too long!</span>
23025           </div>
23026         </form>
23027         <hr>
23028         <tt>user = {{user}}</tt><br/>
23029         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
23030         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
23031         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
23032         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
23033         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23034         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23035         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
23036         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
23037       </div>
23038      </file>
23039      <file name="protractor.js" type="protractor">
23040        var user = element(by.exactBinding('user'));
23041        var userNameValid = element(by.binding('myForm.userName.$valid'));
23042        var lastNameValid = element(by.binding('myForm.lastName.$valid'));
23043        var lastNameError = element(by.binding('myForm.lastName.$error'));
23044        var formValid = element(by.binding('myForm.$valid'));
23045        var userNameInput = element(by.model('user.name'));
23046        var userLastInput = element(by.model('user.last'));
23047
23048        it('should initialize to model', function() {
23049          expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
23050          expect(userNameValid.getText()).toContain('true');
23051          expect(formValid.getText()).toContain('true');
23052        });
23053
23054        it('should be invalid if empty when required', function() {
23055          userNameInput.clear();
23056          userNameInput.sendKeys('');
23057
23058          expect(user.getText()).toContain('{"last":"visitor"}');
23059          expect(userNameValid.getText()).toContain('false');
23060          expect(formValid.getText()).toContain('false');
23061        });
23062
23063        it('should be valid if empty when min length is set', function() {
23064          userLastInput.clear();
23065          userLastInput.sendKeys('');
23066
23067          expect(user.getText()).toContain('{"name":"guest","last":""}');
23068          expect(lastNameValid.getText()).toContain('true');
23069          expect(formValid.getText()).toContain('true');
23070        });
23071
23072        it('should be invalid if less than required min length', function() {
23073          userLastInput.clear();
23074          userLastInput.sendKeys('xx');
23075
23076          expect(user.getText()).toContain('{"name":"guest"}');
23077          expect(lastNameValid.getText()).toContain('false');
23078          expect(lastNameError.getText()).toContain('minlength');
23079          expect(formValid.getText()).toContain('false');
23080        });
23081
23082        it('should be invalid if longer than max length', function() {
23083          userLastInput.clear();
23084          userLastInput.sendKeys('some ridiculously long name');
23085
23086          expect(user.getText()).toContain('{"name":"guest"}');
23087          expect(lastNameValid.getText()).toContain('false');
23088          expect(lastNameError.getText()).toContain('maxlength');
23089          expect(formValid.getText()).toContain('false');
23090        });
23091      </file>
23092    </example>
23093 */
23094var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
23095    function($browser, $sniffer, $filter, $parse) {
23096  return {
23097    restrict: 'E',
23098    require: ['?ngModel'],
23099    link: {
23100      pre: function(scope, element, attr, ctrls) {
23101        if (ctrls[0]) {
23102          (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
23103                                                              $browser, $filter, $parse);
23104        }
23105      }
23106    }
23107  };
23108}];
23109
23110
23111
23112var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
23113/**
23114 * @ngdoc directive
23115 * @name ngValue
23116 *
23117 * @description
23118 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
23119 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
23120 * the bound value.
23121 *
23122 * `ngValue` is useful when dynamically generating lists of radio buttons using
23123 * {@link ngRepeat `ngRepeat`}, as shown below.
23124 *
23125 * Likewise, `ngValue` can be used to generate `<option>` elements for
23126 * the {@link select `select`} element. In that case however, only strings are supported
23127 * for the `value `attribute, so the resulting `ngModel` will always be a string.
23128 * Support for `select` models with non-string values is available via `ngOptions`.
23129 *
23130 * @element input
23131 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
23132 *   of the `input` element
23133 *
23134 * @example
23135    <example name="ngValue-directive" module="valueExample">
23136      <file name="index.html">
23137       <script>
23138          angular.module('valueExample', [])
23139            .controller('ExampleController', ['$scope', function($scope) {
23140              $scope.names = ['pizza', 'unicorns', 'robots'];
23141              $scope.my = { favorite: 'unicorns' };
23142            }]);
23143       </script>
23144        <form ng-controller="ExampleController">
23145          <h2>Which is your favorite?</h2>
23146            <label ng-repeat="name in names" for="{{name}}">
23147              {{name}}
23148              <input type="radio"
23149                     ng-model="my.favorite"
23150                     ng-value="name"
23151                     id="{{name}}"
23152                     name="favorite">
23153            </label>
23154          <div>You chose {{my.favorite}}</div>
23155        </form>
23156      </file>
23157      <file name="protractor.js" type="protractor">
23158        var favorite = element(by.binding('my.favorite'));
23159
23160        it('should initialize to model', function() {
23161          expect(favorite.getText()).toContain('unicorns');
23162        });
23163        it('should bind the values to the inputs', function() {
23164          element.all(by.model('my.favorite')).get(0).click();
23165          expect(favorite.getText()).toContain('pizza');
23166        });
23167      </file>
23168    </example>
23169 */
23170var ngValueDirective = function() {
23171  return {
23172    restrict: 'A',
23173    priority: 100,
23174    compile: function(tpl, tplAttr) {
23175      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
23176        return function ngValueConstantLink(scope, elm, attr) {
23177          attr.$set('value', scope.$eval(attr.ngValue));
23178        };
23179      } else {
23180        return function ngValueLink(scope, elm, attr) {
23181          scope.$watch(attr.ngValue, function valueWatchAction(value) {
23182            attr.$set('value', value);
23183          });
23184        };
23185      }
23186    }
23187  };
23188};
23189
23190/**
23191 * @ngdoc directive
23192 * @name ngBind
23193 * @restrict AC
23194 *
23195 * @description
23196 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
23197 * with the value of a given expression, and to update the text content when the value of that
23198 * expression changes.
23199 *
23200 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
23201 * `{{ expression }}` which is similar but less verbose.
23202 *
23203 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
23204 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
23205 * element attribute, it makes the bindings invisible to the user while the page is loading.
23206 *
23207 * An alternative solution to this problem would be using the
23208 * {@link ng.directive:ngCloak ngCloak} directive.
23209 *
23210 *
23211 * @element ANY
23212 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
23213 *
23214 * @example
23215 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
23216   <example module="bindExample">
23217     <file name="index.html">
23218       <script>
23219         angular.module('bindExample', [])
23220           .controller('ExampleController', ['$scope', function($scope) {
23221             $scope.name = 'Whirled';
23222           }]);
23223       </script>
23224       <div ng-controller="ExampleController">
23225         <label>Enter name: <input type="text" ng-model="name"></label><br>
23226         Hello <span ng-bind="name"></span>!
23227       </div>
23228     </file>
23229     <file name="protractor.js" type="protractor">
23230       it('should check ng-bind', function() {
23231         var nameInput = element(by.model('name'));
23232
23233         expect(element(by.binding('name')).getText()).toBe('Whirled');
23234         nameInput.clear();
23235         nameInput.sendKeys('world');
23236         expect(element(by.binding('name')).getText()).toBe('world');
23237       });
23238     </file>
23239   </example>
23240 */
23241var ngBindDirective = ['$compile', function($compile) {
23242  return {
23243    restrict: 'AC',
23244    compile: function ngBindCompile(templateElement) {
23245      $compile.$$addBindingClass(templateElement);
23246      return function ngBindLink(scope, element, attr) {
23247        $compile.$$addBindingInfo(element, attr.ngBind);
23248        element = element[0];
23249        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
23250          element.textContent = isUndefined(value) ? '' : value;
23251        });
23252      };
23253    }
23254  };
23255}];
23256
23257
23258/**
23259 * @ngdoc directive
23260 * @name ngBindTemplate
23261 *
23262 * @description
23263 * The `ngBindTemplate` directive specifies that the element
23264 * text content should be replaced with the interpolation of the template
23265 * in the `ngBindTemplate` attribute.
23266 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
23267 * expressions. This directive is needed since some HTML elements
23268 * (such as TITLE and OPTION) cannot contain SPAN elements.
23269 *
23270 * @element ANY
23271 * @param {string} ngBindTemplate template of form
23272 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
23273 *
23274 * @example
23275 * Try it here: enter text in text box and watch the greeting change.
23276   <example module="bindExample">
23277     <file name="index.html">
23278       <script>
23279         angular.module('bindExample', [])
23280           .controller('ExampleController', ['$scope', function($scope) {
23281             $scope.salutation = 'Hello';
23282             $scope.name = 'World';
23283           }]);
23284       </script>
23285       <div ng-controller="ExampleController">
23286        <label>Salutation: <input type="text" ng-model="salutation"></label><br>
23287        <label>Name: <input type="text" ng-model="name"></label><br>
23288        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
23289       </div>
23290     </file>
23291     <file name="protractor.js" type="protractor">
23292       it('should check ng-bind', function() {
23293         var salutationElem = element(by.binding('salutation'));
23294         var salutationInput = element(by.model('salutation'));
23295         var nameInput = element(by.model('name'));
23296
23297         expect(salutationElem.getText()).toBe('Hello World!');
23298
23299         salutationInput.clear();
23300         salutationInput.sendKeys('Greetings');
23301         nameInput.clear();
23302         nameInput.sendKeys('user');
23303
23304         expect(salutationElem.getText()).toBe('Greetings user!');
23305       });
23306     </file>
23307   </example>
23308 */
23309var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
23310  return {
23311    compile: function ngBindTemplateCompile(templateElement) {
23312      $compile.$$addBindingClass(templateElement);
23313      return function ngBindTemplateLink(scope, element, attr) {
23314        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
23315        $compile.$$addBindingInfo(element, interpolateFn.expressions);
23316        element = element[0];
23317        attr.$observe('ngBindTemplate', function(value) {
23318          element.textContent = isUndefined(value) ? '' : value;
23319        });
23320      };
23321    }
23322  };
23323}];
23324
23325
23326/**
23327 * @ngdoc directive
23328 * @name ngBindHtml
23329 *
23330 * @description
23331 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
23332 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
23333 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
23334 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
23335 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
23336 *
23337 * You may also bypass sanitization for values you know are safe. To do so, bind to
23338 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
23339 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
23340 *
23341 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
23342 * will have an exception (instead of an exploit.)
23343 *
23344 * @element ANY
23345 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
23346 *
23347 * @example
23348
23349   <example module="bindHtmlExample" deps="angular-sanitize.js">
23350     <file name="index.html">
23351       <div ng-controller="ExampleController">
23352        <p ng-bind-html="myHTML"></p>
23353       </div>
23354     </file>
23355
23356     <file name="script.js">
23357       angular.module('bindHtmlExample', ['ngSanitize'])
23358         .controller('ExampleController', ['$scope', function($scope) {
23359           $scope.myHTML =
23360              'I am an <code>HTML</code>string with ' +
23361              '<a href="#">links!</a> and other <em>stuff</em>';
23362         }]);
23363     </file>
23364
23365     <file name="protractor.js" type="protractor">
23366       it('should check ng-bind-html', function() {
23367         expect(element(by.binding('myHTML')).getText()).toBe(
23368             'I am an HTMLstring with links! and other stuff');
23369       });
23370     </file>
23371   </example>
23372 */
23373var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
23374  return {
23375    restrict: 'A',
23376    compile: function ngBindHtmlCompile(tElement, tAttrs) {
23377      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
23378      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
23379        return (value || '').toString();
23380      });
23381      $compile.$$addBindingClass(tElement);
23382
23383      return function ngBindHtmlLink(scope, element, attr) {
23384        $compile.$$addBindingInfo(element, attr.ngBindHtml);
23385
23386        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
23387          // we re-evaluate the expr because we want a TrustedValueHolderType
23388          // for $sce, not a string
23389          element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
23390        });
23391      };
23392    }
23393  };
23394}];
23395
23396/**
23397 * @ngdoc directive
23398 * @name ngChange
23399 *
23400 * @description
23401 * Evaluate the given expression when the user changes the input.
23402 * The expression is evaluated immediately, unlike the JavaScript onchange event
23403 * which only triggers at the end of a change (usually, when the user leaves the
23404 * form element or presses the return key).
23405 *
23406 * The `ngChange` expression is only evaluated when a change in the input value causes
23407 * a new value to be committed to the model.
23408 *
23409 * It will not be evaluated:
23410 * * if the value returned from the `$parsers` transformation pipeline has not changed
23411 * * if the input has continued to be invalid since the model will stay `null`
23412 * * if the model is changed programmatically and not by a change to the input value
23413 *
23414 *
23415 * Note, this directive requires `ngModel` to be present.
23416 *
23417 * @element input
23418 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
23419 * in input value.
23420 *
23421 * @example
23422 * <example name="ngChange-directive" module="changeExample">
23423 *   <file name="index.html">
23424 *     <script>
23425 *       angular.module('changeExample', [])
23426 *         .controller('ExampleController', ['$scope', function($scope) {
23427 *           $scope.counter = 0;
23428 *           $scope.change = function() {
23429 *             $scope.counter++;
23430 *           };
23431 *         }]);
23432 *     </script>
23433 *     <div ng-controller="ExampleController">
23434 *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
23435 *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
23436 *       <label for="ng-change-example2">Confirmed</label><br />
23437 *       <tt>debug = {{confirmed}}</tt><br/>
23438 *       <tt>counter = {{counter}}</tt><br/>
23439 *     </div>
23440 *   </file>
23441 *   <file name="protractor.js" type="protractor">
23442 *     var counter = element(by.binding('counter'));
23443 *     var debug = element(by.binding('confirmed'));
23444 *
23445 *     it('should evaluate the expression if changing from view', function() {
23446 *       expect(counter.getText()).toContain('0');
23447 *
23448 *       element(by.id('ng-change-example1')).click();
23449 *
23450 *       expect(counter.getText()).toContain('1');
23451 *       expect(debug.getText()).toContain('true');
23452 *     });
23453 *
23454 *     it('should not evaluate the expression if changing from model', function() {
23455 *       element(by.id('ng-change-example2')).click();
23456
23457 *       expect(counter.getText()).toContain('0');
23458 *       expect(debug.getText()).toContain('true');
23459 *     });
23460 *   </file>
23461 * </example>
23462 */
23463var ngChangeDirective = valueFn({
23464  restrict: 'A',
23465  require: 'ngModel',
23466  link: function(scope, element, attr, ctrl) {
23467    ctrl.$viewChangeListeners.push(function() {
23468      scope.$eval(attr.ngChange);
23469    });
23470  }
23471});
23472
23473function classDirective(name, selector) {
23474  name = 'ngClass' + name;
23475  return ['$animate', function($animate) {
23476    return {
23477      restrict: 'AC',
23478      link: function(scope, element, attr) {
23479        var oldVal;
23480
23481        scope.$watch(attr[name], ngClassWatchAction, true);
23482
23483        attr.$observe('class', function(value) {
23484          ngClassWatchAction(scope.$eval(attr[name]));
23485        });
23486
23487
23488        if (name !== 'ngClass') {
23489          scope.$watch('$index', function($index, old$index) {
23490            // jshint bitwise: false
23491            var mod = $index & 1;
23492            if (mod !== (old$index & 1)) {
23493              var classes = arrayClasses(scope.$eval(attr[name]));
23494              mod === selector ?
23495                addClasses(classes) :
23496                removeClasses(classes);
23497            }
23498          });
23499        }
23500
23501        function addClasses(classes) {
23502          var newClasses = digestClassCounts(classes, 1);
23503          attr.$addClass(newClasses);
23504        }
23505
23506        function removeClasses(classes) {
23507          var newClasses = digestClassCounts(classes, -1);
23508          attr.$removeClass(newClasses);
23509        }
23510
23511        function digestClassCounts(classes, count) {
23512          // Use createMap() to prevent class assumptions involving property
23513          // names in Object.prototype
23514          var classCounts = element.data('$classCounts') || createMap();
23515          var classesToUpdate = [];
23516          forEach(classes, function(className) {
23517            if (count > 0 || classCounts[className]) {
23518              classCounts[className] = (classCounts[className] || 0) + count;
23519              if (classCounts[className] === +(count > 0)) {
23520                classesToUpdate.push(className);
23521              }
23522            }
23523          });
23524          element.data('$classCounts', classCounts);
23525          return classesToUpdate.join(' ');
23526        }
23527
23528        function updateClasses(oldClasses, newClasses) {
23529          var toAdd = arrayDifference(newClasses, oldClasses);
23530          var toRemove = arrayDifference(oldClasses, newClasses);
23531          toAdd = digestClassCounts(toAdd, 1);
23532          toRemove = digestClassCounts(toRemove, -1);
23533          if (toAdd && toAdd.length) {
23534            $animate.addClass(element, toAdd);
23535          }
23536          if (toRemove && toRemove.length) {
23537            $animate.removeClass(element, toRemove);
23538          }
23539        }
23540
23541        function ngClassWatchAction(newVal) {
23542          if (selector === true || scope.$index % 2 === selector) {
23543            var newClasses = arrayClasses(newVal || []);
23544            if (!oldVal) {
23545              addClasses(newClasses);
23546            } else if (!equals(newVal,oldVal)) {
23547              var oldClasses = arrayClasses(oldVal);
23548              updateClasses(oldClasses, newClasses);
23549            }
23550          }
23551          oldVal = shallowCopy(newVal);
23552        }
23553      }
23554    };
23555
23556    function arrayDifference(tokens1, tokens2) {
23557      var values = [];
23558
23559      outer:
23560      for (var i = 0; i < tokens1.length; i++) {
23561        var token = tokens1[i];
23562        for (var j = 0; j < tokens2.length; j++) {
23563          if (token == tokens2[j]) continue outer;
23564        }
23565        values.push(token);
23566      }
23567      return values;
23568    }
23569
23570    function arrayClasses(classVal) {
23571      var classes = [];
23572      if (isArray(classVal)) {
23573        forEach(classVal, function(v) {
23574          classes = classes.concat(arrayClasses(v));
23575        });
23576        return classes;
23577      } else if (isString(classVal)) {
23578        return classVal.split(' ');
23579      } else if (isObject(classVal)) {
23580        forEach(classVal, function(v, k) {
23581          if (v) {
23582            classes = classes.concat(k.split(' '));
23583          }
23584        });
23585        return classes;
23586      }
23587      return classVal;
23588    }
23589  }];
23590}
23591
23592/**
23593 * @ngdoc directive
23594 * @name ngClass
23595 * @restrict AC
23596 *
23597 * @description
23598 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
23599 * an expression that represents all classes to be added.
23600 *
23601 * The directive operates in three different ways, depending on which of three types the expression
23602 * evaluates to:
23603 *
23604 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
23605 * names.
23606 *
23607 * 2. If the expression evaluates to an object, then for each key-value pair of the
23608 * object with a truthy value the corresponding key is used as a class name.
23609 *
23610 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
23611 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
23612 * to give you more control over what CSS classes appear. See the code below for an example of this.
23613 *
23614 *
23615 * The directive won't add duplicate classes if a particular class was already set.
23616 *
23617 * When the expression changes, the previously added classes are removed and only then are the
23618 * new classes added.
23619 *
23620 * @animations
23621 * **add** - happens just before the class is applied to the elements
23622 *
23623 * **remove** - happens just before the class is removed from the element
23624 *
23625 * @element ANY
23626 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
23627 *   of the evaluation can be a string representing space delimited class
23628 *   names, an array, or a map of class names to boolean values. In the case of a map, the
23629 *   names of the properties whose values are truthy will be added as css classes to the
23630 *   element.
23631 *
23632 * @example Example that demonstrates basic bindings via ngClass directive.
23633   <example>
23634     <file name="index.html">
23635       <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
23636       <label>
23637          <input type="checkbox" ng-model="deleted">
23638          deleted (apply "strike" class)
23639       </label><br>
23640       <label>
23641          <input type="checkbox" ng-model="important">
23642          important (apply "bold" class)
23643       </label><br>
23644       <label>
23645          <input type="checkbox" ng-model="error">
23646          error (apply "has-error" class)
23647       </label>
23648       <hr>
23649       <p ng-class="style">Using String Syntax</p>
23650       <input type="text" ng-model="style"
23651              placeholder="Type: bold strike red" aria-label="Type: bold strike red">
23652       <hr>
23653       <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
23654       <input ng-model="style1"
23655              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
23656       <input ng-model="style2"
23657              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
23658       <input ng-model="style3"
23659              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
23660       <hr>
23661       <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
23662       <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
23663       <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
23664     </file>
23665     <file name="style.css">
23666       .strike {
23667           text-decoration: line-through;
23668       }
23669       .bold {
23670           font-weight: bold;
23671       }
23672       .red {
23673           color: red;
23674       }
23675       .has-error {
23676           color: red;
23677           background-color: yellow;
23678       }
23679       .orange {
23680           color: orange;
23681       }
23682     </file>
23683     <file name="protractor.js" type="protractor">
23684       var ps = element.all(by.css('p'));
23685
23686       it('should let you toggle the class', function() {
23687
23688         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
23689         expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
23690
23691         element(by.model('important')).click();
23692         expect(ps.first().getAttribute('class')).toMatch(/bold/);
23693
23694         element(by.model('error')).click();
23695         expect(ps.first().getAttribute('class')).toMatch(/has-error/);
23696       });
23697
23698       it('should let you toggle string example', function() {
23699         expect(ps.get(1).getAttribute('class')).toBe('');
23700         element(by.model('style')).clear();
23701         element(by.model('style')).sendKeys('red');
23702         expect(ps.get(1).getAttribute('class')).toBe('red');
23703       });
23704
23705       it('array example should have 3 classes', function() {
23706         expect(ps.get(2).getAttribute('class')).toBe('');
23707         element(by.model('style1')).sendKeys('bold');
23708         element(by.model('style2')).sendKeys('strike');
23709         element(by.model('style3')).sendKeys('red');
23710         expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
23711       });
23712
23713       it('array with map example should have 2 classes', function() {
23714         expect(ps.last().getAttribute('class')).toBe('');
23715         element(by.model('style4')).sendKeys('bold');
23716         element(by.model('warning')).click();
23717         expect(ps.last().getAttribute('class')).toBe('bold orange');
23718       });
23719     </file>
23720   </example>
23721
23722   ## Animations
23723
23724   The example below demonstrates how to perform animations using ngClass.
23725
23726   <example module="ngAnimate" deps="angular-animate.js" animations="true">
23727     <file name="index.html">
23728      <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
23729      <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
23730      <br>
23731      <span class="base-class" ng-class="myVar">Sample Text</span>
23732     </file>
23733     <file name="style.css">
23734       .base-class {
23735         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23736       }
23737
23738       .base-class.my-class {
23739         color: red;
23740         font-size:3em;
23741       }
23742     </file>
23743     <file name="protractor.js" type="protractor">
23744       it('should check ng-class', function() {
23745         expect(element(by.css('.base-class')).getAttribute('class')).not.
23746           toMatch(/my-class/);
23747
23748         element(by.id('setbtn')).click();
23749
23750         expect(element(by.css('.base-class')).getAttribute('class')).
23751           toMatch(/my-class/);
23752
23753         element(by.id('clearbtn')).click();
23754
23755         expect(element(by.css('.base-class')).getAttribute('class')).not.
23756           toMatch(/my-class/);
23757       });
23758     </file>
23759   </example>
23760
23761
23762   ## ngClass and pre-existing CSS3 Transitions/Animations
23763   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
23764   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
23765   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
23766   to view the step by step details of {@link $animate#addClass $animate.addClass} and
23767   {@link $animate#removeClass $animate.removeClass}.
23768 */
23769var ngClassDirective = classDirective('', true);
23770
23771/**
23772 * @ngdoc directive
23773 * @name ngClassOdd
23774 * @restrict AC
23775 *
23776 * @description
23777 * The `ngClassOdd` and `ngClassEven` directives work exactly as
23778 * {@link ng.directive:ngClass ngClass}, except they work in
23779 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23780 *
23781 * This directive can be applied only within the scope of an
23782 * {@link ng.directive:ngRepeat ngRepeat}.
23783 *
23784 * @element ANY
23785 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
23786 *   of the evaluation can be a string representing space delimited class names or an array.
23787 *
23788 * @example
23789   <example>
23790     <file name="index.html">
23791        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23792          <li ng-repeat="name in names">
23793           <span ng-class-odd="'odd'" ng-class-even="'even'">
23794             {{name}}
23795           </span>
23796          </li>
23797        </ol>
23798     </file>
23799     <file name="style.css">
23800       .odd {
23801         color: red;
23802       }
23803       .even {
23804         color: blue;
23805       }
23806     </file>
23807     <file name="protractor.js" type="protractor">
23808       it('should check ng-class-odd and ng-class-even', function() {
23809         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23810           toMatch(/odd/);
23811         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23812           toMatch(/even/);
23813       });
23814     </file>
23815   </example>
23816 */
23817var ngClassOddDirective = classDirective('Odd', 0);
23818
23819/**
23820 * @ngdoc directive
23821 * @name ngClassEven
23822 * @restrict AC
23823 *
23824 * @description
23825 * The `ngClassOdd` and `ngClassEven` directives work exactly as
23826 * {@link ng.directive:ngClass ngClass}, except they work in
23827 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23828 *
23829 * This directive can be applied only within the scope of an
23830 * {@link ng.directive:ngRepeat ngRepeat}.
23831 *
23832 * @element ANY
23833 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
23834 *   result of the evaluation can be a string representing space delimited class names or an array.
23835 *
23836 * @example
23837   <example>
23838     <file name="index.html">
23839        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23840          <li ng-repeat="name in names">
23841           <span ng-class-odd="'odd'" ng-class-even="'even'">
23842             {{name}} &nbsp; &nbsp; &nbsp;
23843           </span>
23844          </li>
23845        </ol>
23846     </file>
23847     <file name="style.css">
23848       .odd {
23849         color: red;
23850       }
23851       .even {
23852         color: blue;
23853       }
23854     </file>
23855     <file name="protractor.js" type="protractor">
23856       it('should check ng-class-odd and ng-class-even', function() {
23857         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23858           toMatch(/odd/);
23859         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23860           toMatch(/even/);
23861       });
23862     </file>
23863   </example>
23864 */
23865var ngClassEvenDirective = classDirective('Even', 1);
23866
23867/**
23868 * @ngdoc directive
23869 * @name ngCloak
23870 * @restrict AC
23871 *
23872 * @description
23873 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
23874 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
23875 * directive to avoid the undesirable flicker effect caused by the html template display.
23876 *
23877 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23878 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23879 * of the browser view.
23880 *
23881 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23882 * `angular.min.js`.
23883 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23884 *
23885 * ```css
23886 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23887 *   display: none !important;
23888 * }
23889 * ```
23890 *
23891 * When this css rule is loaded by the browser, all html elements (including their children) that
23892 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23893 * during the compilation of the template it deletes the `ngCloak` element attribute, making
23894 * the compiled element visible.
23895 *
23896 * For the best result, the `angular.js` script must be loaded in the head section of the html
23897 * document; alternatively, the css rule above must be included in the external stylesheet of the
23898 * application.
23899 *
23900 * @element ANY
23901 *
23902 * @example
23903   <example>
23904     <file name="index.html">
23905        <div id="template1" ng-cloak>{{ 'hello' }}</div>
23906        <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23907     </file>
23908     <file name="protractor.js" type="protractor">
23909       it('should remove the template directive and css class', function() {
23910         expect($('#template1').getAttribute('ng-cloak')).
23911           toBeNull();
23912         expect($('#template2').getAttribute('ng-cloak')).
23913           toBeNull();
23914       });
23915     </file>
23916   </example>
23917 *
23918 */
23919var ngCloakDirective = ngDirective({
23920  compile: function(element, attr) {
23921    attr.$set('ngCloak', undefined);
23922    element.removeClass('ng-cloak');
23923  }
23924});
23925
23926/**
23927 * @ngdoc directive
23928 * @name ngController
23929 *
23930 * @description
23931 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23932 * supports the principles behind the Model-View-Controller design pattern.
23933 *
23934 * MVC components in angular:
23935 *
23936 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23937 *   are accessed through bindings.
23938 * * View — The template (HTML with data bindings) that is rendered into the View.
23939 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23940 *   logic behind the application to decorate the scope with functions and values
23941 *
23942 * Note that you can also attach controllers to the DOM by declaring it in a route definition
23943 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23944 * again using `ng-controller` in the template itself.  This will cause the controller to be attached
23945 * and executed twice.
23946 *
23947 * @element ANY
23948 * @scope
23949 * @priority 500
23950 * @param {expression} ngController Name of a constructor function registered with the current
23951 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23952 * that on the current scope evaluates to a constructor function.
23953 *
23954 * The controller instance can be published into a scope property by specifying
23955 * `ng-controller="as propertyName"`.
23956 *
23957 * If the current `$controllerProvider` is configured to use globals (via
23958 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23959 * also be the name of a globally accessible constructor function (not recommended).
23960 *
23961 * @example
23962 * Here is a simple form for editing user contact information. Adding, remov
23962ing, clearing, and
23963 * greeting are methods declared on the controller (see source tab). These methods can
23964 * easily be called from the angular markup. Any changes to the data are automatically reflected
23965 * in the View without the need for a manual update.
23966 *
23967 * Two different declaration styles are included below:
23968 *
23969 * * one binds methods and properties directly onto the controller using `this`:
23970 * `ng-controller="SettingsController1 as settings"`
23971 * * one injects `$scope` into the controller:
23972 * `ng-controller="SettingsController2"`
23973 *
23974 * The second option is more common in the Angular community, and is generally used in boilerplates
23975 * and in this guide. However, there are advantages to binding properties directly to the controller
23976 * and avoiding scope.
23977 *
23978 * * Using `controller as` makes it obvious which controller you are accessing in the template when
23979 * multiple controllers apply to an element.
23980 * * If you are writing your controllers as classes you have easier access to the properties and
23981 * methods, which will appear on the scope, from inside the controller code.
23982 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23983 * inheritance masking primitives.
23984 *
23985 * This example demonstrates the `controller as` syntax.
23986 *
23987 * <example name="ngControllerAs" module="controllerAsExample">
23988 *   <file name="index.html">
23989 *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23990 *      <label>Name: <input type="text" ng-model="settings.name"/></label>
23991 *      <button ng-click="settings.greet()">greet</button><br/>
23992 *      Contact:
23993 *      <ul>
23994 *        <li ng-repeat="contact in settings.contacts">
23995 *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23996 *             <option>phone</option>
23997 *             <option>email</option>
23998 *          </select>
23999 *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
24000 *          <button ng-click="settings.clearContact(contact)">clear</button>
24001 *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
24002 *        </li>
24003 *        <li><button ng-click="settings.addContact()">add</button></li>
24004 *     </ul>
24005 *    </div>
24006 *   </file>
24007 *   <file name="app.js">
24008 *    angular.module('controllerAsExample', [])
24009 *      .controller('SettingsController1', SettingsController1);
24010 *
24011 *    function SettingsController1() {
24012 *      this.name = "John Smith";
24013 *      this.contacts = [
24014 *        {type: 'phone', value: '408 555 1212'},
24015 *        {type: 'email', value: '[email protected]'} ];
24016 *    }
24017 *
24018 *    SettingsController1.prototype.greet = function() {
24019 *      alert(this.name);
24020 *    };
24021 *
24022 *    SettingsController1.prototype.addContact = function() {
24023 *      this.contacts.push({type: 'email', value: '[email protected]'});
24024 *    };
24025 *
24026 *    SettingsController1.prototype.removeContact = function(contactToRemove) {
24027 *     var index = this.contacts.indexOf(contactToRemove);
24028 *      this.contacts.splice(index, 1);
24029 *    };
24030 *
24031 *    SettingsController1.prototype.clearContact = function(contact) {
24032 *      contact.type = 'phone';
24033 *      contact.value = '';
24034 *    };
24035 *   </file>
24036 *   <file name="protractor.js" type="protractor">
24037 *     it('should check controller as', function() {
24038 *       var container = element(by.id('ctrl-as-exmpl'));
24039 *         expect(container.element(by.model('settings.name'))
24040 *           .getAttribute('value')).toBe('John Smith');
24041 *
24042 *       var firstRepeat =
24043 *           container.element(by.repeater('contact in settings.contacts').row(0));
24044 *       var secondRepeat =
24045 *           container.element(by.repeater('contact in settings.contacts').row(1));
24046 *
24047 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24048 *           .toBe('408 555 1212');
24049 *
24050 *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
24051 *           .toBe('[email protected]');
24052 *
24053 *       firstRepeat.element(by.buttonText('clear')).click();
24054 *
24055 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24056 *           .toBe('');
24057 *
24058 *       container.element(by.buttonText('add')).click();
24059 *
24060 *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
24061 *           .element(by.model('contact.value'))
24062 *           .getAttribute('value'))
24063 *           .toBe('[email protected]');
24064 *     });
24065 *   </file>
24066 * </example>
24067 *
24068 * This example demonstrates the "attach to `$scope`" style of controller.
24069 *
24070 * <example name="ngController" module="controllerExample">
24071 *  <file name="index.html">
24072 *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
24073 *     <label>Name: <input type="text" ng-model="name"/></label>
24074 *     <button ng-click="greet()">greet</button><br/>
24075 *     Contact:
24076 *     <ul>
24077 *       <li ng-repeat="contact in contacts">
24078 *         <select ng-model="contact.type" id="select_{{$index}}">
24079 *            <option>phone</option>
24080 *            <option>email</option>
24081 *         </select>
24082 *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
24083 *         <button ng-click="clearContact(contact)">clear</button>
24084 *         <button ng-click="removeContact(contact)">X</button>
24085 *       </li>
24086 *       <li>[ <button ng-click="addContact()">add</button> ]</li>
24087 *    </ul>
24088 *   </div>
24089 *  </file>
24090 *  <file name="app.js">
24091 *   angular.module('controllerExample', [])
24092 *     .controller('SettingsController2', ['$scope', SettingsController2]);
24093 *
24094 *   function SettingsController2($scope) {
24095 *     $scope.name = "John Smith";
24096 *     $scope.contacts = [
24097 *       {type:'phone', value:'408 555 1212'},
24098 *       {type:'email', value:'[email protected]'} ];
24099 *
24100 *     $scope.greet = function() {
24101 *       alert($scope.name);
24102 *     };
24103 *
24104 *     $scope.addContact = function() {
24105 *       $scope.contacts.push({type:'email', value:'[email protected]'});
24106 *     };
24107 *
24108 *     $scope.removeContact = function(contactToRemove) {
24109 *       var index = $scope.contacts.indexOf(contactToRemove);
24110 *       $scope.contacts.splice(index, 1);
24111 *     };
24112 *
24113 *     $scope.clearContact = function(contact) {
24114 *       contact.type = 'phone';
24115 *       contact.value = '';
24116 *     };
24117 *   }
24118 *  </file>
24119 *  <file name="protractor.js" type="protractor">
24120 *    it('should check controller', function() {
24121 *      var container = element(by.id('ctrl-exmpl'));
24122 *
24123 *      expect(container.element(by.model('name'))
24124 *          .getAttribute('value')).toBe('John Smith');
24125 *
24126 *      var firstRepeat =
24127 *          container.element(by.repeater('contact in contacts').row(0));
24128 *      var secondRepeat =
24129 *          container.element(by.repeater('contact in contacts').row(1));
24130 *
24131 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24132 *          .toBe('408 555 1212');
24133 *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
24134 *          .toBe('[email protected]');
24135 *
24136 *      firstRepeat.element(by.buttonText('clear')).click();
24137 *
24138 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24139 *          .toBe('');
24140 *
24141 *      container.element(by.buttonText('add')).click();
24142 *
24143 *      expect(container.element(by.repeater('contact in contacts').row(2))
24144 *          .element(by.model('contact.value'))
24145 *          .getAttribute('value'))
24146 *          .toBe('[email protected]');
24147 *    });
24148 *  </file>
24149 *</example>
24150
24151 */
24152var ngControllerDirective = [function() {
24153  return {
24154    restrict: 'A',
24155    scope: true,
24156    controller: '@',
24157    priority: 500
24158  };
24159}];
24160
24161/**
24162 * @ngdoc directive
24163 * @name ngCsp
24164 *
24165 * @element html
24166 * @description
24167 *
24168 * Angular has some features that can break certain
24169 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
24170 *
24171 * If you intend to implement these rules then you must tell Angular not to use these features.
24172 *
24173 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
24174 *
24175 *
24176 * The following rules affect Angular:
24177 *
24178 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
24179 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
24180 * increase in the speed of evaluating Angular expressions.
24181 *
24182 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
24183 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
24184 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
24185 * `angular-csp.css` in your HTML manually.
24186 *
24187 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
24188 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
24189 * however, triggers a CSP error to be logged in the console:
24190 *
24191 * ```
24192 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
24193 * script in the following Content Security Policy directive: "default-src 'self'". Note that
24194 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
24195 * ```
24196 *
24197 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
24198 * directive on an element of the HTML document that appears before the `<script>` tag that loads
24199 * the `angular.js` file.
24200 *
24201 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
24202 *
24203 * You can specify which of the CSP related Angular features should be deactivated by providing
24204 * a value for the `ng-csp` attribute. The options are as follows:
24205 *
24206 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
24207 *
24208 * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
24209 *
24210 * You can use these values in the following combinations:
24211 *
24212 *
24213 * * No declaration means that Angular will assume that you can do inline styles, but it will do
24214 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
24215 * of Angular.
24216 *
24217 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
24218 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
24219 * of Angular.
24220 *
24221 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
24222 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
24223 *
24224 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
24225 * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
24226 *
24227 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
24228 * styles nor use eval, which is the same as an empty: ng-csp.
24229 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
24230 *
24231 * @example
24232 * This example shows how to apply the `ngCsp` directive to the `html` tag.
24233   ```html
24234     <!doctype html>
24235     <html ng-app ng-csp>
24236     ...
24237     ...
24238     </html>
24239   ```
24240  * @example
24241      // Note: the suffix `.csp` in the example name triggers
24242      // csp mode in our http server!
24243      <example name="example.csp" module="cspExample" ng-csp="true">
24244        <file name="index.html">
24245          <div ng-controller="MainController as ctrl">
24246            <div>
24247              <button ng-click="ctrl.inc()" id="inc">Increment</button>
24248              <span id="counter">
24249                {{ctrl.counter}}
24250              </span>
24251            </div>
24252
24253            <div>
24254              <button ng-click="ctrl.evil()" id="evil">Evil</button>
24255              <span id="evilError">
24256                {{ctrl.evilError}}
24257              </span>
24258            </div>
24259          </div>
24260        </file>
24261        <file name="script.js">
24262           angular.module('cspExample', [])
24263             .controller('MainController', function() {
24264                this.counter = 0;
24265                this.inc = function() {
24266                  this.counter++;
24267                };
24268                this.evil = function() {
24269                  // jshint evil:true
24270                  try {
24271                    eval('1+2');
24272                  } catch (e) {
24273                    this.evilError = e.message;
24274                  }
24275                };
24276              });
24277        </file>
24278        <file name="protractor.js" type="protractor">
24279          var util, webdriver;
24280
24281          var incBtn = element(by.id('inc'));
24282          var counter = element(by.id('counter'));
24283          var evilBtn = element(by.id('evil'));
24284          var evilError = element(by.id('evilError'));
24285
24286          function getAndClearSevereErrors() {
24287            return browser.manage().logs().get('browser').then(function(browserLog) {
24288              return browserLog.filter(function(logEntry) {
24289                return logEntry.level.value > webdriver.logging.Level.WARNING.value;
24290              });
24291            });
24292          }
24293
24294          function clearErrors() {
24295            getAndClearSevereErrors();
24296          }
24297
24298          function expectNoErrors() {
24299            getAndClearSevereErrors().then(function(filteredLog) {
24300              expect(filteredLog.length).toEqual(0);
24301              if (filteredLog.length) {
24302                console.log('browser console errors: ' + util.inspect(filteredLog));
24303              }
24304            });
24305          }
24306
24307          function expectError(regex) {
24308            getAndClearSevereErrors().then(function(filteredLog) {
24309              var found = false;
24310              filteredLog.forEach(function(log) {
24311                if (log.message.match(regex)) {
24312                  found = true;
24313                }
24314              });
24315              if (!found) {
24316                throw new Error('expected an error that matches ' + regex);
24317              }
24318            });
24319          }
24320
24321          beforeEach(function() {
24322            util = require('util');
24323            webdriver = require('protractor/node_modules/selenium-webdriver');
24324          });
24325
24326          // For now, we only test on Chrome,
24327          // as Safari does not load the page with Protractor's injected scripts,
24328          // and Firefox webdriver always disables content security policy (#6358)
24329          if (browser.params.browser !== 'chrome') {
24330            return;
24331          }
24332
24333          it('should not report errors when the page is loaded', function() {
24334            // clear errors so we are not dependent on previous tests
24335            clearErrors();
24336            // Need to reload the page as the page is already loaded when
24337            // we come here
24338            browser.driver.getCurrentUrl().then(function(url) {
24339              browser.get(url);
24340            });
24341            expectNoErrors();
24342          });
24343
24344          it('should evaluate expressions', function() {
24345            expect(counter.getText()).toEqual('0');
24346            incBtn.click();
24347            expect(counter.getText()).toEqual('1');
24348            expectNoErrors();
24349          });
24350
24351          it('should throw and report an error when using "eval"', function() {
24352            evilBtn.click();
24353            expect(evilError.getText()).toMatch(/Content Security Policy/);
24354            expectError(/Content Security Policy/);
24355          });
24356        </file>
24357      </example>
24358  */
24359
24360// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
24361// bootstrap the system (before $parse is instantiated), for this reason we just have
24362// the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
24363
24364/**
24365 * @ngdoc directive
24366 * @name ngClick
24367 *
24368 * @description
24369 * The ngClick directive allows you to specify custom behavior when
24370 * an element is clicked.
24371 *
24372 * @element ANY
24373 * @priority 0
24374 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
24375 * click. ({@link guide/expression#-event- Event object is available as `$event`})
24376 *
24377 * @example
24378   <example>
24379     <file name="index.html">
24380      <button ng-click="count = count + 1" ng-init="count=0">
24381        Increment
24382      </button>
24383      <span>
24384        count: {{count}}
24385      </span>
24386     </file>
24387     <file name="protractor.js" type="protractor">
24388       it('should check ng-click', function() {
24389         expect(element(by.binding('count')).getText()).toMatch('0');
24390         element(by.css('button')).click();
24391         expect(element(by.binding('count')).getText()).toMatch('1');
24392       });
24393     </file>
24394   </example>
24395 */
24396/*
24397 * A collection of directives that allows creation of custom event handlers that are defined as
24398 * angular expressions and are compiled and executed within the current scope.
24399 */
24400var ngEventDirectives = {};
24401
24402// For events that might fire synchronously during DOM manipulation
24403// we need to execute their event handlers asynchronously using $evalAsync,
24404// so that they are not executed in an inconsistent state.
24405var forceAsyncEvents = {
24406  'blur': true,
24407  'focus': true
24408};
24409forEach(
24410  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
24411  function(eventName) {
24412    var directiveName = directiveNormalize('ng-' + eventName);
24413    ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
24414      return {
24415        restrict: 'A',
24416        compile: function($element, attr) {
24417          // We expose the powerful $event object on the scope that provides access to the Window,
24418          // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
24419          // checks at the cost of speed since event handler expressions are not executed as
24420          // frequently as regular change detection.
24421          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
24422          return function ngEventHandler(scope, element) {
24423            element.on(eventName, function(event) {
24424              var callback = function() {
24425                fn(scope, {$event:event});
24426              };
24427              if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
24428                scope.$evalAsync(callback);
24429              } else {
24430                scope.$apply(callback);
24431              }
24432            });
24433          };
24434        }
24435      };
24436    }];
24437  }
24438);
24439
24440/**
24441 * @ngdoc directive
24442 * @name ngDblclick
24443 *
24444 * @description
24445 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
24446 *
24447 * @element ANY
24448 * @priority 0
24449 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
24450 * a dblclick. (The Event object is available as `$event`)
24451 *
24452 * @example
24453   <example>
24454     <file name="index.html">
24455      <button ng-dblclick="count = count + 1" ng-init="count=0">
24456        Increment (on double click)
24457      </button>
24458      count: {{count}}
24459     </file>
24460   </example>
24461 */
24462
24463
24464/**
24465 * @ngdoc directive
24466 * @name ngMousedown
24467 *
24468 * @description
24469 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
24470 *
24471 * @element ANY
24472 * @priority 0
24473 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
24474 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
24475 *
24476 * @example
24477   <example>
24478     <file name="index.html">
24479      <button ng-mousedown="count = count + 1" ng-init="count=0">
24480        Increment (on mouse down)
24481      </button>
24482      count: {{count}}
24483     </file>
24484   </example>
24485 */
24486
24487
24488/**
24489 * @ngdoc directive
24490 * @name ngMouseup
24491 *
24492 * @description
24493 * Specify custom behavior on mouseup event.
24494 *
24495 * @element ANY
24496 * @priority 0
24497 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
24498 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
24499 *
24500 * @example
24501   <example>
24502     <file name="index.html">
24503      <button ng-mouseup="count = count + 1" ng-init="count=0">
24504        Increment (on mouse up)
24505      </button>
24506      count: {{count}}
24507     </file>
24508   </example>
24509 */
24510
24511/**
24512 * @ngdoc directive
24513 * @name ngMouseover
24514 *
24515 * @description
24516 * Specify custom behavior on mouseover event.
24517 *
24518 * @element ANY
24519 * @priority 0
24520 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
24521 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
24522 *
24523 * @example
24524   <example>
24525     <file name="index.html">
24526      <button ng-mouseover="count = count + 1" ng-init="count=0">
24527        Increment (when mouse is over)
24528      </button>
24529      count: {{count}}
24530     </file>
24531   </example>
24532 */
24533
24534
24535/**
24536 * @ngdoc directive
24537 * @name ngMouseenter
24538 *
24539 * @description
24540 * Specify custom behavior on mouseenter event.
24541 *
24542 * @element ANY
24543 * @priority 0
24544 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
24545 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
24546 *
24547 * @example
24548   <example>
24549     <file name="index.html">
24550      <button ng-mouseenter="count = count + 1" ng-init="count=0">
24551        Increment (when mouse enters)
24552      </button>
24553      count: {{count}}
24554     </file>
24555   </example>
24556 */
24557
24558
24559/**
24560 * @ngdoc directive
24561 * @name ngMouseleave
24562 *
24563 * @description
24564 * Specify custom behavior on mouseleave event.
24565 *
24566 * @element ANY
24567 * @priority 0
24568 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
24569 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
24570 *
24571 * @example
24572   <example>
24573     <file name="index.html">
24574      <button ng-mouseleave="count = count + 1" ng-init="count=0">
24575        Increment (when mouse leaves)
24576      </button>
24577      count: {{count}}
24578     </file>
24579   </example>
24580 */
24581
24582
24583/**
24584 * @ngdoc directive
24585 * @name ngMousemove
24586 *
24587 * @description
24588 * Specify custom behavior on mousemove event.
24589 *
24590 * @element ANY
24591 * @priority 0
24592 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
24593 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
24594 *
24595 * @example
24596   <example>
24597     <file name="index.html">
24598      <button ng-mousemove="count = count + 1" ng-init="count=0">
24599        Increment (when mouse moves)
24600      </button>
24601      count: {{count}}
24602     </file>
24603   </example>
24604 */
24605
24606
24607/**
24608 * @ngdoc directive
24609 * @name ngKeydown
24610 *
24611 * @description
24612 * Specify custom behavior on keydown event.
24613 *
24614 * @element ANY
24615 * @priority 0
24616 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
24617 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
24618 *
24619 * @example
24620   <example>
24621     <file name="index.html">
24622      <input ng-keydown="count = count + 1" ng-init="count=0">
24623      key down count: {{count}}
24624     </file>
24625   </example>
24626 */
24627
24628
24629/**
24630 * @ngdoc directive
24631 * @name ngKeyup
24632 *
24633 * @description
24634 * Specify custom behavior on keyup event.
24635 *
24636 * @element ANY
24637 * @priority 0
24638 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
24639 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
24640 *
24641 * @example
24642   <example>
24643     <file name="index.html">
24644       <p>Typing in the input box below updates the key count</p>
24645       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
24646
24647       <p>Typing in the input box below updates the keycode</p>
24648       <input ng-keyup="event=$event">
24649       <p>event keyCode: {{ event.keyCode }}</p>
24650       <p>event altKey: {{ event.altKey }}</p>
24651     </file>
24652   </example>
24653 */
24654
24655
24656/**
24657 * @ngdoc directive
24658 * @name ngKeypress
24659 *
24660 * @description
24661 * Specify custom behavior on keypress event.
24662 *
24663 * @element ANY
24664 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
24665 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
24666 * and can be interrogated for keyCode, altKey, etc.)
24667 *
24668 * @example
24669   <example>
24670     <file name="index.html">
24671      <input ng-keypress="count = count + 1" ng-init="count=0">
24672      key press count: {{count}}
24673     </file>
24674   </example>
24675 */
24676
24677
24678/**
24679 * @ngdoc directive
24680 * @name ngSubmit
24681 *
24682 * @description
24683 * Enables binding angular expressions to onsubmit events.
24684 *
24685 * Additionally it prevents the default action (which for form means 
24685sending the request to the
24686 * server and reloading the current page), but only if the form does not contain `action`,
24687 * `data-action`, or `x-action` attributes.
24688 *
24689 * <div class="alert alert-warning">
24690 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
24691 * `ngSubmit` handlers together. See the
24692 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
24693 * for a detailed discussion of when `ngSubmit` may be triggered.
24694 * </div>
24695 *
24696 * @element form
24697 * @priority 0
24698 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
24699 * ({@link guide/expression#-event- Event object is available as `$event`})
24700 *
24701 * @example
24702   <example module="submitExample">
24703     <file name="index.html">
24704      <script>
24705        angular.module('submitExample', [])
24706          .controller('ExampleController', ['$scope', function($scope) {
24707            $scope.list = [];
24708            $scope.text = 'hello';
24709            $scope.submit = function() {
24710              if ($scope.text) {
24711                $scope.list.push(this.text);
24712                $scope.text = '';
24713              }
24714            };
24715          }]);
24716      </script>
24717      <form ng-submit="submit()" ng-controller="ExampleController">
24718        Enter text and hit enter:
24719        <input type="text" ng-model="text" name="text" />
24720        <input type="submit" id="submit" value="Submit" />
24721        <pre>list={{list}}</pre>
24722      </form>
24723     </file>
24724     <file name="protractor.js" type="protractor">
24725       it('should check ng-submit', function() {
24726         expect(element(by.binding('list')).getText()).toBe('list=[]');
24727         element(by.css('#submit')).click();
24728         expect(element(by.binding('list')).getText()).toContain('hello');
24729         expect(element(by.model('text')).getAttribute('value')).toBe('');
24730       });
24731       it('should ignore empty strings', function() {
24732         expect(element(by.binding('list')).getText()).toBe('list=[]');
24733         element(by.css('#submit')).click();
24734         element(by.css('#submit')).click();
24735         expect(element(by.binding('list')).getText()).toContain('hello');
24736        });
24737     </file>
24738   </example>
24739 */
24740
24741/**
24742 * @ngdoc directive
24743 * @name ngFocus
24744 *
24745 * @description
24746 * Specify custom behavior on focus event.
24747 *
24748 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
24749 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24750 * during an `$apply` to ensure a consistent state.
24751 *
24752 * @element window, input, select, textarea, a
24753 * @priority 0
24754 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
24755 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
24756 *
24757 * @example
24758 * See {@link ng.directive:ngClick ngClick}
24759 */
24760
24761/**
24762 * @ngdoc directive
24763 * @name ngBlur
24764 *
24765 * @description
24766 * Specify custom behavior on blur event.
24767 *
24768 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
24769 * an element has lost focus.
24770 *
24771 * Note: As the `blur` event is executed synchronously also during DOM manipulations
24772 * (e.g. removing a focussed input),
24773 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24774 * during an `$apply` to ensure a consistent state.
24775 *
24776 * @element window, input, select, textarea, a
24777 * @priority 0
24778 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
24779 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
24780 *
24781 * @example
24782 * See {@link ng.directive:ngClick ngClick}
24783 */
24784
24785/**
24786 * @ngdoc directive
24787 * @name ngCopy
24788 *
24789 * @description
24790 * Specify custom behavior on copy event.
24791 *
24792 * @element window, input, select, textarea, a
24793 * @priority 0
24794 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
24795 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
24796 *
24797 * @example
24798   <example>
24799     <file name="index.html">
24800      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
24801      copied: {{copied}}
24802     </file>
24803   </example>
24804 */
24805
24806/**
24807 * @ngdoc directive
24808 * @name ngCut
24809 *
24810 * @description
24811 * Specify custom behavior on cut event.
24812 *
24813 * @element window, input, select, textarea, a
24814 * @priority 0
24815 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
24816 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
24817 *
24818 * @example
24819   <example>
24820     <file name="index.html">
24821      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
24822      cut: {{cut}}
24823     </file>
24824   </example>
24825 */
24826
24827/**
24828 * @ngdoc directive
24829 * @name ngPaste
24830 *
24831 * @description
24832 * Specify custom behavior on paste event.
24833 *
24834 * @element window, input, select, textarea, a
24835 * @priority 0
24836 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
24837 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
24838 *
24839 * @example
24840   <example>
24841     <file name="index.html">
24842      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
24843      pasted: {{paste}}
24844     </file>
24845   </example>
24846 */
24847
24848/**
24849 * @ngdoc directive
24850 * @name ngIf
24851 * @restrict A
24852 * @multiElement
24853 *
24854 * @description
24855 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
24856 * {expression}. If the expression assigned to `ngIf` evaluates to a false
24857 * value then the element is removed from the DOM, otherwise a clone of the
24858 * element is reinserted into the DOM.
24859 *
24860 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24861 * element in the DOM rather than changing its visibility via the `display` css property.  A common
24862 * case when this difference is significant is when using css selectors that rely on an element's
24863 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classe
24863s.
24864 *
24865 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
24866 * is created when the element is restored.  The scope created within `ngIf` inherits from
24867 * its parent scope using
24868 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
24869 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24870 * a javascript primitive defined in the parent scope. In this case any modifications made to the
24871 * variable within the child scope will override (hide) the value in the parent scope.
24872 *
24873 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
24874 * is if an element's class attribute is directly modified after it's compiled, using something like
24875 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24876 * the added class will be lost because the original compiled state is used to regenerate the element.
24877 *
24878 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24879 * and `leave` effects.
24880 *
24881 * @animations
24882 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24883 * leave - happens just before the `ngIf` contents are removed from the DOM
24884 *
24885 * @element ANY
24886 * @scope
24887 * @priority 600
24888 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24889 *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
24890 *     element is added to the DOM tree.
24891 *
24892 * @example
24893  <example module="ngAnimate" deps="angular-animate.js" animations="true">
24894    <file name="index.html">
24895      <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24896      Show when checked:
24897      <span ng-if="checked" class="animate-if">
24898        This is removed when the checkbox is unchecked.
24899      </span>
24900    </file>
24901    <file name="animations.css">
24902      .animate-if {
24903        background:white;
24904        border:1px solid black;
24905        padding:10px;
24906      }
24907
24908      .animate-if.ng-enter, .animate-if.ng-leave {
24909        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24910      }
24911
24912      .animate-if.ng-enter,
24913      .animate-if.ng-leave.ng-leave-active {
24914        opacity:0;
24915      }
24916
24917      .animate-if.ng-leave,
24918      .animate-if.ng-enter.ng-enter-active {
24919        opacity:1;
24920      }
24921    </file>
24922  </example>
24923 */
24924var ngIfDirective = ['$animate', function($animate) {
24925  return {
24926    multiElement: true,
24927    transclude: 'element',
24928    priority: 600,
24929    terminal: true,
24930    restrict: 'A',
24931    $$tlb: true,
24932    link: function($scope, $element, $attr, ctrl, $transclude) {
24933        var block, childScope, previousElements;
24934        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24935
24936          if (value) {
24937            if (!childScope) {
24938              $transclude(function(clone, newScope) {
24939                childScope = newScope;
24940                clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24941                // Note: We only need the first/last node of the cloned nodes.
24942                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24943                // by a directive with templateUrl when its template arrives.
24944                block = {
24945                  clone: clone
24946                };
24947                $animate.enter(clone, $element.parent(), $element);
24948              });
24949            }
24950          } else {
24951            if (previousElements) {
24952              previousElements.remove();
24953              previousElements = null;
24954            }
24955            if (childScope) {
24956              childScope.$destroy();
24957              childScope = null;
24958            }
24959            if (block) {
24960              previousElements = getBlockNodes(block.clone);
24961              $animate.leave(previousElements).then(function() {
24962                previousElements = null;
24963              });
24964              block = null;
24965            }
24966          }
24967        });
24968    }
24969  };
24970}];
24971
24972/**
24973 * @ngdoc directive
24974 * @name ngInclude
24975 * @restrict ECA
24976 *
24977 * @description
24978 * Fetches, compiles and includes an external HTML fragment.
24979 *
24980 * By default, the template URL is restricted to the same domain and protocol as the
24981 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24982 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24983 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24984 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24985 * ng.$sce Strict Contextual Escaping}.
24986 *
24987 * In addition, the browser's
24988 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24989 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24990 * policy may further restrict whether the template is successfully loaded.
24991 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24992 * access on some browsers.
24993 *
24994 * @animations
24995 * enter - animation is used to bring new content into the browser.
24996 * leave - animation is used to animate existing content away.
24997 *
24998 * The enter and leave animation occur concurrently.
24999 *
25000 * @scope
25001 * @priority 400
25002 *
25003 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
25004 *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
25005 * @param {string=} onload Expression to evaluate when a new partial is loaded.
25006 *                  <div class="alert alert-warning">
25007 *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
25008 *                  a function with the name on the window element, which will usually throw a
25009 *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
25010 *                  different form that {@link guide/directive#normalization matches} `onload`.
25011 *                  </div>
25012   *
25013 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
25014 *                  $anchorScroll} to scroll the viewport after the content is loaded.
25015 *
25016 *                  - If the attribute is not set, disable scrolling.
25017 *                  - If the attribute is set without value, enable scrolling.
25018 *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
25019 *
25020 * @example
25021  <example module="includeExample" deps="angular-animate.js" animations="true">
25022    <file name="index.html">
25023     <div ng-controller="ExampleController">
25024       <select ng-model="template" ng-options="t.name for t in templates">
25025        <option value="">(blank)</option>
25026       </select>
25027       url of the template: <code>{{template.url}}</code>
25028       <hr/>
25029       <div class="slide-animate-container">
25030         <div class="slide-animate" ng-include="template.url"></div>
25031       </div>
25032     </div>
25033    </file>
25034    <file name="script.js">
25035      angular.module('includeExample', ['ngAnimate'])
25036        .controller('ExampleController', ['$scope', function($scope) {
25037          $scope.templates =
25038            [ { name: 'template1.html', url: 'template1.html'},
25039              { name: 'template2.html', url: 'template2.html'} ];
25040          $scope.template = $scope.templates[0];
25041        }]);
25042     </file>
25043    <file name="template1.html">
25044      Content of template1.html
25045    </file>
25046    <file name="template2.html">
25047      Content of template2.html
25048    </file>
25049    <file name="animations.css">
25050      .slide-animate-container {
25051        position:relative;
25052        background:white;
25053        border:1px solid black;
25054        height:40px;
25055        overflow:hidden;
25056      }
25057
25058      .slide-animate {
25059        padding:10px;
25060      }
25061
25062      .slide-animate.ng-enter, .slide-animate.ng-leave {
25063        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25064
25065        position:absolute;
25066        top:0;
25067        left:0;
25068        right:0;
25069        bottom:0;
25070        display:block;
25071        padding:10px;
25072      }
25073
25074      .slide-animate.ng-enter {
25075        top:-50px;
25076      }
25077      .slide-animate.ng-enter.ng-enter-active {
25078        top:0;
25079      }
25080
25081      .slide-animate.ng-leave {
25082        top:0;
25083      }
25084      .slide-animate.ng-leave.ng-leave-active {
25085        top:50px;
25086      }
25087    </file>
25088    <file name="protractor.js" type="protractor">
25089      var templateSelect = element(by.model('template'));
25090      var includeElem = element(by.css('[ng-include]'));
25091
25092      it('should load template1.html', function() {
25093        expect(includeElem.getText()).toMatch(/Content of template1.html/);
25094      });
25095
25096      it('should load template2.html', function() {
25097        if (browser.params.browser == 'firefox') {
25098          // Firefox can't handle using selects
25099          // See https://github.com/angular/protractor/issues/480
25100          return;
25101        }
25102        templateSelect.click();
25103        templateSelect.all(by.css('option')).get(2).click();
25104        expect(includeElem.getText()).toMatch(/Content of template2.html/);
25105      });
25106
25107      it('should change to blank', function() {
25108        if (browser.params.browser == 'firefox') {
25109          // Firefox can't handle using selects
25110          return;
25111        }
25112        templateSelect.click();
25113        templateSelect.all(by.css('option')).get(0).click();
25114        expect(includeElem.isPresent()).toBe(false);
25115      });
25116    </file>
25117  </example>
25118 */
25119
25120
25121/**
25122 * @ngdoc event
25123 * @name ngInclude#$includeContentRequested
25124 * @eventType emit on the scope ngInclude was declared in
25125 * @description
25126 * Emitted every time the ngInclude content is requested.
25127 *
25128 * @param {Object} angularEvent Synthetic event object.
25129 * @param {String} src URL of content to load.
25130 */
25131
25132
25133/**
25134 * @ngdoc event
25135 * @name ngInclude#$includeContentLoaded
25136 * @eventType emit on the current ngInclude scope
25137 * @description
25138 * Emitted every time the ngInclude content is reloaded.
25139 *
25140 * @param {Object} angularEvent Synthetic event object.
25141 * @param {String} src URL of content to load.
25142 */
25143
25144
25145/**
25146 * @ngdoc event
25147 * @name ngInclude#$includeContentError
25148 * @eventType emit on the scope ngInclude was declared in
25149 * @description
25150 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
25151 *
25152 * @param {Object} angularEvent Synthetic event object.
25153 * @param {String} src URL of content to load.
25154 */
25155var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
25156                  function($templateRequest,   $anchorScroll,   $animate) {
25157  return {
25158    restrict: 'ECA',
25159    priority: 400,
25160    terminal: true,
25161    transclude: 'element',
25162    controller: angular.noop,
25163    compile: function(element, attr) {
25164      var srcExp = attr.ngInclude || attr.src,
25165          onloadExp = attr.onload || '',
25166          autoScrollExp = attr.autoscroll;
25167
25168      return function(scope, $element, $attr, ctrl, $transclude) {
25169        var changeCounter = 0,
25170            currentScope,
25171            previousElement,
25172            currentElement;
25173
25174        var cleanupLastIncludeContent = function() {
25175          if (previousElement) {
25176            previousElement.remove();
25177            previousElement = null;
25178          }
25179          if (currentScope) {
25180            currentScope.$destroy();
25181            currentScope = null;
25182          }
25183          if (currentElement) {
25184            $animate.leave(currentElement).then(function() {
25185              previousElement = null;
25186            });
25187            previousElement = currentElement;
25188            currentElement = null;
25189          }
25190        };
25191
25192        scope.$watch(srcExp, function ngIncludeWatchAction(src) {
25193          var afterAnimation = function() {
25194            if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
25195              $anchorScroll();
25196            }
25197          };
25198          var thisChangeId = ++changeCounter;
25199
25200          if (src) {
25201            //set the 2nd param to true to ignore the template request error so that the inner
25202            //contents and scope can be cleaned up.
25203            $templateRequest(src, true).then(function(response) {
25204              if (scope.$$destroyed) return;
25205
25206              if (thisChangeId !== changeCounter) return;
25207              var newScope = scope.$new();
25208              ctrl.template = response;
25209
25210              // Note: This will also link all children of ng-include that were contained in the original
25211              // html. If that content contains controllers, ... they could pollute/change the scope.
25212              // However, using ng-include on an element with additional content does not make sense...
25213              // Note: We can't remove them in the cloneAttchFn of $transclude as that
25214              // function is called before linking the content, which would apply child
25215              // directives to non existing elements.
25216              var clone = $transclude(newScope, function(clone) {
25217                cleanupLastIncludeContent();
25218                $animate.enter(clone, null, $element).then(afterAnimation);
25219              });
25220
25221              currentScope = newScope;
25222              currentElement = clone;
25223
25224              currentScope.$emit('$includeContentLoaded', src);
25225              scope.$eval(onloadExp);
25226            }, function() {
25227              if (scope.$$destroyed) return;
25228
25229              if (thisChangeId === changeCounter) {
25230                cleanupLastIncludeContent();
vendor: 2,918 bytes, lines 25231-25313
25231                scope.$emit('$includeContentError', src);
25232              }
25233            });
25234            scope.$emit('$includeContentRequested', src);
25235          } else {
25236            cleanupLastIncludeContent();
25237            ctrl.template = null;
25238          }
25239        });
25240      };
25241    }
25242  };
25243}];
25244
25245// This directive is called during the $transclude call of the first `ngInclude` directive.
25246// It will replace and compile the content of the element with the loaded template.
25247// We need this directive so that the element content is already filled when
25248// the link function of another directive on the same element as ngInclude
25249// is called.
25250var ngIncludeFillContentDirective = ['$compile',
25251  function($compile) {
25252    return {
25253      restrict: 'ECA',
25254      priority: -400,
25255      require: 'ngInclude',
25256      link: function(scope, $element, $attr, ctrl) {
25257        if (toString.call($element[0]).match(/SVG/)) {
25258          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
25259          // support innerHTML, so detect this here and try to generate the contents
25260          // specially.
25261          $element.empty();
25262          $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
25263              function namespaceAdaptedClone(clone) {
25264            $element.append(clone);
25265          }, {futureParentElement: $element});
25266          return;
25267        }
25268
25269        $element.html(ctrl.template);
25270        $compile($element.contents())(scope);
25271      }
25272    };
25273  }];
25274
25275/**
25276 * @ngdoc directive
25277 * @name ngInit
25278 * @restrict AC
25279 *
25280 * @description
25281 * The `ngInit` directive allows you to evaluate an expression in the
25282 * current scope.
25283 *
25284 * <div class="alert alert-danger">
25285 * This directive can be abused to add unnecessary amounts of logic into your templates.
25286 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
25287 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
25288 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
25289 * rather than `ngInit` to initialize values on a scope.
25290 * </div>
25291 *
25292 * <div class="alert alert-warning">
25293 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
25294 * sure you have parentheses to ensure correct operator precedence:
25295 * <pre class="prettyprint">
25296 * `<div ng-init="test1 = ($index | toString)"></div>`
25297 * </pre>
25298 * </div>
25299 *
25300 * @priority 450
25301 *
25302 * @element ANY
25303 * @param {expression} ngInit {@link guide/expression Expression} to eval.
25304 *
25305 * @example
25306   <example module="initExample">
25307     <file name="index.html">
25308   <script>
25309     angular.module('initExample', [])
25310       .controller('ExampleController', ['$scope', function($scope) {
25311         $scope.list = [['a', 'b'], ['c', 'd']];
25312       }]);
25313   </script>
25314   <div ng-controller="ExampleController">
25315     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
25316       <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
25317          <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
25318       </div>
25319     </div>
25320   </div>
25321     </file>
25322     <file name="protractor.js" type="protractor">
25323       it('should alias index positions', function() {
25324         var elements = element.all(by.css('.example-init'));
25325         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
25326         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
25327         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
25328         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
25329       });
25330     </file>
25331   </example>
25332 */
25333var ngInitDirective = ngDirective({
25334  priority: 450,
25335  compile: function() {
25336    return {
25337      pre: function(scope, element, attrs) {
25338        scope.$eval(attrs.ngInit);
25339      }
25340    };
25341  }
25342});
25343
25344/**
25345 * @ngdoc directive
25346 * @name ngList
25347 *
25348 * @description
25349 * Text input that converts between a delimited string and an array of strings. The default
25350 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
25351 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
25352 *
25353 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
25354 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
25355 *   list item is respected. This implies that the user of the directive is responsible for
25356 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
25357 *   tab or newline character.
25358 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
25359 *   when joining the list items back together) and whitespace around each list item is stripped
25360 *   before it is added to the model.
25361 *
25362 * ### Example with Validation
25363 *
25364 * <example name="ngList-directive" module="listExample">
25365 *   <file name="app.js">
25366 *      angular.module('listExample', [])
25367 *        .controller('ExampleController', ['$scope', function($scope) {
25368 *          $scope.names = ['morpheus', 'neo', 'trinity'];
25369 *        }]);
25370 *   </file>
25371 *   <file name="index.html">
25372 *    <form name="myForm" ng-controller="ExampleController">
25373 *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
25374 *      <span role="alert">
25375 *        <span class="error" ng-show="myForm.namesInput.$error.required">
25376 *        Required!</span>
25377 *      </span>
25378 *      <br>
25379 *      <tt>names = {{names}}</tt><br/>
25380 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
25381 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
25382 *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25383 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25384 *     </form>
25385 *   </file>
25386 *   <file name="protractor.js" type="protractor">
25387 *     var listInput = element(by.model('names'));
25388 *     var names = element(by.exactBinding('names'));
25389 *     var valid = element(by.binding('myForm.namesInput.$valid'));
25390 *     var error = element(by.css('span.error'));
25391 *
25392 *     it('should initialize to model', function() {
25393 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
25394 *       expect(valid.getText()).toContain('true');
25395 *       expect(error.getCssValue('display')).toBe('none');
25396 *     });
25397 *
25398 *     it('should be invalid if empty', function() {
25399 *       listInput.clear();
25400 *       listInput.sendKeys('');
25401 *
25402 *       expect(names.getText()).toContain('');
25403 *       expect(valid.getText()).toContain('false');
25404 *       expect(error.getCssValue('display')).not.toBe('none');
25405 *     });
25406 *   </file>
25407 * </example>
25408 *
25409 * ### Example - splitting on newline
25410 * <example name="ngList-directive-newlines">
25411 *   <file name="index.html">
25412 *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
25413 *    <pre>{{ list | json }}</pre>
25414 *   </file>
25415 *   <file name="protractor.js" type="protractor">
25416 *     it("should split the text by newlines", function() {
25417 *       var listInput = element(by.model('list'));
25418 *       var output = element(by.binding('list | json'));
25419 *       listInput.sendKeys('abc\ndef\nghi');
25420 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
25421 *     });
25422 *   </file>
25423 * </example>
25424 *
25425 * @element input
25426 * @param {string=} ngList optional delimiter that should be used to split the value.
25427 */
25428var ngListDirective = function() {
25429  return {
25430    restrict: 'A',
25431    priority: 100,
25432    require: 'ngModel',
25433    link: function(scope, element, attr, ctrl) {
25434      // We want to control whitespace trimming so we use this convoluted approach
25435      // to access the ngList attribute, which doesn't pre-trim the attribute
25436      var ngList = element.attr(attr.$attr.ngList) || ', ';
25437      var trimValues = attr.ngTrim !== 'false';
25438      var separator = trimValues ? trim(ngList) : ngList;
25439
25440      var parse = function(viewValue) {
25441        // If the viewValue is invalid (say required but empty) it will be `undefined`
25442        if (isUndefined(viewValue)) return;
25443
25444        var list = [];
25445
25446        if (viewValue) {
25447          forEach(viewValue.split(separator), function(value) {
25448            if (value) list.push(trimValues ? trim(value) : value);
25449          });
25450        }
25451
25452        return list;
25453      };
25454
25455      ctrl.$parsers.push(parse);
25456      ctrl.$formatters.push(function(value) {
25457        if (isArray(value)) {
25458          return value.join(ngList);
25459        }
25460
25461        return undefined;
25462      });
25463
25464      // Override the standard $isEmpty because an empty array means the input is empty.
25465      ctrl.$isEmpty = function(value) {
25466        return !value || !value.length;
25467      };
25468    }
25469  };
25470};
25471
25472/* global VALID_CLASS: true,
25473  INVALID_CLASS: true,
25474  PRISTINE_CLASS: true,
25475  DIRTY_CLASS: true,
25476  UNTOUCHED_CLASS: true,
25477  TOUCHED_CLASS: true,
25478*/
25479
25480var VALID_CLASS = 'ng-valid',
25481    INVALID_CLASS = 'ng-invalid',
25482    PRISTINE_CLASS = 'ng-pristine',
25483    DIRTY_CLASS = 'ng-dirty',
25484    UNTOUCHED_CLASS = 'ng-untouched',
25485    TOUCHED_CLASS = 'ng-touched',
25486    PENDING_CLASS = 'ng-pending',
25487    EMPTY_CLASS = 'ng-empty',
25488    NOT_EMPTY_CLASS = 'ng-not-empty';
25489
25490var ngModelMinErr = minErr('ngModel');
25491
25492/**
25493 * @ngdoc type
25494 * @name ngModel.NgModelController
25495 *
25496 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
25497 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
25498 * is set.
25499 * @property {*} $modelValue The value in the model that the control is bound to.
25500 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
25501       the control reads value from the DOM. The functions are called in array order, each passing
25502       its return value through to the next. The last return value is forwarded to the
25503       {@link ngModel.NgModelController#$validators `$validators`} collection.
25504
25505Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
25506`$viewValue`}.
25507
25508Returning `undefined` from a parser means a parse error occurred. In that case,
25509no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
25510will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
25511is set to `true`. The parse error is stored in `ngModel.$error.parse`.
25512
25513 *
25514 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
25515       the model value changes. The functions are called in reverse array order, each passing the value through to the
25516       next. The last return value is used as the actual DOM value.
25517       Used to format / convert values for display in the control.
25518 * ```js
25519 * function formatter(value) {
25520 *   if (value) {
25521 *     return value.toUpperCase();
25522 *   }
25523 * }
25524 * ngModel.$formatters.push(formatter);
25525 * ```
25526 *
25527 * @property {Object.<string, function>} $validators A collection of validators that are applied
25528 *      whenever the model value changes. The key value within the object refers to the name of the
25529 *      validator while the function refers to the validation operation. The validation operation is
25530 *      provided with the model value as an argument and must return a true or false value depending
25531 *      on the response of that validation.
25532 *
25533 * ```js
25534 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
25535 *   var value = modelValue || viewValue;
25536 *   return /[0-9]+/.test(value) &&
25537 *          /[a-z]+/.test(value) &&
25538 *          /[A-Z]+/.test(value) &&
25539 *          /\W+/.test(value);
25540 * };
25541 * ```
25542 *
25543 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
25544 *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
25545 *      is expected to return a promise when it is run during the model validation process. Once the promise
25546 *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
25547 *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
25548 *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
25549 *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
25550 *      will only run once all synchronous validators have passed.
25551 *
25552 * Please note that if $http is used then it is important that the server returns a success HTTP response code
25553 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
25554 *
25555 * ```js
25556 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
25557 *   var value = modelValue || viewValue;
25558 *
25559 *   // Lookup user by username
25560 *   return $http.get('/api/users/' + value).
25561 *      then(function resolved() {
25562 *        //username exists, this means validation fails
25563 *        return $q.reject('exists');
25564 *      }, function rejected() {
25565 *        //username does not exist, therefore this validation passes
25566 *        return true;
25567 *      });
25568 * };
25569 * ```
25570 *
25571 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
25572 *     view value has changed. It is called with no arguments, and its return value is ignored.
25573 *     This can be used in place of additional $watches against the model value.
25574 *
25575 * @property {Object} $error An object hash with all failing validator ids as keys.
25576 * @property {Object} $pending An object hash with all pending validator ids as keys.
25577 *
25578 * @property {boolean} $untouched True if control has not lost focus yet.
25579 * @property {boolean} $touched True if control has lost focus.
25580 * @property {boolean} $pristine True if user has not interacted with the control yet.
25581 * @property {boolean} $dirty True if user has already interacted with the control.
25582 * @property {boolean} $valid True if there is no error.
25583 * @property {boolean}
25583 $invalid True if at least one error on the control.
25584 * @property {string} $name The name attribute of the control.
25585 *
25586 * @description
25587 *
25588 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
25589 * The controller contains services for data-binding, validation, CSS updates, and value formatting
25590 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
25591 * listening to DOM events.
25592 * Such DOM related logic should be provided by other directives which make use of
25593 * `NgModelController` for data-binding to control elements.
25594 * Angular provides this DOM logic for most {@link input `input`} elements.
25595 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
25596 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
25597 *
25598 * @example
25599 * ### Custom Control Example
25600 * This example shows how to use `NgModelController` with a custom control to achieve
25601 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
25602 * collaborate together to achieve the desired result.
25603 *
25604 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
25605 * contents be edited in place by the user.
25606 *
25607 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
25608 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
25609 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
25610 * that content using the `$sce` service.
25611 *
25612 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
25613    <file name="style.css">
25614      [contenteditable] {
25615        border: 1px solid black;
25616        background-color: white;
25617        min-height: 20px;
25618      }
25619
25620      .ng-invalid {
25621        border: 1px solid red;
25622      }
25623
25624    </file>
25625    <file name="script.js">
25626      angular.module('customControl', ['ngSanitize']).
25627        directive('contenteditable', ['$sce', function($sce) {
25628          return {
25629            restrict: 'A', // only activate on element attribute
25630            require: '?ngModel', // get a hold of NgModelController
25631            link: function(scope, element, attrs, ngModel) {
25632              if (!ngModel) return; // do nothing if no ng-model
25633
25634              // Specify how UI should be updated
25635              ngModel.$render = function() {
25636                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
25637              };
25638
25639              // Listen for change events to enable binding
25640              element.on('blur keyup change', function() {
25641                scope.$evalAsync(read);
25642              });
25643              read(); // initialize
25644
25645              // Write data to the model
25646              function read() {
25647                var html = element.html();
25648                // When we clear the content editable the browser leaves a <br> behind
25649                // If strip-br attribute is provided then we strip this out
25650                if ( attrs.stripBr && html == '<br>' ) {
25651                  html = '';
25652                }
25653                ngModel.$setViewValue(html);
25654              }
25655            }
25656          };
25657        }]);
25658    </file>
25659    <file name="index.html">
25660      <form name="myForm">
25661       <div contenteditable
25662            name="myWidget" ng-model="userContent"
25663            strip-br="true"
25664            required>Change me!</div>
25665        <span ng-show="myForm.myWidget.$error.required">Required!</span>
25666       <hr>
25667       <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
25668      </form>
25669    </file>
25670    <file name="protractor.js" type="protractor">
25671    it('should data-bind and become invalid', function() {
25672      if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
25673        // SafariDriver can't handle contenteditable
25674        // and Firefox driver can't clear contenteditables very well
25675        return;
25676      }
25677      var contentEditable = element(by.css('[contenteditable]'));
25678      var content = 'Change me!';
25679
25680      expect(contentEditable.getText()).toEqual(content);
25681
25682      contentEditable.clear();
25683      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
25684      expect(contentEditable.getText()).toEqual('');
25685      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
25686    });
25687    </file>
25688 * </example>
25689 *
25690 *
25691 */
25692var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
25693    function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
25694  this.$viewValue = Number.NaN;
25695  this.$modelValue = Number.NaN;
25696  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
25697  this.$validators = {};
25698  this.$asyncValidators = {};
25699  this.$parsers = [];
25700  this.$formatters = [];
25701  this.$viewChangeListeners = [];
25702  this.$untouched = true;
25703  this.$touched = false;
25704  this.$pristine = true;
25705  this.$dirty = false;
25706  this.$valid = true;
25707  this.$invalid = false;
25708  this.$error = {}; // keep invalid keys here
25709  this.$$success = {}; // keep valid keys here
25710  this.$pending = undefined; // keep pending keys here
25711  this.$name = $interpolate($attr.name || '', false)($scope);
25712  this.$$parentForm = nullFormCtrl;
25713
25714  var parsedNgModel = $parse($attr.ngModel),
25715      parsedNgModelAssign = parsedNgModel.assign,
25716      ngModelGet = parsedNgModel,
25717      ngModelSet = parsedNgModelAssign,
25718      pendingDebounce = null,
25719      parserValid,
25720      ctrl = this;
25721
25722  this.$$setOptions = function(options) {
25723    ctrl.$options = options;
25724    if (options && options.getterSetter) {
25725      var invokeModelGetter = $parse($attr.ngModel + '()'),
25726          invokeModelSetter = $parse($attr.ngModel + '($$$p)');
25727
25728      ngModelGet = function($scope) {
25729        var modelValue = parsedNgModel($scope);
25730        if (isFunction(modelValue)) {
25731          modelValue = invokeModelGetter($scope);
25732        }
25733        return modelValue;
25734      };
25735      ngModelSet = function($scope, newValue) {
25736        if (isFunction(parsedNgModel($scope))) {
25737          invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
25738        } else {
25739          parsedNgModelAssign($scope, ctrl.$modelValue);
25740        }
25741      };
25742    } else if (!parsedNgModel.assign) {
25743      throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
25744          $attr.ngModel, startingTag($element));
25745    }
25746  };
25747
25748  /**
25749   * @ngdoc method
25750   * @name ngModel.NgModelController#$render
25751   *
25752   * @description
25753   * Called when the view needs to be updated. It is expected that the user of the ng-model
25754   * directive will implement this method.
25755   *
25756   * The `$render()` method is invoked in the following situations:
25757   *
25758   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
25759   *   committed value then `$render()` is called to update the input control.
25760   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
25761   *   the `$viewValue` are different from last time.
25762   *
25763   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
25764   * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
25765   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
25766   * invoked if you only change a property on the objects.
25767   */
25768  this.$render = noop;
25769
25770  /**
25771   * @ngdoc method
25772   * @name ngModel.NgModelController#$isEmpty
25773   *
25774   * @description
25775   * This is called when we need to determine if the value of an input is empty.
25776   *
25777   * For instance, the required directive does this to work out if the input has data or not.
25778   *
25779   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
25780   *
25781   * You can override this for input directives whose concept of being empty is different from the
25782   * default. The `checkboxInputType` directive does this because in its case a value of `false`
25783   * implies empty.
25784   *
25785   * @param {*} value The value of the input to check for emptiness.
25786   * @returns {boolean} True if `value` is "empty".
25787   */
25788  this.$isEmpty = function(value) {
25789    return isUndefined(value) || value === '' || value === null || value !== value;
25790  };
25791
25792  this.$$updateEmptyClasses = function(value) {
25793    if (ctrl.$isEmpty(value)) {
25794      $animate.removeClass($element, NOT_EMPTY_CLASS);
25795      $animate.addClass($element, EMPTY_CLASS);
25796    } else {
25797      $animate.removeClass($element, EMPTY_CLASS);
25798      $animate.addClass($element, NOT_EMPTY_CLASS);
25799    }
25800  };
25801
25802
25803  var currentValidationRunId = 0;
25804
25805  /**
25806   * @ngdoc method
25807   * @name ngModel.NgModelController#$setValidity
25808   *
25809   * @description
25810   * Change the validity state, and notify the form.
25811   *
25812   * This method can be called within $parsers/$formatters or a custom validation implementation.
25813   * However, in most cases it should be sufficient to use the `ngModel.$validators` and
25814   * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
25815   *
25816   * @param {string}
25816 validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
25817   *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
25818   *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
25819   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
25820   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
25821   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
25822   * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
25823   *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
25824   *                          Skipped is used by Angular when validators do not run because of parse errors and
25825   *                          when `$asyncValidators` do not run because any of the `$validators` failed.
25826   */
25827  addSetValidityMethod({
25828    ctrl: this,
25829    $element: $element,
25830    set: function(object, property) {
25831      object[property] = true;
25832    },
25833    unset: function(object, property) {
25834      delete object[property];
25835    },
25836    $animate: $animate
25837  });
25838
25839  /**
25840   * @ngdoc method
25841   * @name ngModel.NgModelController#$setPristine
25842   *
25843   * @description
25844   * Sets the control to its pristine state.
25845   *
25846   * This method can be called to remove the `ng-dirty` class and set the control to its pristine
25847   * state (`ng-pristine` class). A model is considered to be pristine when the control
25848   * has not been changed from when first compiled.
25849   */
25850  this.$setPristine = function() {
25851    ctrl.$dirty = false;
25852    ctrl.$pristine = true;
25853    $animate.removeClass($element, DIRTY_CLASS);
25854    $animate.addClass($element, PRISTINE_CLASS);
25855  };
25856
25857  /**
25858   * @ngdoc method
25859   * @name ngModel.NgModelController#$setDirty
25860   *
25861   * @description
25862   * Sets the control to its dirty state.
25863   *
25864   * This method can be called to remove the `ng-pristine` class and set the control to its dirty
25865   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
25866   * from when first compiled.
25867   */
25868  this.$setDirty = function() {
25869    ctrl.$dirty = true;
25870    ctrl.$pristine = false;
25871    $animate.removeClass($element, PRISTINE_CLASS);
25872    $animate.addClass($element, DIRTY_CLASS);
25873    ctrl.$$parentForm.$setDirty();
25874  };
25875
25876  /**
25877   * @ngdoc method
25878   * @name ngModel.NgModelController#$setUntouched
25879   *
25880   * @description
25881   * Sets the control to its untouched state.
25882   *
25883   * This method can be called to remove the `ng-touched` class and set the control to its
25884   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
25885   * by default, however this function can be used to restore that state if the model has
25886   * already been touched by the user.
25887   */
25888  this.$setUntouched = function() {
25889    ctrl.$touched = false;
25890    ctrl.$untouched = true;
25891    $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
25892  };
25893
25894  /**
25895   * @ngdoc method
25896   * @name ngModel.NgModelController#$setTouched
25897   *
25898   * @description
25899   * Sets the control to its touched state.
25900   *
25901   * This method can be called to remove the `ng-untouched` class and set the control to its
25902   * touched state (`ng-touched` class). A model is considered to be touched when the user has
25903   * first focused the control element and then shifted focus away from the control (blur event).
25904   */
25905  this.$setTouched = function() {
25906    ctrl.$touched = true;
25907    ctrl.$untouched = false;
25908    $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25909  };
25910
25911  /**
25912   * @ngdoc method
25913   * @name ngModel.NgModelController#$rollbackViewValue
25914   *
25915   * @description
25916   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25917   * which may be caused by a pending debounced event or because the input is waiting for a some
25918   * future event.
25919   *
25920   * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
25921   * depend on special events such as blur, you can have a situation where there is a period when
25922   * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
25923   *
25924   * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
25925   * and reset the input to the last committed view value.
25926   *
25927   * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
25928   * programmatically before these debounced/future events have resolved/occurred, because Angular's
25929   * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25930   *
25931   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25932   * input which may have such events pending. This is important in order to make sure that the
25933   * input field will be updated with the new model value and any pending operations are cancelled.
25934   *
25935   * <example name="ng-model-cancel-update" module="cancel-update-example">
25936   *   <file name="app.js">
25937   *     angular.module('cancel-update-example', [])
25938   *
25939   *     .controller('CancelUpdateController', ['$scope', function($scope) {
25940   *       $scope.model = {};
25941   *
25942   *       $scope.setEmpty = function(e, value, rollback) {
25943   *         if (e.keyCode == 27) {
25944   *           e.preventDefault();
25945   *           if (rollback) {
25946   *             $scope.myForm[value].$rollbackViewValue();
25947   *           }
25948   *           $scope.model[value] = '';
25949   *         }
25950   *       };
25951   *     }]);
25952   *   </file>
25953   *   <file name="index.html">
25954   *     <div ng-controller="CancelUpdateController">
25955   *        <p>Both of these inputs are only updated if they are blurred. Hitting escape should
25956   *        empty them. Follow these steps and observe the difference:</p>
25957   *       <ol>
25958   *         <li>Type something in the input. You will see that the model is not yet updated</li>
25959   *         <li>Press the Escape key.
25960   *           <ol>
25961   *             <li> In the first example, nothing happens, because the model is already '', and no
25962   *             update is detected. If you blur the input, the model will be set to the current view.
25963   *             </li>
25964   *             <li> In the second example, the pending update is cancelled, and the input is set back
25965   *             to the last committed view value (''). Blurring the input does nothing.
25966   *             </li>
25967   *           </ol>
25968   *         </li>
25969   *       </ol>
25970   *
25971   *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25972   *         <div>
25973   *        <p id="inputDescription1">Without $rollbackViewValue():</p>
25974   *         <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
25975   *                ng-keydown="setEmpty($event, 'value1')">
25976   *         value1: "{{ model.value1 }}"
25977   *         </div>
25978   *
25979   *         <div>
25980   *        <p id="inputDescription2">With $rollbackViewValue():</p>
25981   *         <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
25982   *                ng-keydown="setEmpty($event, 'value2', true)">
25983   *         value2: "{{ model.value2 }}"
25984   *         </div>
25985   *       </form>
25986   *     </div>
25987   *   </file>
25988       <file name="style.css">
25989          div {
25990            display: table-cell;
25991          }
25992          div:nth-child(1) {
25993            padding-right: 30px;
25994          }
25995
25996        </file>
25997   * </example>
25998   */
25999  this.$rollbackViewValue = function() {
26000    $timeout.cancel(pendingDebounce);
26001    ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
26002    ctrl.$render();
26003  };
26004
26005  /**
26006   * @ngdoc method
26007   * @name ngModel.NgModelController#$validate
26008   *
26009   * @description
26010   * Runs each of the registered validators (first synchronous validators and then
26011   * asynchronous validators).
26012   * If the validity changes to invalid, the model will be set to `undefined`,
26013   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
26014   * If the validity changes to valid, it will set the model to the last available valid
26015   * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
26016   */
26017  this.$validate = function() {
26018    // ignore $validate before model is initialized
26019    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
26020      return;
26021    }
26022
26023    var viewValue = ctrl.$$lastCommittedViewValue;
26024    // Note: we use the $$rawModelValue as $modelValue might have been
26025    // set to undefined during a view -> model update that found validation
26026    // errors. We can't parse the view here, since that could change
26027    // the model although neither viewValue nor the model on the scope changed
26028    var modelValue = ctrl.$$rawModelValue;
26029
26030    var prevValid = ctrl.$valid;
26031    var prevModelValue = ctrl.$modelValue;
26032
26033    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
26034
26035    ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
26036      // If there was no change in validity, don't update the model
26037      // This prevents changing an invalid modelValue to undefined
26038      if (!allowInvalid && prevValid !== allValid) {
26039        // Note: Don't check ctrl.$valid here, as we could have
26040        // external validators (e.g. calculated on the server),
26041        // that just call $setValidity and need the model value
26042        // to calculate their validity.
26043        ctrl.$modelValue = allValid ? modelValue : undefined;
26044
26045        if (ctrl.$modelValue !== prevModelValue) {
26046          ctrl.$$writeModelToScope();
26047        }
26048      }
26049    });
26050
26051  };
26052
26053  this.$$runValidators = function(modelValue, viewValue, doneCallback) {
26054    currentValidationRunId++;
26055    var localValidationRunId = currentValidationRunId;
26056
26057    // check parser error
26058    if (!processParseErrors()) {
26059      validationDone(false);
26060      return;
26061    }
26062    if (!processSyncValidators()) {
26063      validationDone(false);
26064      return;
26065    }
26066    processAsyncValidators();
26067
26068    function processParseErrors() {
26069      var errorKey = ctrl.$$parserName || 'parse';
26070      if (isUndefined(parserValid)) {
26071        setValidity(errorKey, null);
26072      } else {
26073        if (!parserValid) {
26074          forEach(ctrl.$validators, function(v, name) {
26075            setValidity(name, null);
26076          });
26077          forEach(ctrl.$asyncValidators, function(v, name) {
26078            setValidity(name, null);
26079          });
26080        }
26081        // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
26082        setValidity(errorKey, parserValid);
26083        return parserValid;
26084      }
26085      return true;
26086    }
26087
26088    function processSyncValidators() {
26089      var syncValidatorsValid = true;
26090      forEach(ctrl.$validators, function(validator, name) {
26091        var result = validator(modelValue, viewValue);
26092        syncValidatorsValid = syncValidatorsValid && result;
26093        setValidity(name, result);
26094      });
26095      if (!syncValidatorsValid) {
26096        forEach(ctrl.$asyncValidators, function(v, name) {
26097          setValidity(name, null);
26098        });
26099        return false;
26100      }
26101      return true;
26102    }
26103
26104    function processAsyncValidators() {
26105      var validatorPromises = [];
26106      var allValid = true;
26107      forEach(ctrl.$asyncValidators, function(validator, name) {
26108        var promise = validator(modelValue, viewValue);
26109        if (!isPromiseLike(promise)) {
26110          throw ngModelMinErr("$asyncValidators",
26111            "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
26112        }
26113        setValidity(name, undefined);
26114        validatorPromises.push(promise.then(function() {
26115          setValidity(name, true);
26116        }, function(error) {
26117          allValid = false;
26118          setValidity(name, false);
26119        }));
26120      });
26121      if (!validatorPromises.length) {
26122        validationDone(true);
26123      } else {
26124        $q.all(validatorPromises).then(function() {
26125          validationDone(allValid);
26126        }, noop);
26127      }
26128    }
26129
26130    function setValidity(name, isValid) {
26131      if (localValidationRunId === currentValidationRunId) {
26132        ctrl.$setValidity(name, isValid);
26133      }
26134    }
26135
26136    function validationDone(allValid) {
26137      if (localValidationRunId === currentValidationRunId) {
26138
26139        doneCallback(allValid);
26140      }
26141    }
26142  };
26143
26144  /**
26145   * @ngdoc method
26146   * @name ngModel.NgModelController#$commitViewValue
26147   *
26148   * @description
26149   * Commit a pending update to the `$modelValue`.
26150   *
26151   * Updates may be pending by a debounced event or because the input is waiting for a some future
26152   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
26153   * usually handles calling this in response to input events.
26154   */
26155  this.$commitViewValue = function() {
26156    var viewValue = ctrl.$viewValue;
26157
26158    $timeout.cancel(pendingDebounce);
26159
26160    // If the view value has not changed then we should just exit, except in the case where there is
26161    // a native validator on the element. In this case the validation state may have changed even though
26162    // the viewValue has stayed empty.
26163    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
26164      return;
26165    }
26166    ctrl.$$updateEmptyClasses(viewValue);
26167    ctrl.$$lastCommittedViewValue = viewValue;
26168
26169    // change to dirty
26170    if (ctrl.$pristine) {
26171      this.$setDirty();
26172    }
26173    this.$$parseAndValidate();
26174  };
26175
26176  this.$$parseAndValidate = function() {
26177    var viewValue = ctrl.$$lastCommittedViewValue;
26178    var modelValue = viewValue;
26179    parserValid = isUndefined(modelValue) ? undefined : true;
26180
26181    if (parserValid) {
26182      for (var i = 0; i < ctrl.$parsers.length; i++) {
26183        modelValue = ctrl.$parsers[i](modelValue);
26184        if (isUndefined(modelValue)) {
26185          parserValid = false;
26186          break;
26187        }
26188      }
26189    }
26190    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
26191      // ctrl.$modelValue has not been touched yet...
26192      ctrl.$modelValue = ngModelGet($scope);
26193    }
26194    var prevModelValue = ctrl.$modelValue;
26195    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
26196    ctrl.$$rawModelValue = modelValue;
26197
26198    if (allowInvalid) {
26199      ctrl.$modelValue = modelValue;
26200      writeToModelIfNeeded();
26201    }
26202
26203    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
26204    // This can happen if e.g. $setViewValue is called from inside a parser
26205    ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
26206      if (!allowInvalid) {
26207        // Note: Don't check ctrl.$valid here, as we could have
26208        // external validators (e.g. calculated on the server),
26209        // that just call $setValidity and need the model value
26210        // to calculate their validity.
26211        ctrl.$modelValue = allValid ? modelValue : undefined;
26212        writeToModelIfNeeded();
26213      }
26214    });
26215
26216    function writeToModelIfNeeded() {
26217      if (ctrl.$modelValue !== prevModelValue) {
26218        ctrl.$$writeModelToScope();
26219      }
26220    }
26221  };
26222
26223  this.$$writeModelToScope = function() {
26224    ngModelSet($scope, ctrl.$modelValue);
26225    forEach(ctrl.$viewChangeListeners, function(listener) {
26226      try {
26227        listener();
26228      } catch (e) {
26229        $exceptionHandler(e);
26230      }
26231    });
26232  };
26233
26234  /**
26235   * @ngdoc method
26236   * @name ngModel.NgModelController#$setViewValue
26237   *
26238   * @description
26239   * Update the view value.
26240   *
26241   * This method should be called when a control wants to change the view value; typically,
26242   * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
26243   * directive calls it when the value of the input changes and {@link ng.directive:select select}
26244   * calls it when an option is selected.
26245   *
26246   * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
26247   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
26248   * value sent directly for processing, finally to be applied to `$modelValue` and then the
26249   * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
26250   * in the `$viewChangeListeners` list, are called.
26251   *
26252   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
26253   * and the `default` trigger is not listed, all those actions will remain pending until one of the
26254   * `updateOn` events is triggered on the DOM element.
26255   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
26256   * directive is used with a custom debounce for this particular event.
26257   * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
26258   * is specified, once the timer runs out.
26259   *
26260   * When used with standard inputs, the view value will always be a string (which is in some cases
26261   * parsed into another type, such as a `Date` object for `input[date]`.)
26262   * However, custom controls might also pass objects to this method. In this case, we should make
26263   * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
26264   * perform a deep watch of objects, it only looks for a change of identity. If you only change
26265   * the property of the object then ngModel will not realize that the object has changed and
26266   * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
26267   * not change properties of the copy once it has been passed to `$setViewValue`.
26268   * Otherwise you may cause the model value on the scope to change incorrectly.
26269   *
26270   * <div class="alert alert-info">
26271   * In any case, the value passed to the method should always reflect the current value
26272   * of the control. For example, if you are calling `$setViewValue` for an input element,
26273   * you should pass the input DOM value. Otherwise, the control and the scope model become
26274   * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
26275   * the control's DOM value in any way. If we want to change the control's DOM value
26276   * programmatically, we should update the `ngModel` scope expression. Its new value will be
26277   * picked up by the model controller, which will run it through the `$formatters`, `$render` it
26278   * to update the DOM, and finally call `$validate` on it.
26279   * </div>
26280   *
26281   * @param {*} value value from the view.
26282   * @param {string} trigger Event that triggered the update.
26283   */
26284  this.$setViewValue = function(value, trigger) {
26285    ctrl.$viewValue = value;
26286    if (!ctrl.$options || ctrl.$options.updateOnDefault) {
26287      ctrl.$$debounceViewValueCommit(trigger);
26288    }
26289  };
26290
26291  this.$$debounceViewValueCommit = function(trigger) {
26292    var debounceDelay = 0,
26293        options = ctrl.$options,
26294        debounce;
26295
26296    if (options && isDefined(options.debounce)) {
26297      debounce = options.debounce;
26298      if (isNumber(debounce)) {
26299        debounceDelay = debounce;
26300      } else if (isNumber(debounce[trigger])) {
26301        debounceDelay = debounce[trigger];
26302      } else if (isNumber(debounce['default'])) {
26303        debounceDelay = debounce['default'];
26304      }
26305    }
26306
26307    $timeout.cancel(pendingDebounce);
26308    if (debounceDelay) {
26309      pendingDebounce = $timeout(function() {
26310        ctrl.$commitViewValue();
26311      }, debounceDelay);
26312    } else if ($rootScope.$$phase) {
26313      ctrl.$commitViewValue();
26314    } else {
26315      $scope.$apply(function() {
26316        ctrl.$commitViewValue();
26317      });
26318    }
26319  };
26320
26321  // model -> value
26322  // Note: we cannot use a normal scope.$watch as we want to detect the following:
26323  // 1. scope value is 'a'
26324  // 2. user enters 'b'
26325  // 3. ng-change kicks in and reverts scope value to 'a'
26326  //    ->
26326 scope value did not change since the last digest as
26327  //       ng-change executes in apply phase
26328  // 4. view should be changed back to 'a'
26329  $scope.$watch(function ngModelWatch() {
26330    var modelValue = ngModelGet($scope);
26331
26332    // if scope model value and ngModel value are out of sync
26333    // TODO(perf): why not move this to the action fn?
26334    if (modelValue !== ctrl.$modelValue &&
26335       // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
26336       (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
26337    ) {
26338      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
26339      parserValid = undefined;
26340
26341      var formatters = ctrl.$formatters,
26342          idx = formatters.length;
26343
26344      var viewValue = modelValue;
26345      while (idx--) {
26346        viewValue = formatters[idx](viewValue);
26347      }
26348      if (ctrl.$viewValue !== viewValue) {
26349        ctrl.$$updateEmptyClasses(viewValue);
26350        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
26351        ctrl.$render();
26352
26353        ctrl.$$runValidators(modelValue, viewValue, noop);
26354      }
26355    }
26356
26357    return modelValue;
26358  });
26359}];
26360
26361
26362/**
26363 * @ngdoc directive
26364 * @name ngModel
26365 *
26366 * @element input
26367 * @priority 1
26368 *
26369 * @description
26370 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
26371 * property on the scope using {@link ngModel.NgModelController NgModelController},
26372 * which is created and exposed by this directive.
26373 *
26374 * `ngModel` is responsible for:
26375 *
26376 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
26377 *   require.
26378 * - Providing validation behavior (i.e. required, number, email, url).
26379 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
26380 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
26381 *   `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
26382 * - Registering the control with its parent {@link ng.directive:form form}.
26383 *
26384 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
26385 * current scope. If the property doesn't already exist on this scope, it will be created
26386 * implicitly and added to the scope.
26387 *
26388 * For best practices on using `ngModel`, see:
26389 *
26390 *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
26391 *
26392 * For basic examples, how to use `ngModel`, see:
26393 *
26394 *  - {@link ng.directive:input input}
26395 *    - {@link input[text] text}
26396 *    - {@link input[checkbox] checkbox}
26397 *    - {@link input[radio] radio}
26398 *    - {@link input[number] number}
26399 *    - {@link input[email] email}
26400 *    - {@link input[url] url}
26401 *    - {@link input[date] date}
26402 *    - {@link input[datetime-local] datetime-local}
26403 *    - {@link input[time] time}
26404 *    - {@link input[month] month}
26405 *    - {@link input[week] week}
26406 *  - {@link ng.directive:select select}
26407 *  - {@link ng.directive:textarea textarea}
26408 *
26409 * # CSS classes
26410 * The following CSS classes are added and removed on the associated input/select/textarea element
26411 * depending on the validity of the model.
26412 *
26413 *  - `ng-valid`: the model is valid
26414 *  - `ng-invalid`: the model is invalid
26415 *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
26416 *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
26417 *  - `ng-pristine`: the control hasn't been interacted with yet
26418 *  - `ng-dirty`: the control has been interacted with
26419 *  - `ng-touched`: the control has been blurred
26420 *  - `ng-untouched`: the control hasn't been blurred
26421 *  - `ng-pending`: any `$asyncValidators` are unfulfilled
26422 *  - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
26423 *     by the {@link ngModel.NgModelController#$isEmpty} method
26424 *  - `ng-not-empty`: the view contains a non-empty value
26425 *
26426 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
26427 *
26428 * ## Animation Hooks
26429 *
26430 * Animations within models are triggered when any of the associated CSS classes are added and removed
26431 * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
26432 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
26433 * The animations that are triggered within ngModel are similar to how they work in ngClass and
26434 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
26435 *
26436 * The following example shows a simple way to utilize CSS transitions to style an input element
26437 * that has been rendered as invalid after it has been validated:
26438 *
26439 * <pre>
26440 * //be sure to include ngAnimate as a module to hook into more
26441 * //advanced animations
26442 * .my-input {
26443 *   transition:0.5s linear all;
26444 *   background: white;
26445 * }
26446 * .my-input.ng-invalid {
26447 *   background: red;
26448 *   color:white;
26449 * }
26450 * </pre>
26451 *
26452 * @example
26453 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
26454     <file name="index.html">
26455       <script>
26456        angular.module('inputExample', [])
26457          .controller('ExampleController', ['$scope', function($scope) {
26458            $scope.val = '1';
26459          }]);
26460       </script>
26461       <style>
26462         .my-input {
26463           transition:all linear 0.5s;
26464           background: transparent;
26465         }
26466         .my-input.ng-invalid {
26467           color:white;
26468           background: red;
26469         }
26470       </style>
26471       <p id="inputDescription">
26472        Update input to see transitions when valid/invalid.
26473        Integer is a valid value.
26474       </p>
26475       <form name="testForm" ng-controller="ExampleController">
26476         <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
26477                aria-describedby="inputDescription" />
26478       </form>
26479     </file>
26480 * </example>
26481 *
26482 * ## Binding to a getter/setter
26483 *
26484 * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
26485 * function that returns a representation of the model when called with zero arguments, and sets
26486 * the internal state of a model when called with an argument. It's sometimes useful to use this
26487 * for models that have an internal representation that's different from what the model exposes
26488 * to the view.
26489 *
26490 * <div class="alert alert-success">
26491 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
26492 * frequently than other parts of your code.
26493 * </div>
26494 *
26495 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
26496 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
26497 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
26498 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
26499 *
26500 * The following example shows how to use `ngModel` with a getter/setter:
26501 *
26502 * @example
26503 * <example name="ngModel-getter-setter" module="getterSetterExample">
26504     <file name="index.html">
26505       <div ng-controller="ExampleController">
26506         <form name="userForm">
26507           <label>Name:
26508             <input type="text" name="userName"
26509                    ng-model="user.name"
26510                    ng-model-options="{ getterSetter: true }" />
26511           </label>
26512         </form>
26513         <pre>user.name = <span ng-bind="user.name()"></span></pre>
26514       </div>
26515     </file>
26516     <file name="app.js">
26517       angular.module('getterSetterExample', [])
26518         .controller('ExampleController', ['$scope', function($scope) {
26519           var _name = 'Brian';
26520           $scope.user = {
26521             name: function(newName) {
26522              // Note that newName can be undefined for two reasons:
26523              // 1. Because it is called as a getter and thus called with no arguments
26524              // 2. Because the property should actually be set to undefined. This happens e.g. if the
26525              //    input is invalid
26526              return arguments.length ? (_name = newName) : _name;
26527             }
26528           };
26529         }]);
26530     </file>
26531 * </example>
26532 */
26533var ngModelDirective = ['$rootScope', function($rootScope) {
26534  return {
26535    restrict: 'A',
26536    require: ['ngModel', '^?form', '^?ngModelOptions'],
26537    controller: NgModelController,
26538    // Prelink needs to run before any input directive
26539    // so that we can set the NgModelOptions in NgModelController
26540    // before anyone else uses it.
26541    priority: 1,
26542    compile: function ngModelCompile(element) {
26543      // Setup initial state of the control
26544      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
26545
26546      return {
26547        pre: function ngModelPreLink(scope, element, attr, ctrls) {
26548          var modelCtrl = ctrls[0],
26549              formCtrl = ctrls[1] || modelCtrl.$$parentForm;
26550
26551          modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
26552
26553          // notify others, especially parent forms
26554          formCtrl.$addControl(modelCtrl);
26555
26556          attr.$observe('name', function(newValue) {
26557            if (modelCtrl.$name !== newValue) {
26558              modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
26559            }
26560          });
26561
26562          scope.$on('$destroy', function() {
26563            modelCtrl.$$parentForm.$removeControl(modelCtrl);
26564          });
26565        },
26566        post: function ngModelPostLink(scope, element, attr, ctrls) {
26567          var modelCtrl = ctrls[0];
26568          if (modelCtrl.$options && modelCtrl.$options.updateOn) {
26569            element.on(modelCtrl.$options.updateOn, function(ev) {
26570              modelCtrl.$$debounceViewValueCommit(ev && ev.type);
26571            });
26572          }
26573
26574          element.on('blur', function(ev) {
26575            if (modelCtrl.$touched) return;
26576
26577            if ($rootScope.$$phase) {
26578              scope.$evalAsync(modelCtrl.$setTouched);
26579            } else {
26580              scope.$apply(modelCtrl.$setTouched);
26581            }
26582          });
26583        }
26584      };
26585    }
26586  };
26587}];
26588
26589var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
26590
26591/**
26592 * @ngdoc directive
26593 * @name ngModelOptions
26594 *
26595 * @description
26596 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
26597 * events that will trigger a model update and/or a debouncing delay so that the actual update only
26598 * takes place when a timer expires; this timer will be reset after another change takes place.
26599 *
26600 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
26601 * be different from the value in the actual model. This means that if you update the model you
26602 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
26603 * order to make sure it is synchronized with the model and that any debounced action is canceled.
26604 *
26605 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
26606 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
26607 * important because `form` controllers are published to the related scope under the name in their
26608 * `name` attribute.
26609 *
26610 * Any pending changes will take place immediately when an enclosing form is submitted via the
26611 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
26612 * to have access to the updated model.
26613 *
26614 * `ngModelOptions` has an effect on the element it's declared on and its desce
26614ndants.
26615 *
26616 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
26617 *   - `updateOn`: string specifying which event should the input be bound to. You can set several
26618 *     events using an space delimited list. There is a special event called `default` that
26619 *     matches the default events belonging of the control.
26620 *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
26621 *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
26622 *     custom value for each event. For example:
26623 *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
26624 *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
26625 *     not validate correctly instead of the default behavior of setting the model to undefined.
26626 *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
26627       `ngModel` as getters/setters.
26628 *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
26629 *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
26630 *     continental US time zone abbreviations, but for general use, use a time zone offset, for
26631 *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
26632 *     If not specified, the timezone of the browser will be used.
26633 *
26634 * @example
26635
26636  The following example shows how to override immediate updates. Changes on the inputs within the
26637  form will update the model only when the control loses focus (blur event). If `escape` key is
26638  pressed while the input field is focused, the value is reset to the value in the current model.
26639
26640  <example name="ngModelOptions-directive-blur" module="optionsExample">
26641    <file name="index.html">
26642      <div ng-controller="ExampleController">
26643        <form name="userForm">
26644          <label>Name:
26645            <input type="text" name="userName"
26646                   ng-model="user.name"
26647                   ng-model-options="{ updateOn: 'blur' }"
26648                   ng-keyup="cancel($event)" />
26649          </label><br />
26650          <label>Other data:
26651            <input type="text" ng-model="user.data" />
26652          </label><br />
26653        </form>
26654        <pre>user.name = <span ng-bind="user.name"></span></pre>
26655        <pre>user.data = <span ng-bind="user.data"></span></pre>
26656      </div>
26657    </file>
26658    <file name="app.js">
26659      angular.module('optionsExample', [])
26660        .controller('ExampleController', ['$scope', function($scope) {
26661          $scope.user = { name: 'John', data: '' };
26662
26663          $scope.cancel = function(e) {
26664            if (e.keyCode == 27) {
26665              $scope.userForm.userName.$rollbackViewValue();
26666            }
26667          };
26668        }]);
26669    </file>
26670    <file name="protractor.js" type="protractor">
26671      var model = element(by.binding('user.name'));
26672      var input = element(by.model('user.name'));
26673      var other = element(by.model('user.data'));
26674
26675      it('should allow custom events', function() {
26676        input.sendKeys(' Doe');
26677        input.click();
26678        expect(model.getText()).toEqual('John');
26679        other.click();
26680        expect(model.getText()).toEqual('John Doe');
26681      });
26682
26683      it('should $rollbackViewValue when model changes', function() {
26684        input.sendKeys(' Doe');
26685        expect(input.getAttribute('value')).toEqual('John Doe');
26686        input.sendKeys(protractor.Key.ESCAPE);
26687        expect(input.getAttribute('value')).toEqual('John');
26688        other.click();
26689        expect(model.getText()).toEqual('John');
26690      });
26691    </file>
26692  </example>
26693
26694  This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
26695  If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
26696
26697  <example name="ngModelOptions-directive-debounce" module="optionsExample">
26698    <file name="index.html">
26699      <div ng-controller="ExampleController">
26700        <form name="userForm">
26701          <label>Name:
26702            <input type="text" name="userName"
26703                   ng-model="user.name"
26704                   ng-model-options="{ debounce: 1000 }" />
26705          </label>
26706          <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
26707          <br />
26708        </form>
26709        <pre>user.name = <span ng-bind="user.name"></span></pre>
26710      </div>
26711    </file>
26712    <file name="app.js">
26713      angular.module('optionsExample', [])
26714        .controller('ExampleController', ['$scope', function($scope) {
26715          $scope.user = { name: 'Igor' };
26716        }]);
26717    </file>
26718  </example>
26719
26720  This one shows how to bind to getter/setters:
26721
26722  <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
26723    <file name="index.html">
26724      <div ng-controller="ExampleController">
26725        <form name="userForm">
26726          <label>Name:
26727            <input type="text" name="userName"
26728                   ng-model="user.name"
26729                   ng-model-options="{ getterSetter: true }" />
26730          </label>
26731        </form>
26732        <pre>user.name = <span ng-bind="user.name()"></span></pre>
26733      </div>
26734    </file>
26735    <file name="app.js">
26736      angular.module('getterSetterExample', [])
26737        .controller('ExampleController', ['$scope', function($scope) {
26738          var _name = 'Brian';
26739          $scope.user = {
26740            name: function(newName) {
26741              // Note that newName can be undefined for two reasons:
26742              // 1. Because it is called as a getter and thus called with no arguments
26743              // 2. Because the property should actually be set to undefined. This happens e.g. if the
26744              //    input is invalid
26745              return arguments.length ? (_name = newName) : _name;
26746            }
26747          };
26748        }]);
26749    </file>
26750  </example>
26751 */
26752var ngModelOptionsDirective = function() {
26753  return {
26754    restrict: 'A',
26755    controller: ['$scope', '$attrs', function($scope, $attrs) {
26756      var that = this;
26757      this.$options = copy($scope.$eval($attrs.ngModelOptions));
26758      // Allow adding/overriding bound events
26759      if (isDefined(this.$options.updateOn)) {
26760        this.$options.updateOnDefault = false;
26761        // extract "default" pseudo-event from list of events that can trigger a model update
26762        this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
26763          that.$options.updateOnDefault = true;
26764          return ' ';
26765        }));
26766      } else {
26767        this.$options.updateOnDefault = true;
26768      }
26769    }]
26770  };
26771};
26772
26773
26774
26775// helper methods
26776function addSetValidityMethod(context) {
26777  var ctrl = context.ctrl,
26778      $element = context.$element,
26779      classCache = {},
26780      set = context.set,
26781      unset = context.unset,
26782      $animate = context.$animate;
26783
26784  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
26785
26786  ctrl.$setValidity = setValidity;
26787
26788  function setValidity(validationErrorKey, state, controller) {
26789    if (isUndefined(state)) {
26790      createAndSet('$pending', validationErrorKey, controller);
26791    } else {
26792      unsetAndCleanup('$pending', validationErrorKey, controller);
26793    }
26794    if (!isBoolean(state)) {
26795      unset(ctrl.$error, validationErrorKey, controller);
26796      unset(ctrl.$$success, validationErrorKey, controller);
26797    } else {
26798      if (state) {
26799        unset(ctrl.$error, validationErrorKey, controller);
26800        set(ctrl.$$success, validationErrorKey, controller);
26801      } else {
26802        set(ctrl.$error, validationErrorKey, controller);
26803        unset(ctrl.$$success, validationErrorKey, controller);
26804      }
26805    }
26806    if (ctrl.$pending) {
26807      cachedToggleClass(PENDING_CLASS, true);
26808      ctrl.$valid = ctrl.$invalid = undefined;
26809      toggleValidationCss('', null);
26810    } else {
26811      cachedToggleClass(PENDING_CLASS, false);
26812      ctrl.$valid = isObjectEmpty(ctrl.$error);
26813      ctrl.$invalid = !ctrl.$valid;
26814      toggleValidationCss('', ctrl.$valid);
26815    }
26816
26817    // re-read the state as the set/unset methods could have
26818    // combined state in ctrl.$error[validationError] (used for forms),
26819    // where setting/unsetting only increments/decrements the value,
26820    // and does not replace it.
26821    var combinedState;
26822    if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
26823      combinedState = undefined;
26824    } else if (ctrl.$error[validationErrorKey]) {
26825      combinedState = false;
26826    } else if (ctrl.$$success[validationErrorKey]) {
26827      combinedState = true;
26828    } else {
26829      combinedState = null;
26830    }
26831
26832    toggleValidationCss(validationErrorKey, combinedState);
26833    ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
26834  }
26835
26836  function createAndSet(name, value, controller) {
26837    if (!ctrl[name]) {
26838      ctrl[name] = {};
26839    }
26840    set(ctrl[name], value, controller);
26841  }
26842
26843  function unsetAndCleanup(name, value, controller) {
26844    if (ctrl[name]) {
26845      unset(ctrl[name], value, controller);
26846    }
26847    if (isObjectEmpty(ctrl[name])) {
26848      ctrl[name] = undefined;
26849    }
26850  }
26851
26852  function cachedToggleClass(className, switchValue) {
26853    if (switchValue && !classCache[className]) {
26854      $animate.addClass($element, className);
26855      classCache[className] = true;
26856    } else if (!switchValue && classCache[className]) {
26857      $animate.removeClass($element, className);
26858      classCache[className] = false;
26859    }
26860  }
26861
26862  function toggleValidationCss(validationErrorKey, isValid) {
26863    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
26864
26865    cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
26866    cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
26867  }
26868}
26869
26870function isObjectEmpty(obj) {
26871  if (obj) {
26872    for (var prop in obj) {
26873      if (obj.hasOwnProperty(prop)) {
26874        return false;
26875      }
26876    }
26877  }
26878  return true;
26879}
26880
26881/**
26882 * @ngdoc directive
26883 * @name ngNonBindable
26884 * @restrict AC
26885 * @priority 1000
26886 *
26887 * @description
26888 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
26889 * DOM element. This is useful if the element contains what appears to be Angular directives and
26890 * bindings but which should be ignored by Angular. This could be the case if you have a site that
26891 * displays snippets of code, for instance.
26892 *
26893 * @element ANY
26894 *
26895 * @example
26896 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
26897 * but the one wrapped in `ngNonBindable` is left alone.
26898 *
26899 * @example
26900    <example>
26901      <file name="index.html">
26902        <div>Normal: {{1 + 2}}</div>
26903        <div ng-non-bindable>Ignored: {{1 + 2}}</div>
26904      </file>
26905      <file name="protractor.js" type="protractor">
26906       it('should check ng-non-bindable', function() {
26907         expect(element(by.binding('1 + 2')).getText()).toContain('3');
26908         expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
26909       });
26910      </file>
26911    </example>
26912 */
26913var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
26914
26915/* global jqLiteRemove */
26916
26917var ngOptionsMinErr = minErr('ngOptions');
26918
26919/**
26920 * @ngdoc directive
26921 * @name ngOptions
26922 * @restrict A
26923 *
26924 * @description
26925 *
26926 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
26927 * elements for the `<select>` element using the array or object obtained by evaluating the
26928 * `ngOptions` comprehension expression.
26929 *
26930 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
26931 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
26932 * increasing speed by not creating a new scope for each repeated instance, as well as providing
26933 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26934 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26935 *  to a non-string value. This is because an option element can only be bound to string values at
26936 * present.
26937 *
26938 * When an item in the `<select>` menu is selected, the array element or object property
26939 * represented by the selected option will be bound to the model identified by the `ngModel`
26940 * directive.
26941 *
26942 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26943 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26944 * option. See example below for demonstration.
26945 *
26946 * ## Complex Models (objects or collections)
26947 *
26948 * By default, `ngModel` watches the model by reference, not value. This is important to know when
26949 * binding the select to a model that is an object or a collection.
26950 *
26951 * One issue occurs if you want to preselect an option. For example, if you set
26952 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
26953 * because the objects are not identical. So by default, you should always reference the item in y
26953our collection
26954 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
26955 *
26956 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
26957 * of the item not by reference, but by the result of the `track by` expression. For example, if your
26958 * collection items have an id property, you would `track by item.id`.
26959 *
26960 * A different issue with objects or collections is that ngModel won't detect if an object property or
26961 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
26962 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
26963 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
26964 * has not changed identity, but only a property on the object or an item in the collection changes.
26965 *
26966 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26967 * if the model is an array). This means that changing a property deeper than the first level inside the
26968 * object/collection will not trigger a re-rendering.
26969 *
26970 * ## `select` **`as`**
26971 *
26972 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26973 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26974 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26975 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26976 *
26977 *
26978 * ### `select` **`as`** and **`track by`**
26979 *
26980 * <div class="alert alert-warning">
26981 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
26982 * </div>
26983 *
26984 * Given this array of items on the $scope:
26985 *
26986 * ```js
26987 * $scope.items = [{
26988 *   id: 1,
26989 *   label: 'aLabel',
26990 *   subItem: { name: 'aSubItem' }
26991 * }, {
26992 *   id: 2,
26993 *   label: 'bLabel',
26994 *   subItem: { name: 'bSubItem' }
26995 * }];
26996 * ```
26997 *
26998 * This will work:
26999 *
27000 * ```html
27001 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
27002 * ```
27003 * ```js
27004 * $scope.selected = $scope.items[0];
27005 * ```
27006 *
27007 * but this will not work:
27008 *
27009 * ```html
27010 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
27011 * ```
27012 * ```js
27013 * $scope.selected = $scope.items[0].subItem;
27014 * ```
27015 *
27016 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
27017 * `items` array. Because the selected option has been set programmatically in the controller, the
27018 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
27019 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
27020 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
27021 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
27022 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
27023 *
27024 *
27025 * @param {string} ngModel Assignable angular expression to data-bind to.
27026 * @param {string=} name Property name of the form under which the control is published.
27027 * @param {string=} required The control is considered valid only if value is entered.
27028 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
27029 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
27030 *    `required` when you want to data-bind to the `required` attribute.
27031 * @param {comprehension_expression=} ngOptions in one of the following forms:
27032 *
27033 *   * for array data sources:
27034 *     * `label` **`for`** `value` **`in`** `array`
27035 *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
27036 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
27037 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
27038 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
27039 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
27040 *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
27041 *        (for including a filter with `track by`)
27042 *   * for object data sources:
27043 *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
27044 *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
27045 *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
27046 *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
27047 *     * `select` **`as`** `label` **`group by`** `group`
27048 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
27049 *     * `select` **`as`** `label` **`disable when`** `disable`
27050 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
27051 *
27052 * Where:
27053 *
27054 *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
27055 *   * `value`: local variable which will refer to each item in the `array` or each property value
27056 *      of `object` during iteration.
27057 *   * `key`: local variable which will refer to a property name in `object` during iteration.
27058 *   * `label`: The result of this expression will be the label for `<option>` element. The
27059 *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
27060 *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
27061 *      element. If not specified, `select` expression will default to `value`.
27062 *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
27063 *      DOM element.
27064 *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
27065 *      element. Return `true` to disable.
27066 *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
27067 *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
27068 *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
27069 *      even when the options are recreated (e.g. reloaded from the server).
27070 *
27071 * @example
27072    <example module="selectExample">
27073      <file name="index.html">
27074        <script>
27075        angular.module('selectExample', [])
27076          .controller('ExampleController', ['$scope', function($scope) {
27077            $scope.colors = [
27078              {name:'black', shade:'dark'},
27079              {name:'white', shade:'light', notAnOption: true},
27080              {name:'red', shade:'dark'},
27081              {name:'blue', shade:'dark', notAnOption: true},
27082              {name:'yellow', shade:'light', notAnOption: false}
27083            ];
27084            $scope.myColor = $scope.colors[2]; // red
27085          }]);
27086        </script>
27087        <div ng-controller="ExampleController">
27088          <ul>
27089            <li ng-repeat="color in colors">
27090              <label>Name: <input ng-model="color.name"></label>
27091              <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
27092              <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
27093            </li>
27094            <li>
27095              <button ng-click="colors.push({})">add</button>
27096            </li>
27097          </ul>
27098          <hr/>
27099          <label>Color (null not allowed):
27100            <select ng-model="myColor" ng-options="color.name for color in colors"></select>
27101          </label><br/>
27102          <label>Color (null allowed):
27103          <span  class="nullable">
27104            <select ng-model="myColor" ng-options="color.name for color in colors">
27105              <option value="">-- choose color --</option>
27106            </select>
27107          </span></label><br/>
27108
27109          <label>Color grouped by shade:
27110            <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
27111            </select>
27112          </label><br/>
27113
27114          <label>Color grouped by shade, with some disabled:
27115            <select ng-model="myColor"
27116                  ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
27117            </select>
27118          </label><br/>
27119
27120
27121
27122          Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
27123          <br/>
27124          <hr/>
27125          Currently selected: {{ {selected_color:myColor} }}
27126          <div style="border:solid 1px black; height:20px"
27127               ng-style="{'background-color':myColor.name}">
27128          </div>
27129        </div>
27130      </file>
27131      <file name="protractor.js" type="protractor">
27132         it('should check ng-options', function() {
27133           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
27134           element.all(by.model('myColor')).first().click();
27135           element.all(by.css('select[ng-model="myColor"] option')).first().click();
27136           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
27137           element(by.css('.nullable select[ng-model="myColor"]')).click();
27138           element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
27139           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
27140         });
27141      </file>
27142    </example>
27143 */
27144
27145// jshint maxlen: false
27146//                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
27147var 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]+?))?$/;
27148                        // 1: value expression (valueFn)
27149                        // 2: label expression (displayFn)
27150                        // 3: group by expression (groupByFn)
27151                        // 4: disable when expression (disableWhenFn)
27152                        // 5: array item variable name
27153                        // 6: object item key variable name
27154                        // 7: object item value variable name
27155                        // 8: collection expression
27156                        // 9: track by expression
27157// jshint maxlen: 100
27158
27159
27160var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
27161
27162  function parseOptionsExpression(optionsExp, selectElement, scope) {
27163
27164    var match = optionsExp.match(NG_OPTIONS_REGEXP);
27165    if (!(match)) {
27166      throw ngOptionsMinErr('iexp',
27167        "Expected expression in form of " +
27168        "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
27169        " but got '{0}'. Element: {1}",
27170        optionsExp, startingTag(selectElement));
27171    }
27172
27173    // Extract the parts from the ngOptions expression
27174
27175    // The variable name for the value of the item in the collection
27176    var valueName = match[5] || match[7];
27177    // The variable name for the key of the item in the collection
27178    var keyName = match[6];
27179
27180    // An expression that generates the viewValue for an option if there is a label expression
27181    var selectAs = / as /.test(match[0]) && match[1];
27182    // An expression that is used to track the id of each object in the options collection
27183    var trackBy = match[9];
27184    // An expression that generates the viewValue for an option if there is no label expression
27185    var valueFn = $parse(match[2] ? match[1] : valueName);
27186    var selectAsFn = selectAs && $parse(selectAs);
27187    var viewValueFn = selectAsFn || valueFn;
27188    var trackByFn = trackBy && $parse(trackBy);
27189
27190    // Get the value by which we are going to track the option
27191    // if we have a trackFn then use that (passing scope and locals)
27192    // otherwise just hash the given viewValue
27193    var getTrackByValueFn = trackBy ?
27194                              function(value, locals) { return trackByFn(scope, locals); } :
27195                              function getHashOfValue(value) { return hashKey(value); };
27196    var getTrackByValue = function(value, key) {
vendor: 5,697 bytes, lines 27197-27350
27197      return getTrackByValueFn(value, getLocals(value, key));
27198    };
27199
27200    var displayFn = $parse(match[2] || match[1]);
27201    var groupByFn = $parse(match[3] || '');
27202    var disableWhenFn = $parse(match[4] || '');
27203    var valuesFn = $parse(match[8]);
27204
27205    var locals = {};
27206    var getLocals = keyName ? function(value, key) {
27207      locals[keyName] = key;
27208      locals[valueName] = value;
27209      return locals;
27210    } : function(value) {
27211      locals[valueName] = value;
27212      return locals;
27213    };
27214
27215
27216    function Option(selectValue, viewValue, label, group, disabled) {
27217      this.selectValue = selectValue;
27218      this.viewValue = viewValue;
27219      this.label = label;
27220      this.group = group;
27221      this.disabled = disabled;
27222    }
27223
27224    function getOptionValuesKeys(optionValues) {
27225      var optionValuesKeys;
27226
27227      if (!keyName && isArrayLike(optionValues)) {
27228        optionValuesKeys = optionValues;
27229      } else {
27230        // if object, extract keys, in enumeration order, unsorted
27231        optionValuesKeys = [];
27232        for (var itemKey in optionValues) {
27233          if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
27234            optionValuesKeys.push(itemKey);
27235          }
27236        }
27237      }
27238      return optionValuesKeys;
27239    }
27240
27241    return {
27242      trackBy: trackBy,
27243      getTrackByValue: getTrackByValue,
27244      getWatchables: $parse(valuesFn, function(optionValues) {
27245        // Create a collection of things that we would like to watch (watchedArray)
27246        // so that they can all be watched using a single $watchCollection
27247        // that only runs the handler once if anything changes
27248        var watchedArray = [];
27249        optionValues = optionValues || [];
27250
27251        var optionValuesKeys = getOptionValuesKeys(optionValues);
27252        var optionValuesLength = optionValuesKeys.length;
27253        for (var index = 0; index < optionValuesLength; index++) {
27254          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
27255          var value = optionValues[key];
27256
27257          var locals = getLocals(optionValues[key], key);
27258          var selectValue = getTrackByValueFn(optionValues[key], locals);
27259          watchedArray.push(selectValue);
27260
27261          // Only need to watch the displayFn if there is a specific label expression
27262          if (match[2] || match[1]) {
27263            var label = displayFn(scope, locals);
27264            watchedArray.push(label);
27265          }
27266
27267          // Only need to watch the disableWhenFn if there is a specific disable expression
27268          if (match[4]) {
27269            var disableWhen = disableWhenFn(scope, locals);
27270            watchedArray.push(disableWhen);
27271          }
27272        }
27273        return watchedArray;
27274      }),
27275
27276      getOptions: function() {
27277
27278        var optionItems = [];
27279        var selectValueMap = {};
27280
27281        // The option values were already computed in the `getWatchables` fn,
27282        // which must have been called to trigger `getOptions`
27283        var optionValues = valuesFn(scope) || [];
27284        var optionValuesKeys = getOptionValuesKeys(optionValues);
27285        var optionValuesLength = optionValuesKeys.length;
27286
27287        for (var index = 0; index < optionValuesLength; index++) {
27288          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
27289          var value = optionValues[key];
27290          var locals = getLocals(value, key);
27291          var viewValue = viewValueFn(scope, locals);
27292          var selectValue = getTrackByValueFn(viewValue, locals);
27293          var label = displayFn(scope, locals);
27294          var group = groupByFn(scope, locals);
27295          var disabled = disableWhenFn(scope, locals);
27296          var optionItem = new Option(selectValue, viewValue, label, group, disabled);
27297
27298          optionItems.push(optionItem);
27299          selectValueMap[selectValue] = optionItem;
27300        }
27301
27302        return {
27303          items: optionItems,
27304          selectValueMap: selectValueMap,
27305          getOptionFromViewValue: function(value) {
27306            return selectValueMap[getTrackByValue(value)];
27307          },
27308          getViewValueFromOption: function(option) {
27309            // If the viewValue could be an object that may be mutated by the application,
27310            // we need to make a copy and not return the reference to the value on the option.
27311            return trackBy ? angular.copy(option.viewValue) : option.viewValue;
27312          }
27313        };
27314      }
27315    };
27316  }
27317
27318
27319  // we can't just jqLite('<option>') since jqLite is not smart enough
27320  // to create it in <select> and IE barfs otherwise.
27321  var optionTemplate = document.createElement('option'),
27322      optGroupTemplate = document.createElement('optgroup');
27323
27324    function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
27325
27326      var selectCtrl = ctrls[0];
27327      var ngModelCtrl = ctrls[1];
27328      var multiple = attr.multiple;
27329
27330      // The emptyOption allows the application developer to provide their own custom "empty"
27331      // option when the viewValue does not match any of the option values.
27332      var emptyOption;
27333      for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
27334        if (children[i].value === '') {
27335          emptyOption = children.eq(i);
27336          break;
27337        }
27338      }
27339
27340      var providedEmptyOption = !!emptyOption;
27341
27342      var unknownOption = jqLite(optionTemplate.cloneNode(false));
27343      unknownOption.val('?');
27344
27345      var options;
27346      var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
27347
27348
27349      var renderEmptyOption = function() {
27350        if (!providedEmptyOption) {
27351          selectElement.prepend(emptyOption);
27352        }
27353        selectElement.val('');
27354        emptyOption.prop('selected', true); // needed for IE
27355        emptyOption.attr('selected', true);
27356      };
27357
27358      var removeEmptyOption = function() {
27359        if (!providedEmptyOption) {
27360          emptyOption.remove();
27361        }
27362      };
27363
27364
27365      var renderUnknownOption = function() {
27366        selectElement.prepend(unknownOption);
27367        selectElement.val('?');
27368        unknownOption.prop('selected', true); // needed for IE
27369        unknownOption.attr('selected', true);
27370      };
27371
27372      var removeUnknownOption = function() {
27373        unknownOption.remove();
27374      };
27375
27376      // Update the controller methods for multiple selectable options
27377      if (!multiple) {
27378
27379        selectCtrl.writeValue = function writeNgOptionsValue(value) {
27380          var option = options.getOptionFromViewValue(value);
27381
27382          if (option && !option.disabled) {
27383            if (selectElement[0].value !== option.selectValue) {
27384              removeUnknownOption();
27385              removeEmptyOption();
27386
27387              selectElement[0].value = option.selectValue;
27388              option.element.selected = true;
27389              option.element.setAttribute('selected', 'selected');
27390            }
27391          } else {
27392            if (value === null || providedEmptyOption) {
27393              removeUnknownOption();
27394              renderEmptyOption();
27395            } else {
27396              removeEmptyOption();
27397              renderUnknownOption();
27398            }
27399          }
27400        };
27401
27402        selectCtrl.readValue = function readNgOptionsValue() {
27403
27404          var selectedOption = options.selectValueMap[selectElement.val()];
27405
27406          if (selectedOption && !selectedOption.disabled) {
27407            removeEmptyOption();
27408            removeUnknownOption();
27409            return options.getViewValueFromOption(selectedOption);
27410          }
27411          return null;
27412        };
27413
27414        // If we are using `track by` then we must watch the tracked value on the model
27415        // since ngModel only watches for object identity change
27416        if (ngOptions.trackBy) {
27417          scope.$watch(
27418            function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
27419            function() { ngModelCtrl.$render(); }
27420          );
27421        }
27422
27423      } else {
27424
27425        ngModelCtrl.$isEmpty = function(value) {
27426          return !value || value.length === 0;
27427        };
27428
27429
27430        selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
27431          options.items.forEach(function(option) {
27432            option.element.selected = false;
27433          });
27434
27435          if (value) {
27436            value.forEach(function(item) {
27437              var option = options.getOptionFromViewValue(item);
27438              if (option && !option.disabled) option.element.selected = true;
27439            });
27440          }
27441        };
27442
27443
27444        selectCtrl.readValue = function readNgOptionsMultiple() {
27445          var selectedValues = selectElement.val() || [],
27446              selections = [];
27447
27448          forEach(selectedValues, function(value) {
27449            var option = options.selectValueMap[value];
27450            if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
27451          });
27452
27453          return selections;
27454        };
27455
27456        // If we are using `track by` then we must watch these tracked values on the model
27457        // since ngModel only watches for object identity change
27458        if (ngOptions.trackBy) {
27459
27460          scope.$watchCollection(function() {
27461            if (isArray(ngModelCtrl.$viewValue)) {
27462              return ngModelCtrl.$viewValue.map(function(value) {
27463                return ngOptions.getTrackByValue(value);
27464              });
27465            }
27466          }, function() {
27467            ngModelCtrl.$render();
27468          });
27469
27470        }
27471      }
27472
27473
27474      if (providedEmptyOption) {
27475
27476        // we need to remove it before calling selectElement.empty() because otherwise IE will
27477        // remove the label from the element. wtf?
27478        emptyOption.remove();
27479
27480        // compile the element since there might be bindings in it
27481        $compile(emptyOption)(scope);
27482
27483        // remove the class, which is added automatically because we recompile the element and it
27484        // becomes the compilation root
27485        emptyOption.removeClass('ng-scope');
27486      } else {
27487        emptyOption = jqLite(optionTemplate.cloneNode(false));
27488      }
27489
27490      // We need to do this here to ensure that the options object is defined
27491      // when we first hit it in writeNgOptionsValue
27492      updateOptions();
27493
27494      // We will re-render the option elements if the option values or labels change
27495      scope.$watchCollection(ngOptions.getWatchables, updateOptions);
27496
27497      // ------------------------------------------------------------------ //
27498
27499
27500      function updateOptionElement(option, element) {
27501        option.element = element;
27502        element.disabled = option.disabled;
27503        // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
27504        // selects in certain circumstances when multiple selects are next to each other and display
27505        // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
27506        // See https://github.com/angular/angular.js/issues/11314 for more info.
27507        // This is unfortunately untestable with unit / e2e tests
27508        if (option.label !== element.label) {
27509          element.label = option.label;
27510          element.textContent = option.label;
27511        }
27512        if (option.value !== element.value) element.value = option.selectValue;
27513      }
27514
27515      function addOrReuseElement(parent, current, type, templateElement) {
27516        var element;
27517        // Check whether we can reuse the next element
27518        if (current && lowercase(current.nodeName) === type) {
27519          // The next element is the right type so reuse it
27520          element = current;
27521        } else {
27522          // The next element is not the right type so create a new one
27523          element = templateElement.cloneNode(false);
27524          if (!current) {
27525            // There are no more elements so just append it to the select
27526            parent.appendChild(element);
27527          } else {
27528            // The next element is not a group so insert the new one
27529            parent.insertBefore(element, current);
27530          }
27531        }
27532        return element;
27533      }
27534
27535
27536      function removeExcessElements(current) {
27537        var next;
27538        while (current) {
27539          next = current.nextSibling;
27540          jqLiteRemove(current);
27541          current = next;
27542        }
27543      }
27544
27545
27546      function skipEmptyAndUnknownOptions(current) {
27547        var emptyOption_ = emptyOption && emptyOption[0];
27548        var unknownOption_ = unknownOption && unknownOption[0];
27549
27550        // We cannot rely on the extracted empty option being the same as the compiled empty option,
27551        // because the compiled empty option might have been replaced by a comment because
27552        // it had an "element" transclusion directive on it (such as ngIf)
27553        if (emptyOption_ || unknownOption_) {
27554          while (current &&
27555                (current === emptyOption_ ||
27556                current === unknownOption_ ||
27557                current.nodeType === NODE_TYPE_COMMENT ||
27558                (nodeName_(current) === 'option' && current.value === ''))) {
27559            current = current.nextSibling;
27560          }
27561        }
27562        return current;
27563      }
27564
27565
27566      function updateOptions() {
27567
27568        var previousValue = options && selectCtrl.readValue();
27569
27570        options = ngOptions.getOptions();
27571
27572        var groupMap = {};
27573        var currentElement = selectElement[0].firstChild;
27574
27575        // Ensure that the empty option is always there if it was explicitly provided
27576        if (providedEmptyOption) {
27577          selectElement.prepend(emptyOption);
27578        }
27579
27580        currentElement = skipEmptyAndUnknownOptions(currentElement);
27581
27582        options.items.forEach(function updateOption(option) {
27583          var group;
27584          var groupElement;
27585          var optionElement;
27586
27587          if (isDefined(option.group)) {
27588
27589            // This option is to live in a group
27590            // See if we have already created this group
27591            group = groupMap[option.group];
27592
27593            if (!group) {
27594
27595              // We have not already created this group
27596              groupElement = addOrReuseElement(selectElement[0],
27597                                               currentElement,
27598                                               'optgroup',
27599                                               optGroupTemplate);
27600              // Move to the next element
27601              currentElement = groupElement.nextSibling;
27602
27603              // Update the label on the group element
27604              groupElement.label = option.group;
27605
27606              // Store it for use later
27607              group = groupMap[option.group] = {
27608                groupElement: groupElement,
27609                currentOptionElement: groupElement.firstChild
27610              };
27611
27612            }
27613
27614            // So now we have a group for this option we add the option to the group
27615            optionElement = addOrReuseElement(group.groupElement,
27616                                              group.currentOptionElement,
27617                                              'option',
27618                                              optionTemplate);
27619            updateOptionElement(option, optionElement);
27620            // Move to the next element
27621            group.currentOptionElement = optionElement.nextSibling;
27622
27623          } else {
27624
27625            // This option is not in a group
27626            optionElement = addOrReuseElement(selectElement[0],
27627                                              currentElement,
27628                                              'option',
27629                                              optionTemplate);
27630            updateOptionElement(option, optionElement);
27631            // Move to the next element
27632            currentElement = optionElement.nextSibling;
27633          }
27634        });
27635
27636
27637        // Now remove all excess options and group
27638        Object.keys(groupMap).forEach(function(key) {
27639          removeExcessElements(groupMap[key].currentOptionElement);
27640        });
27641        removeExcessElements(currentElement);
27642
27643        ngModelCtrl.$render();
27644
27645        // Check to see if the value has changed due to the update to the options
27646        if (!ngModelCtrl.$isEmpty(previousValue)) {
27647          var nextValue = selectCtrl.readValue();
27648          var isNotPrimitive = ngOptions.trackBy || multiple;
27649          if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
27650            ngModelCtrl.$setViewValue(nextValue);
27651            ngModelCtrl.$render();
27652          }
27653        }
27654
27655      }
27656  }
27657
27658  return {
27659    restrict: 'A',
27660    terminal: true,
27661    require: ['select', 'ngModel'],
27662    link: {
27663      pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
27664        // Deactivate the SelectController.register method to prevent
27665        // option directives from accidentally registering themselves
27666        // (and unwanted $destroy handlers etc.)
27667        ctrls[0].registerOption = noop;
27668      },
27669      post: ngOptionsPostLink
27670    }
27671  };
27672}];
27673
27674/**
27675 * @ngdoc directive
27676 * @name ngPluralize
27677 * @restrict EA
27678 *
27679 * @description
27680 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
27681 * These rules are bundled with angular.js, but can be overridden
27682 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
27683 * by specifying the mappings between
27684 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
27685 * and the strings to be displayed.
27686 *
27687 * # Plural categories and explicit number rules
27688 * There are two
27689 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
27690 * in Angular's default en-US locale: "one" and "other".
27691 *
27692 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
27693 * any number that is not 1), an explicit number rule can only match one number. For example, the
27694 * explicit number rule for "3" matches the number 3. There are examples of plural categories
27695 * and explicit number rules throughout the rest of this documentation.
27696 *
27697 * # Configuring ngPluralize
27698 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
27699 * You can also provide an optional attribute, `offset`.
27700 *
27701 * The value of the `count` attribute can be either a string or an {@link guide/expression
27702 * Angular expression}; these are evaluated on the current scope for its bound value.
27703 *
27704 * The `when` attribute specifies the mappings between plural categories and the actual
27705 * string to be displayed. The value of the attribute should be a JSON object.
27706 *
27707 * The following example shows how to configure ngPluralize:
27708 *
27709 * ```html
27710 * <ng-pluralize count="personCount"
27711                 when="{'0': 'Nobody is viewing.',
27712 *                      'one': '1 person is viewing.',
27713 *                      'other': '{} people are viewing.'}">
27714 * </ng-pluralize>
27715 *```
27716 *
27717 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
27718 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
27719 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
27720 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
27721 * show "a dozen people are viewing".
27722 *
27723 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
27724 * into pluralized strings. In the previous example, Angular will replace `{}` with
27725 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
27726 * for <span ng-non-bindable>{{numberExpression}}</span>.
27727 *
27728 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
27729 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
27730 *
27731 * # Configuring ngPluralize with offset
27732 * The `offset` attribute allows further customization of pluralized text, which can result in
27733 * a better user experience. For example, instead of the message "4 people are viewing this document",
27734 * you might display "John, Kate and 2 others are viewing this document".
27735 * The offset attribute allows you to offset a number by any desired value.
27736 * Let's take a look at an example:
27737 *
27738 * ```html
27739 * <ng-pluralize count="personCount" offset=2
27740 *               when="{'0': 'Nobody is viewing.',
27741 *                      '1': '{{person1}} is viewing.',
27742 *                      '2': '{{person1}} and {{person2}} are viewing.',
27743 *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
27744 *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27745 * </ng-pluralize>
27746 * ```
27747 *
27748 * Notice that we are still using two plural categories(one, other), but we added
27749 * three explicit number rules 0, 1 and 2.
27750 * When one person, perhaps John, views the document, "John is viewing" will be shown.
27751 * When three people view the document, no explicit number rule is found, so
27752 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
27753 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
27754 * is shown.
27755 *
27756 * Note that when you specify offsets, you must provide explicit number rules for
27757 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
27758 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
27759 * plural categories "one" and "other".
27760 *
27761 * @param {string|expression} count The variable to be bound to.
27762 * @param {string} when The mapping between plural category to its corresponding strings.
27763 * @param {number=} offset Offset to deduct from the total number.
27764 *
27765 * @example
27766    <example module="pluralizeExample">
27767      <file name="index.html">
27768        <script>
27769          angular.module('pluralizeExample', [])
27770            .controller('ExampleController', ['$scope', function($scope) {
27771              $scope.person1 = 'Igor';
27772              $scope.person2 = 'Misko';
27773              $scope.personCount = 1;
27774            }]);
27775        </script>
27776        <div ng-controller="ExampleController">
27777          <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
27778          <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
27779          <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
27780
27781          <!--- Example with simple pluralization rules for en locale --->
27782          Without Offset:
27783          <ng-pluralize count="personCount"
27784                        when="{'0': 'Nobody is viewing.',
27785                               'one': '1 person is viewing.',
27786                               'other': '{} people are viewing.'}">
27787          </ng-pluralize><br>
27788
27789          <!--- Example with offset --->
27790          With Offset(2):
27791          <ng-pluralize count="personCount" offset=2
27792                        when="{'0': 'Nobody is viewing.',
27793                               '1': '{{person1}} is viewing.',
27794                               '2': '{{person1}} and {{person2}} are viewing.',
27795                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
27796                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27797          </ng-pluralize>
27798        </div>
27799      </file>
27800      <file name="protractor.js" type="protractor">
27801        it('should show correct pluralized string', function() {
27802          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
27803          var withOffset = element.all(by.css('ng-pluralize')).get(1);
27804          var countInput = element(by.model('personCount'));
27805
27806          expect(withoutOffset.getText()).toEqual('1 person is viewing.');
27807          expect(withOffset.getText()).toEqual('Igor is viewing.');
27808
27809          countInput.clear();
27810          countInput.sendKeys('0');
27811
27812          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
27813          expect(withOffset.getText()).toEqual('Nobody is viewing.');
27814
27815          countInput.clear();
27816          countInput.sendKeys('2');
27817
27818          expect(withoutOffset.getText()).toEqual('2 people are viewing.');
27819          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
27820
27821          countInput.clear();
27822          countInput.sendKeys('3');
27823
27824          expect(withoutOffset.getText()).toEqual('3 people are viewing.');
27825          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
27826
27827          countInput.clear();
27828          countInput.sendKeys('4');
27829
27830          expect(withoutOffset.getText()).toEqual('4 people are viewing.');
27831          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
27832        });
27833        it('should show data-bound names', function() {
27834          var withOffset = element.all(by.css('ng-pluralize')).get(1);
27835          var personCount = element(by.model('personCount'));
27836          var person1 = element(by.model('person1'));
27837          var person2 = element(by.model('person2'));
27838          personCount.clear();
27839          personCount.sendKeys('4');
27840          person1.clear();
27841          person1.sendKeys('Di');
27842          person2.clear();
27843          person2.sendKeys('Vojta');
27844          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
27845        });
27846      </file>
27847    </example>
27848 */
27849var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
27850  var BRACE = /{}/g,
27851      IS_WHEN = /^when(Minus)?(.+)$/;
27852
27853  return {
27854    link: function(scope, element, attr) {
27855      var numberExp = attr.count,
27856          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
27857          offset = attr.offset || 0,
27858          whens = scope.$eval(whenExp) || {},
27859          whensExpFns = {},
27860          startSymbol = $interpolate.startSymbol(),
27861          endSymbol = $interpolate.endSymbol(),
27862          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
27863          watchRemover = angular.noop,
27864          lastCount;
27865
vendor: 12,352 bytes, lines 27866-28129
27866      forEach(attr, function(expression, attributeName) {
27867        var tmpMatch = IS_WHEN.exec(attributeName);
27868        if (tmpMatch) {
27869          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
27870          whens[whenKey] = element.attr(attr.$attr[attributeName]);
27871        }
27872      });
27873      forEach(whens, function(expression, key) {
27874        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
27875
27876      });
27877
27878      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
27879        var count = parseFloat(newVal);
27880        var countIsNaN = isNaN(count);
27881
27882        if (!countIsNaN && !(count in whens)) {
27883          // If an explicit number rule such as 1, 2, 3... is defined, just use it.
27884          // Otherwise, check it against pluralization rules in $locale service.
27885          count = $locale.pluralCat(count - offset);
27886        }
27887
27888        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
27889        // In JS `NaN !== NaN`, so we have to explicitly check.
27890        if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
27891          watchRemover();
27892          var whenExpFn = whensExpFns[count];
27893          if (isUndefined(whenExpFn)) {
27894            if (newVal != null) {
27895              $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
27896            }
27897            watchRemover = noop;
27898            updateElementText();
27899          } else {
27900            watchRemover = scope.$watch(whenExpFn, updateElementText);
27901          }
27902          lastCount = count;
27903        }
27904      });
27905
27906      function updateElementText(newText) {
27907        element.text(newText || '');
27908      }
27909    }
27910  };
27911}];
27912
27913/**
27914 * @ngdoc directive
27915 * @name ngRepeat
27916 * @multiElement
27917 *
27918 * @description
27919 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
27920 * instance gets its own scope, where the given loop variable is set to the current collection item,
27921 * and `$index` is set to the item index or key.
27922 *
27923 * Special properties are exposed on the local scope of each template instance, including:
27924 *
27925 * | Variable  | Type            | Details                                                                     |
27926 * |-----------|-----------------|-----------------------------------------------------------------------------|
27927 * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
27928 * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
27929 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
27930 * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
27931 * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
27932 * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
27933 *
27934 * <div class="alert alert-info">
27935 *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
27936 *   This may be useful when, for instance, nesting ngRepeats.
27937 * </div>
27938 *
27939 *
27940 * # Iterating over object properties
27941 *
27942 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
27943 * syntax:
27944 *
27945 * ```js
27946 * <div ng-repeat="(key, value) in myObj"> ... </div>
27947 * ```
27948 *
27949 * You need to be aware that the JavaScript specification does not define the order of keys
27950 * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
27951 * used to sort the keys alphabetically.)
27952 *
27953 * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
27954 * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
27955 * keys in the order in which they were defined, although there are exceptions when keys are deleted
27956 * 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).
27957 *
27958 * If this is not desired, the recommended workaround is to convert your object into an array
27959 * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
27960 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27961 * or implement a `$watch` on the object yourself.
27962 *
27963 *
27964 * # Tracking and Duplicates
27965 *
27966 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
27967 * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
27968 *
27969 * * When an item is added, a new instance of the template is added to the DOM.
27970 * * When an item is removed, its template instance is removed from the DOM.
27971 * * When items are reordered, their respective templates are reordered in the DOM.
27972 *
27973 * To minimize creation of DOM elements, `ngRepeat` uses a function
27974 * to "keep track" of all items in the collection and their corresponding DOM elements.
27975 * For example, if an item is added to the collection, ngRepeat will know that all other items
27976 * already have DOM elements, and will not re-render them.
27977 *
27978 * The default tracking function (which tracks items by their identity) does not allow
27979 * duplicate items in arrays. This is because when there are duplicates, it is not possible
27980 * to maintain a one-to-one mapping between collection items and DOM elements.
27981 *
27982 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27983 * with your own using the `track by` expression.
27984 *
27985 * For example, you may track items by the index of each item in the collection, using the
27986 * special scope property `$index`:
27987 * ```html
27988 *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27989 *      {{n}}
27990 *    </div>
27991 * ```
27992 *
27993 * You may also use arbitrary expressions in `track by`, including references to custom functions
27994 * on the scope:
27995 * ```html
27996 *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27997 *      {{n}}
27998 *    </div>
27999 * ```
28000 *
28001 * <div class="alert alert-success">
28002 * If you are working with objects that have an identifier property, you should track
28003 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
28004 * will not have to rebuild the DOM elements for items it has already rendered, even if the
28005 * JavaScript objects in the collection have been substituted for new ones. For large collections,
28006 * this significantly improves rendering performance. If you don't have a unique identifier,
28007 * `track by $index` can also provide a performance boost.
28008 * </div>
28009 * ```html
28010 *    <div ng-repeat="model in collection track by model.id">
28011 *      {{model.name}}
28012 *    </div>
28013 * ```
28014 *
28015 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
28016 * `$id` function, which tracks items by their identity:
28017 * ```html
28018 *    <div ng-repeat="obj in collection track by $id(obj)">
28019 *      {{obj.prop}}
28020 *    </div>
28021 * ```
28022 *
28023 * <div class="alert alert-warning">
28024 * **Note:** `track by` must always be the last expression:
28025 * </div>
28026 * ```
28027 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
28028 *     {{model.name}}
28029 * </div>
28030 * ```
28031 *
28032 * # Special repeat start and end points
28033 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
28034 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
28035 * 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)
28036 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
28037 *
28038 * The example below makes use of this feature:
28039 * ```html
28040 *   <header ng-repeat-start="item in items">
28041 *     Header {{ item }}
28042 *   </header>
28043 *   <div class="body">
28044 *     Body {{ item }}
28045 *   </div>
28046 *   <footer ng-repeat-end>
28047 *     Footer {{ item }}
28048 *   </footer>
28049 * ```
28050 *
28051 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
28052 * ```html
28053 *   <header>
28054 *     Header A
28055 *   </header>
28056 *   <div class="body">
28057 *     Body A
28058 *   </div>
28059 *   <footer>
28060 *     Footer A
28061 *   </footer>
28062 *   <header>
28063 *     Header B
28064 *   </header>
28065 *   <div class="body">
28066 *     Body B
28067 *   </div>
28068 *   <footer>
28069 *     Footer B
28070 *   </footer>
28071 * ```
28072 *
28073 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
28074 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
28075 *
28076 * @animations
28077 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
28078 *
28079 * **.leave** - when an item is removed from the list or when an item is filtered out
28080 *
28081 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
28082 *
28083 * @element ANY
28084 * @scope
28085 * @priority 1000
28086 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
28087 *   formats are currently supported:
28088 *
28089 *   * `variable in expression` – where variable is the user defined loop variable and `expression`
28090 *     is a scope expression giving the collection to enumerate.
28091 *
28092 *     For example: `album in artist.albums`.
28093 *
28094 *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
28095 *     and `expression` is the scope expression giving the collection to enumerate.
28096 *
28097 *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
28098 *
28099 *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
28100 *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
28101 *     is specified, ng-repeat associates elements by identity. It is an error to have
28102 *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
28103 *     mapped to the same DOM element, which is not possible.)
28104 *
28105 *     Note that the tracking expression must come last, after any filters, and the alias expression.
28106 *
28107 *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
28108 *     will be associated by item identity in the array.
28109 *
28110 *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
28111 *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
28112 *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
28113 *     element in the same way in the DOM.
28114 *
28115 *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
28116 *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
28117 *     property is same.
28118 *
28119 *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
28120 *     to items in conjunction with a tracking expression.
28121 *
28122 *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
28123 *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
28124 *     when a filter is active on the repeater, but the filtered result set is empty.
28125 *
28126 *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
28127 *     the items have been processed through the filter.
28128 *
28129 *     Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax s
28129o it can be used only at the end
28130 *     (and not as operator, inside an expression).
28131 *
28132 *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
28133 *
28134 * @example
28135 * This example initializes the scope to a list of names and
28136 * then uses `ngRepeat` to display every person:
28137  <example module="ngAnimate" deps="angular-animate.js" animations="true">
28138    <file name="index.html">
28139      <div ng-init="friends = [
28140        {name:'John', age:25, gender:'boy'},
28141        {name:'Jessie', age:30, gender:'girl'},
28142        {name:'Johanna', age:28, gender:'girl'},
28143        {name:'Joy', age:15, gender:'girl'},
28144        {name:'Mary', age:28, gender:'girl'},
28145        {name:'Peter', age:95, gender:'boy'},
28146        {name:'Sebastian', age:50, gender:'boy'},
28147        {name:'Erika', age:27, gender:'girl'},
28148        {name:'Patrick', age:40, gender:'boy'},
28149        {name:'Samantha', age:60, gender:'girl'}
28150      ]">
28151        I have {{friends.length}} friends. They are:
28152        <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
28153        <ul class="example-animate-container">
28154          <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
28155            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
28156          </li>
28157          <li class="animate-repeat" ng-if="results.length == 0">
28158            <strong>No results found...</strong>
28159          </li>
28160        </ul>
28161      </div>
28162    </file>
28163    <file name="animations.css">
28164      .example-animate-container {
28165        background:white;
28166        border:1px solid black;
28167        list-style:none;
28168        margin:0;
28169        padding:0 10px;
28170      }
28171
28172      .animate-repeat {
28173        line-height:40px;
28174        list-style:none;
28175        box-sizing:border-box;
28176      }
28177
28178      .animate-repeat.ng-move,
28179      .animate-repeat.ng-enter,
28180      .animate-repeat.ng-leave {
28181        transition:all linear 0.5s;
28182      }
28183
28184      .animate-repeat.ng-leave.ng-leave-active,
28185      .animate-repeat.ng-move,
28186      .animate-repeat.ng-enter {
28187        opacity:0;
28188        max-height:0;
28189      }
28190
28191      .animate-repeat.ng-leave,
28192      .animate-repeat.ng-move.ng-move-active,
28193      .animate-repeat.ng-enter.ng-enter-active {
28194        opacity:1;
28195        max-height:40px;
28196      }
28197    </file>
28198    <file name="protractor.js" type="protractor">
28199      var friends = element.all(by.repeater('friend in friends'));
28200
28201      it('should render initial data set', function() {
28202        expect(friends.count()).toBe(10);
28203        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
28204        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
28205        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
28206        expect(element(by.binding('friends.length')).getText())
28207            .toMatch("I have 10 friends. They are:");
28208      });
28209
28210       it('should update repeater when filter predicate changes', function() {
28211         expect(friends.count()).toBe(10);
28212
28213         element(by.model('q')).sendKeys('ma');
28214
28215         expect(friends.count()).toBe(2);
28216         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
28217         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
28218       });
28219      </file>
28220    </example>
28221 */
28222var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
28223  var NG_REMOVED = '$$NG_REMOVED';
28224  var ngRepeatMinErr = minErr('ngRepeat');
28225
28226  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
28227    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
28228    scope[valueIdentifier] = value;
28229    if (keyIdentifier) scope[keyIdentifier] = key;
28230    scope.$index = index;
28231    scope.$first = (index === 0);
28232    scope.$last = (index === (arrayLength - 1));
28233    scope.$middle = !(scope.$first || scope.$last);
28234    // jshint bitwise: false
28235    scope.$odd = !(scope.$even = (index&1) === 0);
28236    // jshint bitwise: true
28237  };
28238
28239  var getBlockStart = function(block) {
28240    return block.clone[0];
28241  };
28242
28243  var getBlockEnd = function(block) {
28244    return block.clone[block.clone.length - 1];
28245  };
28246
28247
28248  return {
28249    restrict: 'A',
28250    multiElement: true,
28251    transclude: 'element',
28252    priority: 1000,
28253    terminal: true,
28254    $$tlb: true,
28255    compile: function ngRepeatCompile($element, $attr) {
28256      var expression = $attr.ngRepeat;
28257      var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
28258
28259      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*$/);
28260
28261      if (!match) {
28262        throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
28263            expression);
28264      }
28265
28266      var lhs = match[1];
28267      var rhs = match[2];
28268      var aliasAs = match[3];
28269      var trackByExp = match[4];
28270
28271      match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
28272
28273      if (!match) {
28274        throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
28275            lhs);
28276      }
28277      var valueIdentifier = match[3] || match[1];
28278      var keyIdentifier = match[2];
28279
28280      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
28281          /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
28282        throw ngRepeatMinErr('badident', "alias '{0}
vendor: 12,206 bytes, lines 28282-28554
28282' is invalid --- must be a valid JS identifier which is not a reserved name.",
28283          aliasAs);
28284      }
28285
28286      var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
28287      var hashFnLocals = {$id: hashKey};
28288
28289      if (trackByExp) {
28290        trackByExpGetter = $parse(trackByExp);
28291      } else {
28292        trackByIdArrayFn = function(key, value) {
28293          return hashKey(value);
28294        };
28295        trackByIdObjFn = function(key) {
28296          return key;
28297        };
28298      }
28299
28300      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
28301
28302        if (trackByExpGetter) {
28303          trackByIdExpFn = function(key, value, index) {
28304            // assign key, value, and $index to the locals so that they can be used in hash functions
28305            if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
28306            hashFnLocals[valueIdentifier] = value;
28307            hashFnLocals.$index = index;
28308            return trackByExpGetter($scope, hashFnLocals);
28309          };
28310        }
28311
28312        // Store a list of elements from previous run. This is a hash where key is the item from the
28313        // iterator, and the value is objects with following properties.
28314        //   - scope: bound scope
28315        //   - element: previous element.
28316        //   - index: position
28317        //
28318        // We are using no-proto object so that we don't need to guard against inherited props via
28319        // hasOwnProperty.
28320        var lastBlockMap = createMap();
28321
28322        //watch props
28323        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
28324          var index, length,
28325              previousNode = $element[0],     // node that cloned nodes should be inserted after
28326                                              // initialized to the comment node anchor
28327              nextNode,
28328              // Same as lastBlockMap but it has the current state. It will become the
28329              // lastBlockMap on the next iteration.
28330              nextBlockMap = createMap(),
28331              collectionLength,
28332              key, value, // key/value of iteration
28333              trackById,
28334              trackByIdFn,
28335              collectionKeys,
28336              block,       // last object information {scope, element, id}
28337              nextBlockOrder,
28338              elementsToRemove;
28339
28340          if (aliasAs) {
28341            $scope[aliasAs] = collection;
28342          }
28343
28344          if (isArrayLike(collection)) {
28345            collectionKeys = collection;
28346            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
28347          } else {
28348            trackByIdFn = trackByIdExpFn || trackByIdObjFn;
28349            // if object, extract keys, in enumeration order, unsorted
28350            collectionKeys = [];
28351            for (var itemKey in collection) {
28352              if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
28353                collectionKeys.push(itemKey);
28354              }
28355            }
28356          }
28357
28358          collectionLength = collectionKeys.length;
28359          nextBlockOrder = new Array(collectionLength);
28360
28361          // locate existing items
28362          for (index = 0; index < collectionLength; index++) {
28363            key = (collection === collectionKeys) ? index : collectionKeys[index];
28364            value = collection[key];
28365            trackById = trackByIdFn(key, value, index);
28366            if (lastBlockMap[trackById]) {
28367              // found previously seen block
28368              block = lastBlockMap[trackById];
28369              delete lastBlockMap[trackById];
28370              nextBlockMap[trackById] = block;
28371              nextBlockOrder[index] = block;
28372            } else if (nextBlockMap[trackById]) {
28373              // if collision detected. restore lastBlockMap and throw an error
28374              forEach(nextBlockOrder, function(block) {
28375                if (block && block.scope) lastBlockMap[block.id] = block;
28376              });
28377              throw ngRepeatMinErr('dupes',
28378                  "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
28379                  expression, trackById, value);
28380            } else {
28381              // new never before seen block
28382              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
28383              nextBlockMap[trackById] = true;
28384            }
28385          }
28386
28387          // remove leftover items
28388          for (var blockKey in lastBlockMap) {
28389            block = lastBlockMap[blockKey];
28390            elementsToRemove = getBlockNodes(block.clone);
28391            $animate.leave(elementsToRemove);
28392            if (elementsToRemove[0].parentNode) {
28393              // if the element was not removed yet because of pending animation, mark it as deleted
28394              // so that we can ignore it later
28395              for (index = 0, length = elementsToRemove.length; index < length; index++) {
28396                elementsToRemove[index][NG_REMOVED] = true;
28397              }
28398            }
28399            block.scope.$destroy();
28400          }
28401
28402          // we are not using forEach for perf reasons (trying to avoid #call)
28403          for (index = 0; index < collectionLength; index++) {
28404            key = (collection === collectionKeys) ? index : collectionKeys[index];
28405            value = collection[key];
28406            block = nextBlockOrder[index];
28407
28408            if (block.scope) {
28409              // if we have already seen this object, then we need to reuse the
28410              // associated scope/element
28411
28412              nextNode = previousNode;
28413
28414              // skip nodes that are already pending removal via leave animation
28415              do {
28416                nextNode = nextNode.nextSibling;
28417              } while (nextNode && nextNode[NG_REMOVED]);
28418
28419              if (getBlockStart(block) != nextNode) {
28420                // existing item which got moved
28421                $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
28422              }
28423              previousNode = getBlockEnd(block);
28424              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
28425            } else {
28426              // new item which we don't know about
28427              $transclude(function ngRepeatTransclude(clone, scope) {
28428                block.scope = scope;
28429                // http://jsperf.com/clone-vs-createcomment
28430                var endNode = ngRepeatEndComment.cloneNode(false);
28431                clone[clone.length++] = endNode;
28432
28433                // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
28434                $animate.enter(clone, null, jqLite(previousNode));
28435                previousNode = endNode;
28436                // Note: We only need the first/last node of the cloned nodes.
28437                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
28438                // by a directive with templateUrl when its template arrives.
28439                block.clone = clone;
28440                nextBlockMap[block.id] = block;
28441                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
28442              });
28443            }
28444          }
28445          lastBlockMap = nextBlockMap;
28446        });
28447      };
28448    }
28449  };
28450}];
28451
28452var NG_HIDE_CLASS = 'ng-hide';
28453var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
28454/**
28455 * @ngdoc directive
28456 * @name ngShow
28457 * @multiElement
28458 *
28459 * @description
28460 * The `ngShow` directive shows or hides the given HTML element based on the expression
28461 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
28462 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
28463 * in AngularJS and sets the display style to none (using an !important flag).
28464 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
28465 *
28466 * ```html
28467 * <!-- when $scope.myValue is truthy (element is visible) -->
28468 * <div ng-show="myValue"></div>
28469 *
28470 * <!-- when $scope.myValue is falsy (element is hidden) -->
28471 * <div ng-show="myValue" class="ng-hide"></div>
28472 * ```
28473 *
28474 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
28475 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
28476 * from the element causing the element not to appear hidden.
28477 *
28478 * ## Why is !important used?
28479 *
28480 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
28481 * can be easily overridden by heavier selectors. For example, something as simple
28482 * as changing the display style on a HTML list item would make hidden elements appear visible.
28483 * This also becomes a bigger issue when dealing with CSS frameworks.
28484 *
28485 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
28486 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
28487 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
28488 *
28489 * ### Overriding `.ng-hide`
28490 *
28491 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
28492 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
28493 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
28494 * with extra animation classes that can be added.
28495 *
28496 * ```css
28497 * .ng-hide:not(.ng-hide-animate) {
28498 *   /&#42; this is just another form of hiding an element &#42;/
28499 *   display: block!important;
28500 *   position: absolute;
28501 *   top: -9999px;
28502 *   left: -9999px;
28503 * }
28504 * ```
28505 *
28506 * By default you don't need to override in CSS anything and the animations will work around the display style.
28507 *
28508 * ## A note about animations with `ngShow`
28509 *
28510 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
28511 * is true and false. This system works like the animation system present with ngClass except that
28512 * you must also include the !important flag to override the display property
28513 * so that you can perform an animation when the element is hidden during the time of the animation.
28514 *
28515 * ```css
28516 * //
28517 * //a working example can be found at the bottom of this page
28518 * //
28519 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
28520 *   /&#42; this is required as of 1.3x to properly
28521 *      apply all styling in a show/hide animation &#42;/
28522 *   transition: 0s linear all;
28523 * }
28524 *
28525 * .my-element.ng-hide-add-active,
28526 * .my-element.ng-hide-remove-active {
28527 *   /&#42; the transition is defined in the active class &#42;/
28528 *   transition: 1s linear all;
28529 * }
28530 *
28531 * .my-element.ng-hide-add { ... }
28532 * .my-element.ng-hide-add.ng-hide-add-active { ... }
28533 * .my-element.ng-hide-remove { ... }
28534 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
28535 * ```
28536 *
28537 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
28538 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
28539 *
28540 * @animations
28541 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
28542 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
28543 *
28544 * @element ANY
28545 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
28546 *     then the element is shown or hidden respectively.
28547 *
28548 * @example
28549  <example module="ngAnimate" deps="angular-animate.js" animations="true">
28550    <file name="index.html">
28551      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
28552      <div>
28553        Show:
28554        <div class="check-element animate-show" ng-show="checked">
28555          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
28556        </div>
28557      </div>
28558      <div>
28559        Hide:
28560        <div class="check-element animate-show" ng-hide="checked">
28561          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
28562        </div>
28563      </div>
28564    </file>
28565    <file name="glyphicons.css">
28566      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
28567    </file>
28568    <file name="animations.css">
28569      .animate-show {
28570        line-height: 20px;
28571        opacity: 1;
28572        padding: 10px;
28573        border: 1px solid black;
28574        background: white;
28575      }
28576
28577      .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
28578        transition: all linear 0.5s;
28579      }
28580
28581      .animate-show.ng-hide {
28582        line-height: 0;
28583        opacity: 0;
28584        padding: 0 10px;
28585      }
28586
28587      .check-element {
28588        padding: 10px;
28589        border: 1px solid black;
28590        background: white;
28591      }
28592    </file>
28593    <file name="protractor.js" type="protractor">
28594      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
28595      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
28596
28597      it('should check ng-show / ng-hide', function() {
28598        expect(thumbsUp.isDisplayed()).toBeFalsy();
28599        expect(thumbsDown.isDisplayed()).toBeTruthy();
28600
28601        element(by.model('checked')).click();
28602
28603        expect(thumbsUp.isDisplayed()).toBeTruthy();
28604        expect(thumbsDown.isDisplayed()).toBeFalsy();
28605      });
28606    </file>
28607  </example>
28608 */
28609var ngShowDirective = ['$animate', function($animate) {
28610  return {
28611    restrict: 'A',
28612    multiElement: true,
28613    link: function(scope, element, attr) {
28614      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
28615        // we're adding a temporary, animation-specific class for ng-hide since this way
28616        // we can control when the element is actually displayed on screen without having
28617        // to have a global/greedy CSS selector that breaks when other animations are run.
28618        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
28619        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
28620          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
28621        });
28622      });
28623    }
28624  };
28625}];
28626
28627
28628/**
28629 * @ngdoc directive
28630 * @name ngHide
28631 * @multiElement
28632 *
28633 * @description
28634 * The `ngHide` directive shows or hides the given HTML element based on the expression
28635 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
28636 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
28637 * in AngularJS and sets the display style to none (using an !important flag).
28638 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
28639 *
28640 * ```html
28641 * <!-- when $scope.myValue is truthy (element is hidden) -->
28642 * <div ng-hide="myValue" class="ng-hide"></div>
28643 *
28644 * <!-- when $scope.myValue is falsy (element is visible) -->
28645 * <div ng-hide="myValue"></div>
28646 * ```
28647 *
28648 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
28649 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
28650 * from the element causing the element not to appear hidden.
28651 *
28652 * ## Why is !important used?
28653 *
28654 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
28655 * can be easily overridden by heavier selectors. For example, something as simple
28656 * as changing the display style on a HTML list item would make hidden elements appear visible.
28657 * This also becomes a bigger issue when dealing with CSS frameworks.
28658 *
28659 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
28660 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
28661 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
28662 *
28663 * ### Overriding `.ng-hide`
28664 *
28665 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
28666 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
28667 * class in CSS:
28668 *
28669 * ```css
28670 * .ng-hide {
28671 *   /&#42; this is just another form of hiding an element &#42;/
28672 *   display: block!important;
28673 *   position: absolute;
28674 *   top: -9999px;
28675 *   left: -9999px;
28676 * }
28677 * ```
28678 *
28679 * By default you don't need to override in CSS anything and the animations will work around the display style.
28680 *
28681 * ## A note about animations with `ngHide`
28682 *
28683 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
28684 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
28685 * CSS class is added and removed for you instead of your own CSS class.
28686 *
28687 * ```css
28688 * //
28689 * //a working example can be found at the bottom of this page
28690 * //
28691 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
28692 *   transition: 0.5s linear all;
28693 * }
28694 *
28695 * .my-element.ng-hide-add { ... }
28696 * .my-element.ng-hide-add.ng-hide-add-active { ... }
28697 * .my-element.ng-hide-remove { ... }
28698 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
28699 * ```
28700 *
28701 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
28702 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
28703 *
28704 * @animations
28705 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
28706 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
28707 *
28708 * @element ANY
28709 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
28710 *     the element is shown or hidden respectively.
28711 *
28712 * @example
28713  <example module="ngAnimate" deps="angular-animate.js" animations="true">
28714    <file name="index.html">
28715      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
28716      <div>
28717        Show:
28718        <div class="check-element animate-hide" ng-show="checked">
28719          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
28720        </div>
28721      </div>
28722      <div>
28723        Hide:
28724        <div class="check-element animate-hide" ng-hide="checked">
28725          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
28726        </div>
28727      </div>
28728    </file>
28729    <file name="glyphicons.css">
28730      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
28731    </file>
28732    <file name="animations.css">
28733      .animate-hide {
28734        transition: all linear 0.5s;
28735        line-height: 20px;
28736        opacity: 1;
28737        padding: 10px;
28738        border: 1px solid black;
28739        background: white;
28740      }
28741
28742      .animate-hide.ng-hide {
28743        line-height: 0;
28744        opacity: 0;
28745        padding: 0 10px;
28746      }
28747
28748      .check-element {
28749        padding: 10px;
28750        border: 1px solid black;
28751        background: white;
28752      }
28753    </file>
28754    <file name="protractor.js" type="protractor">
28755      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
28756      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
28757
28758      it('should check ng-show / ng-hide', function() {
28759        expect(thumbsUp.isDisplayed()).toBeFalsy();
28760        expect(thumbsDown.isDisplayed()).toBeTruthy();
28761
28762        element(by.model('checked')).click();
28763
28764        expect(thumbsUp.isDisplayed()).toBeTruthy();
28765        expect(thumbsDown.isDisplayed()).toBeFalsy();
28766      });
28767    </file>
28768  </example>
28769 */
28770var ngHideDirective = ['$animate', function($animate) {
28771  return {
28772    restrict: 'A',
28773    multiElement: true,
28774    link: function(scope, element, attr) {
28775      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
28776        // The comment inside of the ngShowDirective explains why we add and
28777        // remove a temporary class for the show/hide animation
28778        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
28779          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
28780        });
28781      });
28782    }
28783  };
28784}];
28785
28786/**
28787 * @ngdoc directive
28788 * @name ngStyle
28789 * @restrict AC
28790 *
28791 * @description
28792 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
28793 *
28794 * @element ANY
28795 * @param {expression} ngStyle
28796 *
28797 * {@link guide/expression Expression} which evals to an
28798 * object whose keys are CSS style names and values are corresponding values for those CSS
28799 * keys.
28800 *
28801 * Since some CSS style names are not valid keys for an object, they must be quoted.
28802 * See the 'background-color' style in the example below.
28803 *
28804 * @example
28805   <example>
28806     <file name="index.html">
28807        <input type="button" value="set color" ng-click="myStyle={color:'red'}">
28808        <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
28809        <input type="button" value="clear" ng-click="myStyle={}">
28810        <br/>
28811        <span ng-style="myStyle">Sample Text</span>
28812        <pre>myStyle={{myStyle}}</pre>
28813     </file>
28814     <file name="style.css">
28815       span {
28816         color: black;
28817       }
28818     </file>
28819     <file name="protractor.js" type="protractor">
28820       var colorSpan = element(by.css('span'));
28821
28822       it('should check ng-style', function() {
28823         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28824         element(by.css('input[value=\'set color\']')).click();
28825         expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
28826         element(by.css('input[value=clear]')).click();
28827         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28828       });
28829     </file>
28830   </example>
28831 */
28832var ngStyleDirective = ngDirective(function(scope, element, attr) {
28833  scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
28834    if (oldStyles && (newStyles !== oldStyles)) {
28835      forEach(oldStyles, function(val, style) { element.css(style, '');});
28836    }
28837    if (newStyles) element.css(newStyles);
28838  }, true);
28839});
28840
28841/**
28842 * @ngdoc directive
28843 * @name ngSwitch
28844 * @restrict EA
28845 *
28846 * @description
28847 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
28848 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
28849 * as specified in the template.
28850 *
28851 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
28852 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
28853 * matches the value obtained from the evaluated expression. In other words, you define a container element
28854 * (where you place the directive), place an expression on the **`on="..."` attribute**
28855 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
28856 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
28857 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
28858 * attribute is displayed.
28859 *
28860 * <div class="alert alert-info">
28861 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
28862 * as literal string values to match against.
28863 * For example, **`ng-switch-when="someVal"`** will match against the string `"s
28863omeVal"` not against the
28864 * value of the expression `$scope.someVal`.
28865 * </div>
28866
28867 * @animations
28868 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
28869 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
28870 *
28871 * @usage
28872 *
28873 * ```
28874 * <ANY ng-switch="expression">
28875 *   <ANY ng-switch-when="matchValue1">...</ANY>
28876 *   <ANY ng-switch-when="matchValue2">...</ANY>
28877 *   <ANY ng-switch-default>...</ANY>
28878 * </ANY>
28879 * ```
28880 *
28881 *
28882 * @scope
28883 * @priority 1200
28884 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
28885 * On child elements add:
28886 *
28887 * * `ngSwitchWhen`: the case statement to match against. If match then this
28888 *   case will be displayed. If the same match appears multiple times, all the
28889 *   elements will be displayed.
28890 * * `ngSwitchDefault`: the default case when no other case match. If there
28891 *   are multiple default cases, all of them will be displayed when no other
28892 *   case match.
28893 *
28894 *
28895 * @example
28896  <example module="switchExample" deps="angular-animate.js" animations="true">
28897    <file name="index.html">
28898      <div ng-controller="ExampleController">
28899        <select ng-model="selection" ng-options="item for item in items">
28900        </select>
28901        <code>selection={{selection}}</code>
28902        <hr/>
28903        <div class="animate-switch-container"
28904          ng-switch on="selection">
28905            <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
28906            <div class="animate-switch" ng-switch-when="home">Home Span</div>
28907            <div class="animate-switch" ng-switch-default>default</div>
28908        </div>
28909      </div>
28910    </file>
28911    <file name="script.js">
28912      angular.module('switchExample', ['ngAnimate'])
28913        .controller('ExampleController', ['$scope', function($scope) {
28914          $scope.items = ['settings', 'home', 'other'];
28915          $scope.selection = $scope.items[0];
28916        }]);
28917    </file>
28918    <file name="animations.css">
28919      .animate-switch-container {
28920        position:relative;
28921        background:white;
28922        border:1px solid black;
28923        height:40px;
28924        overflow:hidden;
28925      }
28926
28927      .animate-switch {
28928        padding:10px;
28929      }
28930
28931      .animate-switch.ng-animate {
28932        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28933
28934        position:absolute;
28935        top:0;
28936        left:0;
28937        right:0;
28938        bottom:0;
28939      }
28940
28941      .animate-switch.ng-leave.ng-leave-active,
28942      .animate-switch.ng-enter {
28943        top:-50px;
28944      }
28945      .animate-switch.ng-leave,
28946      .animate-switch.ng-enter.ng-enter-active {
28947        top:0;
28948      }
28949    </file>
28950    <file name="protractor.js" type="protractor">
28951      var switchElem = element(by.css('[ng-switch]'));
28952      var select = element(by.model('selection'));
28953
28954      it('should start in settings', function() {
28955        expect(switchElem.getText()).toMatch(/Settings Div/);
28956      });
28957      it('should change to home', function() {
28958        select.all(by.css('option')).get(1).click();
28959        expect(switchElem.getText()).toMatch(/Home Span/);
28960      });
28961      it('should select default', function() {
28962        select.all(by.css('option')).get(2).click();
28963        expect(switchElem.getText()).toMatch(/default/);
28964      });
28965    </file>
28966  </example>
28967 */
28968var ngSwitchDirective = ['$animate', function($animate) {
28969  return {
28970    require: 'ngSwitch',
28971
28972    // asks for $scope to fool the BC controller module
28973    controller: ['$scope', function ngSwitchController() {
28974     this.cases = {};
28975    }],
28976    link: function(scope, element, attr, ngSwitchController) {
28977      var watchExpr = attr.ngSwitch || attr.on,
28978          selectedTranscludes = [],
28979          selectedElements = [],
28980          previousLeaveAnimations = [],
28981          selectedScopes = [];
28982
28983      var spliceFactory = function(array, index) {
28984          return function() { array.splice(index, 1); };
28985      };
28986
28987      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28988        var i, ii;
28989        for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28990          $animate.cancel(previousLeaveAnimations[i]);
28991        }
28992        previousLeaveAnimations.length = 0;
28993
28994        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28995          var selected = getBlockNodes(selectedElements[i].clone);
28996          selectedScopes[i].$destroy();
28997          var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28998          promise.then(spliceFactory(previousLeaveAnimations, i));
28999        }
29000
29001        selectedElements.length = 0;
29002        selectedScopes.length = 0;
29003
29004        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
29005          forEach(selectedTranscludes, function(selectedTransclude) {
29006            selectedTransclude.transclude(function(caseElement, selectedScope) {
29007              selectedScopes.push(selectedScope);
29008              var anchor = selectedTransclude.element;
29009              caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
29010              var block = { clone: caseElement };
29011
29012              selectedElements.push(block);
29013              $animate.enter(caseElement, anchor.parent(), anchor);
29014            });
29015          });
29016        }
29017      });
29018    }
29019  };
29020}];
29021
29022var ngSwitchWhenDirective = ngDirective({
29023  transclude: 'element',
29024  priority: 1200,
29025  require: '^ngSwitch',
29026  multiElement: true,
29027  link: function(scope, element, attrs, ctrl, $transclude) {
29028    ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
29029    ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
29030  }
29031});
29032
29033var ngSwitchDefaultDirective = ngDirective({
29034  transclude: 'element',
29035  priority: 1200,
29036  require: '^ngSwitch',
29037  multiElement: true,
29038  link: function(scope, element, attr, ctrl, $transclude) {
29039    ctrl.cases['?'] = (ctrl.cases['?'] || []);
29040    ctrl.cases['?'].push({ transclude: $transclude, element: element });
29041   }
29042});
29043
29044/**
29045 * @ngdoc directive
29046 * @name ngTransclude
29047 * @restrict EAC
29048 *
29049 * @description
29050 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
29051 *
29052 * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
29053 * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
29054 *
29055 * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
29056 * content of this element will be removed before the transcluded content is inserted.
29057 * If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case
29058 * that no transcluded content is provided.
29059 *
29060 * @element ANY
29061 *
29062 * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
29063 *                                               or its value is the same as the name of the attribute then the default slot is used.
29064 *
29065 * @example
29066 * ### Basic transclusion
29067 * This example demonstrates basic transclusion of content into a component directive.
29068 * <example name="simpleTranscludeExample" module="transcludeExample">
29069 *   <file name="index.html">
29070 *     <script>
29071 *       angular.module('transcludeExample', [])
29072 *        .directive('pane', function(){
29073 *           return {
29074 *             restrict: 'E',
29075 *             transclude: true,
29076 *             scope: { title:'@' },
29077 *             template: '<div style="border: 1px solid black;">' +
29078 *                         '<div style="background-color: gray">{{title}}</div>' +
29079 *                         '<ng-transclude></ng-transclude>' +
29080 *                       '</div>'
29081 *           };
29082 *       })
29083 *       .controller('ExampleController', ['$scope', function($scope) {
29084 *         $scope.title = 'Lorem Ipsum';
29085 *         $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
29086 *       }]);
29087 *     </script>
29088 *     <div ng-controller="ExampleController">
29089 *       <input ng-model="title" aria-label="title"> <br/>
29090 *       <textarea ng-model="text" aria-label="text"></textarea> <br/>
29091 *       <pane title="{{title}}">{{text}}</pane>
29092 *     </div>
29093 *   </file>
29094 *   <file name="protractor.js" type="protractor">
29095 *      it('should have transcluded', function() {
29096 *        var titleElement = element(by.model('title'));
29097 *        titleElement.clear();
29098 *        titleElement.sendKeys('TITLE');
29099 *        var textElement = element(by.model('text'));
29100 *        textElement.clear();
29101 *        textElement.sendKeys('TEXT');
29102 *        expect(element(by.binding('title')).getText()).toEqual('TITLE');
29103 *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
29104 *      });
29105 *   </file>
29106 * </example>
29107 *
29108 * @example
29109 * ### Transclude fallback content
29110 * This example shows how to use `NgTransclude` with fallback content, that
29111 * is displayed if no transcluded content is provided.
29112 *
29113 * <example module="transcludeFallbackContentExample">
29114 * <file name="index.html">
29115 * <script>
29116 * angular.module('transcludeFallbackContentExample', [])
29117 * .directive('myButton', function(){
29118 *             return {
29119 *               restrict: 'E',
29120 *               transclude: true,
29121 *               scope: true,
29122 *               template: '<button style="cursor: pointer;">' +
29123 *                           '<ng-transclude>' +
29124 *                             '<b style="color: red;">Button1</b>' +
29125 *                           '</ng-transclude>' +
29126 *                         '</button>'
29127 *             };
29128 *         });
29129 * </script>
29130 * <!-- fallback button content -->
29131 * <my-button id="fallback"></my-button>
29132 * <!-- modified button content -->
29133 * <my-button id="modified">
29134 *   <i style="color: green;">Button2</i>
29135 * </my-button>
29136 * </file>
29137 * <file name="protractor.js" type="protractor">
29138 * it('should have different transclude element content', function() {
29139 *          expect(element(by.id('fallback')).getText()).toBe('Button1');
29140 *          expect(element(by.id('modified')).getText()).toBe('Button2');
29141 *        });
29142 * </file>
29143 * </example>
29144 *
29145 * @example
29146 * ### Multi-slot transclusion
29147 * This example demonstrates using multi-slot transclusion in a component directive.
29148 * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
29149 *   <file name="index.html">
29150 *    <style>
29151 *      .title, .footer {
29152 *        background-color: gray
29153 *      }
29154 *    </style>
29155 *    <div ng-controller="ExampleController">
29156 *      <input ng-model="title" aria-label="title"> <br/>
29157 *      <textarea ng-model="text" aria-label="text"></textarea> <br/>
29158 *      <pane>
29159 *        <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
29160 *        <pane-body><p>{{text}}</p></pane-body>
29161 *      </pane>
29162 *    </div>
29163 *   </file>
29164 *   <file name="app.js">
29165 *    angular.module('multiSlotTranscludeExample', [])
29166 *     .directive('pane', function(){
29167 *        return {
29168 *          restrict: 'E',
29169 *          transclude: {
29170 *            'title': '?paneTitle',
29171 *            'body': 'paneBody',
29172 *            'footer': '?paneFooter'
29173 *          },
29174 *          template: '<div style="border: 1px solid black;">' +
29175 *                      '<div class="title" ng-transclude="title">Fallback Title</div>' +
29176 *                      '<div ng-transclude="body"></div>' +
29177 *                      '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
29178 *                    '</div>'
29179 *        };
29180 *    })
29181 *    .controller('ExampleController', ['$scope', function($scope) {
29182 *      $scope.title = 'Lorem Ipsum';
29183 *      $scope.link = "https://google.com";
29184 *      $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
29185 *    }]);
29186 *   </file>
29187 *   <file name="protractor.js" type="protractor">
29188 *      it('should have transcluded the title and the body', function() {
29189 *        var titleElement = element(by.model('title'));
29190 *        titleElement.clear();
29191 *        titleElement.sendKeys('TITLE');
29192 *        var textElement = element(by.model('text'));
29193 *        textElement.clear();
29194 *        textElement.sendKeys('TEXT');
29195 *        expect(element(by.css('.title')).getText()).toEqual('TITLE');
29196 *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
29197 *        expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
29198 *      });
29199 *   </file>
29200 * </example>
29201 */
29202var ngTranscludeMinErr = minErr('ngTransclude');
29203var ngTranscludeDirective = ngDirective({
29204  restrict: 'EAC',
29205  link: function($scope, $element, $attrs, controller, $transclude) {
29206
29207    if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
29208      // If the attribute is of the form: `ng-transclude="ng-transclude"`
29209      // then treat it like the default
29210      $attrs.ngTransclude = '';
29211    }
29212
29213    function ngTranscludeCloneAttachFn(clone) {
29214      if (clone.length) {
29215        $element.empty();
29216        $element.append(clone);
29217      }
29218    }
29219
29220    if (!$transclude) {
29221      throw ngTranscludeMinErr('orphan',
29222       'Illegal use of ngTransclude directive in the template! ' +
29223       'No parent directive that requires a transclusion found. ' +
29224       'Element: {0}',
29225       startingTag($element));
29226    }
29227
29228    // If there is no slot name defined or the slot name is not optional
29229    // then transclude the slot
29230    var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
29231    $transclude(ngTranscludeCloneAttachFn, null, slotName);
29232  }
29233});
29234
29235/**
29236 * @ngdoc directive
29237 * @name script
29238 * @restrict E
29239 *
29240 * @description
29241 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
29242 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
29243 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
29244 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
29245 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
29246 *
29247 * @param {string} type Must be set to `'text/ng-template'`.
29248 * @param {string} id Cache name of the template.
29249 *
29250 * @example
29251  <example>
29252    <file name="index.html">
29253      <script type="text/ng-template" id="/tpl.html">
29254        Content of the template.
29255      </script>
29256
29257      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
29258      <div id="tpl-content" ng-include src="currentTpl"></div>
29259    </file>
29260    <file name="protractor.js" type="protractor">
29261      it('should load template defined inside script tag', function() {
29262        element(by.css('#tpl-link')).click();
29263        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
29264      });
29265    </file>
29266  </example>
29267 */
29268var scriptDirective = ['$templateCache', function($templateCache) {
29269  return {
29270    restrict: 'E',
29271    terminal: true,
29272    compile: function(element, attr) {
29273      if (attr.type == 'text/ng-template') {
29274        var templateUrl = attr.id,
29275            text = element[0].text;
29276
29277        $templateCache.put(templateUrl, text);
29278      }
29279    }
29280  };
29281}];
29282
29283var noopNgModelController = { $setViewValue: noop, $render: noop };
29284
29285function chromeHack(optionElement) {
29286  // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
29287  // Adding an <option selected="selected"> element to a <select required="required"> should
29288  // automatically select the new element
29289  if (optionElement[0].hasAttribute('selected')) {
29290    optionElement[0].selected = true;
29291  }
29292}
29293
29294/**
29295 * @ngdoc type
29296 * @name  select.SelectController
29297 * @description
29298 * The controller for the `<select>` directive. This provides support for reading
29299 * and writing the selected value(s) of the control and also coordinates dynamically
29300 * added `<option>` elements, perhaps by an `ngRepeat` directive.
29301 */
29302var SelectController =
29303        ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
29304
29305  var self = this,
29306      optionsMap = new HashMap();
29307
29308  // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
29309  self.ngModelCtrl = noopNgModelController;
29310
29311  // The "unknown" option is one that is prepended to the list if the viewValue
29312  // does not match any of the options. When it is rendered the value of the unknown
29313  // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
29314  //
29315  // We can't just jqLite('<option>') since jqLite is not smart enough
29316  // to create it in <select> and IE barfs otherwise.
29317  self.unknownOption = jqLite(document.createElement('option'));
29318  self.renderUnknownOption = function(val) {
29319    var unknownVal = '? ' + hashKey(val) + ' ?';
29320    self.unknownOption.val(unknownVal);
29321    $element.prepend(self.unknownOption);
29322    $element.val(unknownVal);
29323  };
29324
29325  $scope.$on('$destroy', function() {
29326    // disable unknown option so that we don't do work when the whole select is being destroyed
29327    self.renderUnknownOption = noop;
29328  });
29329
29330  self.removeUnknownOption = function() {
29331    if (self.unknownOption.parent()) self.unknownOption.remove();
29332  };
29333
29334
29335  // Read the value of the select control, the implementation of this changes depending
29336  // upon whether the select can have multiple values and whether ngOptions is at work.
29337  self.readValue = function readSingleValue() {
29338    self.removeUnknownOption();
29339    return $element.val();
29340  };
29341
29342
29343  // Write the value to the select control, the implementation of this changes depending
29344  // upon whether the select can have multiple values and whether ngOptions is at work.
29345  self.writeValue = function writeSingleValue(value) {
29346    if (self.hasOption(value)) {
29347      self.removeUnknownOption();
29348      $element.val(value);
29349      if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
29350    } else {
29351      if (value == null && self.emptyOption) {
29352        self.removeUnknownOption();
29353        $element.val('');
29354      } else {
29355        self.renderUnknownOption(value);
29356      }
29357    }
29358  };
29359
29360
29361  // Tell the select control that an option, with the given value, has been added
29362  self.addOption = function(value, element) {
29363    assertNotHasOwnProperty(value, '"option value"');
29364    if (value === '') {
29365      self.emptyOption = element;
29366    }
29367    var count = optionsMap.get(value) || 0;
29368    optionsMap.put(value, count + 1);
29369    self.ngModelCtrl.$render();
29370    chromeHack(element);
29371  };
29372
29373  // Tell the select control that an option, with the given value, has been removed
29374  self.removeOption = function(value) {
29375    var count = optionsMap.get(value);
29376    if (count) {
29377      if (count === 1) {
29378        optionsMap.remove(value);
29379        if (value === '') {
29380          self.emptyOption = undefined;
29381        }
29382      } else {
29383        optionsMap.put(value, count - 1);
29384      }
29385    }
29386  };
29387
29388  // Check whether the select control has an option matching the given value
29389  self.hasOption = function(value) {
29390    return !!optionsMap.get(value);
29391  };
29392
29393
29394  self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
29395
29396    if (interpolateValueFn) {
29397      // The value attribute is interpolated
29398      var oldVal;
29399      optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
29400        if (isDefined(oldVal)) {
29401          self.removeOption(oldVal);
29402        }
29403        oldVal = newVal;
29404        self.addOption(newVal, optionElement);
29405      });
29406    } else if (interpolateTextFn) {
29407      // The text content is interpolated
29408      optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
29409        optionAttrs.$set('value', newVal);
29410        if (oldVal !== newVal) {
29411          self.removeOption(oldVal);
29412        }
29413        self.addOption(newVal, optionElement);
29414      });
29415    } else {
29416      // The value attribute is static
29417      self.addOption(optionAttrs.value, optionElement);
29418    }
29419
29420    optionElement.on('$destroy', function() {
29421      self.removeOption(optionAttrs.value);
29422      self.ngModelCtrl.$render();
29423    });
29424  };
29425}];
29426
29427/**
29428 * @ngdoc directive
29429 * @name select
29430 * @restrict E
29431 *
29432 * @description
29433 * HTML `SELECT` element with angular data-binding.
29434 *
29435 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
29436 * between the scope and the `<select>` control (including setting default values).
29437 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
29438 * {@link ngOptions `ngOptions`} directives.
29439 *
29440 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
29441 * to the model identified by the `ngModel` directive. With static or repeated options, this is
29442 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
29443 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
29444 *
29445 * <div class="alert alert-warning">
29446 * Note that the value of a `select` directive used without `ngOptions` is always a string.
29447 * When the model needs to be bound to a non-string value, you must either explicitly convert it
29448 * using a directive (see example below) or use `ngOptions` to specify the set of options.
29449 * This is because an option element can only be bound to string values at present.
29450 * </div>
29451 *
29452 * If the viewValue of `ngModel` does not match any of the options, then the control
29453 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
29454 *
29455 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
29456 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
29457 * option. See example below for demonstration.
29458 *
29459 * <div class="alert alert-info">
29460 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
29461 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
29462 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
29463 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
29464 * a new scope for each repeated instance.
29465 * </div>
29466 *
29467 *
29468 * @param {string} ngModel Assignable angular expression to data-bind to.
29469 * @param {string=} name Property name of the form under which the control is published.
29470 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
29471 *     bound to the model as an array.
29472 * @param {string=} required Sets `required` validation error key if the value is not entered.
29473 * @param {string=} ngRequired Adds required attribute and required validation constraint to
29474 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
29475 * when you want to data-bind to the required attribute.
29476 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
29477 *    interaction with the select element.
29478 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
29479 * set on the model on selection. See {@link ngOptions `ngOptions`}.
29480 *
29481 * @example
29482 * ### Simple `select` elements with static options
29483 *
29484 * <example name="static-select" module="staticSelect">
29485 * <file name="index.html">
29486 * <div ng-controller="ExampleController">
29487 *   <form name="myForm">
29488 *     <label for="singleSelect"> Single select: </label><br>
29489 *     <select name="singleSelect" ng-model="data.singleSelect">
29490 *       <option value="option-1">Option 1</option>
29491 *       <option value="option-2">Option 2</option>
29492 *     </select><br>
29493 *
29494 *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
29495 *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
29496 *       <option value="">---Please select---</option> <!-- not selected / blank option -->
29497 *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
29498 *       <option value="option-2">Option 2</option>
29499 *     </select><br>
29500 *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
29501 *     <tt>singleSelect = {{data.singleSelect}}</tt>
29502 *
29503 *     <hr>
29504 *     <label for="multipleSelect"> Multiple select: </label><br>
29505 *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
29506 *       <option value="option-1">Option 1</option>
29507 *       <option value="option-2">Option 2</option>
29508 *       <option value="option-3">Option 3</option>
29509 *     </select><br>
29510 *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
29511 *   </form>
29512 * </div>
29513 * </file>
29514 * <file name="app.js">
29515 *  angular.module('staticSelect', [])
29516 *    .controller('ExampleController', ['$scope', function($scope) {
29517 *      $scope.data = {
29518 *       singleSelect: null,
29519 *       multipleSelect: [],
29520 *       option1: 'option-1',
29521 *      };
29522 *
29523 *      $scope.forceUnknownOption = function() {
29524 *        $scope.data.singleSelect = 'nonsense';
29525 *      };
29526 *   }]);
29527 * </file>
29528 *</example>
29529 *
29530 * ### Using `ngRepeat` to generate `select` options
29531 * <example name="ngrepeat-select" module="ngrepeatSelect">
29532 * <file name="index.html">
29533 * <div ng-controller="ExampleController">
29534 *   <form name="myForm">
29535 *     <label for="repeatSelect"> Repeat select: </label>
29536 *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
29537 *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
29538 *     </select>
29539 *   </form>
29540 *   <hr>
29541 *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
29542 * </div>
29543 * </file>
29544 * <file name="app.js">
29545 *  angular.module('ngrepeatSelect', [])
29546 *    .controller('ExampleController', ['$scope', function($scope) {
29547 *      $scope.data = {
29548 *       repeatSelect: null,
29549 *       availableOptions: [
29550 *         {id: '1', name: 'Option A'},
29551 *         {id: '2', name: 'Option B'},
29552 *         {id: '3', name: 'Option C'}
29553 *       ],
29554 *      };
29555 *   }]);
29556 * </file>
29557 *</example>
29558 *
29559 *
29560 * ### Using `select` with `ngOptions` and setting a default value
29561 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
29562 *
29563 * <example name="select-with-default-values" module="defaultValueSelect">
29564 * <file name="index.html">
29565 * <div ng-controller="ExampleController">
29566 *   <form name="myForm">
29567 *     <label for="mySelect">Make a choice:</label>
29568 *     <select name="mySelect" id="mySelect"
29569 *       ng-options="option.name for option in data.availableOptions track by option.id"
29570 *       ng-model="data.selectedOption"></select>
29571 *   </form>
29572 *   <hr>
29573 *   <tt>option = {{data.selectedOption}}</tt><br/>
29574 * </div>
29575 * </file>
29576 * <file name="app.js">
29577 *  angular.module('defaultValueSelect', [])
29578 *    .controller('ExampleController', ['$scope', function($scope) {
29579 *      $scope.data = {
29580 *       availableOptions: [
29581 *         {id: '1', name: 'Option A'},
29582 *         {id: '2', name: 'Option B'},
29583 *         {id: '3', name: 'Option C'}
29584 *       ],
29585 *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
29586 *       };
29587 *   }]);
29588 * </file>
29589 *</example>
29590 *
29591 *
29592 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
29593 *
29594 * <example name="select-with-non-string-options" module="nonStringSelect">
29595 *   <file name="index.html">
29596 *     <select ng-model="model.id" convert-to-number>
29597 *       <option value="0">Zero</option>
29598 *       <option value="1">One</option>
29599 *       <option value="2">Two</option>
29600 *     </select>
29601 *     {{ model }}
29602 *   </file>
29603 *   <file name="app.js">
29604 *     angular.module('nonStringSelect', [])
29605 *       .run(function($rootScope) {
29606 *         $rootScope.model = { id: 2 };
29607 *       })
29608 *       .directive('convertToNumber', function() {
29609 *         return {
29610 *           require: 'ngModel',
29611 *           link: function(scope, element, attrs, ngModel) {
29612 *             ngModel.$parsers.push(function(val) {
29613 *               return parseInt(val, 10);
29614 *             });
29615 *             ngModel.$formatters.push(function(val) {
29616 *               return '' + val;
29617 *             });
29618 *           }
29619 *         };
29620 *       });
29621 *   </file>
29622 *   <file name="protractor.js" type="protractor">
29623 *     it('should initialize to model', function() {
29624 *       var select = element(by.css('select'));
29625 *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
29626 *     });
29627 *   </file>
29628 * </example>
29629 *
29630 */
29631var selectDirective = function() {
29632
29633  return {
29634    restrict: 'E',
29635    require: ['select', '?ngModel'],
29636    controller: SelectController,
29637    priority: 1,
29638    link: {
29639      pre: selectPreLink,
29640      post: selectPostLink
29641    }
29642  };
29643
29644  function selectPreLink(scope, element, attr, ctrls) {
29645
29646      // if ngModel is not defined, we don't need to do anything
29647      var ngModelCtrl = ctrls[1];
29648      if (!ngModelCtrl) return;
29649
29650      var selectCtrl = ctrls[0];
29651
29652      selectCtrl.ngModelCtrl = ngModelCtrl;
29653
29654      // When the selected item(s) changes we delegate getting the value of the select control
29655      // to the `readValue` method, which can be changed if the select can have multiple
29656      // selected values or if the options are being generated by `ngOptions`
29657      element.on('change', function() {
29658        scope.$apply(function() {
29659          ngModelCtrl.$setViewValue(selectCtrl.readValue());
29660        });
29661      });
29662
29663      // If the select allows multiple values then we need to modify how we read and write
29664      // values from and to the control; also what it means for the value to be empty and
29665      // we have to add an extra watch since ngModel doesn't work well with arrays - it
29666      // doesn't trigger rendering if only an item in the array changes.
29667      if (attr.multiple) {
29668
29669        // Read value now needs to check each option to see if it is selected
29670        selectCtrl.readValue = function readMultipleValue() {
29671          var array = [];
29672          forEach(element.find('option'), function(option) {
29673            if (option.selected) {
29674              array.push(option.value);
29675            }
29676          });
29677          return array;
29678        };
29679
29680        // Write value now needs to set the selected property of each matching option
29681        selectCtrl.writeValue = function writeMultipleValue(value) {
29682          var items = new HashMap(value);
vendor: 4,286 bytes, lines 29683-29788
29683          forEach(element.find('option'), function(option) {
29684            option.selected = isDefined(items.get(option.value));
29685          });
29686        };
29687
29688        // we have to do it on each watch since ngModel watches reference, but
29689        // we need to work of an array, so we need to see if anything was inserted/removed
29690        var lastView, lastViewRef = NaN;
29691        scope.$watch(function selectMultipleWatch() {
29692          if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
29693            lastView = shallowCopy(ngModelCtrl.$viewValue);
29694            ngModelCtrl.$render();
29695          }
29696          lastViewRef = ngModelCtrl.$viewValue;
29697        });
29698
29699        // If we are a multiple select then value is now a collection
29700        // so the meaning of $isEmpty changes
29701        ngModelCtrl.$isEmpty = function(value) {
29702          return !value || value.length === 0;
29703        };
29704
29705      }
29706    }
29707
29708    function selectPostLink(scope, element, attrs, ctrls) {
29709      // if ngModel is not defined, we don't need to do anything
29710      var ngModelCtrl = ctrls[1];
29711      if (!ngModelCtrl) return;
29712
29713      var selectCtrl = ctrls[0];
29714
29715      // We delegate rendering to the `writeValue` method, which can be changed
29716      // if the select can have multiple selected values or if the options are being
29717      // generated by `ngOptions`.
29718      // This must be done in the postLink fn to prevent $render to be called before
29719      // all nodes have been linked correctly.
29720      ngModelCtrl.$render = function() {
29721        selectCtrl.writeValue(ngModelCtrl.$viewValue);
29722      };
29723    }
29724};
29725
29726
29727// The option directive is purely designed to communicate the existence (or lack of)
29728// of dynamically created (and destroyed) option elements to their containing select
29729// directive via its controller.
29730var optionDirective = ['$interpolate', function($interpolate) {
29731  return {
29732    restrict: 'E',
29733    priority: 100,
29734    compile: function(element, attr) {
29735
29736      if (isDefined(attr.value)) {
29737        // If the value attribute is defined, check if it contains an interpolation
29738        var interpolateValueFn = $interpolate(attr.value, true);
29739      } else {
29740        // If the value attribute is not defined then we fall back to the
29741        // text content of the option element, which may be interpolated
29742        var interpolateTextFn = $interpolate(element.text(), true);
29743        if (!interpolateTextFn) {
29744          attr.$set('value', element.text());
29745        }
29746      }
29747
29748      return function(scope, element, attr) {
29749
29750        // This is an optimization over using ^^ since we don't want to have to search
29751        // all the way to the root of the DOM for every single option element
29752        var selectCtrlName = '$selectController',
29753            parent = element.parent(),
29754            selectCtrl = parent.data(selectCtrlName) ||
29755              parent.parent().data(selectCtrlName); // in case we are in optgroup
29756
29757        if (selectCtrl) {
29758          selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
29759        }
29760      };
29761    }
29762  };
29763}];
29764
29765var styleDirective = valueFn({
29766  restrict: 'E',
29767  terminal: false
29768});
29769
29770/**
29771 * @ngdoc directive
29772 * @name ngRequired
29773 *
29774 * @description
29775 *
29776 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29777 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
29778 * applied to custom controls.
29779 *
29780 * The directive sets the `required` attribute on the element if the Angular expression inside
29781 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
29782 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
29783 * for more info.
29784 *
29785 * The validator will set the `required` error key to true if the `required` attribute is set and
29786 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty` with the
29787 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
29788 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
29789 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
29790 *
29791 * @example
29792 * <example name="ngRequiredDirective" module="ngRequiredExample">
29793 *   <file name="index.html">
29794 *     <script>
29795 *       angular.module('ngRequiredExample', [])
29796 *         .controller('ExampleController', ['$scope', function($scope) {
29797 *           $scope.required = true;
29798 *         }]);
29799 *     </script>
29800 *     <div ng-controller="ExampleController">
29801 *       <form name="form">
29802 *         <label for="required">Toggle required: </label>
29803 *         <input type="checkbox" ng-model="required" id="required" />
29804 *         <br>
29805 *         <label for="input">This input must be filled if `required` is true: </label>
29806 *         <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
29807 *         <hr>
29808 *         required error set? = <code>{{form.input.$error.required}}</code><br>
29809 *         model = <code>{{model}}</code>
29810 *       </form>
29811 *     </div>
29812 *   </file>
29813 *   <file name="protractor.js" type="protractor">
29814       var required = element(by.binding('form.input.$error.required'));
29815       var model = element(by.binding('model'));
29816       var input = element(by.id('input'));
29817
29818       it('should set the required error', function() {
29819         expect(required.getText()).toContain('true');
29820
29821         input.sendKeys('123');
29822         expect(required.getText()).not.toContain('true');
29823         expect(model.getText()).toContain('123');
29824       });
29825 *   </file>
29826 * </example>
29827 */
29828var requiredDirective = function() {
29829  return {
29830    restrict: 'A',
29831    require: '?ngModel',
29832    link: function(scope, elm, attr, ctrl) {
29833      if (!ctrl) return;
29834      attr.required = true; // force truthy in case we are on non input element
29835
29836      ctrl.$validators.required = function(modelValue, viewValue) {
29837        return !attr.required || !ctrl.$isEmpty(viewValue);
29838      };
29839
29840      attr.$observe('required', function() {
29841        ctrl.$validate();
29842      });
29843    }
29844  };
29845};
29846
29847/**
29848 * @ngdoc directive
29849 * @name ngPattern
29850 *
29851 * @description
29852 *
29853 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29854 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
29855 *
29856 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
29857 * does not match a RegExp which is obtained by evaluating the Angular expression given in the
29858 * `ngPattern` attribute value:
29859 * * If the expression evaluates to a RegExp object, then this is used directly.
29860 * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
29861 * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
29862 *
29863 * <div class="alert alert-info">
29864 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
29865 * start at the index of the last search's match, thus not taking the whole input value into
29866 * account.
29867 * </div>
29868 *
29869 * <div class="alert alert-info">
29870 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
29871 * differences:
29872 * <ol>
29873 *   <li>
29874 *     `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
29875 *     not available.
29876 *   </li>
29877 *   <li>
29878 *     The `ngPattern` attribute must be an expression, while the `pattern` value must be
29879 *     interpolated.
29880 *   </li>
29881 * </ol>
29882 * </div>
29883 *
29884 * @example
29885 * <example name="ngPatternDirective" module="ngPatternExample">
29886 *   <file name="index.html">
29887 *     <script>
29888 *       angular.module('ngPatternExample', [])
29889 *         .controller('ExampleController', ['$scope', function($scope) {
29890 *           $scope.regex = '\\d+';
29891 *         }]);
29892 *     </script>
29893 *     <div ng-controller="ExampleController">
29894 *       <form name="form">
29895 *         <label for="regex">Set a pattern (regex string): </label>
29896 *         <input type="text" ng-model="regex" id="regex" />
29897 *         <br>
29898 *         <label for="input">This input is restricted by the current pattern: </label>
29899 *         <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
29900 *         <hr>
29901 *         input valid? = <code>{{form.input.$valid}}</code><br>
29902 *         model = <code>{{model}}</code>
29903 *       </form>
29904 *     </div>
29905 *   </file>
29906 *   <file name="protractor.js" type="protractor">
29907       var model = element(by.binding('model'));
29908       var input = element(by.id('input'));
29909
29910       it('should validate the input with the default pattern', function() {
29911         input.sendKeys('aaa');
29912         expect(model.getText()).not.toContain('aaa');
29913
29914         input.clear().then(function() {
29915           input.sendKeys('123');
29916           expect(model.getText()).toContain('123');
29917         });
29918       });
29919 *   </file>
29920 * </example>
29921 */
29922var patternDirective = function() {
29923  return {
29924    restrict: 'A',
29925    require: '?ngModel',
29926    link: function(scope, elm, attr, ctrl) {
29927      if (!ctrl) return;
29928
29929      var regexp, patternExp = attr.ngPattern || attr.pattern;
29930      attr.$observe('pattern', function(regex) {
29931        if (isString(regex) && regex.length > 0) {
29932          regex = new RegExp('^' + regex + '$');
29933        }
29934
29935        if (regex && !regex.test) {
29936          throw minErr('ngPattern')('noregexp',
29937            'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
29938            regex, startingTag(elm));
29939        }
29940
29941        regexp = regex || undefined;
29942        ctrl.$validate();
29943      });
29944
29945      ctrl.$validators.pattern = function(modelValue, viewValue) {
29946        // HTML5 pattern constraint validates the input value, so we validate the viewValue
29947        return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
29948      };
29949    }
29950  };
29951};
29952
29953/**
29954 * @ngdoc directive
29955 * @name ngMaxlength
29956 *
29957 * @description
29958 *
29959 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29960 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
29961 *
29962 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
29963 * is longer than the integer obtained by evaluating the Angular expression given in the
29964 * `ngMaxlength` attribute value.
29965 *
29966 * <div class="alert alert-info">
29967 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
29968 * differences:
29969 * <ol>
29970 *   <li>
29971 *     `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
29972 *     validation is not available.
29973 *   </li>
29974 *   <li>
29975 *     The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
29976 *     interpolated.
29977 *   </li>
29978 * </ol>
29979 * </div>
29980 *
29981 * @example
29982 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
29983 *   <file name="index.html">
29984 *     <script>
29985 *       angular.module('ngMaxlengthExample', [])
29986 *         .controller('ExampleController', ['$scope', function($scope) {
29987 *           $scope.maxlength = 5;
29988 *         }]);
29989 *     </script>
29990 *     <div ng-controller="ExampleController">
29991 *       <form name="form">
29992 *         <label for="maxlength">Set a maxlength: </label>
29993 *         <input type="number" ng-model="maxlength" id="maxlength" />
29994 *         <br>
29995 *         <label for="input">This input is restricted by the current maxlength: </label>
29996 *         <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
29997 *         <hr>
29998 *         input valid? = <code>{{form.input.$valid}}</code><br>
29999 *         model = <code>{{model}}</code>
30000 *       </form>
30001 *     </div>
30002 *   </file>
30003 *   <file name="protractor.js" type="protractor">
30004       var model = element(by.binding('model'));
30005       var input = element(by.id('input'));
30006
30007       it('should validate the input with the default maxlength', function() {
30008         input.sendKeys('abcdef');
30009         expect(model.getText()).not.toContain('abcdef');
30010
30011         input.clear().then(function() {
30012           input.sendKeys('abcde');
30013           expect(model.getText()).toContain('abcde');
30014         });
30015       });
30016 *   </file>
30017 * </example>
30018 */
30019var maxlengthDirective = function() {
30020  return {
30021    restrict: 'A',
30022    require: '?ngModel',
30023    link: function(scope, elm, attr, ctrl) {
30024      if (!ctrl) return;
30025
30026      var maxlength = -1;
30027      attr.$observe('maxlength', function(value) {
30028        var intVal = toInt(value);
30029        maxlength = isNaN(intVal) ? -1 : intVal;
30030        ctrl.$validate();
30031      });
30032      ctrl.$validators.maxlength = function(modelValue, viewValue) {
30033        return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
30034      };
30035    }
30036  };
30037};
30038
30039/**
30040 * @ngdoc directive
30041 * @name ngMinlength
30042 *
30043 * @description
30044 *
30045 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
30046 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
30047 *
30048 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
30049 * is shorter than the integer obtained by evaluating the Angular expression given in the
30050 * `ngMinlength` attribute value.
30051 *
30052 * <div class="alert alert-info">
30053 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
30054 * differences:
30055 * <ol>
30056 *   <li>
30057 *     `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
30058 *     validation is not available.
30059 *   </li>
30060 *   <li>
30061 *     The `ngMinlength` value must be an expression, while the `minlength` value must be
30062 *     interpolated.
30063 *   </li>
30064 * </ol>
30065 * </div>
30066 *
30067 * @example
30068 * <example name="ngMinlengthDirective" module="ngMinlengthExample">
30069 *   <file name="index.html">
30070 *     <script>
30071 *       angular.module('ngMinlengthExample', [])
30072 *         .controller('ExampleController', ['$scope', function($scope) {
30073 *           $scope.minlength = 3;
30074 *         }]);
30075 *     </script>
30076 *     <div ng-controller="ExampleController">
30077 *       <form name="form">
30078 *         <label for="minlength">Set a minlength: </label>
30079 *         <input type="number" ng-model="minlength" id="minlength" />
30080 *         <br>
30081 *         <label for="input">This input is restricted by the current minlength: </label>
30082 *         <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
30083 *         <hr>
30084 *         input valid? = <code>{{form.input.$valid}}</code><br>
30085 *         model = <code>{{model}}</code>
30086 *       </form>
30087 *     </div>
30088 *   </file>
30089 *   <file name="protractor.js" type="protractor">
30090       var model = element(by.binding('model'));
30091       var input = element(by.id('input'));
30092
30093       it('should validate the input with the default minlength', function() {
30094         input.sendKeys('ab');
30095         expect(model.getText()).not.toContain('ab');
30096
30097         input.sendKeys('abc');
30098         expect(model.getText()).toContain('abc');
30099       });
30100 *   </file>
30101 * </example>
30102 */
30103var minlengthDirective = function() {
30104  return {
30105    restrict: 'A',
30106    require: '?ngModel',
30107    link: function(scope, elm, attr, ctrl) {
30108      if (!ctrl) return;
30109
30110      var minlength = 0;
30111      attr.$observe('minlength', function(value) {
30112        minlength = toInt(value) || 0;
30113        ctrl.$validate();
30114      });
30115      ctrl.$validators.minlength = function(modelValue, viewValue) {
30116        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
30117      };
30118    }
30119  };
30120};
30121
30122if (window.angular.bootstrap) {
30123  //AngularJS is already loaded, so we can return here...
30124  console.log('WARNING: Tried to load angular more than once.');
30125  return;
30126}
30127
30128//try to bind to jquery now so that one can write jqLite(document).ready()
30129//but we will rebind on bootstrap again.
30130bindJQuery();
30131
30132publishExternalAPI(angular);
30133
30134angular.module("ngLocale", [], ["$provide", function($provide) {
30135var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
30136function getDecimals(n) {
30137  n = n + '';
30138  var i = n.indexOf('.');
30139  return (i == -1) ? 0 : n.length - i - 1;
30140}
30141
30142function getVF(n, opt_precision) {
30143  var v = opt_precision;
30144
30145  if (undefined === v) {
30146    v = Math.min(getDecimals(n), 3);
30147  }
30148
30149  var base = Math.pow(10, v);
30150  var f = ((n * base) | 0) % base;
30151  return {v: v, f: f};
30152}
30153
30154$provide.value("$locale", {
30155  "DATETIME_FORMATS": {
30156    "AMPMS": [
30157      "AM",
30158      "PM"
30159    ],
30160    "DAY": [
30161      "Sunday",
30162      "Monday",
30163      "Tuesday",
30164      "Wednesday",
30165      "Thursday",
30166      "Friday",
30167      "Saturday"
30168    ],
30169    "ERANAMES": [
30170      "Before Christ",
30171      "Anno Domini"
30172    ],
30173    "ERAS": [
30174      "BC",
30175      "AD"
30176    ],
30177    "FIRSTDAYOFWEEK": 6,
30178    "MONTH": [
30179      "January",
30180      "February",
30181      "March",
30182      "April",
30183      "May",
30184      "June",
30185      "July",
30186      "August",
30187      "September",
30188      "October",
30189      "November",
30190      "December"
30191    ],
30192    "SHORTDAY": [
30193      "Sun",
30194      "Mon",
30195      "Tue",
30196      "Wed",
30197      "Thu",
30198      "Fri",
30199      "Sat"
30200    ],
30201    "SHORTMONTH": [
30202      "Jan",
30203      "Feb",
30204      "Mar",
30205      "Apr",
30206      "May",
30207      "Jun",
30208      "Jul",
30209      "Aug",
30210      "Sep",
30211      "Oct",
30212      "Nov",
30213      "Dec"
30214    ],
30215    "STANDALONEMONTH": [
30216      "January",
30217      "February",
30218      "March",
30219      "April",
30220      "May",
30221      "June",
30222      "July",
30223      "August",
30224      "September",
30225      "October",
30226      "November",
30227      "December"
30228    ],
30229    "WEEKENDRANGE": [
30230      5,
30231      6
30232    ],
30233    "fullDate": "EEEE, MMMM d, y",
30234    "longDate": "MMMM d, y",
30235    "medium": "MMM d, y h:mm:ss a",
30236    "mediumDate": "MMM d, y",
30237    "mediumTime": "h:mm:ss a",
30238    "short": "M/d/yy h:mm a",
30239    "shortDate": "M/d/yy",
30240    "shortTime": "h:mm a"
30241  },
30242  "NUMBER_FORMATS": {
30243    "CURRENCY_SYM": "$",
30244    "DECIMAL_SEP": ".",
30245    "GROUP_SEP": ",",
30246    "PATTERNS": [
30247      {
30248        "gSize": 3,
30249        "lgSize": 3,
30250        "maxFrac": 3,
30251        "minFrac": 0,
30252        "minInt": 1,
30253        "negPre": "-",
30254        "negSuf": "",
30255        "posPre": "",
30256        "posSuf": ""
30257      },
30258      {
30259        "gSize": 3,
30260        "lgSize": 3,
30261        "maxFrac": 2,
30262        "minFrac": 2,
30263        "minInt": 1,
30264        "negPre": "-\u00a4",
30265        "negSuf": "",
30266        "posPre": "\u00a4",
30267        "posSuf": ""
30268      }
30269    ]
30270  },
30271  "id": "en-us",
30272  "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;}
30273});
30274}]);
30275
30276  jqLite(document).ready(function() {
30277    angularInit(document, bootstrap);
30278  });
30279
30280})(window, document);
30281
30282!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>');

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