PageSourceSearch

https://tasteofcity.com/Area/UserProfile/Scripts/AngularJS/angular.js

js tasteofcity.com collected 2026-10-03 17:35:58 UTC 1,094,040 bytes, 28,904 lines download raw bytes

1/**
2 * @license AngularJS v1.4.7
3 * (c) 2010-2015 Google, Inc. http://angularjs.org
4 * License: MIT
5 */
6(function(window, document, undefined) {'use strict';
7
8/**
9 * @description
10 *
11 * This object provides a utility for producing rich Error messages within
12 * Angular. It can be called as follows:
13 *
14 * var exampleMinErr = minErr('example');
15 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
16 *
17 * The above creates an instance of minErr in the example namespace. The
18 * resulting error will have a namespaced error code of example.one.  The
19 * resulting error will replace {0} with the value of foo, and {1} with the
20 * value of bar. The object is not restricted in the number of arguments it can
21 * take.
22 *
23 * If fewer arguments are specified than necessary for interpolation, the extra
24 * interpolation markers will be preserved in the final string.
25 *
26 * Since data will be parsed statically during a build step, some restrictions
27 * are applied with respect to how minErr instances are created and called.
28 * Instances should have names of the form namespaceMinErr for a minErr created
29 * using minErr('namespace') . Error codes, namespaces and template strings
30 * should all be static strings, not variables or general expressions.
31 *
32 * @param {string} module The namespace to use for the new minErr instance.
33 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
34 *   error from returned function, for cases when a particular type of error is useful.
35 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
36 */
37
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.4.7/' +
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  if (obj == null || isWindow(obj)) {
271    return false;
272  }
273
274  // Support: iOS 8.2 (not reproducible in simulator)
275  // "length" in obj used to prevent JIT error (gh-11508)
276  var length = "length" in Object(obj) && obj.length;
277
278  if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
279    return true;
280  }
281
282  return isString(obj) || isArray(obj) || length === 0 ||
283         typeof length === 'number' && length > 0 && (length - 1) in obj;
284}
285
286/**
287 * @ngdoc function
288 * @name angular.forEach
289 * @module ng
290 * @kind function
291 *
292 * @description
293 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
294 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
295 * is the value of an object property or an array element, `key` is the object property key or
296 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
297 *
298 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
299 * using the `hasOwnProperty` method.
300 *
301 * Unlike ES262's
302 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
303 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
304 * return the value provided.
305 *
306   ```js
307     var values = {name: 'misko', gender: 'male'};
308     var log = [];
309     angular.forEach(values, function(value, key) {
310       this.push(key + ': ' + value);
311     }, log);
312     expect(log).toEqual(['name: misko', 'gender: male']);
313   ```
314 *
315 * @param {Object|Array} obj Object to iterate over.
316 * @param {Function} iterator Iterator function.
317 * @param {Object=} context Object to become context (`this`) for the iterator function.
318 * @returns {Object|Array} Reference to `obj`.
319 */
320
321function forEach(obj, iterator, context) {
322  var key, length;
323  if (obj) {
324    if (isFunction(obj)) {
325      for (key in obj) {
326        // Need to check if hasOwnProperty exists,
327        // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
328        if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
329          iterator.call(context, obj[key], key, obj);
330        }
331      }
332    } else if (isArray(obj) || isArrayLike(obj)) {
333      var isPrimitive = typeof obj !== 'object';
334      for (key = 0, length = obj.length; key < length; key++) {
335        if (isPrimitive || key in obj) {
336          iterator.call(context, obj[key], key, obj);
337        }
338      }
339    } else if (obj.forEach && obj.forEach !== forEach) {
340        obj.forEach(iterator, context, obj);
341    } else if (isBlankObject(obj)) {
342      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
343      for (key in obj) {
344        iterator.call(context, obj[key], key, obj);
345      }
346    } else if (typeof obj.hasOwnProperty === 'function') {
347      // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
348      for (key in obj) {
349        if (obj.hasOwnProperty(key)) {
350          iterator.call(context, obj[key], key, obj);
351        }
352      }
353    } else {
354      // Slow path for objects which do not have a method `hasOwnProperty`
355      for (key in obj) {
356        if (hasOwnProperty.call(obj, key)) {
357          iterator.call(context, obj[key], key, obj);
358        }
359      }
360    }
361  }
362  return obj;
363}
364
365function forEachSorted(obj, iterator, context) {
366  var keys = Object.keys(obj).sort();
367  for (var i = 0; i < keys.length; i++) {
368    iterator.call(context, obj[keys[i]], keys[i]);
369  }
370  return keys;
371}
372
373
374/**
375 * when using forEach the params are value, key, but it is often useful to have key, value.
376 * @param {function(string, *)} iteratorFn
377 * @returns {function(*, string)}
378 */
379function reverseParams(iteratorFn) {
380  return function(value, key) { iteratorFn(key, value); };
381}
382
383/**
384 * A consistent way of creating unique IDs in angular.
385 *
386 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
387 * we hit number precision issues in JavaScript.
388 *
389 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
390 *
391 * @returns {number}
vendor: 8,821 bytes, lines 391-758
391 an unique alpha-numeric string
392 */
393function nextUid() {
394  return ++uid;
395}
396
397
398/**
399 * Set or clear the hashkey for an object.
400 * @param obj object
401 * @param h the hashkey (!truthy to delete the hashkey)
402 */
403function setHashKey(obj, h) {
404  if (h) {
405    obj.$$hashKey = h;
406  } else {
407    delete obj.$$hashKey;
408  }
409}
410
411
412function baseExtend(dst, objs, deep) {
413  var h = dst.$$hashKey;
414
415  for (var i = 0, ii = objs.length; i < ii; ++i) {
416    var obj = objs[i];
417    if (!isObject(obj) && !isFunction(obj)) continue;
418    var keys = Object.keys(obj);
419    for (var j = 0, jj = keys.length; j < jj; j++) {
420      var key = keys[j];
421      var src = obj[key];
422
423      if (deep && isObject(src)) {
424        if (isDate(src)) {
425          dst[key] = new Date(src.valueOf());
426        } else if (isRegExp(src)) {
427          dst[key] = new RegExp(src);
428        } else {
429          if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
430          baseExtend(dst[key], [src], true);
431        }
432      } else {
433        dst[key] = src;
434      }
435    }
436  }
437
438  setHashKey(dst, h);
439  return dst;
440}
441
442/**
443 * @ngdoc function
444 * @name angular.extend
445 * @module ng
446 * @kind function
447 *
448 * @description
449 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
450 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
451 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
452 *
453 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
454 * {@link angular.merge} for this.
455 *
456 * @param {Object} dst Destination object.
457 * @param {...Object} src Source object(s).
458 * @returns {Object} Reference to `dst`.
459 */
460function extend(dst) {
461  return baseExtend(dst, slice.call(arguments, 1), false);
462}
463
464
465/**
466* @ngdoc function
467* @name angular.merge
468* @module ng
469* @kind function
470*
471* @description
472* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
473* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
474* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
475*
476* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
477* objects, performing a deep copy.
478*
479* @param {Object} dst Destination object.
480* @param {...Object} src Source object(s).
481* @returns {Object} Reference to `dst`.
482*/
483function merge(dst) {
484  return baseExtend(dst, slice.call(arguments, 1), true);
485}
486
487
488
489function toInt(str) {
490  return parseInt(str, 10);
491}
492
493
494function inherit(parent, extra) {
495  return extend(Object.create(parent), extra);
496}
497
498/**
499 * @ngdoc function
500 * @name angular.noop
501 * @module ng
502 * @kind function
503 *
504 * @description
505 * A function that performs no operations. This function can be useful when writing code in the
506 * functional style.
507   ```js
508     function foo(callback) {
509       var result = calculateResult();
510       (callback || angular.noop)(result);
511     }
512   ```
513 */
514function noop() {}
515noop.$inject = [];
516
517
518/**
519 * @ngdoc function
520 * @name angular.identity
521 * @module ng
522 * @kind function
523 *
524 * @description
525 * A function that returns its first argument. This function is useful when writing code in the
526 * functional style.
527 *
528   ```js
529     function transformer(transformationFn, value) {
530       return (transformationFn || angular.identity)(value);
531     };
532   ```
533  * @param {*} value to be returned.
534  * @returns {*} the value passed in.
535 */
536function identity($) {return $;}
537identity.$inject = [];
538
539
540function valueFn(value) {return function() {return value;};}
541
542function hasCustomToString(obj) {
543  return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
544}
545
546
547/**
548 * @ngdoc function
549 * @name angular.isUndefined
550 * @module ng
551 * @kind function
552 *
553 * @description
554 * Determines if a reference is undefined.
555 *
556 * @param {*} value Reference to check.
557 * @returns {boolean} True if `value` is undefined.
558 */
559function isUndefined(value) {return typeof value === 'undefined';}
560
561
562/**
563 * @ngdoc function
564 * @name angular.isDefined
565 * @module ng
566 * @kind function
567 *
568 * @description
569 * Determines if a reference is defined.
570 *
571 * @param {*} value Reference to check.
572 * @returns {boolean} True if `value` is defined.
573 */
574function isDefined(value) {return typeof value !== 'undefined';}
575
576
577/**
578 * @ngdoc function
579 * @name angular.isObject
580 * @module ng
581 * @kind function
582 *
583 * @description
584 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
585 * considered to be objects. Note that JavaScript arrays are objects.
586 *
587 * @param {*} value Reference to check.
588 * @returns {boolean} True if `value` is an `Object` but not `null`.
589 */
590function isObject(value) {
591  // http://jsperf.com/isobject4
592  return value !== null && typeof value === 'object';
593}
594
595
596/**
597 * Determine if a value is an object with a null prototype
598 *
599 * @returns {boolean} True if `value` is an `Object` with a null prototype
600 */
601function isBlankObject(value) {
602  return value !== null && typeof value === 'object' && !getPrototypeOf(value);
603}
604
605
606/**
607 * @ngdoc function
608 * @name angular.isString
609 * @module ng
610 * @kind function
611 *
612 * @description
613 * Determines if a reference is a `String`.
614 *
615 * @param {*} value Reference to check.
616 * @returns {boolean} True if `value` is a `String`.
617 */
618function isString(value) {return typeof value === 'string';}
619
620
621/**
622 * @ngdoc function
623 * @name angular.isNumber
624 * @module ng
625 * @kind function
626 *
627 * @description
628 * Determines if a reference is a `Number`.
629 *
630 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
631 *
632 * If you wish to exclude these then you can use the native
633 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
634 * method.
635 *
636 * @param {*} value Reference to check.
637 * @returns {boolean} True if `value` is a `Number`.
638 */
639function isNumber(value) {return typeof value === 'number';}
640
641
642/**
643 * @ngdoc function
644 * @name angular.isDate
645 * @module ng
646 * @kind function
647 *
648 * @description
649 * Determines if a value is a date.
650 *
651 * @param {*} value Reference to check.
652 * @returns {boolean} True if `value` is a `Date`.
653 */
654function isDate(value) {
655  return toString.call(value) === '[object Date]';
656}
657
658
659/**
660 * @ngdoc function
661 * @name angular.isArray
662 * @module ng
663 * @kind function
664 *
665 * @description
666 * Determines if a reference is an `Array`.
667 *
668 * @param {*} value Reference to check.
669 * @returns {boolean} True if `value` is an `Array`.
670 */
671var isArray = Array.isArray;
672
673/**
674 * @ngdoc function
675 * @name angular.isFunction
676 * @module ng
677 * @kind function
678 *
679 * @description
680 * Determines if a reference is a `Function`.
681 *
682 * @param {*} value Reference to check.
683 * @returns {boolean} True if `value` is a `Function`.
684 */
685function isFunction(value) {return typeof value === 'function';}
686
687
688/**
689 * Determines if a value is a regular expression object.
690 *
691 * @private
692 * @param {*} value Reference to check.
693 * @returns {boolean} True if `value` is a `RegExp`.
694 */
695function isRegExp(value) {
696  return toString.call(value) === '[object RegExp]';
697}
698
699
700/**
701 * Checks if `obj` is a window object.
702 *
703 * @private
704 * @param {*} obj Object to check
705 * @returns {boolean} True if `obj` is a window obj.
706 */
707function isWindow(obj) {
708  return obj && obj.window === obj;
709}
710
711
712function isScope(obj) {
713  return obj && obj.$evalAsync && obj.$watch;
714}
715
716
717function isFile(obj) {
718  return toString.call(obj) === '[object File]';
719}
720
721
722function isFormData(obj) {
723  return toString.call(obj) === '[object FormData]';
724}
725
726
727function isBlob(obj) {
728  return toString.call(obj) === '[object Blob]';
729}
730
731
732function isBoolean(value) {
733  return typeof value === 'boolean';
734}
735
736
737function isPromiseLike(obj) {
738  return obj && isFunction(obj.then);
739}
740
741
742var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/;
743function isTypedArray(value) {
744  return TYPED_ARRAY_REGEXP.test(toString.call(value));
745}
746
747
748var trim = function(value) {
749  return isString(value) ? value.trim() : value;
750};
751
752// Copied from:
753// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
754// Prereq: s is a string.
755var escapeForRegexp = function(s) {
756  return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
757           replace(/\x08/g, '\\x08');
758};
759
760
761/**
762 * @ngdoc function
763 * @name angular.isElement
764 * @module ng
765 * @kind function
766 *
767 * @description
768 * Determines if a reference is a DOM element (or wrapped jQuery element).
769 *
770 * @param {*} value Reference to check.
771 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
772 */
773function isElement(node) {
774  return !!(node &&
775    (node.nodeName  // we are a direct element
776    || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
777}
778
779/**
780 * @param str 'key1,key2,...'
781 * @returns {object} in the form of {key1:true, key2:true, ...}
782 */
783function makeMap(str) {
784  var obj = {}, items = str.split(","), i;
785  for (i = 0; i < items.length; i++) {
786    obj[items[i]] = true;
787  }
788  return obj;
789}
790
791
792function nodeName_(element) {
793  return lowercase(element.nodeName || (element[0] && element[0].nodeName));
794}
795
796function includes(array, obj) {
797  return Array.prototype.indexOf.call(array, obj) != -1;
798}
799
800function arrayRemove(array, value) {
801  var index = array.indexOf(value);
802  if (index >= 0) {
803    array.splice(index, 1);
804  }
805  return index;
806}
807
808/**
809 * @ngdoc function
810 * @name angular.copy
811 * @module ng
812 * @kind function
813 *
814 * @description
815 * Creates a deep copy of `source`, which should be an object or an array.
816 *
817 * * If no destination is supplied, a copy of the object or array is created.
818 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
819 *   are deleted and then all elements/properties from the source are copied to it.
820 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
821 * * If `source` is identical to 'destination' an exception will be thrown.
822 *
823 * @param {*} source The source that will be used to make a copy.
824 *                   Can be any type, including primitives, `null`, and `undefined`.
825 * @param {(Object|Array)=} destination Destination into which the source is copied. If
826 *     provided, must be of the same type as `source`.
827 * @returns {*} The copy or updated `destination`, if `destination` was specified.
828 *
829 * @example
830 <example module="copyExample">
831 <file name="index.html">
832 <div ng-controller="ExampleController">
833 <form novalidate class="simple-form">
834 Name: <input type="text" ng-model="user.name" /><br />
835 E-mail: <input type="email" ng-model="user.email" /><br />
836 Gender: <input type="radio" ng-model="user.gender" value="male" />male
837 <input type="radio" ng-model="user.gender" value="female" />female<br />
838 <button ng-click="reset()">RESET</button>
839 <button ng-click="update(user)">SAVE</button>
840 </form>
841 <pre>form = {{user | json}}</pre>
842 <pre>master = {{master | json}}</pre>
843 </div>
844
845 <script>
846  angular.module('copyExample', [])
847    .controller('ExampleController', ['$scope', function($scope) {
848      $scope.master= {};
849
850      $scope.update = function(user) {
851        // Example with 1 argument
852        $scope.master= angular.copy(user);
853      };
854
855      $scope.reset = function() {
856        // Example with 2 arguments
857        angular.copy($scope.master, $scope.user);
858      };
859
860      $scope.reset();
861    }]);
862 </script>
863 </file>
864 </example>
865 */
866function copy(source, destination, stackSource, stackDest) {
867  if (isWindow(source) || isScope(source)) {
868    throw ngMinErr('cpws',
869      "Can't copy! Making copies of Window or Scope instances is not supported.");
870  }
871  if (isTypedArray(destination)) {
872    throw ngMinErr('cpta',
873      "Can't copy! TypedArray destination cannot be mutated.");
874  }
875
876  if (!destination) {
877    destination = source;
878    if (isObject(source)) {
879      var index;
880      if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
881        return stackDest[index];
882      }
883
884      // TypedArray, Date and RegExp have specific copy functionality and must be
885      // pushed onto the stack before returning.
886      // Array and other objects create the base object and recurse to copy child
887      // objects. The array/object will be pushed onto the stack when recursed.
888      if (isArray(source)) {
889        return copy(source, [], stackSource, stackDest);
890      } else if (isTypedArray(source)) {
891        destination = new source.constructor(source);
892      } else if (isDate(source)) {
893        destination = new Date(source.getTime());
894      } else if (isRegExp(source)) {
895        destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
896        destination.lastIndex = source.lastIndex;
897      } else if (isFunction(source.cloneNode)) {
898          destination = source.cloneNode(true);
899      } else {
900        var emptyObject = Object.create(getPrototypeOf(source));
901        return copy(source, emptyObject, stackSource, stackDest);
902      }
903
904      if (stackDest) {
905        stackSource.push(source);
906        stackDest.push(destination);
907      }
908    }
909  } else {
910    if (source === destination) throw ngMinErr('cpi',
911      "Can't copy! Source and destination are identical.");
912
913    stackSource = stackSource || [];
914    stackDest = stackDest || [];
915
916    if (isObject(source)) {
917      stackSource.push(source);
918      stackDest.push(destination);
919    }
920
921    var result, key;
922    if (isArray(source)) {
923      destination.length = 0;
924      for (var i = 0; i < source.length; i++) {
925        destination.push(copy(source[i], null, stackSource, stackDest));
926      }
927    } else {
928      var h = destination.$$hashKey;
929      if (isArray(destination)) {
930        destination.length = 0;
931      } else {
932        forEach(destination, function(value, key) {
933          delete destination[key];
934        });
935      }
936      if (isBlankObject(source)) {
937        // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
938        for (key in source) {
939          destination[key] = copy(source[key], null, stackSource, stackDest);
940        }
941      } else if (source && typeof source.hasOwnProperty === 'function') {
942        // Slow path, which must rely on hasOwnProperty
943        for (key in source) {
944          if (source.hasOwnProperty(key)) {
945            destination[key] = copy(source[key], null, stackSource, stackDest);
946          }
947        }
948      } else {
949        // Slowest path --- hasOwnProperty can't be called as a method
950        for (key in source) {
951          if (hasOwnProperty.call(source, key)) {
952            destination[key] = copy(source[key], null, stackSource, stackDest);
953          }
954        }
955      }
956      setHashKey(destination,h);
957    }
958  }
959  return destination;
960}
961
962/**
963 * Creates a shallow copy of an object, an array or a primitive.
964 *
965 * Assumes that there are no proto properties for objects.
966 */
967function shallowCopy(src, dst) {
968  if (isArray(src)) {
969    dst = dst || [];
970
971    for (var i = 0, ii = src.length; i < ii; i++) {
972      dst[i] = src[i];
vendor: 14,750 bytes, lines 973-1440
973    }
974  } else if (isObject(src)) {
975    dst = dst || {};
976
977    for (var key in src) {
978      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
979        dst[key] = src[key];
980      }
981    }
982  }
983
984  return dst || src;
985}
986
987
988/**
989 * @ngdoc function
990 * @name angular.equals
991 * @module ng
992 * @kind function
993 *
994 * @description
995 * Determines if two objects or two values are equivalent. Supports value types, regular
996 * expressions, arrays and objects.
997 *
998 * Two objects or values are considered equivalent if at least one of the following is true:
999 *
1000 * * Both objects or values pass `===` comparison.
1001 * * Both objects or values are of the same type and all of their properties are equal by
1002 *   comparing them with `angular.equals`.
1003 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1004 * * Both values represent the same regular expression (In JavaScript,
1005 *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1006 *   representation matches).
1007 *
1008 * During a property comparison, properties of `function` type and properties with names
1009 * that begin with `$` are ignored.
1010 *
1011 * Scope and DOMWindow objects are being compared only by identify (`===`).
1012 *
1013 * @param {*} o1 Object or value to compare.
1014 * @param {*} o2 Object or value to compare.
1015 * @returns {boolean} True if arguments are equal.
1016 */
1017function equals(o1, o2) {
1018  if (o1 === o2) return true;
1019  if (o1 === null || o2 === null) return false;
1020  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1021  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1022  if (t1 == t2) {
1023    if (t1 == 'object') {
1024      if (isArray(o1)) {
1025        if (!isArray(o2)) return false;
1026        if ((length = o1.length) == o2.length) {
1027          for (key = 0; key < length; key++) {
1028            if (!equals(o1[key], o2[key])) return false;
1029          }
1030          return true;
1031        }
1032      } else if (isDate(o1)) {
1033        if (!isDate(o2)) return false;
1034        return equals(o1.getTime(), o2.getTime());
1035      } else if (isRegExp(o1)) {
1036        return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1037      } else {
1038        if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1039          isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1040        keySet = createMap();
1041        for (key in o1) {
1042          if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1043          if (!equals(o1[key], o2[key])) return false;
1044          keySet[key] = true;
1045        }
1046        for (key in o2) {
1047          if (!(key in keySet) &&
1048              key.charAt(0) !== '$' &&
1049              isDefined(o2[key]) &&
1050              !isFunction(o2[key])) return false;
1051        }
1052        return true;
1053      }
1054    }
1055  }
1056  return false;
1057}
1058
1059var csp = function() {
1060  if (!isDefined(csp.rules)) {
1061
1062
1063    var ngCspElement = (document.querySelector('[ng-csp]') ||
1064                    document.querySelector('[data-ng-csp]'));
1065
1066    if (ngCspElement) {
1067      var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1068                    ngCspElement.getAttribute('data-ng-csp');
1069      csp.rules = {
1070        noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1071        noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1072      };
1073    } else {
1074      csp.rules = {
1075        noUnsafeEval: noUnsafeEval(),
1076        noInlineStyle: false
1077      };
1078    }
1079  }
1080
1081  return csp.rules;
1082
1083  function noUnsafeEval() {
1084    try {
1085      /* jshint -W031, -W054 */
1086      new Function('');
1087      /* jshint +W031, +W054 */
1088      return false;
1089    } catch (e) {
1090      return true;
1091    }
1092  }
1093};
1094
1095/**
1096 * @ngdoc directive
1097 * @module ng
1098 * @name ngJq
1099 *
1100 * @element ANY
1101 * @param {string=} ngJq the name of the library available under `window`
1102 * to be used for angular.element
1103 * @description
1104 * Use this directive to force the angular.element library.  This should be
1105 * used to force either jqLite by leaving ng-jq blank or setting the name of
1106 * the jquery variable under window (eg. jQuery).
1107 *
1108 * Since angular looks for this directive when it is loaded (doesn't wait for the
1109 * DOMContentLoaded event), it must be placed on an element that comes before the script
1110 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1111 * others ignored.
1112 *
1113 * @example
1114 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1115 ```html
1116 <!doctype html>
1117 <html ng-app ng-jq>
1118 ...
1119 ...
1120 </html>
1121 ```
1122 * @example
1123 * This example shows how to use a jQuery based library of a different name.
1124 * The library name must be available at the top most 'window'.
1125 ```html
1126 <!doctype html>
1127 <html ng-app ng-jq="jQueryLib">
1128 ...
1129 ...
1130 </html>
1131 ```
1132 */
1133var jq = function() {
1134  if (isDefined(jq.name_)) return jq.name_;
1135  var el;
1136  var i, ii = ngAttrPrefixes.length, prefix, name;
1137  for (i = 0; i < ii; ++i) {
1138    prefix = ngAttrPrefixes[i];
1139    if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1140      name = el.getAttribute(prefix + 'jq');
1141      break;
1142    }
1143  }
1144
1145  return (jq.name_ = name);
1146};
1147
1148function concat(array1, array2, index) {
1149  return array1.concat(slice.call(array2, index));
1150}
1151
1152function sliceArgs(args, startIndex) {
1153  return slice.call(args, startIndex || 0);
1154}
1155
1156
1157/* jshint -W101 */
1158/**
1159 * @ngdoc function
1160 * @name angular.bind
1161 * @module ng
1162 * @kind function
1163 *
1164 * @description
1165 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1166 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1167 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1168 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1169 *
1170 * @param {Object} self Context which `fn` should be evaluated in.
1171 * @param {function()} fn Function to be bound.
1172 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1173 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1174 */
1175/* jshint +W101 */
1176function bind(self, fn) {
1177  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1178  if (isFunction(fn) && !(fn instanceof RegExp)) {
1179    return curryArgs.length
1180      ? function() {
1181          return arguments.length
1182            ? fn.apply(self, concat(curryArgs, arguments, 0))
1183            : fn.apply(self, curryArgs);
1184        }
1185      : function() {
1186          return arguments.length
1187            ? fn.apply(self, arguments)
1188            : fn.call(self);
1189        };
1190  } else {
1191    // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1192    return fn;
1193  }
1194}
1195
1196
1197function toJsonReplacer(key, value) {
1198  var val = value;
1199
1200  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1201    val = undefined;
1202  } else if (isWindow(value)) {
1203    val = '$WINDOW';
1204  } else if (value &&  document === value) {
1205    val = '$DOCUMENT';
1206  } else if (isScope(value)) {
1207    val = '$SCOPE';
1208  }
1209
1210  return val;
1211}
1212
1213
1214/**
1215 * @ngdoc function
1216 * @name angular.toJson
1217 * @module ng
1218 * @kind function
1219 *
1220 * @description
1221 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1222 * stripped since angular uses this notation internally.
1223 *
1224 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1225 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1226 *    If set to an integer, the JSON output will contain that many spaces per indentation.
1227 * @returns {string|undefined} JSON-ified string representing `obj`.
1228 */
1229function toJson(obj, pretty) {
1230  if (typeof obj === 'undefined') return undefined;
1231  if (!isNumber(pretty)) {
1232    pretty = pretty ? 2 : null;
1233  }
1234  return JSON.stringify(obj, toJsonReplacer, pretty);
1235}
1236
1237
1238/**
1239 * @ngdoc function
1240 * @name angular.fromJson
1241 * @module ng
1242 * @kind function
1243 *
1244 * @description
1245 * Deserializes a JSON string.
1246 *
1247 * @param {string} json JSON string to deserialize.
1248 * @returns {Object|Array|string|number} Deserialized JSON string.
1249 */
1250function fromJson(json) {
1251  return isString(json)
1252      ? JSON.parse(json)
1253      : json;
1254}
1255
1256
1257function timezoneToOffset(timezone, fallback) {
1258  var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1259  return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1260}
1261
1262
1263function addDateMinutes(date, minutes) {
1264  date = new Date(date.getTime());
1265  date.setMinutes(date.getMinutes() + minutes);
1266  return date;
1267}
1268
1269
1270function convertTimezoneToLocal(date, timezone, reverse) {
1271  reverse = reverse ? -1 : 1;
1272  var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1273  return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1274}
1275
1276
1277/**
1278 * @returns {string} Returns the string representation of the element.
1279 */
1280function startingTag(element) {
1281  element = jqLite(element).clone();
1282  try {
1283    // turns out IE does not let you set .html() on elements which
1284    // are not allowed to have children. So we just ignore it.
1285    element.empty();
1286  } catch (e) {}
1287  var elemHtml = jqLite('<div>').append(element).html();
1288  try {
1289    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1290        elemHtml.
1291          match(/^(<[^>]+>)/)[1].
1292          replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1293  } catch (e) {
1294    return lowercase(elemHtml);
1295  }
1296
1297}
1298
1299
1300/////////////////////////////////////////////////
1301
1302/**
1303 * Tries to decode the URI component without throwing an exception.
1304 *
1305 * @private
1306 * @param str value potential URI component to check.
1307 * @returns {boolean} True if `value` can be decoded
1308 * with the decodeURIComponent function.
1309 */
1310function tryDecodeURIComponent(value) {
1311  try {
1312    return decodeURIComponent(value);
1313  } catch (e) {
1314    // Ignore any invalid uri component
1315  }
1316}
1317
1318
1319/**
1320 * Parses an escaped url query string into key-value pairs.
1321 * @returns {Object.<string,boolean|Array>}
1322 */
1323function parseKeyValue(/**string*/keyValue) {
1324  var obj = {};
1325  forEach((keyValue || "").split('&'), function(keyValue) {
1326    var splitPoint, key, val;
1327    if (keyValue) {
1328      key = keyValue = keyValue.replace(/\+/g,'%20');
1329      splitPoint = keyValue.indexOf('=');
1330      if (splitPoint !== -1) {
1331        key = keyValue.substring(0, splitPoint);
1332        val = keyValue.substring(splitPoint + 1);
1333      }
1334      key = tryDecodeURIComponent(key);
1335      if (isDefined(key)) {
1336        val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1337        if (!hasOwnProperty.call(obj, key)) {
1338          obj[key] = val;
1339        } else if (isArray(obj[key])) {
1340          obj[key].push(val);
1341        } else {
1342          obj[key] = [obj[key],val];
1343        }
1344      }
1345    }
1346  });
1347  return obj;
1348}
1349
1350function toKeyValue(obj) {
1351  var parts = [];
1352  forEach(obj, function(value, key) {
1353    if (isArray(value)) {
1354      forEach(value, function(arrayValue) {
1355        parts.push(encodeUriQuery(key, true) +
1356                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1357      });
1358    } else {
1359    parts.push(encodeUriQuery(key, true) +
1360               (value === true ? '' : '=' + encodeUriQuery(value, true)));
1361    }
1362  });
1363  return parts.length ? parts.join('&') : '';
1364}
1365
1366
1367/**
1368 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1369 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1370 * segments:
1371 *    segment       = *pchar
1372 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1373 *    pct-encoded   = "%" HEXDIG HEXDIG
1374 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1375 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1376 *                     / "*" / "+" / "," / ";" / "="
1377 */
1378function encodeUriSegment(val) {
1379  return encodeUriQuery(val, true).
1380             replace(/%26/gi, '&').
1381             replace(/%3D/gi, '=').
1382             replace(/%2B/gi, '+');
1383}
1384
1385
1386/**
1387 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1388 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1389 * encoded per http://tools.ietf.org/html/rfc3986:
1390 *    query       = *( pchar / "/" / "?" )
1391 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1392 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1393 *    pct-encoded   = "%" HEXDIG HEXDIG
1394 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1395 *                     / "*" / "+" / "," / ";" / "="
1396 */
1397function encodeUriQuery(val, pctEncodeSpaces) {
1398  return encodeURIComponent(val).
1399             replace(/%40/gi, '@').
1400             replace(/%3A/gi, ':').
1401             replace(/%24/g, '$').
1402             replace(/%2C/gi, ',').
1403             replace(/%3B/gi, ';').
1404             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1405}
1406
1407var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1408
1409function getNgAttribute(element, ngAttr) {
1410  var attr, i, ii = ngAttrPrefixes.length;
1411  for (i = 0; i < ii; ++i) {
1412    attr = ngAttrPrefixes[i] + ngAttr;
1413    if (isString(attr = element.getAttribute(attr))) {
1414      return attr;
1415    }
1416  }
1417  return null;
1418}
1419
1420/**
1421 * @ngdoc directive
1422 * @name ngApp
1423 * @module ng
1424 *
1425 * @element ANY
1426 * @param {angular.Module} ngApp an optional application
1427 *   {@link angular.module module} name to load.
1428 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1429 *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1430 *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1431 *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1432 *   tracking down the root of these bugs.
1433 *
1434 * @description
1435 *
1436 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1437 * designates the **root element** of the application and is typically placed near the root element
1438 * of the page - e.g. on the `<body>` or `<html>` tags.
1439 *
1440 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngAp
1440p`
1441 * found in the document will be used to define the root element to auto-bootstrap as an
1442 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1443 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1444 *
1445 * You can specify an **AngularJS module** to be used as the root module for the application.  This
1446 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1447 * should contain the application code needed or have dependencies on other modules that will
1448 * contain the code. See {@link angular.module} for more information.
1449 *
1450 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1451 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1452 * would not be resolved to `3`.
1453 *
1454 * `ngApp` is the easiest, and most common way to bootstrap an application.
1455 *
1456 <example module="ngAppDemo">
1457   <file name="index.html">
1458   <div ng-controller="ngAppDemoController">
1459     I can add: {{a}} + {{b}} =  {{ a+b }}
1460   </div>
1461   </file>
1462   <file name="script.js">
1463   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1464     $scope.a = 1;
1465     $scope.b = 2;
1466   });
1467   </file>
1468 </example>
1469 *
1470 * Using `ngStrictDi`, you would see something like this:
1471 *
1472 <example ng-app-included="true">
1473   <file name="index.html">
1474   <div ng-app="ngAppStrictDemo" ng-strict-di>
1475       <div ng-controller="GoodController1">
1476           I can add: {{a}} + {{b}} =  {{ a+b }}
1477
1478           <p>This renders because the controller does not fail to
1479              instantiate, by using explicit annotation style (see
1480              script.js for details)
1481           </p>
1482       </div>
1483
1484       <div ng-controller="GoodController2">
1485           Name: <input ng-model="name"><br />
1486           Hello, {{name}}!
1487
1488           <p>This renders because the controller does not fail to
1489              instantiate, by using explicit annotation style
1490              (see script.js for details)
1491           </p>
1492       </div>
1493
1494       <div ng-controller="BadController">
1495           I can add: {{a}} + {{b}} =  {{ a+b }}
1496
1497           <p>The controller could not be instantiated, due to relying
1498              on automatic function annotations (which are disabled in
1499              strict mode). As such, the content of this section is not
1500              interpolated, and there should be an error in your web console.
1501           </p>
1502       </div>
1503   </div>
1504   </file>
1505   <file name="script.js">
1506   angular.module('ngAppStrictDemo', [])
1507     // BadController will fail to instantiate, due to relying on automatic function annotation,
1508     // rather than an explicit annotation
1509     .controller('BadController', function($scope) {
1510       $scope.a = 1;
1511       $scope.b = 2;
1512     })
1513     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1514     // due to using explicit annotations using the array style and $inject property, respectively.
1515     .controller('GoodController1', ['$scope', function($scope) {
1516       $scope.a = 1;
1517       $scope.b = 2;
1518     }])
1519     .controller('GoodController2', GoodController2);
1520     function GoodController2($scope) {
1521       $scope.name = "World";
1522     }
1523     GoodController2.$inject = ['$scope'];
1524   </file>
1525   <file name="style.css">
1526   div[ng-controller] {
1527       margin-bottom: 1em;
1528       -webkit-border-radius: 4px;
1529       border-radius: 4px;
1530       border: 1px solid;
1531       padding: .5em;
1532   }
1533   div[ng-controller^=Good] {
1534       border-color: #d6e9c6;
1535       background-color: #dff0d8;
1536       color: #3c763d;
1537   }
1538   div[ng-controller^=Bad] {
1539       border-color: #ebccd1;
1540       background-color: #f2dede;
1541       color: #a94442;
1542       margin-bottom: 0;
1543   }
1544   </file>
1545 </example>
1546 */
1547function angularInit(element, bootstrap) {
1548  var appElement,
1549      module,
1550      config = {};
1551
1552  // The element `element` has priority over any other element
1553  forEach(ngAttrPrefixes, function(prefix) {
1554    var name = prefix + 'app';
1555
1556    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1557      appElement = element;
1558      module = element.getAttribute(name);
1559    }
1560  });
1561  forEach(ngAttrPrefixes, function(prefix) {
1562    var name = prefix + 'app';
1563    var candidate;
1564
1565    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1566      appElement = candidate;
1567      module = candidate.getAttribute(name);
1568    }
1569  });
1570  if (appElement) {
1571    config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1572    bootstrap(appElement, module ? [module] : [], config);
1573  }
1574}
1575
1576/**
1577 * @ngdoc function
1578 * @name angular.bootstrap
1579 * @module ng
1580 * @description
1581 * Use this function to manually start up angular application.
1582 *
1583 * See: {@link guide/bootstrap Bootstrap}
1584 *
1585 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1586 * They must use {@link ng.directive:ngApp ngApp}.
1587 *
1588 * Angular will detect if it has been loaded into the browser more than once and only allow the
1589 * first loaded script to be bootstrapped and will report a warning to the browser console for
1590 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1591 * multiple instances of Angular try to work on the DOM.
1592 *
1593 * ```html
1594 * <!doctype html>
1595 * <html>
1596 * <body>
1597 * <div ng-controller="WelcomeController">
1598 *   {{greeting}}
1599 * </div>
1600 *
1601 * <script src="angular.js"></script>
1602 * <script>
1603 *   var app = angular.module('demo', [])
1604 *   .controller('WelcomeController', function($scope) {
1605 *       $scope.greeting = 'Welcome!';
1606 *   });
1607 *   angular.bootstrap(document, ['demo']);
1608 * </script>
1609 * </body>
1610 * </html>
1611 * ```
1612 *
1613 * @param {DOMElement} element DOM element which is the root of angular application.
1614 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1615 *     Each item in the array should be the name of a predefined module or a (DI annotated)
1616 *     function that will be invoked by the injector as a `config` block.
1617 *     See: {@link angular.module modules}
1618 * @param {Object=} config an object for defining configuration options for the application. The
1619 *     following keys are supported:
1620 *
1621 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1622 *   assist in finding bugs which break minified code. Defaults to `false`.
1623 *
1624 * @returns {auto.$injector} Returns the newly created injector for this app.
1625 */
1626function bootstrap(element, modules, config) {
1627  if (!isObject(config)) config = {};
1628  var defaultConfig = {
1629    strictDi: false
1630  };
1631  config = extend(defaultConfig, config);
1632  var doBootstrap = function() {
1633    element = jqLite(element);
1634
1635    if (element.injector()) {
1636      var tag = (element[0] === document) ? 'document' : startingTag(element);
1637      //Encode angle brackets to prevent input from being sanitized to empty string #8683
1638      throw ngMinErr(
1639          'btstrpd',
1640          "App Already Bootstrapped with this Element '{0}'",
1641          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1642    }
1643
1644    modules = modules || [];
1645    modules.unshift(['$provide', function($provide) {
1646      $provide.value('$rootElement', element);
1647    }]);
1648
1649    if (config.debugInfoEnabled) {
1650      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1651      modules.push(['$compileProvider', function($compileProvider) {
1652        $compileProvider.debugInfoEnabled(true);
1653      }]);
1654    }
1655
1656    modules.unshift('ng');
1657    var injector = createInjector(modules, config.strictDi);
1658    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1659       function bootstrapApply(scope, element, compile, injector) {
1660        scope.$apply(function() {
1661          element.data('$injector', injector);
1662          compile(element)(scope);
1663        });
1664      }]
1665    );
1666    return injector;
1667  };
1668
1669  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1670  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1671
1672  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1673    config.debugInfoEnabled = true;
1674    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1675  }
1676
1677  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1678    return doBootstrap();
1679  }
1680
1681  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1682  angular.resumeBootstrap = function(extraModules) {
1683    forEach(extraModules, function(module) {
1684      modules.push(module);
1685    });
1686    return doBootstrap();
1687  };
1688
1689  if (isFunction(angular.resumeDeferredBootstrap)) {
1690    angular.resumeDeferredBootstrap();
1691  }
1692}
1693
1694/**
1695 * @ngdoc function
1696 * @name angular.reloadWithDebugInfo
1697 * @module ng
1698 * @description
1699 * Use this function to reload the current application with debug information turned on.
1700 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1701 *
1702 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1703 */
1704function reloadWithDebugInfo() {
1705  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1706  window.location.reload();
1707}
1708
1709/**
1710 * @name angular.getTestability
1711 * @module ng
1712 * @description
1713 * Get the testability service for the instance of Angular on the given
1714 * element.
1715 * @param {DOMElement} element DOM element which is the root of angular application.
1716 */
1717function getTestability(rootElement) {
1718  var injector = angular.element(rootElement).injector();
1719  if (!injector) {
1720    throw ngMinErr('test',
1721      'no injector found for element argument to getTestability');
1722  }
1723  return injector.get('$$testability');
1724}
1725
1726var SNAKE_CASE_REGEXP = /[A-Z]/g;
1727function snake_case(name, separator) {
1728  separator = separator || '_';
1729  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1730    return (pos ? separator : '') + letter.toLowerCase();
1731  });
1732}
1733
1734var bindJQueryFired = false;
1735var skipDestroyOnNextJQueryCleanData;
1736function bindJQuery() {
1737  var originalCleanData;
1738
1739  if (bindJQueryFired) {
1740    return;
1741  }
1742
1743  // bind to jQuery if present;
1744  var jqName = jq();
1745  jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
1746           !jqName             ? undefined     :   // use jqLite
1747                                 window[jqName];   // use jQuery specified by `ngJq`
1748
1749  // Use jQuery if it exists with proper functionality, otherwise default to us.
1750  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1751  // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1752  // versions. It will not work for sure with jQuery <1.7, though.
1753  if (jQuery && jQuery.fn.on) {
1754    jqLite = jQuery;
1755    extend(jQuery.fn, {
1756      scope: JQLitePrototype.scope,
1757      isolateScope: JQLitePrototype.isolateScope,
1758      controller: JQLitePrototype.controller,
1759      injector: JQLitePrototype.injector,
1760      inheritedData: JQLitePrototype.inheritedData
1761    });
vendor: 8,342 bytes, lines 1762-2024
1762
1763    // All nodes removed from the DOM via various jQuery APIs like .remove()
1764    // are passed through jQuery.cleanData. Monkey-patch this method to fire
1765    // the $destroy event on all removed nodes.
1766    originalCleanData = jQuery.cleanData;
1767    jQuery.cleanData = function(elems) {
1768      var events;
1769      if (!skipDestroyOnNextJQueryCleanData) {
1770        for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1771          events = jQuery._data(elem, "events");
1772          if (events && events.$destroy) {
1773            jQuery(elem).triggerHandler('$destroy');
1774          }
1775        }
1776      } else {
1777        skipDestroyOnNextJQueryCleanData = false;
1778      }
1779      originalCleanData(elems);
1780    };
1781  } else {
1782    jqLite = JQLite;
1783  }
1784
1785  angular.element = jqLite;
1786
1787  // Prevent double-proxying.
1788  bindJQueryFired = true;
1789}
1790
1791/**
1792 * throw error if the argument is falsy.
1793 */
1794function assertArg(arg, name, reason) {
1795  if (!arg) {
1796    throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1797  }
1798  return arg;
1799}
1800
1801function assertArgFn(arg, name, acceptArrayAnnotation) {
1802  if (acceptArrayAnnotation && isArray(arg)) {
1803      arg = arg[arg.length - 1];
1804  }
1805
1806  assertArg(isFunction(arg), name, 'not a function, got ' +
1807      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1808  return arg;
1809}
1810
1811/**
1812 * throw error if the name given is hasOwnProperty
1813 * @param  {String} name    the name to test
1814 * @param  {String} context the context in which the name is used, such as module or directive
1815 */
1816function assertNotHasOwnProperty(name, context) {
1817  if (name === 'hasOwnProperty') {
1818    throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1819  }
1820}
1821
1822/**
1823 * Return the value accessible from the object by path. Any undefined traversals are ignored
1824 * @param {Object} obj starting object
1825 * @param {String} path path to traverse
1826 * @param {boolean} [bindFnToScope=true]
1827 * @returns {Object} value as accessible by path
1828 */
1829//TODO(misko): this function needs to be removed
1830function getter(obj, path, bindFnToScope) {
1831  if (!path) return obj;
1832  var keys = path.split('.');
1833  var key;
1834  var lastInstance = obj;
1835  var len = keys.length;
1836
1837  for (var i = 0; i < len; i++) {
1838    key = keys[i];
1839    if (obj) {
1840      obj = (lastInstance = obj)[key];
1841    }
1842  }
1843  if (!bindFnToScope && isFunction(obj)) {
1844    return bind(lastInstance, obj);
1845  }
1846  return obj;
1847}
1848
1849/**
1850 * Return the DOM siblings between the first and last node in the given array.
1851 * @param {Array} array like object
1852 * @returns {Array} the inputted object or a jqLite collection containing the nodes
1853 */
1854function getBlockNodes(nodes) {
1855  // TODO(perf): update `nodes` instead of creating a new object?
1856  var node = nodes[0];
1857  var endNode = nodes[nodes.length - 1];
1858  var blockNodes;
1859
1860  for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1861    if (blockNodes || nodes[i] !== node) {
1862      if (!blockNodes) {
1863        blockNodes = jqLite(slice.call(nodes, 0, i));
1864      }
1865      blockNodes.push(node);
1866    }
1867  }
1868
1869  return blockNodes || nodes;
1870}
1871
1872
1873/**
1874 * Creates a new object without a prototype. This object is useful for lookup without having to
1875 * guard against prototypically inherited properties via hasOwnProperty.
1876 *
1877 * Related micro-benchmarks:
1878 * - http://jsperf.com/object-create2
1879 * - http://jsperf.com/proto-map-lookup/2
1880 * - http://jsperf.com/for-in-vs-object-keys2
1881 *
1882 * @returns {Object}
1883 */
1884function createMap() {
1885  return Object.create(null);
1886}
1887
1888var NODE_TYPE_ELEMENT = 1;
1889var NODE_TYPE_ATTRIBUTE = 2;
1890var NODE_TYPE_TEXT = 3;
1891var NODE_TYPE_COMMENT = 8;
1892var NODE_TYPE_DOCUMENT = 9;
1893var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1894
1895/**
1896 * @ngdoc type
1897 * @name angular.Module
1898 * @module ng
1899 * @description
1900 *
1901 * Interface for configuring angular {@link angular.module modules}.
1902 */
1903
1904function setupModuleLoader(window) {
1905
1906  var $injectorMinErr = minErr('$injector');
1907  var ngMinErr = minErr('ng');
1908
1909  function ensure(obj, name, factory) {
1910    return obj[name] || (obj[name] = factory());
1911  }
1912
1913  var angular = ensure(window, 'angular', Object);
1914
1915  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1916  angular.$$minErr = angular.$$minErr || minErr;
1917
1918  return ensure(angular, 'module', function() {
1919    /** @type {Object.<string, angular.Module>} */
1920    var modules = {};
1921
1922    /**
1923     * @ngdoc function
1924     * @name angular.module
1925     * @module ng
1926     * @description
1927     *
1928     * The `angular.module` is a global place for creating, registering and retrieving Angular
1929     * modules.
1930     * All modules (angular core or 3rd party) that should be available to an application must be
1931     * registered using this mechanism.
1932     *
1933     * Passing one argument retrieves an existing {@link angular.Module},
1934     * whereas passing more than one argument creates a new {@link angular.Module}
1935     *
1936     *
1937     * # Module
1938     *
1939     * A module is a collection of services, directives, controllers, filters, and configuration information.
1940     * `angular.module` is used to configure the {@link auto.$injector $injector}.
1941     *
1942     * ```js
1943     * // Create a new module
1944     * var myModule = angular.module('myModule', []);
1945     *
1946     * // register a new service
1947     * myModule.value('appName', 'MyCoolApp');
1948     *
1949     * // configure existing services inside initialization blocks.
1950     * myModule.config(['$locationProvider', function($locationProvider) {
1951     *   // Configure existing providers
1952     *   $locationProvider.hashPrefix('!');
1953     * }]);
1954     * ```
1955     *
1956     * Then you can create an injector and load your modules like this:
1957     *
1958     * ```js
1959     * var injector = angular.injector(['ng', 'myModule'])
1960     * ```
1961     *
1962     * However it's more likely that you'll just use
1963     * {@link ng.directive:ngApp ngApp} or
1964     * {@link angular.bootstrap} to simplify this process for you.
1965     *
1966     * @param {!string} name The name of the module to create or retrieve.
1967     * @param {!Array.<string>=} requires If specified then new module is being created. If
1968     *        unspecified then the module is being retrieved for further configuration.
1969     * @param {Function=} configFn Optional configuration function for the module. Same as
1970     *        {@link angular.Module#config Module#config()}.
1971     * @returns {module} new module with the {@link angular.Module} api.
1972     */
1973    return function module(name, requires, configFn) {
1974      var assertNotHasOwnProperty = function(name, context) {
1975        if (name === 'hasOwnProperty') {
1976          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
1977        }
1978      };
1979
1980      assertNotHasOwnProperty(name, 'module');
1981      if (requires && modules.hasOwnProperty(name)) {
1982        modules[name] = null;
1983      }
1984      return ensure(modules, name, function() {
1985        if (!requires) {
1986          throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
1987             "the module name or forgot to load it. If registering a module ensure that you " +
1988             "specify the dependencies as the second argument.", name);
1989        }
1990
1991        /** @type {!Array.<Array.<*>>} */
1992        var invokeQueue = [];
1993
1994        /** @type {!Array.<Function>} */
1995        var configBlocks = [];
1996
1997        /** @type {!Array.<Function>} */
1998        var runBlocks = [];
1999
2000        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2001
2002        /** @type {angular.Module} */
2003        var moduleInstance = {
2004          // Private state
2005          _invokeQueue: invokeQueue,
2006          _configBlocks: configBlocks,
2007          _runBlocks: runBlocks,
2008
2009          /**
2010           * @ngdoc property
2011           * @name angular.Module#requires
2012           * @module ng
2013           *
2014           * @description
2015           * Holds the list of modules which the injector will load before the current module is
2016           * loaded.
2017           */
2018          requires: requires,
2019
2020          /**
2021           * @ngdoc property
2022           * @name angular.Module#name
2023           * @module ng
2024           *
2025           * @description
2026           * Name of the module.
2027           */
2028          name: name,
2029
2030
2031          /**
2032           * @ngdoc method
2033           * @name angular.Module#provider
2034           * @module ng
2035           * @param {string} name service name
2036           * @param {Function} providerType Construction function for creating new instance of the
2037           *                                service.
2038           * @description
2039           * See {@link auto.$provide#provider $provide.provider()}.
2040           */
2041          provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2042
2043          /**
2044           * @ngdoc method
2045           * @name angular.Module#factory
2046           * @module ng
2047           * @param {string} name service name
2048           * @param {Function} providerFunction Function for creating new instance of the service.
2049           * @description
2050           * See {@link auto.$provide#factory $provide.factory()}.
2051           */
2052          factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2053
2054          /**
2055           * @ngdoc method
2056           * @name angular.Module#service
2057           * @module ng
2058           * @param {string} name service name
2059           * @param {Function} constructor A constructor function that will be instantiated.
2060           * @description
2061           * See {@link auto.$provide#service $provide.service()}.
2062           */
2063          service: invokeLaterAndSetModuleName('$provide', 'service'),
2064
2065          /**
2066           * @ngdoc method
2067           * @name angular.Module#value
2068           * @module ng
2069           * @param {string} name service name
2070           * @param {*} object Service instance object.
2071           * @description
2072           * See {@link auto.$provide#value $provide.value()}.
2073           */
2074          value: invokeLater('$provide', 'value'),
2075
2076          /**
2077           * @ngdoc method
2078           * @name angular.Module#constant
2079           * @module ng
2080           * @param {string} name constant name
2081           * @param {*} object Constant value.
2082           * @description
2083           * Because the constant are fixed, they get applied before other provide methods.
2084           * See {@link auto.$provide#constant $provide.constant()}.
2085           */
2086          constant: invokeLater('$provide', 'constant', 'unshift'),
2087
2088           /**
2089           * @ngdoc method
2090           * @name angular.Module#decorator
2091           * @module ng
2092           * @param {string} The name of the service to decorate.
2093           * @param {Function} This function will be invoked when the service needs to be
2094           *                                    instantiated and should return the decorated service instance.
2095           * @description
2096           * See {@link auto.$provide#decorator $provide.decorator()}.
2097           */
2098          decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2099
2100          /**
2101           * @ngdoc method
2102           * @name angular.Module#animation
2103           * @module ng
2104           * @param {string} name animation name
2105           * @param {Function} animationFactory Factory function for creating new instance of an
2106           *                                    animation.
2107           * @description
2108           *
2109           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2110           *
2111           *
2112           * Defines an animation hook that can be later used with
2113           * {@link $animate $animate} service and directives that use this service.
2114           *
2115           * ```js
2116           * module.animation('.animation-name', function($inject1, $inject2) {
2117           *   return {
2118           *     eventName : function(element, done) {
2119           *       //code to run the animation
2120           *       //once complete, then run done()
2121           *       return function cancellationFunction(element) {
2122           *         //code to cancel the animation
2123           *       }
2124           *     }
2125           *   }
2126           * })
2127           * ```
2128           *
2129           * See {@link ng.$animateProvider#register $animateProvider.register()} and
2130           * {@link ngAnimate ngAnimate module} for more information.
2131           */
2132          animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2133
2134          /**
2135           * @ngdoc method
2136           * @name angular.Module#filter
2137           * @module ng
2138           * @param {string} name Filter name - this must be a valid angular expression identifier
2139           * @param {Function} filterFactory Factory function for creating new instance of filter.
2140           * @description
2141           * See {@link ng.$filterProvider#register $filterProvider.register()}.
2142           *
2143           * <div class="alert alert-warning">
2144           * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2145           * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2146           * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2147           * (`myapp_subsection_filterx`).
2148           * </div>
2149           */
2150          filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2151
2152          /**
2153           * @ngdoc method
2154           * @name angular.Module#controller
2155           * @module ng
2156           * @param {string|Object} name Controller name, or an object map of controllers where the
2157           *    keys are the names and the values are the constructors.
2158           * @param {Function} constructor Controller constructor function.
2159           * @description
2160           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2161           */
2162          controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2163
2164          /**
2165           * @ngdoc method
2166           * @name angular.Module#directive
2167           * @module ng
2168           * @param {string|Object} name Directive name, or an object map of directives where the
2169           *    keys are the names and the values are the factories.
2170           * @param {Function} directiveFactory Factory function for creating new instance of
2171           * directives.
2172           * @description
2173           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2174           */
2175          directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2176
2177          /**
2178           * @ngdoc method
2179           * @name angular.Module#config
2180           * @module ng
2181           * @param {Function} configFn Execute this function on module load. Useful for service
2182           *    configuration.
2183           * @description
2184           * Use this method to register work which needs to be performed on module loading.
2185           * For more about how to configure services, see
2186           * {@link providers#provider-recipe Provider Recipe}.
2187           */
2188          config: config,
2189
2190          /**
2191           * @ngdoc method
2192           * @name angular.Module#run
2193           * @module ng
2194           * @param {Function} initializationFn Execute this function after injector creation.
2195           *    Useful for application initialization.
2196           * @description
2197           * Use this method to register work which should be performed when the injector is done
2198           * loading all modules.
2199           */
2200          run: function(block) {
2201            runBlocks.push(block);
2202            return this;
2203          }
2204        };
2205
2206        if (configFn) {
2207          config(configFn);
2208        }
2209
2210        return moduleInstance;
2211
2212        /**
2213         * @param {string} provider
2214         * @param {string} method
2215         * @param {String=} insertMethod
2216         * @returns {angular.Module}
2217         */
2218        function invokeLater(provider, method, insertMethod, queue) {
2219          if (!queue) queue = invokeQueue;
2220          return function() {
2221            queue[insertMethod || 'push']([provider, method, arguments]);
2222            return moduleInstance;
2223          };
2224        }
2225
2226        /**
2227         * @param {string} provider
2228         * @param {string} method
2229         * @returns {angular.Module}
2230         */
2231        function invokeLaterAndSetModuleName(provider, method) {
2232          return function(recipeName, factoryFunction) {
2233            if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2234            invokeQueue.push([provider, method, arguments]);
2235            return moduleInstance;
2236          };
2237        }
2238      });
2239    };
2240  });
2241
2242}
2243
2244/* global: toDebugString: true */
2245
2246function serializeObject(obj) {
2247  var seen = [];
2248
2249  return JSON.stringify(obj, function(key, val) {
2250    val = toJsonReplacer(key, val);
2251    if (isObject(val)) {
2252
2253      if (seen.indexOf(val) >= 0) return '...';
2254
2255      seen.push(val);
2256    }
2257    return val;
2258  });
2259}
2260
2261function toDebugString(obj) {
2262  if (typeof obj === 'function') {
2263    return obj.toString().replace(/ \{[\s\S]*$/, '');
2264  } else if (isUndefined(obj)) {
2265    return 'undefined';
2266  } else if (typeof obj !== 'string') {
2267    return serializeObject(obj);
2268  }
2269  return obj;
vendor: 6,462 bytes, lines 2270-2490
2270}
2271
2272/* global angularModule: true,
2273  version: true,
2274
2275  $CompileProvider,
2276
2277  htmlAnchorDirective,
2278  inputDirective,
2279  inputDirective,
2280  formDirective,
2281  scriptDirective,
2282  selectDirective,
2283  styleDirective,
2284  optionDirective,
2285  ngBindDirective,
2286  ngBindHtmlDirective,
2287  ngBindTemplateDirective,
2288  ngClassDirective,
2289  ngClassEvenDirective,
2290  ngClassOddDirective,
2291  ngCloakDirective,
2292  ngControllerDirective,
2293  ngFormDirective,
2294  ngHideDirective,
2295  ngIfDirective,
2296  ngIncludeDirective,
2297  ngIncludeFillContentDirective,
2298  ngInitDirective,
2299  ngNonBindableDirective,
2300  ngPluralizeDirective,
2301  ngRepeatDirective,
2302  ngShowDirective,
2303  ngStyleDirective,
2304  ngSwitchDirective,
2305  ngSwitchWhenDirective,
2306  ngSwitchDefaultDirective,
2307  ngOptionsDirective,
2308  ngTranscludeDirective,
2309  ngModelDirective,
2310  ngListDirective,
2311  ngChangeDirective,
2312  patternDirective,
2313  patternDirective,
2314  requiredDirective,
2315  requiredDirective,
2316  minlengthDirective,
2317  minlengthDirective,
2318  maxlengthDirective,
2319  maxlengthDirective,
2320  ngValueDirective,
2321  ngModelOptionsDirective,
2322  ngAttributeAliasDirectives,
2323  ngEventDirectives,
2324
2325  $AnchorScrollProvider,
2326  $AnimateProvider,
2327  $CoreAnimateCssProvider,
2328  $$CoreAnimateQueueProvider,
2329  $$CoreAnimateRunnerProvider,
2330  $BrowserProvider,
2331  $CacheFactoryProvider,
2332  $ControllerProvider,
2333  $DocumentProvider,
2334  $ExceptionHandlerProvider,
2335  $FilterProvider,
2336  $$ForceReflowProvider,
2337  $InterpolateProvider,
2338  $IntervalProvider,
2339  $$HashMapProvider,
2340  $HttpProvider,
2341  $HttpParamSerializerProvider,
2342  $HttpParamSerializerJQLikeProvider,
2343  $HttpBackendProvider,
2344  $xhrFactoryProvider,
2345  $LocationProvider,
2346  $LogProvider,
2347  $ParseProvider,
2348  $RootScopeProvider,
2349  $QProvider,
2350  $$QProvider,
2351  $$SanitizeUriProvider,
2352  $SceProvider,
2353  $SceDelegateProvider,
2354  $SnifferProvider,
2355  $TemplateCacheProvider,
2356  $TemplateRequestProvider,
2357  $$TestabilityProvider,
2358  $TimeoutProvider,
2359  $$RAFProvider,
2360  $WindowProvider,
2361  $$jqLiteProvider,
2362  $$CookieReaderProvider
2363*/
2364
2365
2366/**
2367 * @ngdoc object
2368 * @name angular.version
2369 * @module ng
2370 * @description
2371 * An object that contains information about the current AngularJS version.
2372 *
2373 * This object has the following properties:
2374 *
2375 * - `full` – `{string}` – Full version string, such as "0.9.18".
2376 * - `major` – `{number}` – Major version number, such as "0".
2377 * - `minor` – `{number}` – Minor version number, such as "9".
2378 * - `dot` – `{number}` – Dot version number, such as "18".
2379 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2380 */
2381var version = {
2382  full: '1.4.7',    // all of these placeholder strings will be replaced by grunt's
2383  major: 1,    // package task
2384  minor: 4,
2385  dot: 7,
2386  codeName: 'dark-luminescence'
2387};
2388
2389
2390function publishExternalAPI(angular) {
2391  extend(angular, {
2392    'bootstrap': bootstrap,
2393    'copy': copy,
2394    'extend': extend,
2395    'merge': merge,
2396    'equals': equals,
2397    'element': jqLite,
2398    'forEach': forEach,
2399    'injector': createInjector,
2400    'noop': noop,
2401    'bind': bind,
2402    'toJson': toJson,
2403    'fromJson': fromJson,
2404    'identity': identity,
2405    'isUndefined': isUndefined,
2406    'isDefined': isDefined,
2407    'isString': isString,
2408    'isFunction': isFunction,
2409    'isObject': isObject,
2410    'isNumber': isNumber,
2411    'isElement': isElement,
2412    'isArray': isArray,
2413    'version': version,
2414    'isDate': isDate,
2415    'lowercase': lowercase,
2416    'uppercase': uppercase,
2417    'callbacks': {counter: 0},
2418    'getTestability': getTestability,
2419    '$$minErr': minErr,
2420    '$$csp': csp,
2421    'reloadWithDebugInfo': reloadWithDebugInfo
2422  });
2423
2424  angularModule = setupModuleLoader(window);
2425
2426  angularModule('ng', ['ngLocale'], ['$provide',
2427    function ngModule($provide) {
2428      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2429      $provide.provider({
2430        $$sanitizeUri: $$SanitizeUriProvider
2431      });
2432      $provide.provider('$compile', $CompileProvider).
2433        directive({
2434            a: htmlAnchorDirective,
2435            input: inputDirective,
2436            textarea: inputDirective,
2437            form: formDirective,
2438            script: scriptDirective,
2439            select: selectDirective,
2440            style: styleDirective,
2441            option: optionDirective,
2442            ngBind: ngBindDirective,
2443            ngBindHtml: ngBindHtmlDirective,
2444            ngBindTemplate: ngBindTemplateDirective,
2445            ngClass: ngClassDirective,
2446            ngClassEven: ngClassEvenDirective,
2447            ngClassOdd: ngClassOddDirective,
2448            ngCloak: ngCloakDirective,
2449            ngController: ngControllerDirective,
2450            ngForm: ngFormDirective,
2451            ngHide: ngHideDirective,
2452            ngIf: ngIfDirective,
2453            ngInclude: ngIncludeDirective,
2454            ngInit: ngInitDirective,
2455            ngNonBindable: ngNonBindableDirective,
2456            ngPluralize: ngPluralizeDirective,
2457            ngRepeat: ngRepeatDirective,
2458            ngShow: ngShowDirective,
2459            ngStyle: ngStyleDirective,
2460            ngSwitch: ngSwitchDirective,
2461            ngSwitchWhen: ngSwitchWhenDirective,
2462            ngSwitchDefault: ngSwitchDefaultDirective,
2463            ngOptions: ngOptionsDirective,
2464            ngTransclude: ngTranscludeDirective,
2465            ngModel: ngModelDirective,
2466            ngList: ngListDirective,
2467            ngChange: ngChangeDirective,
2468            pattern: patternDirective,
2469            ngPattern: patternDirective,
2470            required: requiredDirective,
2471            ngRequired: requiredDirective,
2472            minlength: minlengthDirective,
2473            ngMinlength: minlengthDirective,
2474            maxlength: maxlengthDirective,
2475            ngMaxlength: maxlengthDirective,
2476            ngValue: ngValueDirective,
2477            ngModelOptions: ngModelOptionsDirective
2478        }).
2479        directive({
2480          ngInclude: ngIncludeFillContentDirective
2481        }).
2482        directive(ngAttributeAliasDirectives).
2483        directive(ngEventDirectives);
2484      $provide.provider({
2485        $anchorScroll: $AnchorScrollProvider,
2486        $animate: $AnimateProvider,
2487        $animateCss: $CoreAnimateCssProvider,
2488        $$animateQueue: $$CoreAnimateQueueProvider,
2489        $$AnimateRunner: $$CoreAnimateRunnerProvider,
2490        $browser: $BrowserProvider,
2491        $cacheFactory: $CacheFactoryProvider,
2492        $controller: $ControllerProvider,
2493        $document: $DocumentProvider,
2494        $exceptionHandler: $ExceptionHandlerProvider,
2495        $filter: $FilterProvider,
2496        $$forceReflow: $$ForceReflowProvider,
2497        $interpolate: $InterpolateProvider,
2498        $interval: $IntervalProvider,
2499        $http: $HttpProvider,
2500        $httpParamSerializer: $HttpParamSerializerProvider,
2501        $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2502        $httpBackend: $HttpBackendProvider,
2503        $xhrFactory: $xhrFactoryProvider,
2504        $location: $LocationProvider,
2505        $log: $LogProvider,
2506        $parse: $ParseProvider,
2507        $rootScope: $RootScopeProvider,
2508        $q: $QProvider,
2509        $$q: $$QProvider,
2510        $sce: $SceProvider,
2511        $sceDelegate: $SceDelegateProvider,
2512        $sniffer: $SnifferProvider,
2513        $templateCache: $TemplateCacheProvider,
2514        $templateRequest: $TemplateRequestProvider,
2515        $$testability: $$TestabilityProvider,
2516        $timeout: $TimeoutProvider,
2517        $window: $WindowProvider,
2518        $$rAF: $$RAFProvider,
2519        $$jqLite: $$jqLiteProvider,
2520        $$HashMap: $$HashMapProvider,
2521        $$cookieReader: $$CookieReaderProvider
2522      });
2523    }
2524  ]);
2525}
2526
2527/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2528 *     Any commits to this file should be reviewed with security in mind.  *
2529 *   Changes to this file can potentially create security vulnerabilities. *
2530 *          An approval from 2 Core members with history of modifying      *
2531 *                         this file is required.                          *
2532 *                                                                         *
2533 *  Does the change somehow allow for arbitrary javascript to be executed? *
2534 *    Or allows for someone to change the prototype of built-in objects?   *
2535 *     Or gives undesired access to variables likes document or window?    *
2536 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2537
2538/* global JQLitePrototype: true,
2539  addEventListenerFn: true,
2540  removeEventListenerFn: true,
2541  BOOLEAN_ATTR: true,
2542  ALIASED_ATTR: true,
2543*/
2544
2545//////////////////////////////////
2546//JQLite
2547//////////////////////////////////
2548
2549/**
2550 * @ngdoc function
2551 * @name angular.element
2552 * @module ng
2553 * @kind function
2554 *
2555 * @description
2556 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2557 *
2558 * If jQuery is available, `angular.element` is an alias for the
2559 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2560 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2561 *
2562 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2563 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2564 * commonly needed functionality with the goal of having a very small footprint.</div>
2565 *
2566 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
2567 *
2568 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2569 * jqLite; they are never raw DOM references.</div>
2570 *
2571 * ## Angular's jqLite
2572 * jqLite provides only the following jQuery methods:
2573 *
2574 * - [`addClass()`](http://api.jquery.com/addClass/)
2575 * - [`after()`](http://api.jquery.com/after/)
2576 * - [`append()`](http://api.jquery.com/append/)
2577 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2578 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2579 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2580 * - [`clone()`](http://api.jquery.com/clone/)
2581 * - [`contents()`](http://api.jquery.com/contents/)
2582 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
2583 * - [`data()`](http://api.jquery.com/data/)
2584 * - [`detach()`](http://api.jquery.com/detach/)
2585 * - [`empty()`](http://api.jquery.com/empty/)
2586 * - [`eq()`](http://api.jquery.com/eq/)
2587 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2588 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2589 * - [`html()`](http://api.jquery.com/html/)
2590 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2591 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2592 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2593 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2594 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2595 * - [`prepend()`](http://api.jquery.com/prepend/)
2596 * - [`prop()`](http://api.jquery.com/prop/)
2597 * - [`ready()`](http://api.jquery.com/ready/)
2598 * - [`remove()`](http://api.jquery.com/remove/)
2599 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2600 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2601 * - [`removeData()`](http://api.jquery.com/removeData/)
2602 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2603 * - [`text()`](http://api.jquery.com/text/)
2604 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2605 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2606 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2607 * - [`val()`](http://api.jquery.com/val/)
2608 * - [`wrap()`](http://api.jquery.com/wrap/)
2609 *
2610 * ## jQuery/jqLite Extras
2611 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2612 *
2613 * ### Events
2614 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction a
2614pis and fires this event
2615 *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2616 *    element before it is removed.
2617 *
2618 * ### Methods
2619 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2620 *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2621 *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2622 *   `'ngModel'`).
2623 * - `injector()` - retrieves the injector of the current element or its parent.
2624 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2625 *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2626 *   be enabled.
2627 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2628 *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2629 *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2630 *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2631 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2632 *   parent element is reached.
2633 *
2634 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2635 * @returns {Object} jQuery object.
2636 */
2637
2638JQLite.expando = 'ng339';
2639
2640var jqCache = JQLite.cache = {},
2641    jqId = 1,
2642    addEventListenerFn = function(element, type, fn) {
2643      element.addEventListener(type, fn, false);
2644    },
2645    removeEventListenerFn = function(element, type, fn) {
2646      element.removeEventListener(type, fn, false);
2647    };
2648
2649/*
2650 * !!! This is an undocumented "private" function !!!
2651 */
2652JQLite._data = function(node) {
2653  //jQuery always returns an object on cache miss
2654  return this.cache[node[this.expando]] || {};
2655};
2656
2657function jqNextId() { return ++jqId; }
2658
2659
2660var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2661var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2662var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2663var jqLiteMinErr = minErr('jqLite');
2664
2665/**
2666 * Converts snake_case to camelCase.
2667 * Also there is special case for Moz prefix starting with upper case letter.
2668 * @param name Name to normalize
2669 */
2670function camelCase(name) {
2671  return name.
2672    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2673      return offset ? letter.toUpperCase() : letter;
2674    }).
2675    replace(MOZ_HACK_REGEXP, 'Moz$1');
2676}
2677
2678var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2679var HTML_REGEXP = /<|&#?\w+;/;
2680var TAG_NAME_REGEXP = /<([\w:-]+)/;
2681var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2682
2683var wrapMap = {
2684  'option': [1, '<select multiple="multiple">', '</select>'],
2685
2686  'thead': [1, '<table>', '</table>'],
2687  'col': [2, '<table><colgroup>', '</colgroup></table>'],
2688  'tr': [2, '<table><tbody>', '</tbody></table>'],
2689  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2690  '_default': [0, "", ""]
2691};
2692
2693wrapMap.optgroup = wrapMap.option;
2694wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2695wrapMap.th = wrapMap.td;
2696
2697
2698function jqLiteIsTextNode(html) {
2699  return !HTML_REGEXP.test(html);
2700}
2701
2702function jqLiteAcceptsData(node) {
2703  // The window object can accept data but has no nodeType
2704  // Otherwise we are only interested in elements (1) and documents (9)
2705  var nodeType = node.nodeType;
2706  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2707}
2708
2709function jqLiteHasData(node) {
2710  for (var key in jqCache[node.ng339]) {
2711    return true;
2712  }
2713  return false;
2714}
2715
2716function jqLiteBuildFragment(html, context) {
2717  var tmp, tag, wrap,
2718      fragment = context.createDocumentFragment(),
2719      nodes = [], i;
2720
2721  if (jqLiteIsTextNode(html)) {
2722    // Convert non-html into a text node
2723    nodes.push(context.createTextNode(html));
2724  } else {
2725    // Convert html into DOM nodes
2726    tmp = tmp || fragment.appendChild(context.createElement("div"));
2727    tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2728    wrap = wrapMap[tag] || wrapMap._default;
2729    tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2730
2731    // Descend through wrappers to the right content
2732    i = wrap[0];
2733    while (i--) {
2734      tmp = tmp.lastChild;
2735    }
2736
2737    nodes = concat(nodes, tmp.childNodes);
2738
2739    tmp = fragment.firstChild;
2740    tmp.textContent = "";
2741  }
2742
2743  // Remove wrapper from fragment
2744  fragment.textContent = "";
2745  fragment.innerHTML = ""; // Clear inner HTML
2746  forEach(nodes, function(node) {
2747    fragment.appendChild(node);
2748  });
2749
2750  return fragment;
2751}
2752
2753function jqLiteParseHTML(html, context) {
2754  context = context || document;
2755  var parsed;
2756
2757  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2758    return [context.createElement(parsed[1])];
2759  }
2760
2761  if ((parsed = jqLiteBuildFragment(html, context))) {
2762    return parsed.childNodes;
2763  }
2764
2765  return [];
2766}
2767
2768/////////////////////////////////////////////
2769function JQLite(element) {
2770  if (element instanceof JQLite) {
2771    return element;
2772  }
2773
2774  var argIsString;
2775
2776  if (isString(element)) {
2777    element = trim(element);
2778    argIsString = true;
2779  }
2780  if (!(this instanceof JQLite)) {
2781    if (argIsString && element.charAt(0) != '<') {
2782      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2783    }
2784    return new JQLite(element);
2785  }
2786
2787  if (argIsString) {
2788    jqLiteAddNodes(this, jqLiteParseHTML(element));
2789  } else {
2790    jqLiteAddNodes(this, element);
2791  }
2792}
2793
2794function jqLiteClone(element) {
2795  return element.cloneNode(true);
2796}
2797
2798function jqLiteDealoc(element, onlyDescendants) {
2799  if (!onlyDescendants) jqLiteRemoveData(element);
2800
2801  if (element.querySelectorAll) {
2802    var descendants = element.querySelectorAll('*');
2803    for (var i = 0, l = descendants.length; i < l; i++) {
2804      jqLiteRemoveData(descendants[i]);
2805    }
2806  }
2807}
2808
2809function jqLiteOff(element, type, fn, unsupported) {
2810  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2811
2812  var expandoStore = jqLiteExpandoStore(element);
2813  var events = expandoStore && expandoStore.events;
2814  var handle = expandoStore && expandoStore.handle;
2815
2816  if (!handle) return; //no listeners registered
2817
2818  if (!type) {
2819    for (type in events) {
2820      if (type !== '$destroy') {
2821        removeEventListenerFn(element, type, handle);
2822      }
2823      delete events[type];
2824    }
2825  } else {
2826    forEach(type.split(' '), function(type) {
2827      if (isDefined(fn)) {
2828        var listenerFns = events[type];
2829        arrayRemove(listenerFns || [], fn);
2830        if (listenerFns && listenerFns.length > 0) {
2831          return;
2832        }
2833      }
2834
2835      removeEventListenerFn(element, type, handle);
2836      delete events[type];
2837    });
2838  }
2839}
2840
2841function jqLiteRemoveData(element, name) {
2842  var expandoId = element.ng339;
2843  var expandoStore = expandoId && jqCache[expandoId];
2844
2845  if (expandoStore) {
2846    if (name) {
2847      delete expandoStore.data[name];
2848      return;
2849    }
2850
2851    if (expandoStore.handle) {
2852      if (expandoStore.events.$destroy) {
2853        expandoStore.handle({}, '$destroy');
2854      }
2855      jqLiteOff(element);
2856    }
2857    delete jqCache[expandoId];
2858    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2859  }
2860}
2861
2862
2863function jqLiteExpandoStore(element, createIfNecessary) {
2864  var expandoId = element.ng339,
2865      expandoStore = expandoId && jqCache[expandoId];
2866
2867  if (createIfNecessary && !expandoStore) {
2868    element.ng339 = expandoId = jqNextId();
2869    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2870  }
2871
2872  return expandoStore;
2873}
2874
2875
2876function jqLiteData(element, key, value) {
2877  if (jqLiteAcceptsData(element)) {
2878
2879    var isSimpleSetter = isDefined(value);
2880    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2881    var massGetter = !key;
2882    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2883    var data = expandoStore && expandoStore.data;
2884
2885    if (isSimpleSetter) { // data('key', value)
2886      data[key] = value;
2887    } else {
2888      if (massGetter) {  // data()
2889        return data;
2890      } else {
2891        if (isSimpleGetter) { // data('key')
2892          // don't force creation of expandoStore if it doesn't exist yet
2893          return data && data[key];
2894        } else { // mass-setter: data({key1: val1, key2: val2})
2895          extend(data, key);
2896        }
2897      }
2898    }
2899  }
2900}
2901
2902function jqLiteHasClass(element, selector) {
2903  if (!element.getAttribute) return false;
2904  return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2905      indexOf(" " + selector + " ") > -1);
2906}
2907
2908function jqLiteRemoveClass(element, cssClasses) {
2909  if (cssClasses && element.setAttribute) {
2910    forEach(cssClasses.split(' '), function(cssClass) {
2911      element.setAttribute('class', trim(
2912          (" " + (element.getAttribute('class') || '') + " ")
2913          .replace(/[\n\t]/g, " ")
2914          .replace(" " + trim(cssClass) + " ", " "))
2915      );
2916    });
2917  }
2918}
2919
2920function jqLiteAddClass(element, cssClasses) {
2921  if (cssClasses && element.setAttribute) {
2922    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2923                            .replace(/[\n\t]/g, " ");
2924
2925    forEach(cssClasses.split(' '), function(cssClass) {
2926      cssClass = trim(cssClass);
2927      if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2928        existingClasses += cssClass + ' ';
2929      }
2930    });
2931
2932    element.setAttribute('class', trim(existingClasses));
2933  }
2934}
2935
2936
2937function jqLiteAddNodes(root, elements) {
2938  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
2939
2940  if (elements) {
2941
2942    // if a Node (the most common case)
2943    if (elements.nodeType) {
2944      root[root.length++] = elements;
2945    } else {
2946      var length = elements.length;
2947
2948      // if an Array or NodeList and not a Window
2949      if (typeof length === 'number' && elements.window !== elements) {
2950        if (length) {
2951          for (var i = 0; i < length; i++) {
2952            root[root.length++] = elements[i];
2953          }
2954        }
2955      } else {
2956        root[root.length++] = elements;
2957      }
2958    }
2959  }
2960}
2961
2962
2963function jqLiteController(element, name) {
2964  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
2965}
2966
2967function jqLiteInheritedData(element, name, value) {
2968  // if element is the document object work with the html element instead
2969  // this makes $(document).scope() possible
2970  if (element.nodeType == NODE_TYPE_DOCUMENT) {
2971    element = element.documentElement;
2972  }
2973  var names = isArray(name) ? name : [name];
2974
2975  while (element) {
2976    for (var i = 0, ii = names.length; i < ii; i++) {
2977      if (isDefined(value = jqLite.data(element, names[i]))) return value;
2978    }
2979
2980    // If dealing with a document fragment node with a host element, and no parent, use the host
2981    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
2982    // to lookup parent controllers.
2983    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
2984  }
2985}
2986
2987function jqLiteEmpty(element) {
2988  jqLiteDealoc(element, true);
2989  while (element.firstChild) {
2990    element.removeChild(element.firstChild);
2991  }
2992}
2993
2994function jqLiteRemove(element, keepData) {
2995  if (!keepData) jqLiteDealoc(element);
2996  var parent = element.parentNode;
2997  if (parent) parent.removeChild(element);
2998}
2999
3000
3001function jqLiteDocumentLoaded(action, win) {
3002  win = win || window;
3003  if (win.document.readyState === 'complete') {
3004    // Force the action to be run async for consistent behaviour
3005    // from the action's point of view
3006    // i.e. it will definitely not be in a $apply
3007    win.setTimeout(action);
3008  } else {
3009    // No need to unbind this handler as load is only ever called once
3010    jqLite(win).on('load', action);
3011  }
3012}
3013
3014//////////////////////////////////////////
3015// Functions which are declared directly.
vendor: 9,140 bytes, lines 3016-3317
3016//////////////////////////////////////////
3017var JQLitePrototype = JQLite.prototype = {
3018  ready: function(fn) {
3019    var fired = false;
3020
3021    function trigger() {
3022      if (fired) return;
3023      fired = true;
3024      fn();
3025    }
3026
3027    // check if document is already loaded
3028    if (document.readyState === 'complete') {
3029      setTimeout(trigger);
3030    } else {
3031      this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3032      // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3033      // jshint -W064
3034      JQLite(window).on('load', trigger); // fallback to window.onload for others
3035      // jshint +W064
3036    }
3037  },
3038  toString: function() {
3039    var value = [];
3040    forEach(this, function(e) { value.push('' + e);});
3041    return '[' + value.join(', ') + ']';
3042  },
3043
3044  eq: function(index) {
3045      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3046  },
3047
3048  length: 0,
3049  push: push,
3050  sort: [].sort,
3051  splice: [].splice
3052};
3053
3054//////////////////////////////////////////
3055// Functions iterating getter/setters.
3056// these functions return self on setter and
3057// value on get.
3058//////////////////////////////////////////
3059var BOOLEAN_ATTR = {};
3060forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3061  BOOLEAN_ATTR[lowercase(value)] = value;
3062});
3063var BOOLEAN_ELEMENTS = {};
3064forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3065  BOOLEAN_ELEMENTS[value] = true;
3066});
3067var ALIASED_ATTR = {
3068  'ngMinlength': 'minlength',
3069  'ngMaxlength': 'maxlength',
3070  'ngMin': 'min',
3071  'ngMax': 'max',
3072  'ngPattern': 'pattern'
3073};
3074
3075function getBooleanAttrName(element, name) {
3076  // check dom last since we will most likely fail on name
3077  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3078
3079  // booleanAttr is here twice to minimize DOM access
3080  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3081}
3082
3083function getAliasedAttrName(name) {
3084  return ALIASED_ATTR[name];
3085}
3086
3087forEach({
3088  data: jqLiteData,
3089  removeData: jqLiteRemoveData,
3090  hasData: jqLiteHasData
3091}, function(fn, name) {
3092  JQLite[name] = fn;
3093});
3094
3095forEach({
3096  data: jqLiteData,
3097  inheritedData: jqLiteInheritedData,
3098
3099  scope: function(element) {
3100    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3101    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3102  },
3103
3104  isolateScope: function(element) {
3105    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3106    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3107  },
3108
3109  controller: jqLiteController,
3110
3111  injector: function(element) {
3112    return jqLiteInheritedData(element, '$injector');
3113  },
3114
3115  removeAttr: function(element, name) {
3116    element.removeAttribute(name);
3117  },
3118
3119  hasClass: jqLiteHasClass,
3120
3121  css: function(element, name, value) {
3122    name = camelCase(name);
3123
3124    if (isDefined(value)) {
3125      element.style[name] = value;
3126    } else {
3127      return element.style[name];
3128    }
3129  },
3130
3131  attr: function(element, name, value) {
3132    var nodeType = element.nodeType;
3133    if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3134      return;
3135    }
3136    var lowercasedName = lowercase(name);
3137    if (BOOLEAN_ATTR[lowercasedName]) {
3138      if (isDefined(value)) {
3139        if (!!value) {
3140          element[name] = true;
3141          element.setAttribute(name, lowercasedName);
3142        } else {
3143          element[name] = false;
3144          element.removeAttribute(lowercasedName);
3145        }
3146      } else {
3147        return (element[name] ||
3148                 (element.attributes.getNamedItem(name) || noop).specified)
3149               ? lowercasedName
3150               : undefined;
3151      }
3152    } else if (isDefined(value)) {
3153      element.setAttribute(name, value);
3154    } else if (element.getAttribute) {
3155      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3156      // some elements (e.g. Document) don't have get attribute, so return undefined
3157      var ret = element.getAttribute(name, 2);
3158      // normalize non-existing attributes to undefined (as jQuery)
3159      return ret === null ? undefined : ret;
3160    }
3161  },
3162
3163  prop: function(element, name, value) {
3164    if (isDefined(value)) {
3165      element[name] = value;
3166    } else {
3167      return element[name];
3168    }
3169  },
3170
3171  text: (function() {
3172    getText.$dv = '';
3173    return getText;
3174
3175    function getText(element, value) {
3176      if (isUndefined(value)) {
3177        var nodeType = element.nodeType;
3178        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3179      }
3180      element.textContent = value;
3181    }
3182  })(),
3183
3184  val: function(element, value) {
3185    if (isUndefined(value)) {
3186      if (element.multiple && nodeName_(element) === 'select') {
3187        var result = [];
3188        forEach(element.options, function(option) {
3189          if (option.selected) {
3190            result.push(option.value || option.text);
3191          }
3192        });
3193        return result.length === 0 ? null : result;
3194      }
3195      return element.value;
3196    }
3197    element.value = value;
3198  },
3199
3200  html: function(element, value) {
3201    if (isUndefined(value)) {
3202      return element.innerHTML;
3203    }
3204    jqLiteDealoc(element, true);
3205    element.innerHTML = value;
3206  },
3207
3208  empty: jqLiteEmpty
3209}, function(fn, name) {
3210  /**
3211   * Properties: writes return selection, reads return first value
3212   */
3213  JQLite.prototype[name] = function(arg1, arg2) {
3214    var i, key;
3215    var nodeCount = this.length;
3216
3217    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3218    // in a way that survives minification.
3219    // jqLiteEmpty takes no arguments but is a setter.
3220    if (fn !== jqLiteEmpty &&
3221        (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3222      if (isObject(arg1)) {
3223
3224        // we are a write, but the object properties are the key/values
3225        for (i = 0; i < nodeCount; i++) {
3226          if (fn === jqLiteData) {
3227            // data() takes the whole object in jQuery
3228            fn(this[i], arg1);
3229          } else {
3230            for (key in arg1) {
3231              fn(this[i], key, arg1[key]);
3232            }
3233          }
3234        }
3235        // return self for chaining
3236        return this;
3237      } else {
3238        // we are a read, so read the first child.
3239        // TODO: do we still need this?
3240        var value = fn.$dv;
3241        // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3242        var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3243        for (var j = 0; j < jj; j++) {
3244          var nodeValue = fn(this[j], arg1, arg2);
3245          value = value ? value + nodeValue : nodeValue;
3246        }
3247        return value;
3248      }
3249    } else {
3250      // we are a write, so apply to all children
3251      for (i = 0; i < nodeCount; i++) {
3252        fn(this[i], arg1, arg2);
3253      }
3254      // return self for chaining
3255      return this;
3256    }
3257  };
3258});
3259
3260function createEventHandler(element, events) {
3261  var eventHandler = function(event, type) {
3262    // jQuery specific api
3263    event.isDefaultPrevented = function() {
3264      return event.defaultPrevented;
3265    };
3266
3267    var eventFns = events[type || event.type];
3268    var eventFnsLength = eventFns ? eventFns.length : 0;
3269
3270    if (!eventFnsLength) return;
3271
3272    if (isUndefined(event.immediatePropagationStopped)) {
3273      var originalStopImmediatePropagation = event.stopImmediatePropagation;
3274      event.stopImmediatePropagation = function() {
3275        event.immediatePropagationStopped = true;
3276
3277        if (event.stopPropagation) {
3278          event.stopPropagation();
3279        }
3280
3281        if (originalStopImmediatePropagation) {
3282          originalStopImmediatePropagation.call(event);
3283        }
3284      };
3285    }
3286
3287    event.isImmediatePropagationStopped = function() {
3288      return event.immediatePropagationStopped === true;
3289    };
3290
3291    // Copy event handlers in case event handlers array is modified during execution.
3292    if ((eventFnsLength > 1)) {
3293      eventFns = shallowCopy(eventFns);
3294    }
3295
3296    for (var i = 0; i < eventFnsLength; i++) {
3297      if (!event.isImmediatePropagationStopped()) {
3298        eventFns[i].call(element, event);
3299      }
3300    }
3301  };
3302
3303  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3304  //       events on `element`
3305  eventHandler.elem = element;
3306  return eventHandler;
3307}
3308
3309//////////////////////////////////////////
3310// Functions iterating traversal.
3311// These functions chain results into a single
3312// selector.
3313//////////////////////////////////////////
3314forEach({
3315  removeData: jqLiteRemoveData,
3316
3317  on: function jqLiteOn(element, type, fn, unsupported) {
3318    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3319
3320    // Do not add event handlers to non-elements because they will not be cleaned up.
3321    if (!jqLiteAcceptsData(element)) {
3322      return;
3323    }
3324
3325    var expandoStore = jqLiteExpandoStore(element, true);
3326    var events = expandoStore.events;
3327    var handle = expandoStore.handle;
3328
3329    if (!handle) {
3330      handle = expandoStore.handle = createEventHandler(element, events);
3331    }
3332
3333    // http://jsperf.com/string-indexof-vs-split
3334    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3335    var i = types.length;
3336
3337    while (i--) {
3338      type = types[i];
3339      var eventFns = events[type];
3340
3341      if (!eventFns) {
3342        events[type] = [];
3343
3344        if (type === 'mouseenter' || type === 'mouseleave') {
3345          // Refer to jQuery's implementation of mouseenter & mouseleave
3346          // Read about mouseenter and mouseleave:
3347          // http://www.quirksmode.org/js/events_mouse.html#link8
3348
3349          jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
3350            var target = this, related = event.relatedTarget;
3351            // For mousenter/leave call the handler if related is outside the target.
3352            // NB: No relatedTarget if the mouse left/entered the browser window
3353            if (!related || (related !== target && !target.contains(related))) {
3354              handle(event, type);
3355            }
3356          });
3357
3358        } else {
3359          if (type !== '$destroy') {
3360            addEventListenerFn(element, type, handle);
3361          }
3362        }
3363        eventFns = events[type];
3364      }
3365      eventFns.push(fn);
3366    }
3367  },
3368
3369  off: jqLiteOff,
3370
3371  one: function(element, type, fn) {
3372    element = jqLite(element);
3373
3374    //add the listener twice so that when it is called
3375    //you can remove the original function and still be
3376    //able to call element.off(ev, fn) normally
3377    element.on(type, function onFn() {
3378      element.off(type, fn);
3379      element.off(type, onFn);
3380    });
3381    element.on(type, fn);
3382  },
3383
3384  replaceWith: function(element, replaceNode) {
3385    var index, parent = element.parentNode;
3386    jqLiteDealoc(element);
3387    forEach(new JQLite(replaceNode), function(node) {
3388      if (index) {
3389        parent.insertBefore(node, index.nextSibling);
3390      } else {
3391        parent.replaceChild(node, element);
3392      }
3393      index = node;
3394    });
3395  },
3396
3397  children: function(element) {
3398    var children = [];
3399    forEach(element.childNodes, function(element) {
3400      if (element.nodeType === NODE_TYPE_ELEMENT) {
3401        children.push(element);
3402      }
3403    });
3404    return children;
3405  },
3406
3407  contents: function(element) {
3408    return element.contentDocument || element.childNodes || [];
3409  },
3410
3411  append: function(element, node) {
3412    var nodeType = element.nodeType;
3413    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3414
3415    node = new JQLite(node);
3416
3417    for (var i = 0, ii = node.length; i < ii; i++) {
3418      var child = node[i];
3419      element.appendChild(child);
3420    }
3421  },
3422
3423  prepend: function(element, node) {
3424    if (element.nodeType === NODE_TYPE_ELEMENT) {
3425      var index = element.firstChild;
3426      forEach(new JQLite(node), function(child) {
3427        element.insertBefore(child, index);
3428      });
3429    }
3430  },
3431
3432  wrap: function(element, wrapNode) {
3433    wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3434    var parent = element.parentNode;
3435    if (parent) {
3436      parent.replaceChild(wrapNode, element);
3437    }
3438    wrapNode.appendChild(element);
3439  },
3440
3441  remove: jqLiteRemove,
3442
3443  detach: function(element) {
3444    jqLiteRemove(element, true);
3445  },
3446
3447  after: function(element, newElement) {
3448    var index = element, parent = element.parentNode;
3449    newElement = new JQLite(newElement);
3450
3451    for (var i = 0, ii = newElement.length; i < ii; i++) {
3452      var node = newElement[i];
3453      parent.insertBefore(node, index.nextSibling);
3454      index = node;
3455    }
3456  },
3457
3458  addClass: jqLiteAddClass,
3459  removeClass: jqLiteRemoveClass,
3460
3461  toggleClass: function(element, selector, condition) {
3462    if (selector) {
vendor: 4,371 bytes, lines 3463-3601
3463      forEach(selector.split(' '), function(className) {
3464        var classCondition = condition;
3465        if (isUndefined(classCondition)) {
3466          classCondition = !jqLiteHasClass(element, className);
3467        }
3468        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3469      });
3470    }
3471  },
3472
3473  parent: function(element) {
3474    var parent = element.parentNode;
3475    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3476  },
3477
3478  next: function(element) {
3479    return element.nextElementSibling;
3480  },
3481
3482  find: function(element, selector) {
3483    if (element.getElementsByTagName) {
3484      return element.getElementsByTagName(selector);
3485    } else {
3486      return [];
3487    }
3488  },
3489
3490  clone: jqLiteClone,
3491
3492  triggerHandler: function(element, event, extraParameters) {
3493
3494    var dummyEvent, eventFnsCopy, handlerArgs;
3495    var eventName = event.type || event;
3496    var expandoStore = jqLiteExpandoStore(element);
3497    var events = expandoStore && expandoStore.events;
3498    var eventFns = events && events[eventName];
3499
3500    if (eventFns) {
3501      // Create a dummy event to pass to the handlers
3502      dummyEvent = {
3503        preventDefault: function() { this.defaultPrevented = true; },
3504        isDefaultPrevented: function() { return this.defaultPrevented === true; },
3505        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3506        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3507        stopPropagation: noop,
3508        type: eventName,
3509        target: element
3510      };
3511
3512      // If a custom event was provided then extend our dummy event with it
3513      if (event.type) {
3514        dummyEvent = extend(dummyEvent, event);
3515      }
3516
3517      // Copy event handlers in case event handlers array is modified during execution.
3518      eventFnsCopy = shallowCopy(eventFns);
3519      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3520
3521      forEach(eventFnsCopy, function(fn) {
3522        if (!dummyEvent.isImmediatePropagationStopped()) {
3523          fn.apply(element, handlerArgs);
3524        }
3525      });
3526    }
3527  }
3528}, function(fn, name) {
3529  /**
3530   * chaining functions
3531   */
3532  JQLite.prototype[name] = function(arg1, arg2, arg3) {
3533    var value;
3534
3535    for (var i = 0, ii = this.length; i < ii; i++) {
3536      if (isUndefined(value)) {
3537        value = fn(this[i], arg1, arg2, arg3);
3538        if (isDefined(value)) {
3539          // any function which returns a value needs to be wrapped
3540          value = jqLite(value);
3541        }
3542      } else {
3543        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3544      }
3545    }
3546    return isDefined(value) ? value : this;
3547  };
3548
3549  // bind legacy bind/unbind to on/off
3550  JQLite.prototype.bind = JQLite.prototype.on;
3551  JQLite.prototype.unbind = JQLite.prototype.off;
3552});
3553
3554
3555// Provider for private $$jqLite service
3556function $$jqLiteProvider() {
3557  this.$get = function $$jqLite() {
3558    return extend(JQLite, {
3559      hasClass: function(node, classes) {
3560        if (node.attr) node = node[0];
3561        return jqLiteHasClass(node, classes);
3562      },
3563      addClass: function(node, classes) {
3564        if (node.attr) node = node[0];
3565        return jqLiteAddClass(node, classes);
3566      },
3567      removeClass: function(node, classes) {
3568        if (node.attr) node = node[0];
3569        return jqLiteRemoveClass(node, classes);
3570      }
3571    });
3572  };
3573}
3574
3575/**
3576 * Computes a hash of an 'obj'.
3577 * Hash of a:
3578 *  string is string
3579 *  number is number as string
3580 *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3581 *         that is also assigned to the $$hashKey property of the object.
3582 *
3583 * @param obj
3584 * @returns {string} hash string such that the same input will have the same hash string.
3585 *         The resulting string key is in 'type:hashKey' format.
3586 */
3587function hashKey(obj, nextUidFn) {
3588  var key = obj && obj.$$hashKey;
3589
3590  if (key) {
3591    if (typeof key === 'function') {
3592      key = obj.$$hashKey();
3593    }
3594    return key;
3595  }
3596
3597  var objType = typeof obj;
3598  if (objType == 'function' || (objType == 'object' && obj !== null)) {
3599    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3600  } else {
3601    key = objType + ':' + obj;
vendor: 1,877 bytes, lines 3602-3677
3602  }
3603
3604  return key;
3605}
3606
3607/**
3608 * HashMap which can use objects as keys
3609 */
3610function HashMap(array, isolatedUid) {
3611  if (isolatedUid) {
3612    var uid = 0;
3613    this.nextUid = function() {
3614      return ++uid;
3615    };
3616  }
3617  forEach(array, this.put, this);
3618}
3619HashMap.prototype = {
3620  /**
3621   * Store key value pair
3622   * @param key key to store can be any type
3623   * @param value value to store can be any type
3624   */
3625  put: function(key, value) {
3626    this[hashKey(key, this.nextUid)] = value;
3627  },
3628
3629  /**
3630   * @param key
3631   * @returns {Object} the value for the key
3632   */
3633  get: function(key) {
3634    return this[hashKey(key, this.nextUid)];
3635  },
3636
3637  /**
3638   * Remove the key/value pair
3639   * @param key
3640   */
3641  remove: function(key) {
3642    var value = this[key = hashKey(key, this.nextUid)];
3643    delete this[key];
3644    return value;
3645  }
3646};
3647
3648var $$HashMapProvider = [function() {
3649  this.$get = [function() {
3650    return HashMap;
3651  }];
3652}];
3653
3654/**
3655 * @ngdoc function
3656 * @module ng
3657 * @name angular.injector
3658 * @kind function
3659 *
3660 * @description
3661 * Creates an injector object that can be used for retrieving services as well as for
3662 * dependency injection (see {@link guide/di dependency injection}).
3663 *
3664 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3665 *     {@link angular.module}. The `ng` module must be explicitly added.
3666 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3667 *     disallows argument name annotation inference.
3668 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3669 *
3670 * @example
3671 * Typical usage
3672 * ```js
3673 *   // create an injector
3674 *   var $injector = angular.injector(['ng']);
3675 *
3676 *   // use the injector to kick off your application
3677 *   // use the type inference to auto inject arguments, or use impl
3677icit injection
3678 *   $injector.invoke(function($rootScope, $compile, $document) {
3679 *     $compile($document)($rootScope);
3680 *     $rootScope.$digest();
3681 *   });
3682 * ```
3683 *
3684 * Sometimes you want to get access to the injector of a currently running Angular app
3685 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3686 * application has been bootstrapped. You can do this using the extra `injector()` added
3687 * to JQuery/jqLite elements. See {@link angular.element}.
3688 *
3689 * *This is fairly rare but could be the case if a third party library is injecting the
3690 * markup.*
3691 *
3692 * In the following example a new block of HTML containing a `ng-controller`
3693 * directive is added to the end of the document body by JQuery. We then compile and link
3694 * it into the current AngularJS scope.
3695 *
3696 * ```js
3697 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3698 * $(document.body).append($div);
3699 *
3700 * angular.element(document).injector().invoke(function($compile) {
3701 *   var scope = angular.element($div).scope();
3702 *   $compile($div)(scope);
3703 * });
3704 * ```
3705 */
3706
3707
3708/**
3709 * @ngdoc module
3710 * @name auto
3711 * @description
3712 *
3713 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3714 */
3715
3716var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3717var FN_ARG_SPLIT = /,/;
3718var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3719var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3720var $injectorMinErr = minErr('$injector');
3721
3722function anonFn(fn) {
3723  // For anonymous functions, showing at the very least the function signature can help in
3724  // debugging.
3725  var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3726      args = fnText.match(FN_ARGS);
3727  if (args) {
3728    return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3729  }
3730  return 'fn';
3731}
3732
3733function annotate(fn, strictDi, name) {
3734  var $inject,
3735      fnText,
3736      argDecl,
3737      last;
3738
3739  if (typeof fn === 'function') {
3740    if (!($inject = fn.$inject)) {
3741      $inject = [];
3742      if (fn.length) {
3743        if (strictDi) {
3744          if (!isString(name) || !name) {
3745            name = fn.name || anonFn(fn);
3746          }
3747          throw $injectorMinErr('strictdi',
3748            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3749        }
3750        fnText = fn.toString().replace(STRIP_COMMENTS, '');
3751        argDecl = fnText.match(FN_ARGS);
3752        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3753          arg.replace(FN_ARG, function(all, underscore, name) {
3754            $inject.push(name);
3755          });
3756        });
3757      }
3758      fn.$inject = $inject;
3759    }
3760  } else if (isArray(fn)) {
3761    last = fn.length - 1;
3762    assertArgFn(fn[last], 'fn');
3763    $inject = fn.slice(0, last);
3764  } else {
3765    assertArgFn(fn, 'fn', true);
3766  }
3767  return $inject;
3768}
3769
3770///////////////////////////////////////
3771
3772/**
3773 * @ngdoc service
3774 * @name $injector
3775 *
3776 * @description
3777 *
3778 * `$injector` is used to retrieve object instances as defined by
3779 * {@link auto.$provide provider}, instantiate types, invoke methods,
3780 * and load modules.
3781 *
3782 * The following always holds true:
3783 *
3784 * ```js
3785 *   var $injector = angular.injector();
3786 *   expect($injector.get('$injector')).toBe($injector);
3787 *   expect($injector.invoke(function($injector) {
3788 *     return $injector;
3789 *   })).toBe($injector);
3790 * ```
3791 *
3792 * # Injection Function Annotation
3793 *
3794 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3795 * following are all valid ways of annotating function with injection arguments and are equivalent.
3796 *
3797 * ```js
3798 *   // inferred (only works if code not minified/obfuscated)
3799 *   $injector.invoke(function(serviceA){});
3800 *
3801 *   // annotated
3802 *   function explicit(serviceA) {};
3803 *   explicit.$inject = ['serviceA'];
3804 *   $injector.invoke(explicit);
3805 *
3806 *   // inline
3807 *   $injector.invoke(['serviceA', function(serviceA){}]);
3808 * ```
3809 *
3810 * ## Inference
3811 *
3812 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3813 * can then be parsed and the function arguments can be extracted. This method of discovering
3814 * annotations is disallowed when the injector is in strict mode.
3815 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3816 * argument names.
3817 *
3818 * ## `$inject` Annotation
3819 * By adding an `$inject` property onto a function the injection parameters can be specified.
3820 *
3821 * ## Inline
3822 * As an array of injection names, where the last item in the array is the function to call.
3823 */
3824
3825/**
3826 * @ngdoc method
3827 * @name $injector#get
3828 *
3829 * @description
3830 * Return an instance of the service.
3831 *
3832 * @param {string} name The name of the instance to retrieve.
3833 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3834 * @return {*} The instance.
3835 */
3836
3837/**
3838 * @ngdoc method
3839 * @name $injector#invoke
3840 *
3841 * @description
3842 * Invoke the method and supply the method arguments from the `$injector`.
3843 *
3844 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3845 *   injected according to the {@link guide/di $inject Annotation} rules.
3846 * @param {Object=} self The `this` for the invoked method.
3847 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3848 *                         object first, before the `$injector` is consulted.
3849 * @returns {*} the value returned by the invoked `fn` function.
3850 */
3851
3852/**
3853 * @ngdoc method
3854 * @name $injector#has
3855 *
3856 * @description
3857 * Allows the user to query if the particular service exists.
3858 *
3859 * @param {string} name Name of the service to query.
3860 * @returns {boolean} `true` if injector has given service.
3861 */
3862
3863/**
3864 * @ngdoc method
3865 * @name $injector#instantiate
3866 * @description
3867 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3868 * operator, and supplies all of the arguments to the constructor function as specified by the
3869 * constructor annotation.
3870 *
3871 * @param {Function} Type Annotated constructor function.
3872 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3873 * object first, before the `$injector` is consulted.
3874 * @returns {Object} new instance of `Type`.
3875 */
3876
3877/**
3878 * @ngdoc method
3879 * @name $injector#annotate
3880 *
3881 * @description
3882 * Returns an array of service names which the function is requesting for injection. This API is
3883 * used by the injector to determine which services need to be injected into the function when the
3884 * function is invoked. There are three ways in which the function can be annotated with the needed
3885 * dependencies.
3886 *
3887 * # Argument names
3888 *
3889 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3890 * by converting the function into a string using `toString()` method and extracting the argument
3891 * names.
3892 * ```js
3893 *   // Given
3894 *   function MyController($scope, $route) {
3895 *     // ...
3896 *   }
3897 *
3898 *   // Then
3899 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3900 * ```
3901 *
3902 * You can disallow this method by using strict injection mode.
3903 *
3904 * This method does not work with code minification / obfuscation. For this reason the following
3905 * annotation strategies are supported.
3906 *
3907 * # The `$inject` property
3908 *
3909 * If a function has an `$inject` property and its value is an array of strings, then the strings
3910 * represent names of services to be injected into the function.
3911 * ```js
3912 *   // Given
3913 *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
3914 *     // ...
3915 *   }
3916 *   // Define function dependencies
3917 *   MyController['$inject'] = ['$scope', '$route'];
3918 *
3919 *   // Then
3920 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3921 * ```
3922 *
3923 * # The array notation
3924 *
3925 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3926 * is very inconvenient. In these situations using the array notation to specify the dependencies in
3927 * a way that survives minification is a better choice:
3928 *
3929 * ```js
3930 *   // We wish to write this (not minification / obfuscation safe)
3931 *   injector.invoke(function($compile, $rootScope) {
3932 *     // ...
3933 *   });
3934 *
3935 *   // We are forced to write break inlining
3936 *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
3937 *     // ...
3938 *   };
3939 *   tmpFn.$inject = ['$compile', '$rootScope'];
3940 *   injector.invoke(tmpFn);
3941 *
3942 *   // To better support inline function the inline annotation is supported
3943 *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
3944 *     // ...
3945 *   }]);
3946 *
3947 *   // Therefore
3948 *   expect(injector.annotate(
3949 *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
3950 *    ).toEqual(['$compile', '$rootScope']);
3951 * ```
3952 *
3953 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
3954 * be retrieved as described above.
3955 *
3956 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
3957 *
3958 * @returns {Array.<string>} The names of the services which the function requires.
3959 */
3960
3961
3962
3963
3964/**
3965 * @ngdoc service
3966 * @name $provide
3967 *
3968 * @description
3969 *
3970 * The {@link auto.$provide $provide} service has a number of methods for registering components
3971 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
3972 * {@link angular.Module}.
3973 *
3974 * An Angular **service** is a singleton object created by a **service factory**.  These **service
3975 * factories** are functions which, in turn, are created by a **service provider**.
3976 * The **service providers** are constructor functions. When instantiated they must contain a
3977 * property called `$get`, which holds the **service factory** function.
3978 *
3979 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
3980 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
3981 * function to get the instance of the **service**.
3982 *
3983 * Often services have no configuration options and there is no need to add methods to the service
3984 * provider.  The provider will be no more than a constructor function with a `$get` property. For
3985 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
3986 * services without specifying a provider.
3987 *
3988 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
3989 *     {@link auto.$injector $injector}
3990 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
3991 *     providers and services.
3992 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
3993 *     services, not providers.
3994 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
3995 *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
3996 *     given factory function.
3997 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
3998 *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
3999 *      a new object using the given constructor function.
4000 *
4001 * See the individual methods for more information and examples.
4002 */
4003
4004/**
4005 * @ngdoc method
4006 * @name $provide#provider
4007 * @description
4008 *
4009 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4010 * are constructor functions, whose instances are responsible for "providing" a factory for a
4011 * service.
4012 *
4013 * Service provider names start with the name of the service they provide followed by `Provider`.
4014 * For example, the {@link ng.$log $log} service has a provider called
4015 * {@link ng.$logProvider $logProvider}.
4016 *
4017 * Service provider objects can have additional methods which allow configuration of the provider
4018 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4019 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4020 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4021 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4022 * console or not.
4023 *
4024 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4025                        'Provider'` key.
4026 * @param {(Object|function())} provider If the provider is:
4027 *
4028 *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4029 *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4030 *   - `Constructor`: a new instance of the provider will be created using
4031 *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4032 *
4033 * @returns {Object} registered provider instance
4034
4035 * @example
4036 *
4037 * The following example shows how to create a simple event tracking service and register it using
4038 * {@link auto.$provide#provider $provide.provider()}.
4039 *
4040 * ```js
4041 *  // Define the eventTracker provider
4042 *  function EventTrackerProvider() {
4043 *    var trackingUrl = '/track';
4044 *
4045 *    // A provider method for configuring where the tracked events should been saved
4046 *    this.setTrackingUrl = function(url) {
4047 *      trackingUrl = url;
4048 *    };
4049 *
4050 *    // The service factory function
4051 *    this.$get = ['$http', function($http) {
4052 *      var trackedEvents = {};
4053 *      return {
4054 *        // Call this to track an event
4055 *        event: function(event) {
4056 *          var count = trackedEvents[event] || 0;
4057 *          count += 1;
4058 *          trackedEvents[event] = count;
4059 *          return count;
4060 *        },
4061 *        // Call this to save the tracked events to the trackingUrl
4062 *        save: function() {
4063 *          $http.post(trackingUrl, trackedEvents);
4064 *        }
4065 *      };
4066 *    }];
4067 *  }
4068 *
4069 *  describe('eventTracker', function() {
4070 *    var postSpy;
4071 *
4072 *    beforeEach(module(function($provide) {
4073 *      // Register the eventTracker provider
4074 *      $provide.provider('eventTracker', EventTrackerProvider);
4075 *    }));
4076 *
4077 *    beforeEach(module(function(eventTrackerProvider) {
4078 *      // Configure eventTracker provider
4079 *      eventTrackerProvider.setTrackingUrl('/custom-track');
4080 *    }));
4081 *
4082 *    it('tracks events', inject(function(eventTracker) {
4083 *      expect(eventTracker.event('login')).toEqual(1);
4084 *      expect(eventTracker.event('login')).toEqual(2);
4085 *    }));
4086 *
4087 *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4088 *      postSpy = spyOn($http, 'post');
4089 *      eventTracker.event('login');
4090 *      eventTracker.save();
4091 *      expect(postSpy).toHaveBeenCalled();
4092 *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4093 *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4094 *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4095 *    }));
4096 *  });
4097 * ```
4098 */
4099
4100/**
4101 * @ngdoc method
4102 * @name $provide#factory
4103 * @description
4104 *
4105 * Register a **service factory**, which will be called to return the service instance.
4106 * This is short for registering a service where its provider consists of only a `$get` property,
4107 * which is the given service factory function.
4108 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4109 * configure your service in a provider.
4110 *
4111 * @param {string} name The name of the instance.
4112 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4113 *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4114 * @returns {Object} registered provider instance
4115 *
4116 * @example
4117 * Here is an example of registering a service
4118 * ```js
4119 *   $provide.factory('ping', ['$http', function($http) {
4120 *     return function ping() {
4121 *       return $http.send('/ping');
4122 *     };
4123 *   }]);
4124 * ```
4125 * You would then inject and use this service like this:
4126 * ```js
4127 *   someModule.controller('Ctrl', ['ping', function(ping) {
4128 *     ping();
4129 *   }]);
4130 * ```
4131 */
4132
4133
4134/**
4135 * @ngdoc method
4136 * @name $provide#service
4137 * @description
4138 *
4139 * Register a **service constructor**, which will be invoked with `new` to create the service
4140 * instance.
4141 * This is short for registering a service where its provider's `$get` property is the service
4142 * constructor function that will be used to instantiate the service instance.
4143 *
4144 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4145 * as a type/class.
4146 *
4147 * @param {string} name The name of the instance.
4148 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4149 *     that will be instantiated.
4150 * @returns {Object} registered provider instance
4151 *
4152 * @example
4153 * Here is an example of registering a service using
4154 * {@link auto.$provide#service $provide.service(class)}.
4155 * ```js
4156 *   var Ping = function($http) {
4157 *     this.$http = $http;
4158 *   };
4159 *
4160 *   Ping.$inject = ['$http'];
4161 *
4162 *   Ping.prototype.send = function() {
4163 *     return this.$http.get('/ping');
4164 *   };
4165 *   $provide.service('ping', Ping);
4166 * ```
4167 * You would then inject and use this service like this:
4168 * ```js
4169 *   someModule.controller('Ctrl', ['ping', function(ping) {
4170 *     ping.send();
4171 *   }]);
4172 * ```
4173 */
4174
4175
4176/**
4177 * @ngdoc method
4178 * @name $provide#value
4179 * @description
4180 *
4181 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4182 * number, an array, an object or a function.  This is short for registering a service where its
4183 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4184 * service**.
4185 *
4186 * Value services are similar to constant services, except that they cannot be injected into a
4187 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4188 * an Angular
4189 * {@link auto.$provide#decorator decorator}.
4190 *
4191 * @param {string} name The name of the instance.
4192 * @param {*} value The value.
4193 * @returns {Object} registered provider instance
4194 *
4195 * @example
4196 * Here are some examples of creating value services.
4197 * ```js
4198 *   $provide.value('ADMIN_USER', 'admin');
4199 *
4200 *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4201 *
4202 *   $provide.value('halfOf', function(value) {
4203 *     return value / 2;
4204 *   });
4205 * ```
4206 */
4207
4208
4209/**
4210 * @ngdoc method
4211 * @name $provide#constant
4212 * @description
4213 *
4214 * Register a **constant service**, such as a string, a number, an array, an object or a function,
4215 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
4216 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4217 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4218 *
4219 * @param {string} name The name of the constant.
4220 * @param {*} value The constant value.
4221 * @returns {Object} registered instance
4222 *
4223 * @example
4224 * Here a some examples of creating constants:
4225 * ```js
4226 *   $provide.constant('SHARD_HEIGHT', 306);
4227 *
4228 *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4229 *
4230 *   $provide.constant('double', function(value) {
4231 *     return value * 2;
4232 *   });
4233 * ```
4234 */
4235
4236
4237/**
4238 * @ngdoc method
4239 * @name $provide#decorator
4240 * @description
4241 *
4242 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4243 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
4244 * service. The object returned by the decorator may be the original service, or a new service
4245 * object which replaces or wraps and delegates to the original service.
4246 *
4247 * @param {string} name The name of the service to decorate.
4248 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4249 *    instantiated and should return the decorated service instance. The function is called using
4250 *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4251 *    Local injection arguments:
4252 *
4253 *    * `$delegate` - The original service instance, which can be monkey patched, configured,
4254 *      decorated or delegated to.
4255 *
4256 * @example
4257 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4258 * calls to {@link ng.$log#error $log.warn()}.
4259 * ```js
4260 *   $provide.decorator('$log', ['$delegate', function($delegate) {
4261 *     $delegate.warn = $delegate.error;
4262 *     return $delegate;
4263 *   }]);
4264 * ```
4265 */
4266
4267
4268function createInjector(modulesToLoad, strictDi) {
4269  strictDi = (strictDi === true);
4270  var INSTANTIATING = {},
4271      providerSuffix = 'Provider',
4272      path = [],
4273      loadedModules = new HashMap([], true),
4274      providerCache = {
4275        $provide: {
4276            provider: supportObject(provider),
4277            factory: supportObject(factory),
4278            service: supportObject(service),
4279            value: supportObject(value),
4280            constant: supportObject(constant),
4281            decorator: decorator
4282          }
4283      },
4284      providerInjector = (providerCache.$injector =
4285          createInternalInjector(providerCache, function(serviceName, caller) {
4286            if (angular.isString(caller)) {
4287              path.push(caller);
4288            }
4289            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4290          })),
4291      instanceCache = {},
4292      instanceInjector = (instanceCache.$injector =
4293          createInternalInjector(instanceCache, function(serviceName, caller) {
4294            var provider = providerInjector.get(serviceName + providerSuffix, caller);
4295            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4296          }));
4297
4298
4299  forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4300
4301  return instanceInjector;
4302
4303  ////////////////////////////////////
4304  // $provider
4305  ////////////////////////////////////
4306
4307  function supportObject(delegate) {
4308    return function(key, value) {
4309      if (isObject(key)) {
4310        forEach(key, reverseParams(delegate));
4311      } else {
4312        return delegate(key, value);
4313      }
4314    };
4315  }
4316
4317  function provider(name, provider_) {
4318    assertNotHasOwnProperty(name, 'service');
4319    if (isFunction(provider_) || isArray(provider_)) {
4320      provider_ = providerInjector.instantiate(provider_);
4321    }
4322    if (!provider_.$get) {
4323      throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4324    }
4325    return providerCache[name + providerSuffix] = provider_;
4326  }
4327
4328  function enforceReturnValue(name, factory) {
4329    return function enforcedReturnValue() {
4330      var result = instanceInjector.invoke(factory, this);
4331      if (isUndefined(result)) {
4332        throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4333      }
4334      return result;
4335    };
4336  }
4337
4338  function factory(name, factoryFn, enforce) {
4339    return provider(name, {
4340      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4341    });
vendor: 4,556 bytes, lines 4342-4475
4342  }
4343
4344  function service(name, constructor) {
4345    return factory(name, ['$injector', function($injector) {
4346      return $injector.instantiate(constructor);
4347    }]);
4348  }
4349
4350  function value(name, val) { return factory(name, valueFn(val), false); }
4351
4352  function constant(name, value) {
4353    assertNotHasOwnProperty(name, 'constant');
4354    providerCache[name] = value;
4355    instanceCache[name] = value;
4356  }
4357
4358  function decorator(serviceName, decorFn) {
4359    var origProvider = providerInjector.get(serviceName + providerSuffix),
4360        orig$get = origProvider.$get;
4361
4362    origProvider.$get = function() {
4363      var origInstance = instanceInjector.invoke(orig$get, origProvider);
4364      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4365    };
4366  }
4367
4368  ////////////////////////////////////
4369  // Module Loading
4370  ////////////////////////////////////
4371  function loadModules(modulesToLoad) {
4372    assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4373    var runBlocks = [], moduleFn;
4374    forEach(modulesToLoad, function(module) {
4375      if (loadedModules.get(module)) return;
4376      loadedModules.put(module, true);
4377
4378      function runInvokeQueue(queue) {
4379        var i, ii;
4380        for (i = 0, ii = queue.length; i < ii; i++) {
4381          var invokeArgs = queue[i],
4382              provider = providerInjector.get(invokeArgs[0]);
4383
4384          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4385        }
4386      }
4387
4388      try {
4389        if (isString(module)) {
4390          moduleFn = angularModule(module);
4391          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4392          runInvokeQueue(moduleFn._invokeQueue);
4393          runInvokeQueue(moduleFn._configBlocks);
4394        } else if (isFunction(module)) {
4395            runBlocks.push(providerInjector.invoke(module));
4396        } else if (isArray(module)) {
4397            runBlocks.push(providerInjector.invoke(module));
4398        } else {
4399          assertArgFn(module, 'module');
4400        }
4401      } catch (e) {
4402        if (isArray(module)) {
4403          module = module[module.length - 1];
4404        }
4405        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4406          // Safari & FF's stack traces don't contain error.message content
4407          // unlike those of Chrome and IE
4408          // So if stack doesn't contain message, we create a new string that contains both.
4409          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4410          /* jshint -W022 */
4411          e = e.message + '\n' + e.stack;
4412        }
4413        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4414                  module, e.stack || e.message || e);
4415      }
4416    });
4417    return runBlocks;
4418  }
4419
4420  ////////////////////////////////////
4421  // internal Injector
4422  ////////////////////////////////////
4423
4424  function createInternalInjector(cache, factory) {
4425
4426    function getService(serviceName, caller) {
4427      if (cache.hasOwnProperty(serviceName)) {
4428        if (cache[serviceName] === INSTANTIATING) {
4429          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4430                    serviceName + ' <- ' + path.join(' <- '));
4431        }
4432        return cache[serviceName];
4433      } else {
4434        try {
4435          path.unshift(serviceName);
4436          cache[serviceName] = INSTANTIATING;
4437          return cache[serviceName] = factory(serviceName, caller);
4438        } catch (err) {
4439          if (cache[serviceName] === INSTANTIATING) {
4440            delete cache[serviceName];
4441          }
4442          throw err;
4443        } finally {
4444          path.shift();
4445        }
4446      }
4447    }
4448
4449    function invoke(fn, self, locals, serviceName) {
4450      if (typeof locals === 'string') {
4451        serviceName = locals;
4452        locals = null;
4453      }
4454
4455      var args = [],
4456          $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4457          length, i,
4458          key;
4459
4460      for (i = 0, length = $inject.length; i < length; i++) {
4461        key = $inject[i];
4462        if (typeof key !== 'string') {
4463          throw $injectorMinErr('itkn',
4464                  'Incorrect injection token! Expected service name as string, got {0}', key);
4465        }
4466        args.push(
4467          locals && locals.hasOwnProperty(key)
4468          ? locals[key]
4469          : getService(key, serviceName)
4470        );
4471      }
4472      if (isArray(fn)) {
4473        fn = fn[length];
4474      }
4475
vendor: 4,918 bytes, lines 4476-4591
4476      // http://jsperf.com/angularjs-invoke-apply-vs-switch
4477      // #5388
4478      return fn.apply(self, args);
4479    }
4480
4481    function instantiate(Type, locals, serviceName) {
4482      // Check if Type is annotated and use just the given function at n-1 as parameter
4483      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4484      // Object creation: http://jsperf.com/create-constructor/2
4485      var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4486      var returnedValue = invoke(Type, instance, locals, serviceName);
4487
4488      return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4489    }
4490
4491    return {
4492      invoke: invoke,
4493      instantiate: instantiate,
4494      get: getService,
4495      annotate: createInjector.$$annotate,
4496      has: function(name) {
4497        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4498      }
4499    };
4500  }
4501}
4502
4503createInjector.$$annotate = annotate;
4504
4505/**
4506 * @ngdoc provider
4507 * @name $anchorScrollProvider
4508 *
4509 * @description
4510 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4511 * {@link ng.$location#hash $location.hash()} changes.
4512 */
4513function $AnchorScrollProvider() {
4514
4515  var autoScrollingEnabled = true;
4516
4517  /**
4518   * @ngdoc method
4519   * @name $anchorScrollProvider#disableAutoScrolling
4520   *
4521   * @description
4522   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4523   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4524   * Use this method to disable automatic scrolling.
4525   *
4526   * If automatic scrolling is disabled, one must explicitly call
4527   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4528   * current hash.
4529   */
4530  this.disableAutoScrolling = function() {
4531    autoScrollingEnabled = false;
4532  };
4533
4534  /**
4535   * @ngdoc service
4536   * @name $anchorScroll
4537   * @kind function
4538   * @requires $window
4539   * @requires $location
4540   * @requires $rootScope
4541   *
4542   * @description
4543   * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4544   * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4545   * in the
4546   * [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
4547   *
4548   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4549   * match any anchor whenever it changes. This can be disabled by calling
4550   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4551   *
4552   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4553   * vertical scroll-offset (either fixed or dynamic).
4554   *
4555   * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4556   *                       {@link ng.$location#hash $location.hash()} will be used.
4557   *
4558   * @property {(number|function|jqLite)} yOffset
4559   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4560   * positioned elements at the top of the page, such as navbars, headers etc.
4561   *
4562   * `yOffset` can be specified in various ways:
4563   * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4564   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4565   *   a number representing the offset (in pixels).<br /><br />
4566   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4567   *   the top of the page to the element's bottom will be used as offset.<br />
4568   *   **Note**: The element will be taken into account only as long as its `position` is set to
4569   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4570   *   their height and/or positioning according to the viewport's size.
4571   *
4572   * <br />
4573   * <div class="alert alert-warning">
4574   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4575   * not some child element.
4576   * </div>
4577   *
4578   * @example
4579     <example module="anchorScrollExample">
4580       <file name="index.html">
4581         <div id="scrollArea" ng-controller="ScrollController">
4582           <a ng-click="gotoBottom()">Go to bottom</a>
4583           <a id="bottom"></a> You're at the bottom!
4584         </div>
4585       </file>
4586       <file name="script.js">
4587         angular.module('anchorScrollExample', [])
4588           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4589             function ($scope, $location, $anchorScroll) {
4590               $scope.gotoBottom = function() {
4591                 // set the location.hash to the id of
4592                 // the element you wish to scroll to.
4593                 $location.hash('bottom');
4594
4595                 // call $anchorScroll()
4596                 $anchorScroll();
4597               };
4598             }]);
4599       </file>
4600       <file name="style.css">
4601         #scrollArea {
4602           height: 280px;
4603           overflow: auto;
4604         }
4605
4606         #bottom {
4607           display: block;
4608           margin-top: 2000px;
4609         }
4610       </file>
4611     </example>
4612   *
4613   * <hr />
4614   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4615   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4616   *
4617   * @example
4618     <example module="anchorScrollOffsetExample">
4619       <file name="index.html">
4620         <div class="fixed-header" ng-controller="headerCtrl">
4621           <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4622             Go to anchor {{x}}
4623           </a>
4624         </div>
4625         <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4626           Anchor {{x}} of 5
4627         </div>
4628       </file>
4629       <file name="script.js">
4630         angular.module('anchorScrollOffsetExample', [])
4631           .run(['$anchorScroll', function($anchorScroll) {
4632             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4633           }])
4634           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4635             function ($anchorScroll, $location, $scope) {
vendor: 5,324 bytes, lines 4636-4805
4636               $scope.gotoAnchor = function(x) {
4637                 var newHash = 'anchor' + x;
4638                 if ($location.hash() !== newHash) {
4639                   // set the $location.hash to `newHash` and
4640                   // $anchorScroll will automatically scroll to it
4641                   $location.hash('anchor' + x);
4642                 } else {
4643                   // call $anchorScroll() explicitly,
4644                   // since $location.hash hasn't changed
4645                   $anchorScroll();
4646                 }
4647               };
4648             }
4649           ]);
4650       </file>
4651       <file name="style.css">
4652         body {
4653           padding-top: 50px;
4654         }
4655
4656         .anchor {
4657           border: 2px dashed DarkOrchid;
4658           padding: 10px 10px 200px 10px;
4659         }
4660
4661         .fixed-header {
4662           background-color: rgba(0, 0, 0, 0.2);
4663           height: 50px;
4664           position: fixed;
4665           top: 0; left: 0; right: 0;
4666         }
4667
4668         .fixed-header > a {
4669           display: inline-block;
4670           margin: 5px 15px;
4671         }
4672       </file>
4673     </example>
4674   */
4675  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4676    var document = $window.document;
4677
4678    // Helper function to get first anchor from a NodeList
4679    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4680    //  and working in all supported browsers.)
4681    function getFirstAnchor(list) {
4682      var result = null;
4683      Array.prototype.some.call(list, function(element) {
4684        if (nodeName_(element) === 'a') {
4685          result = element;
4686          return true;
4687        }
4688      });
4689      return result;
4690    }
4691
4692    function getYOffset() {
4693
4694      var offset = scroll.yOffset;
4695
4696      if (isFunction(offset)) {
4697        offset = offset();
4698      } else if (isElement(offset)) {
4699        var elem = offset[0];
4700        var style = $window.getComputedStyle(elem);
4701        if (style.position !== 'fixed') {
4702          offset = 0;
4703        } else {
4704          offset = elem.getBoundingClientRect().bottom;
4705        }
4706      } else if (!isNumber(offset)) {
4707        offset = 0;
4708      }
4709
4710      return offset;
4711    }
4712
4713    function scrollTo(elem) {
4714      if (elem) {
4715        elem.scrollIntoView();
4716
4717        var offset = getYOffset();
4718
4719        if (offset) {
4720          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4721          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4722          // top of the viewport.
4723          //
4724          // IF the number of pixels from the top of `elem` to the end of the page's content is less
4725          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4726          // way down the page.
4727          //
4728          // This is often the case for elements near the bottom of the page.
4729          //
4730          // In such cases we do not need to scroll the whole `offset` up, just the difference between
4731          // the top of the element and the offset, which is enough to align the top of `elem` at the
4732          // desired position.
4733          var elemTop = elem.getBoundingClientRect().top;
4734          $window.scrollBy(0, elemTop - offset);
4735        }
4736      } else {
4737        $window.scrollTo(0, 0);
4738      }
4739    }
4740
4741    function scroll(hash) {
4742      hash = isString(hash) ? hash : $location.hash();
4743      var elm;
4744
4745      // empty hash, scroll to the top of the page
4746      if (!hash) scrollTo(null);
4747
4748      // element with given id
4749      else if ((elm = document.getElementById(hash))) scrollTo(elm);
4750
4751      // first anchor with given name :-D
4752      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4753
4754      // no element and hash == 'top', scroll to the top of the page
4755      else if (hash === 'top') scrollTo(null);
4756    }
4757
4758    // does not scroll when user clicks on anchor link that is currently on
4759    // (no url change, no $location.hash() change), browser native does scroll
4760    if (autoScrollingEnabled) {
4761      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4762        function autoScrollWatchAction(newVal, oldVal) {
4763          // skip the initial scroll if $location.hash is empty
4764          if (newVal === oldVal && newVal === '') return;
4765
4766          jqLiteDocumentLoaded(function() {
4767            $rootScope.$evalAsync(scroll);
4768          });
4769        });
4770    }
4771
4772    return scroll;
4773  }];
4774}
4775
4776var $animateMinErr = minErr('$animate');
4777var ELEMENT_NODE = 1;
4778var NG_ANIMATE_CLASSNAME = 'ng-animate';
4779
4780function mergeClasses(a,b) {
4781  if (!a && !b) return '';
4782  if (!a) return b;
4783  if (!b) return a;
4784  if (isArray(a)) a = a.join(' ');
4785  if (isArray(b)) b = b.join(' ');
4786  return a + ' ' + b;
4787}
4788
4789function extractElementNode(element) {
4790  for (var i = 0; i < element.length; i++) {
4791    var elm = element[i];
4792    if (elm.nodeType === ELEMENT_NODE) {
4793      return elm;
4794    }
4795  }
4796}
4797
4798function splitClasses(classes) {
4799  if (isString(classes)) {
4800    classes = classes.split(' ');
4801  }
4802
4803  // Use createMap() to prevent class assumptions involving property names in
4804  // Object.prototype
4805  var obj = createMap();
4806  forEach(classes, function(klass) {
4807    // sometimes the split leaves empty string values
4808    // incase extra spaces were applied to the options
4809    if (klass.length) {
4810      obj[klass] = true;
4811    }
4812  });
4813  return obj;
4814}
4815
4816// if any other type of options value besides an Object value is
4817// passed into the $animate.method() animation then this helper code
4818// will be run which will ignore it. While this patch is not the
4819// greatest solution to this, a lot of existing plugins depend on
4820// $animate to either call the callback (< 1.2) or return a promise
4821// that can be changed. This helper function ensures that the options
4822// are wiped clean incase a callback function is provided.
4823function prepareAnimateOptions(options) {
4824  return isObject(options)
4825      ? options
4826      : {};
4827}
4828
4829var $$CoreAnimateRunnerProvider = function() {
4830  this.$get = ['$q', '$$rAF', function($q, $$rAF) {
4831    function AnimateRunner() {}
4832    AnimateRunner.all = noop;
4833    AnimateRunner.chain = noop;
4834    AnimateRunner.prototype = {
4835      end: noop,
4836      cancel: noop,
4837      resume: noop,
4838      pause: noop,
4839      complete: noop,
4840      then: function(pass, fail) {
4841        return $q(function(resolve) {
4842          $$rAF(function() {
4843            resolve();
4844          });
4845        }).then(pass, fail);
4846      }
4847    };
4848    return AnimateRunner;
4849  }];
4850};
4851
4852// this is prefixed with Core since it conflicts with
4853// the animateQueueProvider defined in ngAnimate/animateQueue.js
4854var $$CoreAnimateQueueProvider = function() {
4855  var postDigestQueue = new HashMap();
4856  var postDigestElements = [];
4857
4858  this.$get = ['$$AnimateRunner', '$rootScope',
4859       function($$AnimateRunner,   $rootScope) {
4860    return {
4861      enabled: noop,
4862      on: noop,
4863      off: noop,
4864      pin: noop,
4865
4866      push: function(element, event, options, domOperation) {
4867        domOperation        && domOperation();
4868
4869        options = options || {};
4870        options.from        && element.css(options.from);
4871        options.to          && element.css(options.to);
4872
4873        if (options.addClass || options.removeClass) {
4874          addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
4875        }
4876
4877        return new $$AnimateRunner(); // jshint ignore:line
4878      }
4879    };
4880
4881
4882    function updateData(data, classes, value) {
4883      var changed = false;
4884      if (classes) {
4885        classes = isString(classes) ? classes.split(' ') :
4886                  isArray(classes) ? classes : [];
4887        forEach(classes, function(className) {
4888          if (className) {
4889            changed = true;
4890            data[className] = value;
4891          }
4892        });
4893      }
4894      return changed;
4895    }
4896
4897    function handleCSSClassChanges() {
4898      forEach(postDigestElements, function(element) {
4899        var data = postDigestQueue.get(element);
4900        if (data) {
4901          var existing = splitClasses(element.attr('class'));
4902          var toAdd = '';
4903          var toRemove = '';
4904          forEach(data, function(status, className) {
4905            var hasClass = !!existing[className];
4906            if (status !== hasClass) {
4907              if (status) {
4908                toAdd += (toAdd.length ? ' ' : '') + className;
4909              } else {
4910                toRemove += (toRemove.length ? ' ' : '') + className;
4911              }
4912            }
4913          });
4914
4915          forEach(element, function(elm) {
4916            toAdd    && jqLiteAddClass(elm, toAdd);
4917            toRemove && jqLiteRemoveClass(elm, toRemove);
4918          });
4919          postDigestQueue.remove(element);
4920        }
4921      });
4922      postDigestElements.length = 0;
4923    }
4924
4925
4926    function addRemoveClassesPostDigest(element, add, remove) {
4927      var data = postDigestQueue.get(element) || {};
4928
4929      var classesAdded = updateData(data, add, true);
4930      var classesRemoved = updateData(data, remove, false);
4931
4932      if (classesAdded || classesRemoved) {
4933
4934        postDigestQueue.put(element, data);
4935        postDigestElements.push(element);
4936
4937        if (postDigestElements.length === 1) {
4938          $rootScope.$$postDigest(handleCSSClassChanges);
4939        }
4940      }
4941    }
4942  }];
4943};
4944
4945/**
4946 * @ngdoc provider
4947 * @name $animateProvider
4948 *
4949 * @description
4950 * Default implementation of $animate that doesn't perform any animations, instead just
4951 * synchronously performs DOM updates and resolves the returned runner promise.
4952 *
4953 * In order to enable animations the `ngAnimate` module has to be loaded.
4954 *
4955 * To see the functional implementation check out `src/ngAnimate/animate.js`.
4956 */
4957var $AnimateProvider = ['$provide', function($provide) {
4958  var provider = this;
4959
4960  this.$$registeredAnimations = Object.create(null);
4961
4962   /**
4963   * @ngdoc method
4964   * @name $animateProvider#register
4965   *
4966   * @description
4967   * Registers a new injectable animation factory function. The factory function produces the
4968   * animation object which contains callback functions for each event that is expected to be
4969   * animated.
4970   *
4971   *   * `eventFn`: `function(element, ... , doneFunction, options)`
4972   *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
4973   *   on the type of animation additional arguments will be injected into the animation function. The
4974   *   list below explains the function signatures for the different animation methods:
4975   *
4976   *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
4977   *   - addClass: function(element, addedClasses, doneFunction, options)
4978   *   - removeClass: function(element, removedClasses, doneFunction, options)
4979   *   - enter, leave, move: function(element, doneFunction, options)
4980   *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
4981   *
4982   *   Make sure to trigger the `doneFunction` once the animation is fully complete.
4983   *
4984   * ```js
4985   *   return {
4986   *     //enter, leave, move signature
4987   *     eventFn : function(element, done, options) {
4988   *       //code to run the animation
4989   *       //once complete, then run done()
4990   *       return function endFunction(wasCancelled) {
4991   *         //code to cancel the animation
4992   *       }
4993   *     }
4994   *   }
4995   * ```
4996   *
4997   * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
4998   * @param {Function} factory The factory function that will be executed to return the animation
4999   *                           object.
5000   */
5001  this.register = function(name, factory) {
5002    if (name && name.charAt(0) !== '.') {
5003      throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5004    }
5005
5006    var key = name + '-animation';
5007    provider.$$registeredAnimations[name.substr(1)] = key;
5008    $provide.factory(key, factory);
5009  };
5010
5011  /**
5012   * @ngdoc method
5013   * @name $animateProvider#classNameFilter
5014   *
5015   * @description
5016   * Sets and/or returns the CSS class regular expression that is checked when performing
5017   * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5018   * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5019   * When setting the `classNameFilter` value, animations will only be performed on elements
5020   * that successfully match the filter expression. This in turn can boost performance
5021   * for low-powered devices as well as applications containing a lot of structural operations.
5022   * @param {RegExp=} expression The className expression which will be checked against all animations
5023   * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5024   */
5025  this.classNameFilter = function(expression) {
5026    if (arguments.length === 1) {
5027      this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5028      if (this.$$classNameFilter) {
5029        var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5030        if (reservedRegex.test(this.$$classNameFilter.toString())) {
5031          throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5032
5033        }
5034      }
5035    }
5036    return this.$$classNameFilter;
5037  };
5038
5039  this.$get = ['$$animateQueue', function($$animateQueue) {
5040    function domInsert(element, parentElement, afterElement) {
5041      // if for some reason the previous element was removed
5042      // from the dom sometime before this code runs then let's
5043      // just stick to using the parent element as the anchor
5044      if (afterElement) {
5045        var afterNode = extractElementNode(afterElement);
5046        if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5047          afterElement = null;
5048        }
5049      }
5050      afterElement ? afterElement.after(element) : parentElement.prepend(element);
5051    }
5052
5053    /**
5054     * @ngdoc service
5055     * @name $animate
5056     * @description The $animate service exposes a series of DOM utility methods that provide support
5057     * for animation hooks. The default behavior is the application of DOM operations, however,
5058     * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5059     * to ensure that animation runs with the triggered DOM operation.
5060     *
5061     * By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't
5062     * included and only when it is active then the animation hooks that `$animate` triggers will be
5063     * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5064     * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5065     * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5066     *
5067     * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5068     *
5069     * To learn more about enabling animation support, click here to visit the
5070     * {@link ngAnimate ngAnimate module page}.
5071     */
5072    return {
5073      // we don't call it directly since non-existant arguments may
5074      // be interpreted as null within the sub enabled function
5075
5076      /**
5077       *
5078       * @ngdoc method
5079       * @name $animate#on
5080       * @kind function
5081       * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5082       *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5083       *    is fired with the following params:
5084       *
5085       * ```js
5086       * $animate.on('enter', container,
5087       *    function callback(element, phase) {
5088       *      // cool we detected an enter animation within the container
5089       *    }
5090       * );
5091       * ```
5092       *
5093       * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5094       * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5095       *     as well as among its children
5096       * @param {Function} callback the callback function that will be fired when the listener is triggered
5097       *
5098       * The arguments present in the callback function are:
5099       * * `element` - The captured DOM element that the animation was fired on.
5100       * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5101       */
5102      on: $$animateQueue.on,
5103
5104      /**
5105       *
5106       * @ngdoc method
5107       * @name $animate#off
5108       * @kind function
5109       * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5110       * can be used in three different ways depending on the arguments:
5111       *
5112       * ```js
5113       * // remove all the animation event listeners listening for `enter`
5114       * $animate.off('enter');
5115       *
5116       * // remove all the animation event listeners listening for `enter` on the given element and its children
5117       * $animate.off('enter', container);
5118       *
5119       * // remove the event listener function provided by `listenerFn` that is set
5120       * // to listen for `enter` on the given `element` as well as its children
5121       * $animate.off('enter', container, callback);
5122       * ```
5123       *
5124       * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5125       * @param {DOMElement=} container the container element the event listener was placed on
5126       * @param {Function=} callback the callback function that was registered as the listener
5127       */
5128      off: $$animateQueue.off,
5129
5130      /**
5131       * @ngdoc method
5132       * @name $animate#pin
5133       * @kind function
5134       * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5135       *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5136       *    element despite being outside the realm of the application or within another application. Say for example if the application
5137       *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5138       *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5139       *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5140       *
5141       *    Note that this feature is only active when the `ngAnimate` module is used.
5142       *
5143       * @param {DOMElement} element the external element that will be pinned
5144       * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5145       */
5146      pin: $$animateQueue.pin,
5147
5148      /**
5149       *
5150       * @ngdoc method
5151       * @name $animate#enabled
5152       * @kind function
5153       * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5154       * function can be called in four ways:
5155       *
5156       * ```js
5157       * // returns true or false
5158       * $animate.enabled();
5159       *
5160       * // changes the enabled state for all animations
5161       * $animate.enabled(false);
5162       * $animate.enabled(true);
5163       *
5164       * // returns true or false if animations are enabled for an element
5165       * $animate.enabled(element);
5166       *
5167       * // changes the enabled state for an element and its children
5168       * $animate.enabled(element, true);
5169       * $animate.enabled(element, false);
5170       * ```
5171       *
5172       * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5173       * @param {boolean=} enabled whether or not the animations will be enabled for the element
5174       *
5175       * @return {boolean} whether or not animations are enabled
5176       */
5177      enabled: $$animateQueue.enabled,
5178
5179      /**
5180       * @ngdoc method
5181       * @name $animate#cancel
5182       * @kind function
5183       * @description Cancels the provided animation.
5184       *
5185       * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5186       */
5187      cancel: function(runner) {
5188        runner.end && runner.end();
5189      },
5190
5191      /**
5192       *
5193       * @ngdoc method
5194       * @name $animate#enter
5195       * @kind function
5196       * @description Inserts the element into the DOM either after the `after` element (if provided) or
5197       *   as the first child within the `parent` element and then triggers an animation.
5198       *   A promise is returned that will be resolved during the next digest once the animation
5199       *   has completed.
5200       *
5201       * @param {DOMElement} element the element which will be inserted into the DOM
5202       * @param {DOMElement} parent the parent element which will append the element as
5203       *   a child (so long as the after element is not present)
5204       * @param {DOMElement=} after the sibling element after which the element will be appended
5205       * @param {object=} options an optional collection of options/styles that will be applied to the element
5206       *
5207       * @return {Promise} the animation callback promise
5208       */
5209      enter: function(element, parent, after, options) {
5210        parent = parent && jqLite(parent);
5211        after = after && jqLite(after);
5212        parent = parent || after.parent();
5213        domInsert(element, parent, after);
5214        return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5215      },
5216
5217      /**
5218       *
5219       * @ngdoc method
5220       * @name $animate#move
5221       * @kind function
5222       * @description Inserts (moves) the element into its new position in the DOM either after
5223       *   the `after` element (if provided) or as the first child within the `parent` element
5224       *   and then triggers an animation. A promise is returned that will be resolved
5225       *   during the next digest once the animation has completed.
5226       *
5227       * @param {DOMElement} element the element which will be moved into the new DOM position
5228       * @param {DOMElement} parent the parent element which will append the element as
5229       *   a child (so long as the after element is not present)
5230       * @param {DOMElement=} after the sibling element after which the element will be appended
5231       * @param {object=} options an optional collection of options/styles that will be applied to the element
5232       *
5233       * @return {Promise} the animation callback promise
5234       */
5235      move: function(element, parent, after, options) {
5236        parent = parent && jqLite(parent);
5237        after = after && jqLite(after);
5238        parent = parent || after.parent();
5239        domInsert(element, parent, after);
5240        return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5241      },
5242
5243      /**
5244       * @ngdoc method
5245       * @name $animate#leave
5246       * @kind function
5247       * @description Triggers an animation and then removes the element from the DOM.
5248       * When the function is called a promise is returned that will be resolved during the next
5249       * digest once the animation has completed.
5250       *
5251       * @param {DOMElement} element the element which will be removed from the DOM
5252       * @param {object=} options an optional collection of options/styles that will be applied to the element
5253       *
5254       * @return {Promise} the animation callback promise
5255       */
5256      leave: function(element, options) {
5257        return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5258          element.remove();
5259        });
5260      },
5261
5262      /**
5263       * @ngdoc method
5264       * @name $animate#addClass
5265       * @kind function
5266       *
5267       * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5268       *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5269       *   animation if element already contains the CSS class or if the class is removed at a later step.
5270       *   Note that class-based animations are treated differently compared to structural animations
5271       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5272       *   depending if CSS or JavaScript animations are used.
5273       *
5274       * @param {DOMElement} element the element which the CSS classes will be applied to
5275       * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5276       * @param {object=} options an optional collection of options/styles that will be applied to the element
5277       *
5278       * @return {Promise} the animation callback promise
5279       */
5280      addClass: function(element, className, options) {
5281        options = prepareAnimateOptions(options);
5282        options.addClass = mergeClasses(options.addclass, className);
5283        return $$animateQueue.push(element, 'addClass', options);
5284      },
5285
5286      /**
5287       * @ngdoc method
5288       * @name $animate#removeClass
5289       * @kind function
5290       *
5291       * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5292       *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5293       *   animation if element does not contain the CSS class or if the class is added at a later step.
5294       *   Note that class-based animations are treated differently compared to structural animations
5295       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5296       *   depending if CSS or JavaScript animations are used.
5297       *
5298       * @param {DOMElement} element the element which the CSS classes will be applied to
5299       * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5300       * @param {object=} options an optional collection of options/styles that will be applied to the element
5301       *
5302       * @return {Promise} the animation callback promise
5303       */
5304      removeClass: function(element, className, options) {
5305        options = prepareAnimateOptions(options);
5306        options.removeClass = mergeClasses(options.removeClass, className);
5307        return $$animateQueue.push(element, 'removeClass', options);
5308      },
5309
5310      /**
5311       * @ngdoc method
5312       * @name $animate#setClass
5313       * @kind function
5314       *
5315       * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5316       *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5317       *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5318       *    passed. Note that class-based animations are treated differently compared to structural animations
5319       *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5320       *    depending if CSS or JavaScript animations are used.
5321       *
5322       * @param {DOMElement} element the element which the CSS classes will be applied to
5323       * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5324       * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5325       * @param {object=} options an optional collection of options/styles that will be applied to the element
5326       *
5327       * @return {Promise} the animation callback promise
5328       */
5329      setClass: function(element, add, remove, options) {
5330        options = prepareAnimateOptions(options);
5331        options.addClass = mergeClasses(options.addClass, add);
5332        options.removeClass = mergeClasses(options.removeClass, remove);
5333        return $$animateQueue.push(element, 'setClass', options);
5334      },
5335
5336      /**
5337       * @ngdoc method
5338       * @name $animate#animate
5339       * @kind function
5340       *
5341       * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5342       * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
5343       * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
5344       * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
5345       * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
5346       *
5347       * @param {DOMElement} element the element which the CSS styles will be applied to
5348       * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5349       * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5350       * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5351       *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5352       *    (Note that if no animation is detected then this value will not be appplied to the element.)
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      animate: function(element, from, to, className, options) {
5358        options = prepareAnimateOptions(options);
5359        options.from = options.from ? extend(options.from, from) : from;
5360        options.to   = options.to   ? extend(options.to, to)     : to;
5361
5362        className = className || 'ng-inline-animate';
5363        options.tempClasses = mergeClasses(options.tempClasses, className);
5364        return $$animateQueue.push(element, 'animate', options);
5365      }
5366    };
5367  }];
5368}];
5369
5370/**
5371 * @ngdoc service
5372 * @name $animateCss
5373 * @kind object
5374 *
5375 * @description
5376 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5377 * then the `$animateCss` service will actually perform animations.
5378 *
5379 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5380 */
5381var $CoreAnimateCssProvider = function() {
5382  this.$get = ['$$rAF', '$q', function($$rAF, $q) {
5383
5384    var RAFPromise = function() {};
5385    RAFPromise.prototype = {
5386      done: function(cancel) {
5387        this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
5388      },
5389      end: function() {
5390        this.done();
5391      },
5392      cancel: function() {
5393        this.done(true);
5394      },
5395      getPromise: function() {
5396        if (!this.defer) {
5397          this.defer = $q.defer();
5398        }
5399        return this.defer.promise;
5400      },
5401      then: function(f1,f2) {
5402        return this.getPromise().then(f1,f2);
5403      },
5404      'catch': function(f1) {
5405        return this.getPromise()['catch'](f1);
5406      },
5407      'finally': function(f1) {
5408        return this.getPromise()['finally'](f1);
5409      }
5410    };
5411
5412    return function(element, options) {
5413      // there is no point in applying the styles since
5414      // there is no animation that goes on at all in
5415      // this version of $animateCss.
5416      if (options.cleanupStyles) {
5417        options.from = options.to = null;
5418      }
5419
5420      if (options.from) {
5421        element.css(options.from);
5422        options.from = null;
5423      }
5424
5425      var closed, runner = new RAFPromise();
5426      return {
5427        start: run,
5428        end: run
5429      };
5430
5431      function run() {
5432        $$rAF(function() {
5433          close();
5434          if (!closed) {
5435            runner.done();
5436          }
5437          closed = true;
5438        });
5439        return runner;
5440      }
5441
5442      function close() {
5443        if (options.addClass) {
5444          element.addClass(options.addClass);
5445          options.addClass = null;
5446        }
5447        if (options.removeClass) {
5448          element.removeClass(options.removeClass);
5449          options.removeClass = null;
5450        }
5451        if (options.to) {
5452          element.css(options.to);
5453          options.to = null;
5454        }
5455      }
5456    };
5457  }];
5458};
5459
5460/* global stripHash: true */
5461
5462/**
5463 * ! This is a private undocumented service !
5464 *
5465 * @name $browser
5466 * @requires $log
5467 * @description
5468 * This object has two goals:
5469 *
5470 * - hide all the global state in the browser caused by the window object
5471 * - abstract away all the browser specific features and inconsistencies
5472 *
5473 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5474 * service, which can be used for convenient testing of the application without the interaction with
5475 * the real browser apis.
5476 */
5477/**
5478 * @param {object} window The global window object.
5479 * @param {object} document jQuery wrapped document.
5480 * @param {object} $log window.console or an object with the same interface.
5481 * @param {object} $sniffer $sniffer service
5482 */
5483function Browser(window, document, $log, $sniffer) {
5484  var self = this,
5485      rawDocument = document[0],
5486      location = window.location,
5487      history = window.history,
5488      setTimeout = window.setTimeout,
5489      clearTimeout = window.clearTimeout,
5490      pendingDeferIds = {};
5491
5492  self.isMock = false;
5493
5494  var outstandingRequestCount = 0;
5495  var outstandingRequestCallbacks = [];
5496
5497  // TODO(vojta): remove this temporary api
5498  self.$$completeOutstandingRequest = completeOutstandingRequest;
5499  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5500
5501  /**
5502   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5503   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5504   */
5505  function completeOutstandingRequest(fn) {
5506    try {
5507      fn.apply(null, sliceArgs(arguments, 1));
5508    } finally {
5509      outstandingRequestCount--;
5510      if (outstandingRequestCount === 0) {
5511        while (outstandingRequestCallbacks.length) {
5512          try {
5513            outstandingRequestCallbacks.pop()();
5514          } catch (e) {
5515            $log.error(e);
5516          }
5517        }
5518      }
5519    }
5520  }
5521
5522  function getHash(url) {
5523    var index = url.indexOf('#');
5524    return index === -1 ? '' : url.substr(index);
5525  }
5526
5527  /**
5528   * @private
5529   * Note: this method is used only by scenario runner
5530   * TODO(vojta): prefix this method with $$ ?
5531   * @param {function()} callback Function that will be called when no outstanding request
5532   */
5533  self.notifyWhenNoOutstandingRequests = function(callback) {
5534    if (outstandingRequestCount === 0) {
5535      callback();
5536    } else {
5537      outstandingRequestCallbacks.push(callback);
5538    }
5539  };
5540
5541  //////////////////////////////////////////////////////////////
5542  // URL API
5543  //////////////////////////////////////////////////////////////
5544
5545  var cachedState, lastHistoryState,
5546      lastBrowserUrl = location.href,
5547      baseElement = document.find('base'),
5548      pendingLocation = null;
5549
5550  cacheState();
5551  lastHistoryState = cachedState;
5552
5553  /**
5554   * @name $browser#url
5555   *
5556   * @description
5557   * GETTER:
5558   * Without any argument, this method just returns current value of location.href.
5559   *
5560   * SETTER:
5561   * With at least one argument, this method sets url to new value.
5562   * If html5 history api supported, pushState/replaceState is used, otherwise
5563   * location.href/location.replace is used.
5564   * Returns its own instance to allow chaining
5565   *
5566   * NOTE: this api is intended for use only by the $location service. Please use the
5567   * {@link ng.$location $location service} to change url.
5568   *
5569   * @param {string} url New url (when used as setter)
5570   * @param {boolean=} replace Should new url replace current history record?
5571   * @param {object=} state object to use with pushState/replaceState
5572   */
5573  self.url = function(url, replace, state) {
5574    // In modern browsers `history.state` is `null` by default; treating it separately
5575    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5576    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5577    if (isUndefined(state)) {
5578      state = null;
5579    }
5580
5581    // Android Browser BFCache causes location, history reference to become stale.
5582    if (location !== window.location) location = window.location;
5583    if (history !== window.history) history = window.history;
5584
5585    // setter
5586    if (url) {
5587      var sameState = lastHistoryState === state;
5588
5589      // Don't change anything if previous and current URLs and states match. This also prevents
5590      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5591      // See https://github.com/angular/angular.js/commit/ffb2701
5592      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5593        return self;
5594      }
5595      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5596      lastBrowserUrl = url;
5597      lastHistoryState = state;
5598      // Don't use history API if only the hash changed
5599      // due to a bug in IE10/IE11 which leads
5600      // to not firing a `hashchange` nor `popstate` event
5601      // in some cases (see #9143).
5602      if ($sniffer.history && (!sameBase || !sameState)) {
5603        history[replace ? 'replaceState' : 'pushState'](state, '', url);
5604        cacheState();
5605        // Do the assignment again so that those two variables are referentially identical.
5606        lastHistoryState = cachedState;
5607      } else {
5608        if (!sameBase || pendingLocation) {
5609          pendingLocation = url;
5610        }
5611        if (replace) {
5612          location.replace(url);
5613        } else if (!sameBase) {
5614          location.href = url;
5615        } else {
5616          location.hash = getHash(url);
5617        }
5618        if (location.href !== url) {
5619          pendingLocation = url;
5620        }
5621      }
5622      return self;
5623    // getter
5624    } else {
5625      // - pendingLocation is needed as browsers don't allow to read out
5626      //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5627      //   https://openradar.appspot.com/22186109).
5628      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5629      return pendingLocation || location.href.replace(/%27/g,"'");
5630    }
5631  };
5632
5633  /**
5634   * @name $browser#state
5635   *
5636   * @description
5637   * This method is a getter.
5638   *
5639   * Return history.state or null if history.state is undefined.
5640   *
5641   * @returns {object} state
5642   */
5643  self.state = function() {
5644    return cachedState;
5645  };
5646
5647  var urlChangeListeners = [],
5648      urlChangeInit = false;
5649
5650  function cacheStateAndFireUrlChange() {
5651    pendingLocation = null;
5652    cacheState();
5653    fireUrlChange();
5654  }
5655
5656  function getCurrentState() {
5657    try {
5658      return history.state;
5659    } catch (e) {
5660      // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5661    }
5662  }
5663
5664  // This variable should be used *only* inside the cacheState function.
5665  var lastCachedState = null;
5666  function cacheState() {
5667    // This should be the only place in $browser where `history.state` is read.
5668    cachedState = getCurrentState();
5669    cachedState = isUndefined(cachedState) ? null : cachedState;
5670
5671    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5672    if (equals(cachedState, lastCachedState)) {
5673      cachedState = lastCachedState;
5674    }
5675    lastCachedState = cachedState;
5676  }
5677
5678  function fireUrlChange() {
5679    if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5680      return;
5681    }
5682
5683    lastBrowserUrl = self.url();
5684    lastHistoryState = cachedState;
5685    forEach(urlChangeListeners, function(listener) {
5686      listener(self.url(), cachedState);
5687    });
5688  }
5689
5690  /**
5691   * @name $browser#onUrlChange
5692   *
5693   * @description
5694   * Register callback function that will be called, when url changes.
5695   *
5696   * It's only called when the url is changed from outside of angular:
5697   * - user types different url into address bar
5698   * - user clicks on history (forward/back) button
5699   * - user clicks on a link
5700   *
5701   * It's not called when url is changed by $browser.url() method
5702   *
5703   * The listener gets called with new url as parameter.
5704   *
5705   * NOTE: this api is intended for use only by the $location service. Please use the
5706   * {@link ng.$location $location service} to monitor url changes in angular apps.
5707   *
5708   * @param {function(string)} listener Listener function to be called when url changes.
5709   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5710   */
5711  self.onUrlChange = function(callback) {
5712    // TODO(vojta): refactor to use node's syntax for events
5713    if (!urlChangeInit) {
5714      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5715      // don't fire popstate when user change the address bar and don't fire hashchange when url
5716      // changed by push/replaceState
5717
5718      // html5 history api - popstate event
5719      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5720      // hashchange event
5721      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5722
5723      urlChangeInit = true;
5724    }
5725
5726    urlChangeListeners.push(callback);
5727    return callback;
5728  };
5729
5730  /**
5731   * @private
5732   * Remove popstate and hashchange handler from window.
5733   *
5734   * NOTE: this api is intended for use only by $rootScope.
5735   */
5736  self.$$applicationDestroyed = function() {
5737    jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5738  };
5739
5740  /**
5741   * Checks whether the url has changed outside of Angular.
5742   * Needs to be exported to be able to check for changes that have been done in sync,
5743   * as hashchange/popstate events fire in async.
5744   */
5745  self.$$checkUrlChange = fireUrlChange;
5746
5747  //////////////////////////////////////////////////////////////
5748  // Misc API
5749  //////////////////////////////////////////////////////////////
5750
5751  /**
5752   * @name $browser#baseHref
5753   *
5754   * @description
5755   * Returns current <base href>
5756   * (always relative - without domain)
5757   *
5758   * @returns {string} The current base href
5759   */
5760  self.baseHref = function() {
5761    var href = baseElement.attr('href');
5762    return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5763  };
5764
5765  /**
5766   * @name $browser#defer
5767   * @param {function()} fn A function, who's execution should be deferred.
5768   * @param {number=} [delay=0] of milliseconds to defer the function execution.
5769   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5770   *
5771   * @description
5772   * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5773   *
5774   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5775   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5776   * via `$browser.defer.flush()`.
5777   *
5778   */
5779  self.defer = function(fn, delay) {
5780    var timeoutId;
5781    outstandingRequestCount++;
5782    timeoutId = setTimeout(function() {
5783      delete pendingDeferIds[timeoutId];
5784      completeOutstandingRequest(fn);
5785    }, delay || 0);
5786    pendingDeferIds[timeoutId] = true;
5787    return timeoutId;
5788  };
5789
5790
5791  /**
5792   * @name $browser#defer.cancel
5793   *
5794   * @description
5795   * Cancels a deferred task identified with `deferId`.
5796   *
5797   * @param {*} deferId Token returned by the `$browser.defer` function.
5798   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5799   *                    canceled.
5800   */
5801  self.defer.cancel = function(deferId) {
5802    if (pendingDeferIds[deferId]) {
5803      delete pendingDeferIds[deferId];
5804      clearTimeout(deferId);
5805      completeOutstandingRequest(noop);
5806      return true;
5807    }
5808    return false;
5809  };
5810
5811}
5812
5813function $BrowserProvider() {
5814  this.$get = ['$window', '$log', '$sniffer', '$document',
5815      function($window, $log, $sniffer, $document) {
5816        return new Browser($window, $document, $log, $sniffer);
5817      }];
5818}
5819
5820/**
5821 * @ngdoc service
5822 * @name $cacheFactory
5823 *
5824 * @description
5825 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5826 * them.
5827 *
5828 * ```js
5829 *
5830 *  var cache = $cacheFactory('cacheId');
5831 *  expect($cacheFactory.get('cacheId')).toBe(cache);
5832 *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5833 *
5834 *  cache.put("key", "value");
5835 *  cache.put("another key", "another value");
5836 *
5837 *  // We've specified no options on creation
5838 *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5839 *
5840 * ```
5841 *
5842 *
5843 * @param {string} cacheId Name or id of the newly created cache.
5844 * @param {object=} options Options object that specifies the cache behavior. Properties:
5845 *
5846 *   - `{number=}` `capacity` — turns the cache into LRU cache.
5847 *
5848 * @returns {object} Newly created cache object with the following set of methods:
5849 *
5850 * - `{object}` `info()` — Returns id, size, and options of cache.
5851 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5852 *   it.
5853 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5854 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5855 * - `{void}` `removeAll()` — Removes all cached values.
5856 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5857 *
5858 * @example
5859   <example module="cacheExampleApp">
5860     <file name="index.html">
5861       <div ng-controller="CacheController">
5862         <input ng-model="newCacheKey" placeholder="Key">
5863         <input ng-model="newCacheValue" placeholder="Value">
5864         <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5865
5866         <p ng-if="keys.length">Cached Values</p>
5867         <div ng-repeat="key in keys">
5868           <span ng-bind="key"></span>
5869           <span>: </span>
5870           <b ng-bind="cache.get(key)"></b>
5871         </div>
5872
5873         <p>Cache Info</p>
5874         <div ng-repeat="(key, value) in cache.info()">
5875           <span ng-bind="key"></span>
5876           <span>: </span>
5877           <b ng-bind="value"></b>
5878         </div>
5879       </div>
5880     </file>
5881     <file name="script.js">
5882       angular.module('cacheExampleApp', []).
5883         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
5884           $scope.keys = [];
5885           $scope.cache = $cacheFactory('cacheId');
5886           $scope.put = function(key, value) {
5887             if (angular.isUndefined($scope.cache.get(key))) {
5888               $scope.keys.push(key);
5889             }
5890             $scope.cache.put(key, angular.isUndefined(value) ? null : value);
5891           };
5892         }]);
5893     </file>
5894     <file name="style.css">
5895       p {
5896         margin: 10px 0 3px;
5897       }
5898     </file>
5899   </example>
5900 */
5901function $CacheFactoryProvider() {
5902
5903  this.$get = function() {
5904    var caches = {};
5905
5906    function cacheFactory(cacheId, options) {
5907      if (cacheId in caches) {
5908        throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
5909      }
5910
5911      var size = 0,
5912          stats = extend({}, options, {id: cacheId}),
5913          data = {},
5914          capacity = (options && options.capacity) || Number.MAX_VALUE,
5915          lruHash = {},
5916          freshEnd = null,
5917          staleEnd = null;
5918
5919      /**
5920       * @ngdoc type
5921       * @name $cacheFactory.Cache
5922       *
5923       * @description
5924       * A cache object used to store and retrieve data, primarily used by
5925       * {@link $http $http} and the {@link ng.directive:script script} directive to cache
5926       * templates and other data.
5927       *
5928       * ```js
5929       *  angular.module('superCache')
5930       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
5931       *      return $cacheFactory('super-cache');
5932       *    }]);
5933       * ```
5934       *
5935       * Example test:
5936       *
5937       * ```js
5938       *  it('should behave like a cache', inject(function(superCache) {
5939       *    superCache.put('key', 'value');
5940       *    superCache.put('another key', 'another value');
5941       *
5942       *    expect(superCache.info()).toEqual({
5943       *      id: 'super-cache',
5944       *      size: 2
5945       *    });
5946       *
5947       *    superCache.remove('another key');
5948       *    expect(superCache.get('another key')).toBeUndefined();
5949       *
5950       *    superCache.removeAll();
5951       *    expect(superCache.info()).toEqual({
5952       *      id: 'super-cache',
5953       *      size: 0
5954       *    });
5955       *  }));
5956       * ```
5957       */
5958      return caches[cacheId] = {
5959
5960        /**
5961         * @ngdoc method
5962         * @name $cacheFactory.Cache#put
5963         * @kind function
5964         *
5965         * @description
5966         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
5967         * retrieved later, and incrementing the size of the cache if the key was not already
5968         * present in the cache. If behaving like an LRU cache, it will also remove stale
5969         * entries from the set.
5970         *
5971         * It will not insert undefined values into the cache.
5972         *
5973         * @param {string} key the key under which the cached data is stored.
5974         * @param {*} value the value to store alongside the key. If it is undefined, the key
5975         *    will not be stored.
5976         * @returns {*} the value stored.
5977         */
5978        put: function(key, value) {
5979          if (isUndefined(value)) return;
5980          if (capacity < Number.MAX_VALUE) {
5981            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
5982
5983            refresh(lruEntry);
5984          }
5985
5986          if (!(key in data)) size++;
5987          data[key] = value;
5988
5989          if (size > capacity) {
5990            this.remove(staleEnd.key);
5991          }
5992
5993          return value;
5994        },
5995
5996        /**
5997         * @ngdoc method
5998         * @name $cacheFactory.Cache#get
5999         * @kind function
6000         *
6001         * @description
6002         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6003         *
6004         * @param {string} key the key of the data to be retrieved
6005         * @returns {*} the value stored.
6006         */
6007        get: function(key) {
6008          if (capacity < Number.MAX_VALUE) {
6009            var lruEntry = lruHash[key];
6010
6011            if (!lruEntry) return;
6012
6013            refresh(lruEntry);
6014          }
6015
6016          return data[key];
6017        },
6018
6019
6020        /**
6021         * @ngdoc method
6022         * @name $cacheFactory.Cache#remove
6023         * @kind function
6024         *
6025         * @description
6026         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6027         *
6028         * @param {string} key the key of the entry to be removed
6029         */
6030        remove: function(key) {
6031          if (capacity < Number.MAX_VALUE) {
6032            var lruEntry = lruHash[key];
6033
6034            if (!lruEntry) return;
6035
6036            if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6037            if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6038            link(lruEntry.n,lruEntry.p);
6039
6040            delete lruHash[key];
6041          }
6042
6043          delete data[key];
6044          size--;
6045        },
6046
6047
6048        /**
6049         * @ngdoc method
6050         * @name $cacheFactory.Cache#removeAll
6051         * @kind function
6052         *
6053         * @description
6054         * Clears the cache object of any entries.
6055         */
6056        removeAll: function() {
6057          data = {};
6058          size = 0;
6059          lruHash = {};
6060          freshEnd = staleEnd = null;
6061        },
6062
6063
6064        /**
6065         * @ngdoc method
6066         * @name $cacheFactory.Cache#destroy
6067         * @kind function
6068         *
6069         * @description
6070         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6071         * removing it from the {@link $cacheFactory $cacheFactory} set.
6072         */
6073        destroy: function() {
6074          data = null;
6075          stats = null;
6076          lruHash = null;
6077          delete caches[cacheId];
6078        },
6079
6080
6081        /**
6082         * @ngdoc method
6083         * @name $cacheFactory.Cache#info
6084         * @kind function
6085         *
6086         * @description
6087         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6088         *
6089         * @returns {object} an object with the following properties:
6090         *   <ul>
6091         *     <li>**id**: the id of the cache instance</li>
6092         *     <li>**size**: the number of entries kept in the cache instance</li>
6093         *     <li>**...**: any additional properties from the options object when creating the
6094         *       cache.</li>
6095         *   </ul>
6096         */
6097        info: function() {
6098          return extend({}, stats, {size: size});
6099        }
6100      };
6101
6102
6103      /**
6104       * makes the `entry` the freshEnd of the LRU linked list
6105       */
6106      function refresh(entry) {
6107        if (entry != freshEnd) {
6108          if (!staleEnd) {
6109            staleEnd = entry;
6110          } else if (staleEnd == entry) {
6111            staleEnd = entry.n;
vendor: 5,797 bytes, lines 6112-6287
6112          }
6113
6114          link(entry.n, entry.p);
6115          link(entry, freshEnd);
6116          freshEnd = entry;
6117          freshEnd.n = null;
6118        }
6119      }
6120
6121
6122      /**
6123       * bidirectionally links two entries of the LRU linked list
6124       */
6125      function link(nextEntry, prevEntry) {
6126        if (nextEntry != prevEntry) {
6127          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6128          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6129        }
6130      }
6131    }
6132
6133
6134  /**
6135   * @ngdoc method
6136   * @name $cacheFactory#info
6137   *
6138   * @description
6139   * Get information about all the caches that have been created
6140   *
6141   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6142   */
6143    cacheFactory.info = function() {
6144      var info = {};
6145      forEach(caches, function(cache, cacheId) {
6146        info[cacheId] = cache.info();
6147      });
6148      return info;
6149    };
6150
6151
6152  /**
6153   * @ngdoc method
6154   * @name $cacheFactory#get
6155   *
6156   * @description
6157   * Get access to a cache object by the `cacheId` used when it was created.
6158   *
6159   * @param {string} cacheId Name or id of a cache to access.
6160   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6161   */
6162    cacheFactory.get = function(cacheId) {
6163      return caches[cacheId];
6164    };
6165
6166
6167    return cacheFactory;
6168  };
6169}
6170
6171/**
6172 * @ngdoc service
6173 * @name $templateCache
6174 *
6175 * @description
6176 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6177 * can load templates directly into the cache in a `script` tag, or by consuming the
6178 * `$templateCache` service directly.
6179 *
6180 * Adding via the `script` tag:
6181 *
6182 * ```html
6183 *   <script type="text/ng-template" id="templateId.html">
6184 *     <p>This is the content of the template</p>
6185 *   </script>
6186 * ```
6187 *
6188 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6189 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6190 * element with ng-app attribute), otherwise the template will be ignored.
6191 *
6192 * Adding via the `$templateCache` service:
6193 *
6194 * ```js
6195 * var myApp = angular.module('myApp', []);
6196 * myApp.run(function($templateCache) {
6197 *   $templateCache.put('templateId.html', 'This is the content of the template');
6198 * });
6199 * ```
6200 *
6201 * To retrieve the template later, simply use it in your HTML:
6202 * ```html
6203 * <div ng-include=" 'templateId.html' "></div>
6204 * ```
6205 *
6206 * or get it via Javascript:
6207 * ```js
6208 * $templateCache.get('templateId.html')
6209 * ```
6210 *
6211 * See {@link ng.$cacheFactory $cacheFactory}.
6212 *
6213 */
6214function $TemplateCacheProvider() {
6215  this.$get = ['$cacheFactory', function($cacheFactory) {
6216    return $cacheFactory('templates');
6217  }];
6218}
6219
6220/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6221 *     Any commits to this file should be reviewed with security in mind.  *
6222 *   Changes to this file can potentially create security vulnerabilities. *
6223 *          An approval from 2 Core members with history of modifying      *
6224 *                         this file is required.                          *
6225 *                                                                         *
6226 *  Does the change somehow allow for arbitrary javascript to be executed? *
6227 *    Or allows for someone to change the prototype of built-in objects?   *
6228 *     Or gives undesired access to variables likes document or window?    *
6229 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6230
6231/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6232 *
6233 * DOM-related variables:
6234 *
6235 * - "node" - DOM Node
6236 * - "element" - DOM Element or Node
6237 * - "$node" or "$element" - jqLite-wrapped node or element
6238 *
6239 *
6240 * Compiler related stuff:
6241 *
6242 * - "linkFn" - linking fn of a single directive
6243 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6244 * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
6245 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6246 */
6247
6248
6249/**
6250 * @ngdoc service
6251 * @name $compile
6252 * @kind function
6253 *
6254 * @description
6255 * Compiles an HTML string or DOM into a template and produces a template function, which
6256 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6257 *
6258 * The compilation is a process of walking the DOM tree and matching DOM elements to
6259 * {@link ng.$compileProvider#directive directives}.
6260 *
6261 * <div class="alert alert-warning">
6262 * **Note:** This document is an in-depth reference of all directive options.
6263 * For a gentle introduction to directives with examples of common use cases,
6264 * see the {@link guide/directive directive guide}.
6265 * </div>
6266 *
6267 * ## Comprehensive Directive API
6268 *
6269 * There are many different options for a directive.
6270 *
6271 * The difference resides in the return value of the factory function.
6272 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6273 * or just the `postLink` function (all other properties will have the default values).
6274 *
6275 * <div class="alert alert-success">
6276 * **Best Practice:** It's recommended to use the "directive definition object" form.
6277 * </div>
6278 *
6279 * Here's an example directive declared with a Directive Definition Object:
6280 *
6281 * ```js
6282 *   var myModule = angular.module(...);
6283 *
6284 *   myModule.directive('directiveName', function factory(injectables) {
6285 *     var directiveDefinitionObject = {
6286 *       priority: 0,
6287 *       template: '<div></div>
6287', // or // function(tElement, tAttrs) { ... },
6288 *       // or
6289 *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6290 *       transclude: false,
6291 *       restrict: 'A',
6292 *       templateNamespace: 'html',
6293 *       scope: false,
6294 *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6295 *       controllerAs: 'stringIdentifier',
6296 *       bindToController: false,
6297 *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6298 *       compile: function compile(tElement, tAttrs, transclude) {
6299 *         return {
6300 *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6301 *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
6302 *         }
6303 *         // or
6304 *         // return function postLink( ... ) { ... }
6305 *       },
6306 *       // or
6307 *       // link: {
6308 *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6309 *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
6310 *       // }
6311 *       // or
6312 *       // link: function postLink( ... ) { ... }
6313 *     };
6314 *     return directiveDefinitionObject;
6315 *   });
6316 * ```
6317 *
6318 * <div class="alert alert-warning">
6319 * **Note:** Any unspecified options will use the default value. You can see the default values below.
6320 * </div>
6321 *
6322 * Therefore the above can be simplified as:
6323 *
6324 * ```js
6325 *   var myModule = angular.module(...);
6326 *
6327 *   myModule.directive('directiveName', function factory(injectables) {
6328 *     var directiveDefinitionObject = {
6329 *       link: function postLink(scope, iElement, iAttrs) { ... }
6330 *     };
6331 *     return directiveDefinitionObject;
6332 *     // or
6333 *     // return function postLink(scope, iElement, iAttrs) { ... }
6334 *   });
6335 * ```
6336 *
6337 *
6338 *
6339 * ### Directive Definition Object
6340 *
6341 * The directive definition object provides instructions to the {@link ng.$compile
6342 * compiler}. The attributes are:
6343 *
6344 * #### `multiElement`
6345 * When this property is set to true, the HTML compiler will collect DOM nodes between
6346 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6347 * together as the directive elements. It is recommended that this feature be used on directives
6348 * which are not strictly behavioural (such as {@link ngClick}), and which
6349 * do not manipulate or replace child nodes (such as {@link ngInclude}).
6350 *
6351 * #### `priority`
6352 * When there are multiple directives defined on a single DOM element, sometimes it
6353 * is necessary to specify the order in which the directives are applied. The `priority` is used
6354 * to sort the directives before their `compile` functions get called. Priority is defined as a
6355 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6356 * are also run in priority order, but post-link functions are run in reverse order. The order
6357 * of directives with the same priority is undefined. The default priority is `0`.
6358 *
6359 * #### `terminal`
6360 * If set to true then the current `priority` will be the last set of directives
6361 * which will execute (any directives at the current priority will still execute
6362 * as the order of execution on same `priority` is undefined). Note that expressions
6363 * and other directives used in the directive's template will also be excluded from execution.
6364 *
6365 * #### `scope`
6366 * The scope property can be `true`, an object or a falsy value:
6367 *
6368 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6369 *
6370 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6371 * the directive's element. If multiple directives on the same element request a new scope,
6372 * only one new scope is created. The new scope rule does not apply for the root of the template
6373 * since the root of the template always gets a new scope.
6374 *
6375 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6376 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6377 * scope. This is useful when creating reusable components, which should not accidentally read or modify
6378 * data in the parent scope.
6379 *
6380 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6381 * directive's element. These local properties are useful for aliasing values for templates. The keys in
6382 * the object hash map to the name of the property on the isolate scope; the values define how the property
6383 * is bound to the parent scope, via matching attributes on the directive's element:
6384 *
6385 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6386 *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
6387 *   attribute name is assumed to be the same as the local name.
6388 *   Given `<widget my-attr="hello {{name}}">` and widget definition
6389 *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
6390 *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
6391 *   `localName` property on the widget scope. The `name` is read from the parent scope (not
6392 *   component scope).
6393 *
6394 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
6395 *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
6396 *   name is specified then the attribute name is assumed to be the same as the local name.
6397 *   Given `<widget my-attr="parentModel">` and widget definition of
6398 *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
6399 *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6400 *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
6401 *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
6402 *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
6403 *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
6404 *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
6405 *
6406 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
6407 *   If no `attr` name is specified then the attribute name is assumed to be the same as the
6408 *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
6409 *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
6410 *   a function wrapper for the `count = count + value` expression. Often it's desirable to
6411 *   pass data from the isolated scope via an expression to the parent scope, this can be
6412 *   done by passing a map of local variable names and values into the expression wrapper fn.
6413 *   For example, if the expression is `increment(amount)` then we can specify the amount value
6414 *   by calling the `localFn` as `localFn({amount: 22})`.
6415 *
6416 * In general it's possible to apply more than one directive to one element, but there might be limitations
6417 * depending on the type of scope required by the directives. The following points will help explain these limitations.
6418 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6419 *
6420 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6421 * * **child scope** + **no scope** =>  Both directives will share one single child scope
6422 * * **child scope** + **child scope** =>  Both directives will share one single child scope
6423 * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
6424 * its parent's scope
6425 * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6426 * be applied to the same element.
6427 * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
6428 * cannot be applied to the same element.
6429 *
6430 *
6431 * #### `bindToController`
6432 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
6433 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6434 * is instantiated, the initial values of the isolate scope bindings are already available.
6435 *
6436 * #### `controller`
6437 * Controller constructor function. The controller is instantiated before the
6438 * pre-linking phase and can be accessed by other directives (see
6439 * `require` attribute). This allows the directives to communicate with each other and augment
6440 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6441 *
6442 * * `$scope` - Current scope associated with the element
6443 * * `$element` - Current element
6444 * * `$attrs` - Current attributes object for the element
6445 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6446 *   `function([scope], cloneLinkingFn, futureParentElement)`.
6447 *    * `scope`: optional argument to override the scope.
6448 *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6449 *    * `futureParentElement`:
6450 *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6451 *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6452 *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6453 *          and when the `cloneLinkinFn` is passed,
6454 *          as those elements need to created and cloned in a special way when they are defined outside their
6455 *          usual containers (e.g. like `<svg>`).
6456 *        * See also the `directive.templateNamespace` property.
6457 *
6458 *
6459 * #### `require`
6460 * Require another directive and inject its controller as the fourth argument to the linking function. The
6461 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6462 * injected argument will be an array in corresponding order. If no such directive can be
6463 * found, or if the directive does not have a controller, then an error is raised (unless no link function
6464 * is specified, in which case error checking is skipped). The name can be prefixed with:
6465 *
6466 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6467 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6468 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6469 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6470 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6471 *   `null` to the `link` fn if not found.
6472 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6473 *   `null` to the `link` fn if not found.
6474 *
6475 *
6476 * #### `controllerAs`
6477 * Identifier name for a reference to the controller in the directive's scope.
6478 * This allows the controller to be referenced from the directive template. This is especially
6479 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6480 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6481 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6482 *
6483 *
6484 * #### `restrict`
6485 * String of subset of `EACM` which restricts the directive to a specific directive
6486 * declaration style. If omitted, the defaults (elements and attributes) are used.
6487 *
6488 * * `E` - Element name (default): `<my-directive></my-directive>`
6489 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6490 * * `C` - Class: `<div class="my-directive: exp;"></div>`
6491 * * `M` - Comment: `<!-- directive: my-directive exp -->`
6492 *
6493 *
6494 * #### `templateNamespace`
6495 * String representing the document type used by the markup in the template.
6496 * AngularJS needs this information as those elements need to be created and cloned
6497 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6498 *
6499 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6500 *   top-level elements such as `<svg>` or `<math>`.
6501 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6502 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6503 *
6504 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6505 *
6506 * #### `template`
6507 * HTML markup that may:
6508 * * Replace the contents of the directive's element (default).
6509 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6510 * * Wrap the contents of the directive's element (if `transclude` is true).
6511 *
6512 * Value may be:
6513 *
6514 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6515 * * A function which takes two arguments `tElement` and `tAttrs` (described 
6515in the `compile`
6516 *   function api below) and returns a string value.
6517 *
6518 *
6519 * #### `templateUrl`
6520 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6521 *
6522 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6523 * for later when the template has been resolved.  In the meantime it will continue to compile and link
6524 * sibling and parent elements as though this element had not contained any directives.
6525 *
6526 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6527 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6528 * case when only one deeply nested directive has `templateUrl`.
6529 *
6530 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6531 *
6532 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6533 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6534 * a string value representing the url.  In either case, the template URL is passed through {@link
6535 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6536 *
6537 *
6538 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6539 * specify what the template should replace. Defaults to `false`.
6540 *
6541 * * `true` - the template will replace the directive's element.
6542 * * `false` - the template will replace the contents of the directive's element.
6543 *
6544 * The replacement process migrates all of the attributes / classes from the old element to the new
6545 * one. See the {@link guide/directive#template-expanding-directive
6546 * Directives Guide} for an example.
6547 *
6548 * There are very few scenarios where element replacement is required for the application function,
6549 * the main one being reusable custom components that are used within SVG contexts
6550 * (because SVG doesn't work with custom elements in the DOM tree).
6551 *
6552 * #### `transclude`
6553 * Extract the contents of the element where the directive appears and make it available to the directive.
6554 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6555 * {@link $compile#transclusion Transclusion} section below.
6556 *
6557 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6558 * directive's element or the entire element:
6559 *
6560 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6561 * * `'element'` - transclude the whole of the directive's element including any directives on this
6562 *   element that defined at a lower priority than this directive. When used, the `template`
6563 *   property is ignored.
6564 *
6565 *
6566 * #### `compile`
6567 *
6568 * ```js
6569 *   function compile(tElement, tAttrs, transclude) { ... }
6570 * ```
6571 *
6572 * The compile function deals with transforming the template DOM. Since most directives do not do
6573 * template transformation, it is not used often. The compile function takes the following arguments:
6574 *
6575 *   * `tElement` - template element - The element where the directive has been declared. It is
6576 *     safe to do template transformation on the element and child elements only.
6577 *
6578 *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6579 *     between all directive compile functions.
6580 *
6581 *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6582 *
6583 * <div class="alert alert-warning">
6584 * **Note:** The template instance and the link instance may be different objects if the template has
6585 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6586 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6587 * should be done in a linking function rather than in a compile function.
6588 * </div>
6589
6590 * <div class="alert alert-warning">
6591 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6592 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6593 * stack overflow errors.
6594 *
6595 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6596 * a directive's template instead of relying on automatic template compilation via `template` or
6597 * `templateUrl` declaration or manual compilation inside the compile function.
6598 * </div>
6599 *
6600 * <div class="alert alert-danger">
6601 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6602 *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6603 *   to the link function instead.
6604 * </div>
6605
6606 * A compile function can have a return value which can be either a function or an object.
6607 *
6608 * * returning a (post-link) function - is equivalent to registering the linking function via the
6609 *   `link` property of the config object when the compile function is empty.
6610 *
6611 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6612 *   control when a linking function should be called during the linking phase. See info about
6613 *   pre-linking and post-linking functions below.
6614 *
6615 *
6616 * #### `link`
6617 * This property is used only if the `compile` property is not defined.
6618 *
6619 * ```js
6620 *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6621 * ```
6622 *
6623 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6624 * executed after the template has been cloned. This is where most of the directive logic will be
6625 * put.
6626 *
6627 *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6628 *     directive for registering {@link ng.$rootScope.Scope#$watch w
6628atches}.
6629 *
6630 *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6631 *     manipulate the children of the element only in `postLink` function since the children have
6632 *     already been linked.
6633 *
6634 *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6635 *     between all directive linking functions.
6636 *
6637 *   * `controller` - the directive's required controller instance(s) - Instances are shared
6638 *     among all directives, which allows the directives to use the controllers as a communication
6639 *     channel. The exact value depends on the directive's `require` property:
6640 *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6641 *       * `string`: the controller instance
6642 *       * `array`: array of controller instances
6643 *
6644 *     If a required controller cannot be found, and it is optional, the instance is `null`,
6645 *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6646 *
6647 *     Note that you can also require the directive's own controller - it will be made available like
6648 *     any other controller.
6649 *
6650 *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6651 *     This is the same as the `$transclude`
6652 *     parameter of directive controllers, see there for details.
6653 *     `function([scope], cloneLinkingFn, futureParentElement)`.
6654 *
6655 * #### Pre-linking function
6656 *
6657 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6658 * compiler linking function will fail to locate the correct elements for linking.
6659 *
6660 * #### Post-linking function
6661 *
6662 * Executed after the child elements are linked.
6663 *
6664 * Note that child elements that contain `templateUrl` directives will not have been compiled
6665 * and linked since they are waiting for their template to load asynchronously and their own
6666 * compilation and linking has been suspended until that occurs.
6667 *
6668 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6669 * for their async templates to be resolved.
6670 *
6671 *
6672 * ### Transclusion
6673 *
6674 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6675 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6676 * scope from where they were taken.
6677 *
6678 * Transclusion is used (often with {@link ngTransclude}) to insert the
6679 * original contents of a directive's element into a specified place in the template of the directive.
6680 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6681 * content has access to the properties on the scope from which it was taken, even if the directive
6682 * has isolated scope.
6683 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6684 *
6685 * This makes it possible for the widget to have private state for its template, while the transcluded
6686 * content has access to its originating scope.
6687 *
6688 * <div class="alert alert-warning">
6689 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6690 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6691 * Testing Transclusion Directives}.
6692 * </div>
6693 *
6694 * #### Transclusion Functions
6695 *
6696 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6697 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6698 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6699 *
6700 * <div class="alert alert-info">
6701 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6702 * ngTransclude will deal with it for us.
6703 * </div>
6704 *
6705 * If you want to manually control the insertion and removal of the transcluded content in your directive
6706 * then you must use this transclude function. When you call a transclude function it returns a a jq
6706Lite/JQuery
6707 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6708 *
6709 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6710 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6711 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6712 *
6713 * <div class="alert alert-info">
6714 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6715 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6716 * </div>
6717 *
6718 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6719 * attach function**:
6720 *
6721 * ```js
6722 * var transcludedContent, transclusionScope;
6723 *
6724 * $transclude(function(clone, scope) {
6725 *   element.append(clone);
6726 *   transcludedContent = clone;
6727 *   transclusionScope = scope;
6728 * });
6729 * ```
6730 *
6731 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6732 * associated transclusion scope:
6733 *
6734 * ```js
6735 * transcludedContent.remove();
6736 * transclusionScope.$destroy();
6737 * ```
6738 *
6739 * <div class="alert alert-info">
6740 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6741 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
6742 * then you are also responsible for calling `$destroy` on the transclusion scope.
6743 * </div>
6744 *
6745 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6746 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6747 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6748 *
6749 *
6750 * #### Transclusion Scopes
6751 *
6752 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6753 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6754 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6755 * was taken.
6756 *
6757 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6758 * like this:
6759 *
6760 * ```html
6761 * <div ng-app>
6762 *   <div isolate>
6763 *     <div transclusion>
6764 *     </div>
6765 *   </div>
6766 * </div>
6767 * ```
6768 *
6769 * The `$parent` scope hierarchy will look like this:
6770 *
6771 * ```
6772 * - $rootScope
6773 *   - isolate
6774 *     - transclusion
6775 * ```
6776 *
6777 * but the scopes will inherit prototypically from different scopes to their `$parent`.
6778 *
6779 * ```
6780 * - $rootScope
6781 *   - transclusion
6782 * - isolate
6783 * ```
6784 *
6785 *
6786 * ### Attributes
6787 *
6788 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6789 * `link()` or `compile()` functions. It has a variety of uses.
6790 *
6791 * accessing *Normalized attribute names:*
6792 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6793 * the attributes object allows for normalized access to
6794 *   the attributes.
6795 *
6796 * * *Directive inter-communication:* All directives share the same instance of the attributes
6797 *   object which allows the directives to use the attributes object as inter directive
6798 *   communication.
6799 *
6800 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6801 *   allowing other directives to read the interpolated value.
6802 *
6803 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6804 *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6805 *   the only way to easily get the actual value because during the linking phase the interpolation
6806 *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
6807 *
6808 * ```js
6809 * function linkingFn(scope, elm, attrs, ctrl) {
6810 *   // get the attribute value
6811 *   console.log(attrs.ngModel);
6812 *
6813 *   // change the attribute
6814 *   attrs.$set('ngModel', 'new value');
6815 *
6816 *   // observe changes to interpolated attribute
6817 *   attrs.$observe('ngModel', function(value) {
6818 *     console.log('ngModel has changed value to ' + value);
6819 *   });
6820 * }
6821 * ```
6822 *
6823 * ## Example
6824 *
6825 * <div class="alert alert-warning">
6826 * **Note**: Typically directives are registered with `module.directive`. The example below is
6827 * to illustrate how `$compile` works.
6828 * </div>
6829 *
6830 <example module="compileExample">
6831   <file name="index.html">
6832    <script>
6833      angular.module('compileExample', [], function($compileProvider) {
6834        // configure new 'compile' directive by passing a directive
6835        // factory function. The factory function injects the '$compile'
6836        $compileProvider.directive('compile', function($compile) {
6837          // directive factory creates a link function
6838          return function(scope, element, attrs) {
6839            scope.$watch(
6840              function(scope) {
6841                 // watch the 'compile' expression for changes
6842                return scope.$eval(attrs.compile);
6843              },
6844              function(value) {
6845                // when the 'compile' expression changes
6846                // assign it into the current DOM
6847                element.html(value);
6848
6849                // compile the new DOM and link it to the current
6850                // scope.
6851                // NOTE: we only compile .childNodes so that
6852                // we don't get into infinite loop compiling ourselves
6853                $compile(element.contents())(scope);
6854              }
6855            );
6856          };
6857        });
6858      })
6859      .controller('GreeterController', ['$scope', function($scope) {
6860        $scope.name = 'Angular';
6861        $scope.html = 'Hello {{name}}';
6862      }]);
6863    </script>
6864    <div ng-controller="GreeterController">
6865      <input ng-model="name"> <br/>
6866      <textarea ng-model="html"></textarea> <br/>
6867      <div compile="html"></div>
6868    </div>
6869   </file>
6870   <file name="protractor.js" type="protractor">
6871     it('should auto compile', function() {
6872       var textarea = $('textarea');
6873       var output = $('div[compile]');
6874       // The initial state reads 'Hello Angular'.
6875       expect(output.getText()).toBe('Hello Angular');
6876       textarea.clear();
6877       textarea.sendKeys('{{name}}!');
6878       expect(output.getText()).toBe('Angular!');
6879     });
6880   </file>
6881 </example>
6882
6883 *
6884 *
6885 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
6886 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
6887 *
6888 * <div class="alert alert-danger">
6889 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
6890 *   e.g. will not use the right outer scope. Please pass the transclude function as a
6891 *   `parentBoundTranscludeFn` to the link function instead.
6892 * </div>
6893 *
6894 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
6895 *                 root element(s), not their children)
6896 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
6897 * (a DOM element/tree) to a scope. Where:
6898 *
6899 *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
6900 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
6901 *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
6902 *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
6903 *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
6904 *
6905 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
6906 *      * `scope` - is the current scope with which the linking function is working with.
6907 *
6908 *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
6909 *  keys may be used to control linking behavior:
6910 *
6911 *      * `parentBoundTranscludeFn` - the transclude function made available to
6912 *        directives; if given, it will be passed through to the link functions of
6913 *        directives found in `element` during compilation.
6914 *      * `transcludeControllers` - an object hash with keys that map controller names
6915 *        to controller instances; if given, it will make the controllers
6916 *        available to directives.
6917 *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
6918 *        the cloned elements; only needed for transcludes that are allowed to contain non html
6919 *        elements (e.g. SVG elements). See also the directive.controller property.
6920 *
6921 * Calling the linking function returns the element of the template. It is either the original
6922 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
6923 *
6924 * After linking the view is not updated until after a call to $digest which typically is done by
6925 * Angular automatically.
6926 *
6927 * If you need access to the bound view, there are two ways to do it:
6928 *
6929 * - If you are not asking the linking function to clone the template, create the DOM element(s)
6930 *   before you send them to the compiler and keep this reference around.
6931 *   ```js
6932 *     var element = $compile('<p>{{total}}</p>')(scope);
6933 *   ```
6934 *
6935 * - if on the other hand, you need the element to be cloned, the view reference from the original
6936 *   example would not point to the clone, but rather to the original template that was cloned. In
6937 *   this case, you can access the clone via the cloneAttachFn:
6938 *   ```js
6939 *     var templateElement = angular.element('<p>{{total}}</p>'),
6940 *         scope = ....;
6941 *
6942 *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
6943 *       //attach the clone to DOM document at the right place
6944 *     });
6945 *
6946 *     //now we have reference to the cloned DOM via `clonedElement`
6947 *   ```
6948 *
6949 *
6950 * For information on how the compiler works, see the
6951 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
6952 */
6953
6954var $compileMinErr = minErr('$compile');
6955
6956/**
6957 * @ngdoc provider
6958 * @name $compileProvider
6959 *
6960 * @description
6961 */
vendor: 11,436 bytes, lines 6962-7248
6962$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
6963function $CompileProvider($provide, $$sanitizeUriProvider) {
6964  var hasDirectives = {},
6965      Suffix = 'Directive',
6966      COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
6967      CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
6968      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
6969      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
6970
6971  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
6972  // The assumption is that future DOM event attribute names will begin with
6973  // 'on' and be composed of only English letters.
6974  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
6975
6976  function parseIsolateBindings(scope, directiveName, isController) {
6977    var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
6978
6979    var bindings = {};
6980
6981    forEach(scope, function(definition, scopeName) {
6982      var match = definition.match(LOCAL_REGEXP);
6983
6984      if (!match) {
6985        throw $compileMinErr('iscp',
6986            "Invalid {3} for directive '{0}'." +
6987            " Definition: {... {1}: '{2}' ...}",
6988            directiveName, scopeName, definition,
6989            (isController ? "controller bindings definition" :
6990            "isolate scope definition"));
6991      }
6992
6993      bindings[scopeName] = {
6994        mode: match[1][0],
6995        collection: match[2] === '*',
6996        optional: match[3] === '?',
6997        attrName: match[4] || scopeName
6998      };
6999    });
7000
7001    return bindings;
7002  }
7003
7004  function parseDirectiveBindings(directive, directiveName) {
7005    var bindings = {
7006      isolateScope: null,
7007      bindToController: null
7008    };
7009    if (isObject(directive.scope)) {
7010      if (directive.bindToController === true) {
7011        bindings.bindToController = parseIsolateBindings(directive.scope,
7012                                                         directiveName, true);
7013        bindings.isolateScope = {};
7014      } else {
7015        bindings.isolateScope = parseIsolateBindings(directive.scope,
7016                                                     directiveName, false);
7017      }
7018    }
7019    if (isObject(directive.bindToController)) {
7020      bindings.bindToController =
7021          parseIsolateBindings(directive.bindToController, directiveName, true);
7022    }
7023    if (isObject(bindings.bindToController)) {
7024      var controller = directive.controller;
7025      var controllerAs = directive.controllerAs;
7026      if (!controller) {
7027        // There is no controller, there may or may not be a controllerAs property
7028        throw $compileMinErr('noctrl',
7029              "Cannot bind to controller without directive '{0}'s controller.",
7030              directiveName);
7031      } else if (!identifierForController(controller, controllerAs)) {
7032        // There is a controller, but no identifier or controllerAs property
7033        throw $compileMinErr('noident',
7034              "Cannot bind to controller without identifier for directive '{0}'.",
7035              directiveName);
7036      }
7037    }
7038    return bindings;
7039  }
7040
7041  function assertValidDirectiveName(name) {
7042    var letter = name.charAt(0);
7043    if (!letter || letter !== lowercase(letter)) {
7044      throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
7045    }
7046    if (name !== name.trim()) {
7047      throw $compileMinErr('baddir',
7048            "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7049            name);
7050    }
7051  }
7052
7053  /**
7054   * @ngdoc method
7055   * @name $compileProvider#directive
7056   * @kind function
7057   *
7058   * @description
7059   * Register a new directive with the compiler.
7060   *
7061   * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7062   *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7063   *    names and the values are the factories.
7064   * @param {Function|Array} directiveFactory An injectable directive factory function. See
7065   *    {@link guide/directive} for more info.
7066   * @returns {ng.$compileProvider} Self for chaining.
7067   */
7068   this.directive = function registerDirective(name, directiveFactory) {
7069    assertNotHasOwnProperty(name, 'directive');
7070    if (isString(name)) {
7071      assertValidDirectiveName(name);
7072      assertArg(directiveFactory, 'directiveFactory');
7073      if (!hasDirectives.hasOwnProperty(name)) {
7074        hasDirectives[name] = [];
7075        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7076          function($injector, $exceptionHandler) {
7077            var directives = [];
7078            forEach(hasDirectives[name], function(directiveFactory, index) {
7079              try {
7080                var directive = $injector.invoke(directiveFactory);
7081                if (isFunction(directive)) {
7082                  directive = { compile: valueFn(directive) };
7083                } else if (!directive.compile && directive.link) {
7084                  directive.compile = valueFn(directive.link);
7085                }
7086                directive.priority = directive.priority || 0;
7087                directive.index = index;
7088                directive.name = directive.name || name;
7089                directive.require = directive.require || (directive.controller && directive.name);
7090                directive.restrict = directive.restrict || 'EA';
7091                var bindings = directive.$$bindings =
7092                    parseDirectiveBindings(directive, directive.name);
7093                if (isObject(bindings.isolateScope)) {
7094                  directive.$$isolateBindings = bindings.isolateScope;
7095                }
7096                directive.$$moduleName = directiveFactory.$$moduleName;
7097                directives.push(directive);
7098              } catch (e) {
7099                $exceptionHandler(e);
7100              }
7101            });
7102            return directives;
7103          }]);
7104      }
7105      hasDirectives[name].push(directiveFactory);
7106    } else {
7107      forEach(name, reverseParams(registerDirective));
7108    }
7109    return this;
7110  };
7111
7112
7113  /**
7114   * @ngdoc method
7115   * @name $compileProvider#aHrefSanitizationWhitelist
7116   * @kind function
7117   *
7118   * @description
7119   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7120   * urls during a[href] sanitization.
7121   *
7122   * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7123   *
7124   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7125   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7126   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7127   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7128   *
7129   * @param {RegExp=} regexp New regexp to whitelist urls with.
7130   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7131   *    chaining otherwise.
7132   */
7133  this.aHrefSanitizationWhitelist = function(regexp) {
7134    if (isDefined(regexp)) {
7135      $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7136      return this;
7137    } else {
7138      return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7139    }
7140  };
7141
7142
7143  /**
7144   * @ngdoc method
7145   * @name $compileProvider#imgSrcSanitizationWhitelist
7146   * @kind function
7147   *
7148   * @description
7149   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7150   * urls during img[src] sanitization.
7151   *
7152   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7153   *
7154   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7155   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7156   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7157   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7158   *
7159   * @param {RegExp=} regexp New regexp to whitelist urls with.
7160   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7161   *    chaining otherwise.
7162   */
7163  this.imgSrcSanitizationWhitelist = function(regexp) {
7164    if (isDefined(regexp)) {
7165      $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7166      return this;
7167    } else {
7168      return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7169    }
7170  };
7171
7172  /**
7173   * @ngdoc method
7174   * @name  $compileProvider#debugInfoEnabled
7175   *
7176   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7177   * current debugInfoEnabled state
7178   * @returns {*} current value if used as getter or itself (chaining) if used as setter
7179   *
7180   * @kind function
7181   *
7182   * @description
7183   * Call this method to enable/disable various debug runtime information in the compiler such as adding
7184   * binding information and a reference to the current scope on to DOM elements.
7185   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7186   * * `ng-binding` CSS class
7187   * * `$binding` data property containing an array of the binding expressions
7188   *
7189   * You may want to disable this in production for a significant performance boost. See
7190   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7191   *
7192   * The default value is true.
7193   */
7194  var debugInfoEnabled = true;
7195  this.debugInfoEnabled = function(enabled) {
7196    if (isDefined(enabled)) {
7197      debugInfoEnabled = enabled;
7198      return this;
7199    }
7200    return debugInfoEnabled;
7201  };
7202
7203  this.$get = [
7204            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7205            '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
7206    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
7207             $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
7208
7209    var Attributes = function(element, attributesToCopy) {
7210      if (attributesToCopy) {
7211        var keys = Object.keys(attributesToCopy);
7212        var i, l, key;
7213
7214        for (i = 0, l = keys.length; i < l; i++) {
7215          key = keys[i];
7216          this[key] = attributesToCopy[key];
7217        }
7218      } else {
7219        this.$attr = {};
7220      }
7221
7222      this.$$element = element;
7223    };
7224
7225    Attributes.prototype = {
7226      /**
7227       * @ngdoc method
7228       * @name $compile.directive.Attributes#$normalize
7229       * @kind function
7230       *
7231       * @description
7232       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7233       * `data-`) to its normalized, camelCase form.
7234       *
7235       * Also there is special case for Moz prefix starting with upper case letter.
7236       *
7237       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7238       *
7239       * @param {string} name Name to normalize
7240       */
7241      $normalize: directiveNormalize,
7242
7243
7244      /**
7245       * @ngdoc method
7246       * @name $compile.directive.Attributes#$addClass
7247       * @kind function
7248       *
7249       * @description
7250       * Adds the CSS class value specified by the classVal parameter to the element. If animations
7251       * are enabled then an animation will be triggered for the class addition.
7252       *
7253       * @param {string} classVal The className value that will be added to the element
7254       */
7255      $addClass: function(classVal) {
7256        if (classVal && classVal.length > 0) {
7257          $animate.addClass(this.$$element, classVal);
7258        }
7259      },
7260
7261      /**
7262       * @ngdoc method
7263       * @name $compile.directive.Attributes#$removeClass
7264       * @kind function
7265       *
7266       * @description
7267       * Removes the CSS class value specified by the classVal parameter from the element. If
7268       * animations are enabled then an animation will be triggered for the class removal.
7269       *
7270       * @param {string} classVal The className value that will be removed from the element
7271       */
7272      $removeClass: function(classVal) {
7273        if (classVal && classVal.length > 0) {
7274          $animate.removeClass(this.$$element, classVal);
7275        }
7276      },
7277
7278      /**
7279       * @ngdoc method
7280       * @name $compile.directive.Attributes#$updateClass
7281       * @kind function
7282       *
7283       * @description
7284       * Adds and removes the appropriate CSS class values to the element based on the difference
7285       * between the new and old CSS class values (specified as newClasses and oldClasses).
7286       *
7287       * @param {string} newClasses The current CSS className value
7288       * @param {string} oldClasses The former CSS className value
7289       */
7290      $updateClass: function(newClasses, oldClasses) {
7291        var toAdd = tokenDifference(newClasses, oldClasses);
7292        if (toAdd && toAdd.length) {
7293          $animate.addClass(this.$$element, toAdd);
7294        }
7295
7296        var toRemove = tokenDifference(oldClasses, newClasses);
7297        if (toRemove && toRemove.length) {
7298          $animate.removeClass(this.$$element, toRemove);
7299        }
7300      },
7301
7302      /**
7303       * Set a normalized attribute on the element in a way such that all directives
7304       * can share the attribute. This function properly handles boolean attributes.
7305       * @param {string} key Normalized key. (ie ngAttribute)
7306       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7307       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7308       *     Defaults to true.
7309       * @param {string=} attrName Optional none normalized name. Defaults to key.
7310       */
7311      $set: function(key, value, writeAttr, attrName) {
7312        // TODO: decide whether or not to throw an error if "class"
7313        //is set through this function since it may cause $updateClass to
7314        //become unstable.
7315
7316        var node = this.$$element[0],
7317            booleanKey = getBooleanAttrName(node, key),
7318            aliasedKey = getAliasedAttrName(key),
7319            observer = key,
7320            nodeName;
7321
7322        if (booleanKey) {
7323          this.$$element.prop(key, value);
7324          attrName = booleanKey;
7325        } else if (aliasedKey) {
7326          this[aliasedKey] = value;
7327          observer = aliasedKey;
7328        }
7329
7330        this[key] = value;
7331
7332        // translate normalized key to actual key
7333        if (attrName) {
7334          this.$attr[key] = attrName;
7335        } else {
7336          attrName = this.$attr[key];
7337          if (!attrName) {
7338            this.$attr[key] = attrName = snake_case(key, '-');
7339          }
7340        }
7341
7342        nodeName = nodeName_(this.$$element);
7343
7344        if ((nodeName === 'a' && key === 'href') ||
7345            (nodeName === 'img' && key === 'src')) {
7346          // sanitize a[href] and img[src] values
7347          this[key] = value = $$sanitizeUri(value, key === 'src');
7348        } else if (nodeName === 'img' && key === 'srcset') {
7349          // sanitize img[srcset] values
7350          var result = "";
7351
7352          // first check if there are spaces because it's not the same pattern
7353          var trimmedSrcset = trim(value);
7354          //                (   999x   ,|   999w   ,|   ,|,   )
7355          var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7356          var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7357
7358          // split srcset into tuple of uri and descriptor except for the last item
7359          var rawUris = trimmedSrcset.split(pattern);
7360
7361          // for each tuples
7362          var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7363          for (var i = 0; i < nbrUrisWith2parts; i++) {
7364            var innerIdx = i * 2;
7365            // sanitize the uri
7366            result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7367            // add the descriptor
7368            result += (" " + trim(rawUris[innerIdx + 1]));
7369          }
7370
7371          // split the last item into uri and descriptor
7372          var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7373
7374          // sanitize the last uri
7375          result += $$sanitizeUri(trim(lastTuple[0]), true);
7376
7377          // and add the last descriptor if any
7378          if (lastTuple.length === 2) {
7379            result += (" " + trim(lastTuple[1]));
7380          }
7381          this[key] = value = result;
7382        }
7383
7384        if (writeAttr !== false) {
7385          if (value === null || isUndefined(value)) {
7386            this.$$element.removeAttr(attrName);
7387          } else {
7388            this.$$element.attr(attrName, value);
7389          }
7390        }
7391
7392        // fire observers
7393        var $$observers = this.$$observers;
vendor: 11,170 bytes, lines 7394-7657
7394        $$observers && forEach($$observers[observer], function(fn) {
7395          try {
7396            fn(value);
7397          } catch (e) {
7398            $exceptionHandler(e);
7399          }
7400        });
7401      },
7402
7403
7404      /**
7405       * @ngdoc method
7406       * @name $compile.directive.Attributes#$observe
7407       * @kind function
7408       *
7409       * @description
7410       * Observes an interpolated attribute.
7411       *
7412       * The observer function will be invoked once during the next `$digest` following
7413       * compilation. The observer is then invoked whenever the interpolated value
7414       * changes.
7415       *
7416       * @param {string} key Normalized key. (ie ngAttribute) .
7417       * @param {function(interpolatedValue)} fn Function that will be called whenever
7418                the interpolated value of the attribute changes.
7419       *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
7420       * @returns {function()} Returns a deregistration function for this observer.
7421       */
7422      $observe: function(key, fn) {
7423        var attrs = this,
7424            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7425            listeners = ($$observers[key] || ($$observers[key] = []));
7426
7427        listeners.push(fn);
7428        $rootScope.$evalAsync(function() {
7429          if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7430            // no one registered attribute interpolation function, so lets call it manually
7431            fn(attrs[key]);
7432          }
7433        });
7434
7435        return function() {
7436          arrayRemove(listeners, fn);
7437        };
7438      }
7439    };
7440
7441
7442    function safeAddClass($element, className) {
7443      try {
7444        $element.addClass(className);
7445      } catch (e) {
7446        // ignore, since it means that we are trying to set class on
7447        // SVG element, where class name is read-only.
7448      }
7449    }
7450
7451
7452    var startSymbol = $interpolate.startSymbol(),
7453        endSymbol = $interpolate.endSymbol(),
7454        denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
7455            ? identity
7456            : function denormalizeTemplate(template) {
7457              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7458        },
7459        NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7460
7461    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7462      var bindings = $element.data('$binding') || [];
7463
7464      if (isArray(binding)) {
7465        bindings = bindings.concat(binding);
7466      } else {
7467        bindings.push(binding);
7468      }
7469
7470      $element.data('$binding', bindings);
7471    } : noop;
7472
7473    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7474      safeAddClass($element, 'ng-binding');
7475    } : noop;
7476
7477    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7478      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7479      $element.data(dataName, scope);
7480    } : noop;
7481
7482    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7483      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7484    } : noop;
7485
7486    return compile;
7487
7488    //    ====
7489
7490    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7491                        previousCompileContext) {
7492      if (!($compileNodes instanceof jqLite)) {
7493        // jquery always rewraps, whereas we need to preserve the original selector so that we can
7494        // modify it.
7495        $compileNodes = jqLite($compileNodes);
7496      }
7497      // We can not compile top level text elements since text nodes can be merged and we will
7498      // not be able to attach scope data to them, so we will wrap them in <span>
7499      forEach($compileNodes, function(node, index) {
7500        if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
7501          $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
7502        }
7503      });
7504      var compositeLinkFn =
7505              compileNodes($compileNodes, transcludeFn, $compileNodes,
7506                           maxPriority, ignoreDirective, previousCompileContext);
7507      compile.$$addScopeClass($compileNodes);
7508      var namespace = null;
7509      return function publicLinkFn(scope, cloneConnectFn, options) {
7510        assertArg(scope, 'scope');
7511
7512        options = options || {};
7513        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7514          transcludeControllers = options.transcludeControllers,
7515          futureParentElement = options.futureParentElement;
7516
7517        // When `parentBoundTranscludeFn` is passed, it is a
7518        // `controllersBoundTransclude` function (it was previously passed
7519        // as `transclude` to directive.link) so we must unwrap it to get
7520        // its `boundTranscludeFn`
7521        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7522          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7523        }
7524
7525        if (!namespace) {
7526          namespace = detectNamespaceForChildElements(futureParentElement);
7527        }
7528        var $linkNode;
7529        if (namespace !== 'html') {
7530          // When using a directive with replace:true and templateUrl the $compileNodes
7531          // (or a child element inside of them)
7532          // might change, so we need to recreate the namespace adapted compileNodes
7533          // for call to the link function.
7534          // Note: This will already clone the nodes...
7535          $linkNode = jqLite(
7536            wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7537          );
7538        } else if (cloneConnectFn) {
7539          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7540          // and sometimes changes the structure of the DOM.
7541          $linkNode = JQLitePrototype.clone.call($compileNodes);
7542        } else {
7543          $linkNode = $compileNodes;
7544        }
7545
7546        if (transcludeControllers) {
7547          for (var controllerName in transcludeControllers) {
7548            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7549          }
7550        }
7551
7552        compile.$$addScopeInfo($linkNode, scope);
7553
7554        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7555        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7556        return $linkNode;
7557      };
7558    }
7559
7560    function detectNamespaceForChildElements(parentElement) {
7561      // TODO: Make this detect MathML as well...
7562      var node = parentElement && parentElement[0];
7563      if (!node) {
7564        return 'html';
7565      } else {
7566        return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7567      }
7568    }
7569
7570    /**
7571     * Compile function matches each node in nodeList against the directives. Once all directives
7572     * for a particular node are collected their compile functions are executed. The compile
7573     * functions return values - the linking functions - are combined into a composite linking
7574     * function, which is the a linking function for the node.
7575     *
7576     * @param {NodeList} nodeList an array of nodes or NodeList to compile
7577     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7578     *        scope argument is auto-generated to the new child of the transcluded parent scope.
7579     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7580     *        the rootElement must be set the jqLite collection of the compile root. This is
7581     *        needed so that the jqLite collection items can be replaced with widgets.
7582     * @param {number=} maxPriority Max directive priority.
7583     * @returns {Function} A composite linking function of all of the matched directives or null.
7584     */
7585    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7586                            previousCompileContext) {
7587      var linkFns = [],
7588          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7589
7590      for (var i = 0; i < nodeList.length; i++) {
7591        attrs = new Attributes();
7592
7593        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7594        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7595                                        ignoreDirective);
7596
7597        nodeLinkFn = (directives.length)
7598            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7599                                      null, [], [], previousCompileContext)
7600            : null;
7601
7602        if (nodeLinkFn && nodeLinkFn.scope) {
7603          compile.$$addScopeClass(attrs.$$element);
7604        }
7605
7606        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7607                      !(childNodes = nodeList[i].childNodes) ||
7608                      !childNodes.length)
7609            ? null
7610            : compileNodes(childNodes,
7611                 nodeLinkFn ? (
7612                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7613                     && nodeLinkFn.transclude) : transcludeFn);
7614
7615        if (nodeLinkFn || childLinkFn) {
7616          linkFns.push(i, nodeLinkFn, childLinkFn);
7617          linkFnFound = true;
7618          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7619        }
7620
7621        //use the previous context only for the first element in the virtual group
7622        previousCompileContext = null;
7623      }
7624
7625      // return a linking function if we have found anything, null otherwise
7626      return linkFnFound ? compositeLinkFn : null;
7627
7628      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7629        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7630        var stableNodeList;
7631
7632
7633        if (nodeLinkFnFound) {
7634          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7635          // offsets don't get screwed up
7636          var nodeListLength = nodeList.length;
7637          stableNodeList = new Array(nodeListLength);
7638
7639          // create a sparse array by only copying the elements which have a linkFn
7640          for (i = 0; i < linkFns.length; i+=3) {
7641            idx = linkFns[i];
7642            stableNodeList[idx] = nodeList[idx];
7643          }
7644        } else {
7645          stableNodeList = nodeList;
7646        }
7647
7648        for (i = 0, ii = linkFns.length; i < ii;) {
7649          node = stableNodeList[linkFns[i++]];
7650          nodeLinkFn = linkFns[i++];
7651          childLinkFn = linkFns[i++];
7652
7653          if (nodeLinkFn) {
7654            if (nodeLinkFn.scope) {
7655              childScope = scope.$new();
7656              compile.$$addScopeInfo(jqLite(node), childScope);
7657              var destroyBindings = nodeLinkFn.$$destroyBindings;
7658              if (destroyBindings) {
7659                nodeLinkFn.$$destroyBindings = null;
7660                childScope.$on('$destroyed', destroyBindings);
7661              }
7662            } else {
7663              childScope = scope;
7664            }
7665
7666            if (nodeLinkFn.transcludeOnThisElement) {
7667              childBoundTranscludeFn = createBoundTranscludeFn(
7668                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7669
7670            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7671              childBoundTranscludeFn = parentBoundTranscludeFn;
7672
7673            } else if (!parentBoundTranscludeFn && transcludeFn) {
7674              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7675
7676            } else {
7677              childBoundTranscludeFn = null;
7678            }
7679
7680            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn,
7681                       nodeLinkFn);
7682
7683          } else if (childLinkFn) {
7684            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7685          }
7686        }
7687      }
7688    }
7689
7690    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7691
7692      var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7693
7694        if (!transcludedScope) {
7695          transcludedScope = scope.$new(false, containingScope);
7696          transcludedScope.$$transcluded = true;
7697        }
7698
7699        return transcludeFn(transcludedScope, cloneFn, {
7700          parentBoundTranscludeFn: previousBoundTranscludeFn,
7701          transcludeControllers: controllers,
7702          futureParentElement: futureParentElement
7703        });
7704      };
7705
7706      return boundTranscludeFn;
7707    }
7708
7709    /**
7710     * Looks for directives on the given node and adds them to the directive collection which is
7711     * sorted.
7712     *
7713     * @param node Node to search.
7714     * @param directives An array to which the directives are added to. This array is sorted before
7715     *        the function returns.
7716     * @param attrs The shared attrs object which is used to populate the normalized attributes.
7717     * @param {number=} maxPriority Max directive priority.
7718     */
7719    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7720      var nodeType = node.nodeType,
7721          attrsMap = attrs.$attr,
7722          match,
7723          className;
7724
7725      switch (nodeType) {
7726        case NODE_TYPE_ELEMENT: /* Element */
7727          // use the node name: <directive>
7728          addDirective(directives,
7729              directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7730
7731          // iterate over the attributes
7732          for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7733                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7734            var attrStartName = false;
7735            var attrEndName = false;
7736
7737            attr = nAttrs[j];
7738            name = attr.name;
7739            value = trim(attr.value);
7740
7741            // support ngAttr attribute binding
7742            ngAttrName = directiveNormalize(name);
7743            if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7744              name = name.replace(PREFIX_REGEXP, '')
7745                .substr(8).replace(/_(.)/g, function(match, letter) {
7746                  return letter.toUpperCase();
7747                });
7748            }
7749
7750            var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
7751            if (directiveIsMultiElement(directiveNName)) {
7752              if (ngAttrName === directiveNName + 'Start') {
7753                attrStartName = name;
7754                attrEndName = name.substr(0, name.length - 5) + 'end';
7755                name = name.substr(0, name.length - 6);
7756              }
7757            }
7758
7759            nName = directiveNormalize(name.toLowerCase());
7760            attrsMap[nName] = name;
7761            if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7762                attrs[nName] = value;
7763                if (getBooleanAttrName(node, nName)) {
7764                  attrs[nName] = true; // presence means true
7765                }
7766            }
7767            addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7768            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7769                          attrEndName);
7770          }
7771
7772          // use class as directive
7773          className = node.className;
7774          if (isObject(className)) {
7775              // Maybe SVGAnimatedString
7776              className = className.animVal;
7777          }
7778          if (isString(className) && className !== '') {
7779            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7780              nName = directiveNormalize(match[2]);
7781              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7782                attrs[nName] = trim(match[3]);
7783              }
7784              className = className.substr(match.index + match[0].length);
7785            }
7786          }
7787          break;
7788        case NODE_TYPE_TEXT: /* Text Node */
7789          if (msie === 11) {
7790            // Workaround for #11781
7791            while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
7792              node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
7793              node.parentNode.removeChild(node.nextSibling);
7794            }
7795          }
7796          addTextInterpolateDirective(directives, node.nodeValue);
7797          break;
7798        case NODE_TYPE_COMMENT: /* Comment */
7799          try {
7800            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7801            if (match) {
7802              nName = directiveNormalize(match[1]);
7803              if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7804                attrs[nName] = trim(match[2]);
7805              }
7806            }
7807          } catch (e) {
7808            // turns out that under some circumstances IE9 throws errors when one attempts to read
7809            // comment's node value.
7810            // Just ignore it and continue. (Can't seem to reproduce in test case.)
7811          }
7812          break;
7813      }
7814
7815      directives.sort(byPriority);
7816      return directives;
7817    }
7818
7819    /**
7820     * Given a node with an directive-start it collects all of the siblings until it finds
7821     * directive-end.
7822     * @param node
7823     * @param attrStart
7824     * @param attrEnd
7825     * @returns {*}
7826     */
7827    function groupScan(node, attrStart, attrEnd) {
7828      var nodes = [];
7829      var depth = 0;
7830      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7831        do {
7832          if (!node) {
7833            throw $compileMinErr('uterdir',
7834                      "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7835                      attrStart, attrEnd);
7836          }
7837          if (node.nodeType == NODE_TYPE_ELEMENT) {
7838            if (node.hasAttribute(attrStart)) depth++;
7839            if (node.hasAttribute(attrEnd)) depth--;
7840          }
7841          nodes.push(node);
7842          node = node.nextSibling;
7843        } while (depth > 0);
7844      } else {
7845        nodes.push(node);
7846      }
7847
7848      return jqLite(nodes);
7849    }
7850
7851    /**
7852     * Wrapper for linking function which converts normal linking function into a grouped
7853     * linking function.
7854     * @param linkFn
7855     * @param attrStart
7856     * @param attrEnd
7857     * @returns {Function}
7858     */
7859    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7860      return function(scope, element, attrs, controllers, transcludeFn) {
7861        element = groupScan(element[0], attrStart, attrEnd);
7862        return linkFn(scope, element, attrs, controllers, transcludeFn);
7863      };
7864    }
7865
7866    /**
7867     * Once the directives have been collected, their compile functions are executed. This method
7868     * is responsible for inlining directive templates as well as terminating the application
7869     * of the directives if the terminal directive has been reached.
7870     *
7871     * @param {Array} directives Array of collected directives to execute their compile function.
7872     *        this needs to be pre-sorted by priority order.
7873     * @param {Node} compileNode The raw DOM node to apply the compile functions to
7874     * @param {Object} templateAttrs The shared attribute function
7875     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7876     *                                                  scope argument is auto-generated to the new
7877     *                                                  child of the transcluded parent scope.
7878     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
7879     *                              argument has the root jqLite array so that we can replace nodes
7880     *                              on it.
7881     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
7882     *                                           compiling the transclusion.
7883     * @param {Array.<Function>} preLinkFns
7884     * @param {Array.<Function>} postLinkFns
7885     * @param {Object} previousCompileContext Context used for previous compilation of the current
7886     *                                        node
7887     * @returns {Function} linkFn
7888     */
7889    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
7890                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
7891                                   previousCompileContext) {
7892      previousCompileContext = previousCompileContext || {};
7893
7894      var terminalPriority = -Number.MAX_VALUE,
7895          newScopeDirective = previousCompileContext.newScopeDirective,
7896          controllerDirectives = previousCompileContext.controllerDirectives,
7897          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
7898          templateDirective = previousCompileContext.templateDirective,
7899          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
7900          hasTranscludeDirective = false,
7901          hasTemplate = false,
7902          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
7903          $compileNode = templateAttrs.$$element = jqLite(compileNode),
7904          directive,
7905          directiveName,
7906          $template,
7907          replaceDirective = originalReplaceDirective,
7908          childTranscludeFn = transcludeFn,
7909          linkFn,
7910          directiveValue;
7911
7912      // executes all directives on the current element
7913      for (var i = 0, ii = directives.length; i < ii; i++) {
7914        directive = directives[i];
7915        var attrStart = directive.$$start;
7916        var attrEnd = directive.$$end;
7917
7918        // collect multiblock sections
7919        if (attrStart) {
7920          $compileNode = groupScan(compileNode, attrStart, attrEnd);
7921        }
7922        $template = undefined;
7923
7924        if (terminalPriority > directive.priority) {
7925          break; // prevent further processing of directives
7926        }
7927
7928        if (directiveValue = directive.scope) {
7929
7930          // skip the check for directives with async templates, we'll check the derived sync
7931          // directive when the template arrives
7932          if (!directive.templateUrl) {
7933            if (isObject(directiveValue)) {
7934              // This directive is trying to add an isolated scope.
7935              // Check that there is no scope of any kind already
7936              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
7937                                directive, $compileNode);
7938              newIsolateScopeDirective = directive;
7939            } else {
7940              // This directive is trying to add a child scope.
7941              // Check that there is no isolated scope already
7942              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
7943                                $compileNode);
7944            }
7945          }
7946
7947          newScopeDirective = newScopeDirective || directive;
7948        }
7949
7950        directiveName = directive.name;
7951
7952        if (!directive.templateUrl && directive.controller) {
7953          directiveValue = directive.controller;
7954          controllerDirectives = controllerDirectives || createMap();
7955          assertNoDuplicate("'" + directiveName + "' controller",
7956              controllerDirectives[directiveName], directive, $compileNode);
7957          controllerDirectives[directiveName] = directive;
7958        }
7959
7960        if (directiveValue = directive.transclude) {
7961          hasTranscludeDirective = true;
7962
7963          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
7964          // This option should only be used by directives that know how to safely handle element transclusion,
7965          // where the transcluded nodes are added or replaced after linking.
7966          if (!directive.$$tlb) {
7967            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
7968            nonTlbTranscludeDirective = directive;
7969          }
7970
7971          if (directiveValue == 'element') {
7972            hasElementTranscludeDirective = true;
7973            terminalPriority = directive.priority;
7974            $template = $compileNode;
7975            $compileNode = templateAttrs.$$element =
7976                jqLite(document.createComment(' ' + directiveName + ': ' +
7977                                              templateAttrs[directiveName] + ' '));
7978            compileNode = $compileNode[0];
7979            replaceWith(jqCollection, sliceArgs($template), compileNode);
7980
7981            childTranscludeFn = compile($template, transcludeFn, terminalPriority,
7982                                        replaceDirective && replaceDirective.name, {
7983                                          // Don't pass in:
7984                                          // - controllerDirectives - otherwise we'll create duplicates controllers
7985                                          // - newIsolateScopeDirective or templateDirective - comb
vendor: 10,483 bytes, lines 7985-8225
7985ining templates with
7986                                          //   element transclusion doesn't make sense.
7987                                          //
7988                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
7989                                          // on the same element more than once.
7990                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective
7991                                        });
7992          } else {
7993            $template = jqLite(jqLiteClone(compileNode)).contents();
7994            $compileNode.empty(); // clear contents
7995            childTranscludeFn = compile($template, transcludeFn);
7996          }
7997        }
7998
7999        if (directive.template) {
8000          hasTemplate = true;
8001          assertNoDuplicate('template', templateDirective, directive, $compileNode);
8002          templateDirective = directive;
8003
8004          directiveValue = (isFunction(directive.template))
8005              ? directive.template($compileNode, templateAttrs)
8006              : directive.template;
8007
8008          directiveValue = denormalizeTemplate(directiveValue);
8009
8010          if (directive.replace) {
8011            replaceDirective = directive;
8012            if (jqLiteIsTextNode(directiveValue)) {
8013              $template = [];
8014            } else {
8015              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8016            }
8017            compileNode = $template[0];
8018
8019            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8020              throw $compileMinErr('tplrt',
8021                  "Template for directive '{0}' must have exactly one root element. {1}",
8022                  directiveName, '');
8023            }
8024
8025            replaceWith(jqCollection, $compileNode, compileNode);
8026
8027            var newTemplateAttrs = {$attr: {}};
8028
8029            // combine directives from the original node and from the template:
8030            // - take the array of directives for this element
8031            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8032            // - collect directives from the template and sort them by priority
8033            // - combine directives as: processed + template + unprocessed
8034            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8035            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8036
8037            if (newIsolateScopeDirective) {
8038              markDirectivesAsIsolate(templateDirectives);
8039            }
8040            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8041            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8042
8043            ii = directives.length;
8044          } else {
8045            $compileNode.html(directiveValue);
8046          }
8047        }
8048
8049        if (directive.templateUrl) {
8050          hasTemplate = true;
8051          assertNoDuplicate('template', templateDirective, directive, $compileNode);
8052          templateDirective = directive;
8053
8054          if (directive.replace) {
8055            replaceDirective = directive;
8056          }
8057
8058          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8059              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8060                controllerDirectives: controllerDirectives,
8061                newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8062                newIsolateScopeDirective: newIsolateScopeDirective,
8063                templateDirective: templateDirective,
8064                nonTlbTranscludeDirective: nonTlbTranscludeDirective
8065              });
8066          ii = directives.length;
8067        } else if (directive.compile) {
8068          try {
8069            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8070            if (isFunction(linkFn)) {
8071              addLinkFns(null, linkFn, attrStart, attrEnd);
8072            } else if (linkFn) {
8073              addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8074            }
8075          } catch (e) {
8076            $exceptionHandler(e, startingTag($compileNode));
8077          }
8078        }
8079
8080        if (directive.terminal) {
8081          nodeLinkFn.terminal = true;
8082          terminalPriority = Math.max(terminalPriority, directive.priority);
8083        }
8084
8085      }
8086
8087      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8088      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8089      nodeLinkFn.templateOnThisElement = hasTemplate;
8090      nodeLinkFn.transclude = childTranscludeFn;
8091
8092      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8093
8094      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8095      return nodeLinkFn;
8096
8097      ////////////////////
8098
8099      function addLinkFns(pre, post, attrStart, attrEnd) {
8100        if (pre) {
8101          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8102          pre.require = directive.require;
8103          pre.directiveName = directiveName;
8104          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8105            pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8106          }
8107          preLinkFns.push(pre);
8108        }
8109        if (post) {
8110          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8111          post.require = directive.require;
8112          post.directiveName = directiveName;
8113          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8114            post = cloneAndAnnotateFn(post, {isolateScope: true});
8115          }
8116          postLinkFns.push(post);
8117        }
8118      }
8119
8120
8121      function getControllers(directiveName, require, $element, elementControllers) {
8122        var value;
8123
8124        if (isString(require)) {
8125          var match = require.match(REQUIRE_PREFIX_REGEXP);
8126          var name = require.substring(match[0].length);
8127          var inheritType = match[1] || match[3];
8128          var optional = match[2] === '?';
8129
8130          //If only parents then start at the parent element
8131          if (inheritType === '^^') {
8132            $element = $element.parent();
8133          //Otherwise attempt getting the controller from elementControllers in case
8134          //the element is transcluded (and has no data) and to avoid .data if possible
8135          } else {
8136            value = elementControllers && elementControllers[name];
8137            value = value && value.instance;
8138          }
8139
8140          if (!value) {
8141            var dataName = '$' + name + 'Controller';
8142            value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8143          }
8144
8145          if (!value && !optional) {
8146            throw $compileMinErr('ctreq',
8147                "Controller '{0}', required by directive '{1}', can't be found!",
8148                name, directiveName);
8149          }
8150        } else if (isArray(require)) {
8151          value = [];
8152          for (var i = 0, ii = require.length; i < ii; i++) {
8153            value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8154          }
8155        }
8156
8157        return value || null;
8158      }
8159
8160      function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8161        var elementControllers = createMap();
8162        for (var controllerKey in controllerDirectives) {
8163          var directive = controllerDirectives[controllerKey];
8164          var locals = {
8165            $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8166            $element: $element,
8167            $attrs: attrs,
8168            $transclude: transcludeFn
8169          };
8170
8171          var controller = directive.controller;
8172          if (controller == '@') {
8173            controller = attrs[directive.name];
8174          }
8175
8176          var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8177
8178          // For directives with element transclusion the element is a comment,
8179          // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
8180          // clean up (http://bugs.jquery.com/ticket/8335).
8181          // Instead, we save the controllers for the element in a local hash and attach to .data
8182          // later, once we have the actual element.
8183          elementControllers[directive.name] = controllerInstance;
8184          if (!hasElementTranscludeDirective) {
8185            $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8186          }
8187        }
8188        return elementControllers;
8189      }
8190
8191      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn,
8192                          thisLinkFn) {
8193        var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
8194            attrs;
8195
8196        if (compileNode === linkNode) {
8197          attrs = templateAttrs;
8198          $element = templateAttrs.$$element;
8199        } else {
8200          $element = jqLite(linkNode);
8201          attrs = new Attributes($element, templateAttrs);
8202        }
8203
8204        if (newIsolateScopeDirective) {
8205          isolateScope = scope.$new(true);
8206        }
8207
8208        if (boundTranscludeFn) {
8209          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8210          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8211          transcludeFn = controllersBoundTransclude;
8212          transcludeFn.$$boundTransclude = boundTranscludeFn;
8213        }
8214
8215        if (controllerDirectives) {
8216          elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8217        }
8218
8219        if (newIsolateScopeDirective) {
8220          // Initialize isolate scope bindings for new isolate scope directive.
8221          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8222              templateDirective === newIsolateScopeDirective.$$originalDirective)));
8223          compile.$$addScopeClass($element, true);
8224          isolateScope.$$isolateBindings =
8225              newIsolateScopeDirective.$$isolateBindings;
8226          initializeDirectiveBindings(scope, attrs, isolateScope,
8227                                      isolateScope.$$isolateBindings,
8228                                      newIsolateScopeDirective, isolateScope);
8229        }
8230        if (elementControllers) {
8231          // Initialize bindToController bindings for new/isolate scopes
8232          var scopeDirective = newIsolateScopeDirective || newScopeDirective;
8233          var bindings;
8234          var controllerForBindings;
8235          if (scopeDirective && elementControllers[scopeDirective.name]) {
8236            bindings = scopeDirective.$$bindings.bindToController;
8237            controller = elementControllers[scopeDirective.name];
8238
8239            if (controller && controller.identifier && bindings) {
8240              controllerForBindings = controller;
8241              thisLinkFn.$$destroyBindings =
8242                  initializeDirectiveBindings(scope, attrs, controller.instance,
8243                                              bindings, scopeDirective);
8244            }
8245          }
8246          for (i in elementControllers) {
8247            controller = elementControllers[i];
8248            var controllerResult = controller();
8249
8250            if (controllerResult !== controller.instance) {
8251              // If the controller constructor has a return value, overwrite the instance
8252              // from setupControllers and update the element data
8253              controller.instance = controllerResult;
8254              $element.data('$' + i + 'Controller', controllerResult);
8255              if (controller === controllerForBindings) {
8256                // Remove and re-install bindToController bindings
8257                thisLinkFn.$$destroyBindings();
8258                thisLinkFn.$$destroyBindings =
8259                  initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
8260              }
8261            }
8262          }
8263        }
8264
8265        // PRELINKING
8266        for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8267          linkFn = preLinkFns[i];
8268          invokeLinkFn(linkFn,
8269              linkFn.isolateScope ? isolateScope : scope,
8270              $element,
8271              attrs,
8272              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8273              transcludeFn
8274          );
8275        }
8276
8277        // RECURSION
8278        // We only pass the isolate scope, if the isolate directive has a template,
8279        // otherwise the child elements do not belong to the isolate directive.
8280        var scopeToChild = scope;
8281        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8282          scopeToChild = isolateScope;
8283        }
8284        childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8285
8286        // POSTLINKING
8287        for (i = postLinkFns.length - 1; i >= 0; i--) {
8288          linkFn = postLinkFns[i];
8289          invokeLinkFn(linkFn,
8290              linkFn.isolateScope ? isolateScope : scope,
8291              $element,
8292              attrs,
8293              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8294              transcludeFn
8295          );
8296        }
8297
8298        // This is the function that is injected as `$transclude`.
8299        // Note: all arguments are optional!
8300        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8301          var transcludeControllers;
8302
8303          // No scope passed in:
8304          if (!isScope(scope)) {
8305            futureParentElement = cloneAttachFn;
8306            cloneAttachFn = scope;
8307            scope = undefined;
8308          }
8309
8310          if (hasElementTranscludeDirective) {
8311            transcludeControllers = elementControllers;
8312          }
8313          if (!futureParentElement) {
8314            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8315          }
8316          return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8317        }
8318      }
8319    }
8320
8321    function markDirectivesAsIsolate(directives) {
8322      // mark all directives as needing isolate scope.
8323      for (var j = 0, jj = directives.length; j < jj; j++) {
8324        directives[j] = inherit(directives[j], {$$isolateScope: true});
8325      }
8326    }
8327
8328    /**
8329     * looks up the directive and decorates it with exception handling and proper parameters. We
8330     * call this the boundDirective.
8331     *
8332     * @param {string} name name of the directive to look up.
8333     * @param {string} location The directive must be found in specific format.
8334     *   String containing any of theses characters:
8335     *
8336     *   * `E`: element name
8337     *   * `A': attribute
8338     *   * `C`: class
8339     *   * `M`: comment
8340     * @returns {boolean} true if directive was added.
8341     */
8342    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8343                          endAttrName) {
8344      if (name === ignoreDirective) return null;
8345      var match = null;
8346      if (hasDirectives.hasOwnProperty(name)) {
8347        for (var directive, directives = $injector.get(name + Suffix),
8348            i = 0, ii = directives.length; i < ii; i++) {
8349          try {
8350            directive = directives[i];
8351            if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8352                 directive.restrict.indexOf(location) != -1) {
8353              if (startAttrName) {
8354                directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8355              }
8356              tDirectives.push(directive);
8357              match = directive;
8358            }
8359          } catch (e) { $exceptionHandler(e); }
8360        }
8361      }
8362      return match;
8363    }
8364
8365
8366    /**
8367     * looks up the directive and returns true if it is a multi-element directive,
8368     * and therefore requires DOM nodes between -start and -end markers to be grouped
8369     * together.
8370     *
8371     * @param {string} name name of the directive to look up.
8372     * @returns true if directive was registered as multi-element.
8373     */
8374    function directiveIsMultiElement(name) {
8375      if (hasDirectives.hasOwnProperty(name)) {
8376        for (var directive, directives = $injector.get(name + Suffix),
8377            i = 0, ii = directives.length; i < ii; i++) {
8378          directive = directives[i];
8379          if (directive.multiElement) {
8380            return true;
8381          }
8382        }
8383      }
8384      return false;
8385    }
8386
8387    /**
8388     * When the element is replaced with HTML template then the new attributes
8389     * on the template need to be merged with the existing attributes in the DOM.
8390     * The desired effect is to have both of the attributes present.
8391     *
8392     * @param {object} dst destination attributes (original DOM)
8393     * @param {object} src source attributes (from the directive template)
8394     */
8395    function mergeTemplateAttributes(dst, src) {
8396      var srcAttr = src.$attr,
8397          dstAttr = dst.$attr,
8398          $element = dst.$$element;
8399
8400      // reapply the old attributes to the new element
8401      forEach(dst, function(value, key) {
8402        if (key.charAt(0) != '$') {
8403          if (src[key] && src[key] !== value) {
8404            value += (key === 'style' ? ';' : ' ') + src[key];
8405          }
8406          dst.$set(key, value, true, srcAttr[key]);
8407        }
8408      });
8409
8410      // copy the new attributes on the old attrs object
8411      forEach(src, function(value, key) {
8412        if (key == 'class') {
8413          safeAddClass($element, value);
8414          dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8415        } else if (key == 'style') {
8416          $element.attr('style', $element.attr('style') + ';' + value);
8417          dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8418          // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8419          // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8420          // have an attribute like "has-own-property" or "data-has-own-property", etc.
8421        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8422          dst[key] = value;
8423          dstAttr[key] = srcAttr[key];
8424        }
8425      });
8426    }
8427
8428
8429    function compileTemplateUrl(directives, $compileNode, tAttrs,
8430        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8431      var linkQueue = [],
8432          afterTemplateNodeLinkFn,
8433          afterTemplateChildLinkFn,
8434          beforeTemplateCompileNode = $compileNode[0],
8435          origAsyncDirective = directives.shift(),
8436          derivedSyncDirective = inherit(origAsyncDirective, {
8437            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8438          }),
8439          templateUrl = (isFunction(origAsyncDirective.templateUrl))
8440              ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8441              : origAsyncDirective.templateUrl,
8442          templateNamespace = origAsyncDirective.templateNamespace;
8443
8444      $compileNode.empty();
8445
8446      $templateRequest(templateUrl)
8447        .then(function(content) {
8448          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8449
8450          content = denormalizeTemplate(content);
8451
8452          if (origAsyncDirective.replace) {
8453            if (jqLiteIsTextNode(content)) {
8454              $template = [];
8455            } else {
8456              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8457            }
8458            compileNode = $template[0];
8459
8460            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8461              throw $compileMinErr('tplrt',
8462                  "Template for directive '{0}' must have exactly one root element. {1}",
8463                  origAsyncDirective.name, templateUrl);
8464            }
8465
8466            tempTemplateAttrs = {$attr: {}};
8467            replaceWith($rootElement, $compileNode, compileNode);
8468            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8469
8470            if (isObject(origAsyncDirective.scope)) {
8471              markDirectivesAsIsolate(templateDirectives);
8472            }
8473            directives = templateDirectives.concat(directives);
8474            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8475          } else {
8476            compileNode = beforeTemplateCompileNode;
8477            $compileNode.html(content);
8478          }
8479
8480          directives.unshift(derivedSyncDirective);
8481
8482          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8483              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8484              previousCompileContext);
8485          forEach($rootElement, function(node, i) {
8486            if (node == compileNode) {
8487              $rootElement[i] = $compileNode[0];
8488            }
8489          });
8490          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8491
8492          while (linkQueue.length) {
8493            var scope = linkQueue.shift(),
8494                beforeTemplateLinkNode = linkQueue.shift(),
8495                linkRootElement = linkQueue.shift(),
8496                boundTranscludeFn = linkQueue.shift(),
8497                linkNode = $compileNode[0];
8498
8499            if (scope.$$destroyed) continue;
8500
8501            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
vendor: 5,406 bytes, lines 8502-8639
8502              var oldClasses = beforeTemplateLinkNode.className;
8503
8504              if (!(previousCompileContext.hasElementTranscludeDirective &&
8505                  origAsyncDirective.replace)) {
8506                // it was cloned therefore we have to clone as well.
8507                linkNode = jqLiteClone(compileNode);
8508              }
8509              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8510
8511              // Copy in CSS classes from original node
8512              safeAddClass(jqLite(linkNode), oldClasses);
8513            }
8514            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8515              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8516            } else {
8517              childBoundTranscludeFn = boundTranscludeFn;
8518            }
8519            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8520              childBoundTranscludeFn, afterTemplateNodeLinkFn);
8521          }
8522          linkQueue = null;
8523        });
8524
8525      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8526        var childBoundTranscludeFn = boundTranscludeFn;
8527        if (scope.$$destroyed) return;
8528        if (linkQueue) {
8529          linkQueue.push(scope,
8530                         node,
8531                         rootElement,
8532                         childBoundTranscludeFn);
8533        } else {
8534          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8535            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8536          }
8537          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn,
8538                                  afterTemplateNodeLinkFn);
8539        }
8540      };
8541    }
8542
8543
8544    /**
8545     * Sorting function for bound directives.
8546     */
8547    function byPriority(a, b) {
8548      var diff = b.priority - a.priority;
8549      if (diff !== 0) return diff;
8550      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8551      return a.index - b.index;
8552    }
8553
8554    function assertNoDuplicate(what, previousDirective, directive, element) {
8555
8556      function wrapModuleNameIfDefined(moduleName) {
8557        return moduleName ?
8558          (' (module: ' + moduleName + ')') :
8559          '';
8560      }
8561
8562      if (previousDirective) {
8563        throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8564            previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8565            directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8566      }
8567    }
8568
8569
8570    function addTextInterpolateDirective(directives, text) {
8571      var interpolateFn = $interpolate(text, true);
8572      if (interpolateFn) {
8573        directives.push({
8574          priority: 0,
8575          compile: function textInterpolateCompileFn(templateNode) {
8576            var templateNodeParent = templateNode.parent(),
8577                hasCompileParent = !!templateNodeParent.length;
8578
8579            // When transcluding a template that has bindings in the root
8580            // we don't have a parent and thus need to add the class during linking fn.
8581            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8582
8583            return function textInterpolateLinkFn(scope, node) {
8584              var parent = node.parent();
8585              if (!hasCompileParent) compile.$$addBindingClass(parent);
8586              compile.$$addBindingInfo(parent, interpolateFn.expressions);
8587              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8588                node[0].nodeValue = value;
8589              });
8590            };
8591          }
8592        });
8593      }
8594    }
8595
8596
8597    function wrapTemplate(type, template) {
8598      type = lowercase(type || 'html');
8599      switch (type) {
8600      case 'svg':
8601      case 'math':
8602        var wrapper = document.createElement('div');
8603        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8604        return wrapper.childNodes[0].childNodes;
8605      default:
8606        return template;
8607      }
8608    }
8609
8610
8611    function getTrustedContext(node, attrNormalizedName) {
8612      if (attrNormalizedName == "srcdoc") {
8613        return $sce.HTML;
8614      }
8615      var tag = nodeName_(node);
8616      // maction[xlink:href] can source SVG.  It's not limited to <maction>.
8617      if (attrNormalizedName == "xlinkHref" ||
8618          (tag == "form" && attrNormalizedName == "action") ||
8619          (tag != "img" && (attrNormalizedName == "src" ||
8620                            attrNormalizedName == "ngSrc"))) {
8621        return $sce.RESOURCE_URL;
8622      }
8623    }
8624
8625
8626    function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8627      var trustedContext = getTrustedContext(node, name);
8628      allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8629
8630      var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8631
8632      // no interpolation found -> ignore
8633      if (!interpolateFn) return;
8634
8635
8636      if (name === "multiple" && nodeName_(node) === "select") {
8637        throw $compileMinErr("selmulti",
8638            "Binding to the 'multiple' attribute is not supported. Element: {0}",
8639            startingTag(node));
8640      }
8641
8642      directives.push({
8643        priority: 100,
8644        compile: function() {
8645            return {
8646              pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8647                var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
8648
8649                if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8650                  throw $compileMinErr('nodomevents',
8651                      "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
8652                          "ng- versions (such as ng-click instead of onclick) instead.");
8653                }
8654
8655                // If the attribute has changed since last $interpolate()ed
8656                var newValue = attr[name];
8657                if (newValue !== value) {
8658                  // we need to interpolate again since the attribute value has been updated
8659                  // (e.g. by another directive's compile function)
8660                  // ensure unset/empty values make interpolateFn falsy
8661                  interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8662                  value = newValue;
8663                }
8664
8665                // if attribute was updated so that there is no interpolation going on we don't want to
8666                // register any observers
8667                if (!interpolateFn) return;
8668
8669                // initialize attr object so that it's ready in case we need the value for isolate
8670                // scope initialization, otherwise the value would not be available from isolate
8671                // directive's linking fn during linking phase
8672                attr[name] = interpolateFn(scope);
8673
8674                ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8675                (attr.$$observers && attr.$$observers[name].$$scope || scope).
8676                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8677                    //special case for class attribute addition + removal
8678                    //so that class changes can tap into the animation
8679                    //hooks provided by the $animate service. Be sure to
8680                    //skip animations when the first digest occurs (when
8681                    //both the new and the old values are the same) since
8682                    //the CSS classes are the non-interpolated values
8683                    if (name === 'class' && newValue != oldValue) {
8684                      attr.$updateClass(newValue, oldValue);
8685                    } else {
8686                      attr.$set(name, newValue);
8687                    }
8688                  });
8689              }
8690            };
8691          }
8692      });
8693    }
8694
8695
8696    /**
8697     * This is a special jqLite.replaceWith, which can replace items which
8698     * have no parents, provided that the containing jqLite collection is provided.
8699     *
8700     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8701     *                               in the root of the tree.
8702     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8703     *                                  the shell, but replace its DOM node reference.
8704     * @param {Node} newNode The new DOM node.
8705     */
8706    function replaceWith($rootElement, elementsToRemove, newNode) {
8707      var firstElementToRemove = elementsToRemove[0],
8708          removeCount = elementsToRemove.length,
8709          parent = firstElementToRemove.parentNode,
8710          i, ii;
8711
8712      if ($rootElement) {
8713        for (i = 0, ii = $rootElement.length; i < ii; i++) {
8714          if ($rootElement[i] == firstElementToRemove) {
8715            $rootElement[i++] = newNode;
8716            for (var j = i, j2 = j + removeCount - 1,
8717                     jj = $rootElement.length;
8718                 j < jj; j++, j2++) {
8719              if (j2 < jj) {
8720                $rootElement[j] = $rootElement[j2];
8721              } else {
8722                delete $rootElement[j];
8723              }
8724            }
8725            $rootElement.length -= removeCount - 1;
8726
8727            // If the replaced element is also the jQuery .context then replace it
8728            // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8729            // http://api.jquery.com/context/
8730            if ($rootElement.context === firstElementToRemove) {
8731              $rootElement.context = newNode;
8732            }
8733            break;
8734          }
8735        }
8736      }
8737
8738      if (parent) {
8739        parent.replaceChild(newNode, firstElementToRemove);
8740      }
8741
8742      // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8743      var fragment = document.createDocumentFragment();
8744      fragment.appendChild(firstElementToRemove);
8745
8746      if (jqLite.hasData(firstElementToRemove)) {
8747        // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8748        // data here because there's no public interface in jQuery to do that and copying over
8749        // event listeners (which is the main use of private data) wouldn't work anyway.
8750        jqLite(newNode).data(jqLite(firstElementToRemove).data());
8751
8752        // Remove data of the replaced element. We cannot just call .remove()
8753        // on the element it since that would deallocate scope that is needed
8754        // for the new node. Instead, remove the data "manually".
8755        if (!jQuery) {
8756          delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8757        } else {
8758          // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8759          // the replaced element. The cleanData version monkey-patched by Angular would cause
8760          // the scope to be trashed and we do need the very same scope to work with the new
8761          // element. However, we cannot just cache the non-patched version and use it here as
8762          // that would break if another library patches the method after Angular does (one
8763          // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8764          // skipped this one time.
8765          skipDestroyOnNextJQueryCleanData = true;
8766          jQuery.cleanData([firstElementToRemove]);
8767        }
8768      }
8769
8770      for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8771        var element = elementsToRemove[k];
8772        jqLite(element).remove(); // must do this way to clean up expando
8773        fragment.appendChild(element);
8774        delete elementsToRemove[k];
8775      }
8776
8777      elementsToRemove[0] = newNode;
8778      elementsToRemove.length = 1;
8779    }
8780
8781
8782    function cloneAndAnnotateFn(fn, annotation) {
8783      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8784    }
8785
8786
8787    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8788      try {
8789        linkFn(scope, $element, attrs, controllers, transcludeFn);
8790      } catch (e) {
8791        $exceptionHandler(e, startingTag($element));
8792      }
8793    }
8794
8795
8796    // Set up $watches for isolate scope and controller bindings. This process
8797    // only occurs for isolate scopes and new scopes with controllerAs.
8798    function initializeDirectiveBindings(scope, attrs, destination, bindings,
8799                                         directive, newScope) {
8800      var onNewScopeDestroyed;
8801      forEach(bindings, function(definition, scopeName) {
8802        var attrName = definition.attrName,
8803        optional = definition.optional,
8804        mode = definition.mode, // @, =, or &
8805        lastValue,
8806        parentGet, parentSet, compare;
8807
8808        switch (mode) {
8809
8810          case '@':
8811            if (!optional && !hasOwnProperty.call(attrs, attrName)) {
8812              destination[scopeName] = attrs[attrName] = void 0;
8813            }
8814            attrs.$observe(attrName, function(value) {
8815              if (isString(value)) {
8816                destination[scopeName] = value;
8817              }
8818            });
8819            attrs.$$observers[attrName].$$scope = scope;
8820            if (isString(attrs[attrName])) {
8821              // If the attribute has been provided then we trigger an interpolation to ensure
8822              // the value is there for use in the link fn
8823              destination[scopeName] = $interpolate(attrs[attrName])(scope);
8824            }
8825            break;
8826
8827          case '=':
8828            if (!hasOwnProperty.call(attrs, attrName)) {
8829              if (optional) break;
8830              attrs[attrName] = void 0;
8831            }
8832            if (optional && !attrs[attrName]) break;
8833
8834            parentGet = $parse(attrs[attrName]);
8835            if (parentGet.literal) {
8836              compare = equals;
8837            } else {
8838              compare = function(a, b) { return a === b || (a !== a && b !== b); };
8839            }
8840            parentSet = parentGet.assign || function() {
8841              // reset the change, or we will throw this exception on every $digest
8842              lastValue = destination[scopeName] = parentGet(scope);
8843              throw $compileMinErr('nonassign',
8844                  "Expression '{0}' used with directive '{1}' is non-assignable!",
8845                  attrs[attrName], directive.name);
8846            };
8847            lastValue = destination[scopeName] = parentGet(scope);
8848            var parentValueWatch = function parentValueWatch(parentValue) {
8849              if (!compare(parentValue, destination[scopeName])) {
8850                // we are out of sync and need to copy
8851                if (!compare(parentValue, lastValue)) {
8852                  // parent changed and it has precedence
8853                  destination[scopeName] = parentValue;
8854                } else {
8855                  // if the parent can be assigned then do so
8856                  parentSet(scope, parentValue = destination[scopeName]);
8857                }
8858              }
8859              return lastValue = parentValue;
8860            };
8861            parentValueWatch.$stateful = true;
8862            var unwatch;
8863            if (definition.collection) {
8864              unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
8865            } else {
8866              unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
8867            }
8868            onNewScopeDestroyed = (onNewScopeDestroyed || []);
8869            onNewScopeDestroyed.push(unwatch);
8870            break;
8871
8872          case '&':
8873            // Don't assign Object.prototype method to scope
8874            parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
8875
8876            // Don't assign noop to destination if expression is not val
8876id
8877            if (parentGet === noop && optional) break;
8878
8879            destination[scopeName] = function(locals) {
8880              return parentGet(scope, locals);
8881            };
8882            break;
8883        }
8884      });
8885      var destroyBindings = onNewScopeDestroyed ? function destroyBindings() {
8886        for (var i = 0, ii = onNewScopeDestroyed.length; i < ii; ++i) {
8887          onNewScopeDestroyed[i]();
8888        }
8889      } : noop;
8890      if (newScope && destroyBindings !== noop) {
8891        newScope.$on('$destroy', destroyBindings);
8892        return noop;
8893      }
8894      return destroyBindings;
8895    }
8896  }];
8897}
8898
8899var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
8900/**
8901 * Converts all accepted directives format into proper directive name.
8902 * @param name Name to normalize
8903 */
8904function directiveNormalize(name) {
8905  return camelCase(name.replace(PREFIX_REGEXP, ''));
8906}
8907
8908/**
8909 * @ngdoc type
8910 * @name $compile.directive.Attributes
8911 *
8912 * @description
8913 * A shared object between directive compile / linking functions which contains normalized DOM
8914 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
8915 * needed since all of these are treated as equivalent in Angular:
8916 *
8917 * ```
8918 *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
8919 * ```
8920 */
8921
8922/**
8923 * @ngdoc property
8924 * @name $compile.directive.Attributes#$attr
8925 *
8926 * @description
8927 * A map of DOM element attribute names to the normalized name. This is
8928 * needed to do reverse lookup from normalized name back to actual name.
8929 */
8930
8931
8932/**
8933 * @ngdoc method
8934 * @name $compile.directive.Attributes#$set
8935 * @kind function
8936 *
8937 * @description
8938 * Set DOM element attribute value.
8939 *
8940 *
8941 * @param {string} name Normalized element attribute name of the property to modify. The name is
8942 *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
8943 *          property to the original name.
8944 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
8945 */
8946
8947
8948
8949/**
8950 * Closure compiler type information
8951 */
8952
8953function nodesetLinkingFn(
8954  /* angular.Scope */ scope,
8955  /* NodeList */ nodeList,
8956  /* Element */ rootElement,
8957  /* function(Function) */ boundTranscludeFn
8958) {}
8959
8960function directiveLinkingFn(
8961  /* nodesetLinkingFn */ nodesetLinkingFn,
8962  /* angular.Scope */ scope,
8963  /* Node */ node,
8964  /* Element */ rootElement,
8965  /* function(Function) */ boundTranscludeFn
8966) {}
8967
8968function tokenDifference(str1, str2) {
8969  var values = '',
8970      tokens1 = str1.split(/\s+/),
8971      tokens2 = str2.split(/\s+/);
8972
8973  outer:
8974  for (var i = 0; i < tokens1.length; i++) {
8975    var token = tokens1[i];
8976    for (var j = 0; j < tokens2.length; j++) {
8977      if (token == tokens2[j]) continue outer;
8978    }
8979    values += (values.length > 0 ? ' ' : '') + token;
8980  }
8981  return values;
8982}
8983
8984function removeComments(jqNodes) {
8985  jqNodes = jqLite(jqNodes);
8986  var i = jqNodes.length;
8987
8988  if (i <= 1) {
8989    return jqNodes;
8990  }
8991
8992  while (i--) {
8993    var node = jqNodes[i];
8994    if (node.nodeType === NODE_TYPE_COMMENT) {
8995      splice.call(jqNodes, i, 1);
8996    }
8997  }
8998  return jqNodes;
8999}
9000
9001var $controllerMinErr = minErr('$controller');
9002
9003
9004var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
9005function identifierForController(controller, ident) {
9006  if (ident && isString(ident)) return ident;
9007  if (isString(controller)) {
9008    var match = CNTRL_REG.exec(controller);
9009    if (match) return match[3];
9010  }
9011}
9012
9013
9014/**
9015 * @ngdoc provider
9016 * @name $controllerProvider
9017 * @description
9018 * The {@link ng.$controller $controller service} is used by Angular to create new
9019 * controllers.
9020 *
9021 * This provider allows controller registration via the
9022 * {@link ng.$controllerProvider#register register} method.
9023 */
9024function $ControllerProvider() {
9025  var controllers = {},
9026      globals = false;
9027
9028  /**
9029   * @ngdoc method
9030   * @name $controllerProvider#register
9031   * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9032   *    the names and the values are the constructors.
9033   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9034   *    annotations in the array notation).
9035   */
9036  this.register = function(name, constructor) {
9037    assertNotHasOwnProperty(name, 'controller');
9038    if (isObject(name)) {
9039      extend(controllers, name);
9040    } else {
9041      controllers[name] = constructor;
9042    }
9043  };
9044
9045  /**
9046   * @ngdoc method
9047   * @name $controllerProvider#allowGlobals
9048   * @description If called, allows `$controller` to find controller constructors on `window`
9049   */
9050  this.allowGlobals = function() {
9051    globals = true;
9052  };
9053
9054
9055  this.$get = ['$injector', '$window', function($injector, $window) {
9056
9057    /**
9058     * @ngdoc service
9059     * @name $controller
9060     * @requires $injector
9061     *
9062     * @param {Function|string} constructor If called with a function then it's considered to be the
9063     *    controller constructor function. Otherwise it's considered to be a string which is used
9064     *    to retrieve the controller constructor using the following steps:
9065     *
9066     *    * check if a controller with given name is registered via `$controllerProvider`
9067     *    * check if evaluating the string on the current scope returns a constructor
9068     *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9069     *      `window` object (not recommended)
9070     *
9071     *    The string can use the `controller as property` syntax, where the controller instance is published
9072     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9073     *    to work correctly.
9074     *
9075     * @param {Object} locals Injection locals for Controller.
9076     * @return {Object} Instance of given controller.
9077     *
vendor: 4,129 bytes, lines 9078-9182
9078     * @description
9079     * `$controller` service is responsible for instantiating controllers.
9080     *
9081     * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9082     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9083     */
9084    return function(expression, locals, later, ident) {
9085      // PRIVATE API:
9086      //   param `later` --- indicates that the controller's constructor is invoked at a later time.
9087      //                     If true, $controller will allocate the object with the correct
9088      //                     prototype chain, but will not invoke the controller until a returned
9089      //                     callback is invoked.
9090      //   param `ident` --- An optional label which overrides the label parsed from the controller
9091      //                     expression, if any.
9092      var instance, match, constructor, identifier;
9093      later = later === true;
9094      if (ident && isString(ident)) {
9095        identifier = ident;
9096      }
9097
9098      if (isString(expression)) {
9099        match = expression.match(CNTRL_REG);
9100        if (!match) {
9101          throw $controllerMinErr('ctrlfmt',
9102            "Badly formed controller string '{0}'. " +
9103            "Must match `__name__ as __id__` or `__name__`.", expression);
9104        }
9105        constructor = match[1],
9106        identifier = identifier || match[3];
9107        expression = controllers.hasOwnProperty(constructor)
9108            ? controllers[constructor]
9109            : getter(locals.$scope, constructor, true) ||
9110                (globals ? getter($window, constructor, true) : undefined);
9111
9112        assertArgFn(expression, constructor, true);
9113      }
9114
9115      if (later) {
9116        // Instantiate controller later:
9117        // This machinery is used to create an instance of the object before calling the
9118        // controller's constructor itself.
9119        //
9120        // This allows properties to be added to the controller before the constructor is
9121        // invoked. Primarily, this is used for isolate scope bindings in $compile.
9122        //
9123        // This feature is not intended for use by applications, and is thus not documented
9124        // publicly.
9125        // Object creation: http://jsperf.com/create-constructor/2
9126        var controllerPrototype = (isArray(expression) ?
9127          expression[expression.length - 1] : expression).prototype;
9128        instance = Object.create(controllerPrototype || null);
9129
9130        if (identifier) {
9131          addIdentifier(locals, identifier, instance, constructor || expression.name);
9132        }
9133
9134        var instantiate;
9135        return instantiate = extend(function() {
9136          var result = $injector.invoke(expression, instance, locals, constructor);
9137          if (result !== instance && (isObject(result) || isFunction(result))) {
9138            instance = result;
9139            if (identifier) {
9140              // If result changed, re-assign controllerAs value to scope.
9141              addIdentifier(locals, identifier, instance, constructor || expression.name);
9142            }
9143          }
9144          return instance;
9145        }, {
9146          instance: instance,
9147          identifier: identifier
9148        });
9149      }
9150
9151      instance = $injector.instantiate(expression, locals, constructor);
9152
9153      if (identifier) {
9154        addIdentifier(locals, identifier, instance, constructor || expression.name);
9155      }
9156
9157      return instance;
9158    };
9159
9160    function addIdentifier(locals, identifier, instance, name) {
9161      if (!(locals && isObject(locals.$scope))) {
9162        throw minErr('$controller')('noscp',
9163          "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9164          name, identifier);
9165      }
9166
9167      locals.$scope[identifier] = instance;
9168    }
9169  }];
9170}
9171
9172/**
9173 * @ngdoc service
9174 * @name $document
9175 * @requires $window
9176 *
9177 * @description
9178 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9179 *
9180 * @example
9181   <example module="documentExample">
9182     <file name="index.html">
9183       <div ng-controller="ExampleController">
9184         <p>$document title: <b ng-bind="title"></b></p>
9185         <p>window.document title: <b ng-bind="windowTitle"></b></p>
9186       </div>
9187     </file>
9188     <file name="script.js">
9189       angular.module('documentExample', [])
9190         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9191           $scope.title = $document[0].title;
9192           $scope.windowTitle = angular.element(window.document)[0].title;
9193         }]);
9194     </file>
9195   </example>
9196 */
9197function $DocumentProvider() {
9198  this.$get = ['$window', function(window) {
9199    return jqLite(window.document);
9200  }];
9201}
9202
9203/**
9204 * @ngdoc service
9205 * @name $exceptionHandler
9206 * @requires ng.$log
9207 *
9208 * @description
9209 * Any uncaught exception in angular expressions is delegated to this service.
9210 * The default implementation simply delegates to `$log.error` which logs it into
9211 * the browser console.
9212 *
9213 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9214 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9215 *
9216 * ## Example:
9217 *
9218 * ```js
9219 *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9220 *     return function(exception, cause) {
9221 *       exception.message += ' (caused by "' + cause + '")';
9222 *       throw exception;
9223 *     };
9224 *   });
9225 * ```
9226 *
9227 * This example will override the normal action of `$exceptionHandler`, to make angular
9228 * exceptions fail hard when they happen, instead of just logging to the console.
9229 *
9230 * <hr />
9231 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9232 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9233 * (unless executed during a digest).
9234 *
9235 * If you wish, you can manually delegate exceptions, e.g.
9236 * `try { ... } catch(e) { $exceptionHandler(e); }`
9237 *
9238 * @param {Error} exception Exception associated with the error.
9239 * @param {string=} cause optional information about the context in which
9240 *       the error was thrown.
9241 *
9242 */
9243function $ExceptionHandlerProvider() {
9244  this.$get = ['$log', function($log) {
9245    return function(exception, cause) {
9246      $log.error.apply($log, arguments);
9247    };
9248  }];
9249}
9250
9251var $$ForceReflowProvider = function() {
9252  this.$get = ['$document', function($document) {
9253    return function(domNode) {
9254      //the line below will force the browser to perform a repaint so
9255      //that all the animated elements within the animation frame will
9256      //be properly updated and drawn on screen. This is required to
9257      //ensure that the preparation animation is properly flushed so that
9258      //the active state picks up from there. DO NOT REMOVE THIS LINE.
9259      //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9260      //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9261      //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9262      if (domNode) {
9263        if (!domNode.nodeType && domNode instanceof jqLite) {
9264          domNode = domNode[0];
9265        }
9266      } else {
9267        domNode = $document[0].body;
9268      }
9269      return domNode.offsetWidth + 1;
9270    };
9271  }];
9272};
9273
9274var APPLICATION_JSON = 'application/json';
9275var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9276var JSON_START = /^\[|^\{(?!\{)/;
9277var JSON_ENDS = {
9278  '[': /]$/,
9279  '{': /}$/
9280};
9281var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9282var $httpMinErr = minErr('$http');
9283var $httpMinErrLegacyFn = function(method) {
9284  return function() {
9285    throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9286  };
9287};
9288
9289function serializeValue(v) {
9290  if (isObject(v)) {
9291    return isDate(v) ? v.toISOString() : toJson(v);
9292  }
9293  return v;
9294}
9295
9296
9297function $HttpParamSerializerProvider() {
9298  /**
9299   * @ngdoc service
9300   * @name $httpParamSerializer
9301   * @description
9302   *
9303   * Default {@link $http `$http`} params serializer that converts objects to strings
9304   * according to the following rules:
9305   *
9306   * * `{'foo': 'bar'}` results in `foo=bar`
9307   * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9308   * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9309   * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9310   *
9311   * Note that serializer will sort the request parameters alphabetically.
9312   * */
9313
9314  this.$get = function() {
9315    return function ngParamSerializer(params) {
9316      if (!params) return '';
9317      var parts = [];
vendor: 3,809 bytes, lines 9318-9444
9318      forEachSorted(params, function(value, key) {
9319        if (value === null || isUndefined(value)) return;
9320        if (isArray(value)) {
9321          forEach(value, function(v, k) {
9322            parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
9323          });
9324        } else {
9325          parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9326        }
9327      });
9328
9329      return parts.join('&');
9330    };
9331  };
9332}
9333
9334function $HttpParamSerializerJQLikeProvider() {
9335  /**
9336   * @ngdoc service
9337   * @name $httpParamSerializerJQLike
9338   * @description
9339   *
9340   * Alternative {@link $http `$http`} params serializer that follows
9341   * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9342   * The serializer will also sort the params alphabetically.
9343   *
9344   * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9345   *
9346   * ```js
9347   * $http({
9348   *   url: myUrl,
9349   *   method: 'GET',
9350   *   params: myParams,
9351   *   paramSerializer: '$httpParamSerializerJQLike'
9352   * });
9353   * ```
9354   *
9355   * It is also possible to set it as the default `paramSerializer` in the
9356   * {@link $httpProvider#defaults `$httpProvider`}.
9357   *
9358   * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9359   * form data for submission:
9360   *
9361   * ```js
9362   * .controller(function($http, $httpParamSerializerJQLike) {
9363   *   //...
9364   *
9365   *   $http({
9366   *     url: myUrl,
9367   *     method: 'POST',
9368   *     data: $httpParamSerializerJQLike(myData),
9369   *     headers: {
9370   *       'Content-Type': 'application/x-www-form-urlencoded'
9371   *     }
9372   *   });
9373   *
9374   * });
9375   * ```
9376   *
9377   * */
9378  this.$get = function() {
9379    return function jQueryLikeParamSerializer(params) {
9380      if (!params) return '';
9381      var parts = [];
9382      serialize(params, '', true);
9383      return parts.join('&');
9384
9385      function serialize(toSerialize, prefix, topLevel) {
9386        if (toSerialize === null || isUndefined(toSerialize)) return;
9387        if (isArray(toSerialize)) {
9388          forEach(toSerialize, function(value, index) {
9389            serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
9390          });
9391        } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9392          forEachSorted(toSerialize, function(value, key) {
9393            serialize(value, prefix +
9394                (topLevel ? '' : '[') +
9395                key +
9396                (topLevel ? '' : ']'));
9397          });
9398        } else {
9399          parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9400        }
9401      }
9402    };
9403  };
9404}
9405
9406function defaultHttpResponseTransform(data, headers) {
9407  if (isString(data)) {
9408    // Strip json vulnerability protection prefix and trim whitespace
9409    var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9410
9411    if (tempData) {
9412      var contentType = headers('Content-Type');
9413      if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9414        data = fromJson(tempData);
9415      }
9416    }
9417  }
9418
9419  return data;
9420}
9421
9422function isJsonLike(str) {
9423    var jsonStart = str.match(JSON_START);
9424    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9425}
9426
9427/**
9428 * Parse headers into key value object
9429 *
9430 * @param {string} headers Raw headers as a string
9431 * @returns {Object} Parsed headers as key value object
9432 */
9433function parseHeaders(headers) {
9434  var parsed = createMap(), i;
9435
9436  function fillInParsed(key, val) {
9437    if (key) {
9438      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9439    }
9440  }
9441
9442  if (isString(headers)) {
9443    forEach(headers.split('\n'), function(line) {
9444      i = line.indexOf(':');
vendor: 5,581 bytes, lines 9445-9612
9445      fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9446    });
9447  } else if (isObject(headers)) {
9448    forEach(headers, function(headerVal, headerKey) {
9449      fillInParsed(lowercase(headerKey), trim(headerVal));
9450    });
9451  }
9452
9453  return parsed;
9454}
9455
9456
9457/**
9458 * Returns a function that provides access to parsed headers.
9459 *
9460 * Headers are lazy parsed when first requested.
9461 * @see parseHeaders
9462 *
9463 * @param {(string|Object)} headers Headers to provide access to.
9464 * @returns {function(string=)} Returns a getter function which if called with:
9465 *
9466 *   - if called with single an argument returns a single header value or null
9467 *   - if called with no arguments returns an object containing all headers.
9468 */
9469function headersGetter(headers) {
9470  var headersObj;
9471
9472  return function(name) {
9473    if (!headersObj) headersObj =  parseHeaders(headers);
9474
9475    if (name) {
9476      var value = headersObj[lowercase(name)];
9477      if (value === void 0) {
9478        value = null;
9479      }
9480      return value;
9481    }
9482
9483    return headersObj;
9484  };
9485}
9486
9487
9488/**
9489 * Chain all given functions
9490 *
9491 * This function is used for both request and response transforming
9492 *
9493 * @param {*} data Data to transform.
9494 * @param {function(string=)} headers HTTP headers getter fn.
9495 * @param {number} status HTTP status code of the response.
9496 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9497 * @returns {*} Transformed data.
9498 */
9499function transformData(data, headers, status, fns) {
9500  if (isFunction(fns)) {
9501    return fns(data, headers, status);
9502  }
9503
9504  forEach(fns, function(fn) {
9505    data = fn(data, headers, status);
9506  });
9507
9508  return data;
9509}
9510
9511
9512function isSuccess(status) {
9513  return 200 <= status && status < 300;
9514}
9515
9516
9517/**
9518 * @ngdoc provider
9519 * @name $httpProvider
9520 * @description
9521 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9522 * */
9523function $HttpProvider() {
9524  /**
9525   * @ngdoc property
9526   * @name $httpProvider#defaults
9527   * @description
9528   *
9529   * Object containing default values for all {@link ng.$http $http} requests.
9530   *
9531   * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
9532   * that will provide the cache for all requests who set their `cache` property to `true`.
9533   * If you set the `defaults.cache = false` then only requests that specify their own custom
9534   * cache object will be cached. See {@link $http#caching $http Caching} for more information.
9535   *
9536   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9537   * Defaults value is `'XSRF-TOKEN'`.
9538   *
9539   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9540   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9541   *
9542   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9543   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9544   * setting default headers.
9545   *     - **`defaults.headers.common`**
9546   *     - **`defaults.headers.post`**
9547   *     - **`defaults.headers.put`**
9548   *     - **`defaults.headers.patch`**
9549   *
9550   *
9551   * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9552   *  used to the prepare string representation of request parameters (specified as an object).
9553   *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9554   *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9555   *
9556   **/
9557  var defaults = this.defaults = {
9558    // transform incoming response data
9559    transformResponse: [defaultHttpResponseTransform],
9560
9561    // transform outgoing request data
9562    transformRequest: [function(d) {
9563      return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9564    }],
9565
9566    // default headers
9567    headers: {
9568      common: {
9569        'Accept': 'application/json, text/plain, */*'
9570      },
9571      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9572      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9573      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9574    },
9575
9576    xsrfCookieName: 'XSRF-TOKEN',
9577    xsrfHeaderName: 'X-XSRF-TOKEN',
9578
9579    paramSerializer: '$httpParamSerializer'
9580  };
9581
9582  var useApplyAsync = false;
9583  /**
9584   * @ngdoc method
9585   * @name $httpProvider#useApplyAsync
9586   * @description
9587   *
9588   * Configure $http service to combine processing of multiple http responses received at around
9589   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9590   * significant performance improvement for bigger applications that make many HTTP requests
9591   * concurrently (common during application bootstrap).
9592   *
9593   * Defaults to false. If no value is specified, returns the current configured value.
9594   *
9595   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9596   *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9597   *    to load and share the same digest cycle.
9598   *
9599   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9600   *    otherwise, returns the current configured value.
9601   **/
9602  this.useApplyAsync = function(value) {
9603    if (isDefined(value)) {
9604      useApplyAsync = !!value;
9605      return this;
9606    }
9607    return useApplyAsync;
9608  };
9609
9610  var useLegacyPromise = true;
9611  /**
9612   * @ngdoc method
9613   * @name $httpProvider#useLegacyPromiseExtensions
9614   * @description
9615   *
9616   * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9617   * This should be used to make sure that applications work without these methods.
9618   *
9619   * Defaults to false. If no value is specified, returns the current configured value.
9620   *
9621   * @param {boolean=} value If true, `$http` will return a normal promise without the `success` and `error` methods.
9622   *
9623   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9624   *    otherwise, returns the current configured value.
9625   **/
9626  this.useLegacyPromiseExtensions = function(value) {
9627    if (isDefined(value)) {
9628      useLegacyPromise = !!value;
9629      return this;
9630    }
9631    return useLegacyPromise;
9632  };
9633
9634  /**
9635   * @ngdoc property
9636   * @name $httpProvider#interceptors
9637   * @description
9638   *
9639   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9640   * pre-processing of request or postprocessing of responses.
9641   *
9642   * These service factories are ordered by request, i.e. they are applied in the same order as the
9643   * array, on request, but reverse order, on response.
9644   *
9645   * {@link ng.$http#interceptors Interceptors detailed info}
9646   **/
9647  var interceptorFactories = this.interceptors = [];
9648
9649  this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9650      function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9651
9652    var defaultCache = $cacheFactory('$http');
9653
9654    /**
9655     * Make sure that default param serializer is exposed as a function
9656     */
9657    defaults.paramSerializer = isString(defaults.paramSerializer) ?
9658      $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9659
9660    /**
9661     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9662     * The reversal is needed so that we can build up the interception chain around the
9663     * server request.
9664     */
9665    var reversedInterceptors = [];
9666
9667    forEach(interceptorFactories, function(interceptorFactory) {
9668      reversedInterceptors.unshift(isString(interceptorFactory)
9669          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9670    });
9671
9672    /**
9673     * @ngdoc service
9674     * @kind function
9675     * @name $http
9676     * @requires ng.$httpBackend
9677     * @requires $cacheFactory
9678     * @requires $rootScope
9679     * @requires $q
9680     * @requires $injector
9681     *
9682     * @description
9683     * The `$http` service is a core Angular service that facilitates communication with the remote
9684     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9685     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9686     *
9687     * For unit testing applications that use `$http` service, see
9688     * {@link ngMock.$httpBackend $httpBackend mock}.
9689     *
9690     * For a higher level of abstraction, please check out the {@link ngResource.$resource
9691     * $resource} service.
9692     *
9693     * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
9694     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9695     * it is important to familiarize yourself with these APIs and the guarantees they provide.
9696     *
9697     *
9698     * ## General usage
9699     * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
9700     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
9701     *
9702     * ```js
9703     *   // Simple GET request example:
9704     *   $http({
9705     *     method: 'GET',
9706     *     url: '/someUrl'
9707     *   }).then(function successCallback(response) {
9708     *       // this callback will be called asynchronously
9709     *       // when the response is available
9710     *     }, function errorCallback(response) {
9711     *       // called asynchronously if an error occurs
9712     *       // or server returns response with an error status.
9713     *     });
9714     * ```
9715     *
9716     * The response object has these properties:
9717     *
9718     *   - **data** – `{string|Object}` – The response body transformed with the transform
9719     *     functions.
9720     *   - **status** – `{number}` – HTTP status code of the response.
9721     *   - **headers** – `{function([headerName])}` – Header getter function.
9722     *   - **config** – `{Object}` – The configuration object that was used to generate the request.
9723     *   - **statusText** – `{string}` – HTTP status text of the response.
9724     *
9725     * A response status code between 200 and 299 is considered a success status and
9726     * will result in the success callback being called. Note that if the response is a redirect,
9727     * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9728     * called for such responses.
9729     *
9730     *
9731     * ## Shortcut methods
9732     *
9733     * Shortcut methods are also available. All shortcut methods require passing in the URL, and
9734     * request data must be passed in for POST/PUT requests. An optional config can be passed as the
9735     * last argument.
9736     *
9737     * ```js
9738     *   $http.get('/someUrl', config).then(successCallback, errorCallback);
9739     *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
9740     * ```
9741     *
9742     * Complete list of shortcut methods:
9743     *
9744     * - {@link ng.$http#get $http.get}
9745     * - {@link ng.$http#head $http.head}
9746     * - {@link ng.$http#post $http.post}
9747     * - {@link ng.$http#put $http.put}
9748     * - {@link ng.$http#delete $http.delete}
9749     * - {@link ng.$http#jsonp $http.jsonp}
9750     * - {@link ng.$http#patch $http.patch}
9751     *
9752     *
9753     * ## Writing Unit Tests that use $http
9754     * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9755     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
9756     * request using trained responses.
9757     *
9758     * ```
9759     * $httpBackend.expectGET(...);
9760     * $http.get(...);
9761     * $httpBackend.flush();
9762     * ```
9763     *
9764     * ## Deprecation Notice
9765     * <div class="alert alert-danger">
9766     *   The `$http` legacy promise methods `success` and `error` have been deprecated.
9767     *   Use the standard `then` method instead.
9768     *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
9769     *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
9770     * </div>
9771     *
9772     * ## Setting HTTP Headers
9773     *
9774     * The $http service will automatically add certain HTTP headers to all requests. These defaults
9775     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
9776     * object, which currently contains this default configuration:
9777     *
9778     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
9779     *   - `Accept: application/json, text/plain, * / *`
9780     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
9781     *   - `Content-Type: application/json`
9782     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
9783     *   - `Content-Type: application/json`
9784     *
9785     * To add or overwrite these defaults, simply add or remove a property from these configuration
9786     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
9787     * with the lowercased HTTP method name as the key, e.g.
9788     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
9789     *
9790     * The defaults can also be set at runtime via the `$http.defaults` object in the same
9791     * fashion. For example:
9792     *
9793     * ```
9794     * module.run(function($http) {
9795     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
9796     * });
9797     * ```
9798     *
9799     * In addition, you can supply a `headers` property in the config object passed when
9800     * calling `$http(config)`, which overrides the defaults without changing them globally.
9801     *
9802     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
9803     * Use the `headers` property, setting the desired header to `undefined`. For example:
9804     *
9805     * ```js
9806     * var req = {
9807     *  method: 'POST',
9808     *  url: 'http://example.com',
9809     *  headers: {
9810     *    'Content-Type': undefined
9811     *  },
9812     *  data: { test: 'test' }
9813     * }
9814     *
9815     * $http(req).then(function(){...}, function(){...});
9816     * ```
9817     *
9818     * ## Transforming Requests and Responses
9819     *
9820     * Both requests and responses can be transformed using transformation functions: `transformRequest`
9821     * and `transformResponse`. These properties can be a single function that returns
9822     * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
9823     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
9824     *
9825     * ### Default Transformations
9826     *
9827     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
9828     * `defaults.transformResponse` properties. If a request does not provide its own transformations
9829     * then these will be applied.
9830     *
9831     * You can augment or replace the default transformations by modifying these properties by adding to or
9832     * replacing the array.
9833     *
9834     * Angular provides the following default transformations:
9835     *
9836     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
9837     *
9838     * - If the `data` property of the request configuration object contains an object, serialize it
9839     *   into JSON format.
9840     *
9841     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
9842     *
9843     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
9844     *  - If JSON response is detected, deserialize it using a JSON parser.
9845     *
9846     *
9847     * ### Overriding the Default Transformations Per Request
9848     *
9849     * If you wish override the request/response transformations only for a single request then provide
9850     * `transformRequest` and/or `transformResponse` properties on the configuration object passed
9851     * into `$http`.
9852     *
9853     * Note that if you provide these properties on the config object the default transformations will be
9854     * overwritten. If you wish to augment the default transformations then you must include them in your
9855     * local transformation array.
9856     *
9857     * The following code demonstrates adding a new response transformation to be run after the default response
9858     * transformations have been run.
9859     *
9860     * ```js
9861     * function appendTransform(defaults, transform) {
9862     *
9863     *   // We can't guarantee that the default transformation is an array
9864     *   defaults = angular.isArray(defaults) ? defaults : [defaults];
9865     *
9866     *   // Append the new transformation to the defaults
9867     *   return defaults.concat(transform);
9868     * }
9869     *
9870     * $http({
9871     *   url: '...',
9872     *   method: 'GET',
9873     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
9874     *     return doTransform(value);
9875     *   })
9876     * });
9877     * ```
9878     *
9879     *
9880     * ## Caching
9881     *
9882     * To enable caching, set the request configuration `cache` property to `true` (to use default
9883     * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
9884     * When the cache is enabled, `$http` stores the response from the server in the specified
9885     * cache. The next time the same request is made, the response is served from the cache without
9886     * sending a request to the server.
9887     *
9888     * Note that even if the response is served from cache, delivery of the data is asynchronous in
9889     * the same way that real requests are.
9890     *
9891     * If there are multiple GET requests for the same URL that should be cached using the same
9892     * cache, but the cache is not populated yet, only one request to the server will be made and
9893     * the remaining requests will be fulfilled using the response from the first request.
9894     *
9895     * You can change the default cache to a new object (built with
9896     * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
9897     * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
9898     * their `cache` property to `true` will now use this cache object.
9899     *
9900     * If you set the default cache to `false` then only requests that specify their own custom
9901     * cache object will be cached.
9902     *
9903     * ## Interceptors
9904     *
9905     * Before you start creating interceptors, be sure to understand the
9906     * {@link ng.$q $q and deferred/promise APIs}.
9907     *
9908     * For purposes of global error handling, authentication, or any kind of synchronous or
9909     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
9910     * able to intercept requests before they are handed to the server and
9911     * responses before they are handed over to the application code that
9912     * initiated these requests. The interceptors leverage the {@link ng.$q
9913     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
9914     *
9915     * The interceptors are service factories that are registered with the `$httpProvider` by
9916     * adding them to the `$httpProvider.interceptors` array. The factory is called and
9917     * injected with dependencies (if specified) and returns the interceptor.
9918     *
9919     * There are two kinds of interceptors (and two kinds of rejection interceptors):
9920     *
9921     *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
9922     *     modify the `config` object or create a new one. The function needs to return the `config`
9923     *     object directly, or a promise containing the `config` or a new `config` object.
9924     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
9925     *     resolved with a rejection.
9926     *   * `response`: interceptors get called with http `response` object. The function is free to
9927     *     modify the `response` object or create a new one. The function needs to return the `response`
9928     *     object directly, or as a promise containing the `response` or a new `response` object.
9929     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
9930     *     resolved with a rejection.
9931     *
9932     *
9933     * ```js
9934     *   // register the interceptor as a service
9935     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
9936     *     return {
9937     *       // optional method
9938     *       'request': function(config) {
9939     *         // do something on success
9940     *         return config;
9941     *       },
9942     *
9943     *       // optional method
9944     *      'requestError': function(rejection) {
9945     *         // do something on error
9946     *         if (canRecover(rejection)) {
9947     *           return responseOrNewPromise
9948     *         }
9949     *         return $q.reject(rejection);
9950     *       },
9951     *
9952     *
9953     *
9954     *       // optional method
9955     *       'response': function(response) {
9956     *         // do something on success
9957     *         return response;
9958     *       },
9959     *
9960     *       // optional method
9961     *      'responseError': function(rejection) {
9962     *         // do something on error
9963     *         if (canRecover(rejection)) {
9964     *           return responseOrNewPromise
9965     *         }
9966     *         return $q.reject(rejection);
9967     *       }
9968     *     };
9969     *   });
9970     *
9971     *   $httpProvider.interceptors.push('myHttpInterceptor');
9972     *
9973     *
9974     *   // alternatively, register the interceptor via an anonymous factory
9975     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
9976     *     return {
9977     *      'request': function(config) {
9978     *          // same as above
9979     *       },
9980     *
9981     *       'response': function(response) {
9982     *          // same as above
9983     *       }
9984     *     };
9985     *   });
9986     * ```
9987     *
9988     * ## Security Considerations
9989     *
9990     * When designing web applications, consider security threats from:
9991     *
9992     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9993     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
9994     *
9995     * Both server and the client must cooperate in order to eliminate these threats. Angular comes
9996     * pre-configured with strategies that address these issues, but for this to work backend server
9997     * cooperation is required.
9998     *
9999     * ### JSON Vulnerability Protection
10000     *
10001     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10002     * allows third party website to turn your JSON resource URL into
10003     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10004     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10005     * Angular will automatically strip the prefix before processing it as JSON.
10006     *
10007     * For example if your server needs to return:
10008     * ```js
10009     * ['one','two']
10010     * ```
10011     *
10012     * which is vulnerable to attack, your server can return:
10013     * ```js
10014     * )]}',
10015     * ['one','two']
10016     * ```
10017     *
10018     * Angular will strip the prefix, before processing the JSON.
10019     *
10020     *
10021     * ### Cross Site Request Forgery (XSRF) Protection
10022     *
10023     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
10024     * an unauthorized site can gain your user's private data. Angular provides a mechanism
10025     * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
10026     * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
10027     * JavaScript that runs on your domain could read the cookie, your server can be assured that
10028     * the XHR came from JavaScript running on your domain. The header will not be set for
10029     * cross-domain requests.
10030     *
10031     * To take advantage of this, your server needs to set a token in a JavaScript readable session
10032     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10033     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10034     * that only JavaScript running on your domain could have sent the request. The token must be
10035     * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10036     * making up its own tokens). We recommend that the token is a digest of your site's
10037     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
10038     * for added security.
10039     *
10040     * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10041     * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10042     * or the per-request config object.
10043     *
10044     * In order to prevent collisions in environments where multiple Angular apps share the
10045     * same domain or subdomain, we recommend that each application uses unique cookie name.
10046     *
10047     * @param {object} config Object describing the request to be made and how it should be
10048     *    processed. The object has following properties:
10049     *
10050     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10051     *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10052     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10053     *      with the `paramSerializer` and appended as GET parameters.
10054     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
10055     *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
10056     *      HTTP headers to send to the server. If the return value of a function is null, the
10057     *      header will not be sent. Functions accept a config object as an argument.
10058     *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10059     *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10060     *    - **transformRequest** –
10061     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>
10061}` –
10062     *      transform function or an array of such functions. The transform function takes the http
10063     *      request body and headers and returns its transformed (typically serialized) version.
10064     *      See {@link ng.$http#overriding-the-default-transformations-per-request
10065     *      Overriding the Default Transformations}
10066     *    - **transformResponse** –
10067     *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10068     *      transform function or an array of such functions. The transform function takes the http
10069     *      response body, headers and status and returns its transformed (typically deserialized) version.
10070     *      See {@link ng.$http#overriding-the-default-transformations-per-request
10071     *      Overriding the Default TransformationjqLiks}
10072     *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10073     *      prepare the string representation of request parameters (specified as an object).
10074     *      If specified as string, it is interpreted as function registered with the
10075     *      {@link $injector $injector}, which means you can create your own serializer
10076     *      by registering it as a {@link auto.$provide#service service}.
10077     *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10078     *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10079     *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
10080     *      GET request, otherwise if a cache instance built with
10081     *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
10082     *      caching.
10083     *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10084     *      that should abort the request when resolved.
10085     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10086     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10087     *      for more information.
10088     *    - **responseType** - `{string}` - see
10089     *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10090     *
10091     * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10092     *                        when the request succeeds or fails.
10093     *
10094     *
10095     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10096     *   requests. This is primarily meant to be used for debugging purposes.
10097     *
10098     *
10099     * @example
10100<example module="httpExample">
10101<file name="index.html">
10102  <div ng-controller="FetchController">
10103    <select ng-model="method" aria-label="Request method">
10104      <option>GET</option>
10105      <option>JSONP</option>
10106    </select>
10107    <input type="text" ng-model="url" size="80" aria-label="URL" />
10108    <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10109    <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10110    <button id="samplejsonpbtn"
10111      ng-click="updateModel('JSONP',
10112                    'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10113      Sample JSONP
10114    </button>
10115    <button id="invalidjsonpbtn"
10116      ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10117        Invalid JSONP
10118      </button>
10119    <pre>http status code: {{status}}</pre>
10120    <pre>http response data: {{data}}</pre>
10121  </div>
10122</file>
10123<file name="script.js">
10124  angular.module('httpExample', [])
10125    .controller('FetchController', ['$scope', '$http', '$templateCache',
10126      function($scope, $http, $templateCache) {
10127        $scope.method = 'GET';
10128        $scope.url = 'http-hello.html';
10129
10130        $scope.fetch = function() {
10131          $scope.code = null;
10132          $scope.response = null;
10133
10134          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10135            then(function(response) {
10136              $scope.status = response.status;
10137              $scope.data = response.data;
10138            }, function(response) {
10139              $scope.data = response.data || "Request failed";
10140              $scope.status = response.status;
10141          });
10142        };
10143
10144        $scope.updateModel = function(method, url) {
10145          $scope.method = method;
10146          $scope.url = url;
10147        };
10148      }]);
10149</file>
10150<file name="http-hello.html">
10151  Hello, $http!
10152</file>
10153<file name="protractor.js" type="protractor">
10154  var status = element(by.binding('status'));
10155  var data = element(by.binding('data'));
10156  var fetchBtn = element(by.id('fetchbtn'));
10157  var sampleGetBtn = element(by.id('samplegetbtn'));
10158  var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10159  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10160
10161  it('should make an xhr GET request', function() {
10162    sampleGetBtn.click();
10163    fetchBtn.click();
10164    expect(status.getText()).toMatch('200');
10165    expect(data.getText()).toMatch(/Hello, \$http!/);
10166  });
10167
10168// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10169// it('should make a JSONP request to angularjs.org', function() {
10170//   sampleJsonpBtn.click();
10171//   fetchBtn.click();
10172//   expect(status.getText()).toMatch('200');
10173//   expect(data.getText()).toMatch(/Super Hero!/);
10174// });
10175
10176  it('should make JSONP request to invalid URL and invoke the error handler',
10177      function() {
10178    invalidJsonpBtn.click();
10179    fetchBtn.click();
10180    expect(status.getText()).toMatch('0');
10181    expect(data.getText()).toMatch('Request failed');
10182  });
10183</file>
10184</example>
10185     */
10186    function $http(requestConfig) {
10187
10188      if (!angular.isObject(requestConfig)) {
10189        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
10190      }
10191
10192      var config = extend({
10193        method: 'get',
10194        transformRequest: defaults.transformRequest,
10195        transformResponse: defaults.transformResponse,
10196        paramSerializer: defaults.paramSerializer
10197      }, requestConfig);
10198
10199      config.headers = mergeHeaders(requestConfig);
10200      config.method = uppercase(config.method);
10201      config.paramSerializer = isString(config.paramSerializer) ?
10202        $injector.get(config.paramSerializer) : config.paramSerializer;
10203
10204      var serverRequest = function(config) {
10205        var headers = config.headers;
10206        var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10207
10208        // strip content-type if data is undefined
10209        if (isUndefined(reqData)) {
10210          forEach(headers, function(value, header) {
10211            if (lowercase(header) === 'content-type') {
10212                delete headers[header];
10213            }
10214          });
10215        }
10216
10217        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10218          config.withCredentials = defaults.withCredentials;
10219        }
10220
10221        // send request
10222        return sendReq(config, reqData).then(transformResponse, transformResponse);
10223      };
10224
10225      var chain = [serverRequest, undefined];
10226      var promise = $q.when(config);
10227
10228      // apply interceptors
10229      forEach(reversedInterceptors, function(interceptor) {
10230        if (interceptor.request || interceptor.requestError) {
10231          chain.unshift(interceptor.request, interceptor.requestError);
10232        }
10233        if (interceptor.response || interceptor.responseError) {
10234          chain.push(interceptor.response, interceptor.responseError);
10235        }
10236      });
10237
10238      while (chain.length) {
10239        var thenFn = chain.shift();
10240        var rejectFn = chain.shift();
10241
10242        promise = promise.then(thenFn, rejectFn);
10243      }
10244
10245      if (useLegacyPromise) {
10246        promise.success = function(fn) {
10247          assertArgFn(fn, 'fn');
10248
10249          promise.then(function(response) {
10250            fn(response.data, response.status, response.headers, config);
10251          });
10252          return promise;
10253        };
10254
10255        promise.error = function(fn) {
10256          assertArgFn(fn, 'fn');
10257
10258          promise.then(null, function(response) {
10259            fn(response.data, response.status, response.headers, config);
10260          });
10261          return promise;
10262        };
10263      } else {
10264        promise.success = $httpMinErrLegacyFn('success');
10265        promise.error = $httpMinErrLegacyFn('error');
10266      }
10267
10268      return promise;
10269
10270      function transformResponse(response) {
10271        // make a copy since the response must be cacheable
10272        var resp = extend({}, response);
10273        if (!response.data) {
10274          resp.data = response.data;
10275        } else {
10276          resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
10277        }
10278        return (isSuccess(response.status))
10279          ? resp
10280          : $q.reject(resp);
10281      }
10282
10283      function executeHeaderFns(headers, config) {
10284        var headerContent, processedHeaders = {};
10285
10286        forEach(headers, function(headerFn, header) {
10287          if (isFunction(headerFn)) {
10288            headerContent = headerFn(config);
10289            if (headerContent != null) {
10290              processedHeaders[header] = headerContent;
10291            }
10292          } else {
10293            processedHeaders[header] = headerFn;
10294          }
10295        });
10296
10297        return processedHeaders;
10298      }
10299
10300      function mergeHeaders(config) {
10301        var defHeaders = defaults.headers,
10302            reqHeaders = extend({}, config.headers),
10303            defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10304
10305        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10306
vendor: 9,399 bytes, lines 10307-10607
10307        // using for-in instead of forEach to avoid unecessary iteration after header has been found
10308        defaultHeadersIteration:
10309        for (defHeaderName in defHeaders) {
10310          lowercaseDefHeaderName = lowercase(defHeaderName);
10311
10312          for (reqHeaderName in reqHeaders) {
10313            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10314              continue defaultHeadersIteration;
10315            }
10316          }
10317
10318          reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10319        }
10320
10321        // execute if header value is a function for merged headers
10322        return executeHeaderFns(reqHeaders, shallowCopy(config));
10323      }
10324    }
10325
10326    $http.pendingRequests = [];
10327
10328    /**
10329     * @ngdoc method
10330     * @name $http#get
10331     *
10332     * @description
10333     * Shortcut method to perform `GET` request.
10334     *
10335     * @param {string} url Relative or absolute URL specifying the destination of the request
10336     * @param {Object=} config Optional configuration object
10337     * @returns {HttpPromise} Future object
10338     */
10339
10340    /**
10341     * @ngdoc method
10342     * @name $http#delete
10343     *
10344     * @description
10345     * Shortcut method to perform `DELETE` request.
10346     *
10347     * @param {string} url Relative or absolute URL specifying the destination of the request
10348     * @param {Object=} config Optional configuration object
10349     * @returns {HttpPromise} Future object
10350     */
10351
10352    /**
10353     * @ngdoc method
10354     * @name $http#head
10355     *
10356     * @description
10357     * Shortcut method to perform `HEAD` request.
10358     *
10359     * @param {string} url Relative or absolute URL specifying the destination of the request
10360     * @param {Object=} config Optional configuration object
10361     * @returns {HttpPromise} Future object
10362     */
10363
10364    /**
10365     * @ngdoc method
10366     * @name $http#jsonp
10367     *
10368     * @description
10369     * Shortcut method to perform `JSONP` request.
10370     *
10371     * @param {string} url Relative or absolute URL specifying the destination of the request.
10372     *                     The name of the callback should be the string `JSON_CALLBACK`.
10373     * @param {Object=} config Optional configuration object
10374     * @returns {HttpPromise} Future object
10375     */
10376    createShortMethods('get', 'delete', 'head', 'jsonp');
10377
10378    /**
10379     * @ngdoc method
10380     * @name $http#post
10381     *
10382     * @description
10383     * Shortcut method to perform `POST` request.
10384     *
10385     * @param {string} url Relative or absolute URL specifying the destination of the request
10386     * @param {*} data Request content
10387     * @param {Object=} config Optional configuration object
10388     * @returns {HttpPromise} Future object
10389     */
10390
10391    /**
10392     * @ngdoc method
10393     * @name $http#put
10394     *
10395     * @description
10396     * Shortcut method to perform `PUT` request.
10397     *
10398     * @param {string} url Relative or absolute URL specifying the destination of the request
10399     * @param {*} data Request content
10400     * @param {Object=} config Optional configuration object
10401     * @returns {HttpPromise} Future object
10402     */
10403
10404     /**
10405      * @ngdoc method
10406      * @name $http#patch
10407      *
10408      * @description
10409      * Shortcut method to perform `PATCH` request.
10410      *
10411      * @param {string} url Relative or absolute URL specifying the destination of the request
10412      * @param {*} data Request content
10413      * @param {Object=} config Optional configuration object
10414      * @returns {HttpPromise} Future object
10415      */
10416    createShortMethodsWithData('post', 'put', 'patch');
10417
10418        /**
10419         * @ngdoc property
10420         * @name $http#defaults
10421         *
10422         * @description
10423         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10424         * default headers, withCredentials as well as request and response transformations.
10425         *
10426         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10427         */
10428    $http.defaults = defaults;
10429
10430
10431    return $http;
10432
10433
10434    function createShortMethods(names) {
10435      forEach(arguments, function(name) {
10436        $http[name] = function(url, config) {
10437          return $http(extend({}, config || {}, {
10438            method: name,
10439            url: url
10440          }));
10441        };
10442      });
10443    }
10444
10445
10446    function createShortMethodsWithData(name) {
10447      forEach(arguments, function(name) {
10448        $http[name] = function(url, data, config) {
10449          return $http(extend({}, config || {}, {
10450            method: name,
10451            url: url,
10452            data: data
10453          }));
10454        };
10455      });
10456    }
10457
10458
10459    /**
10460     * Makes the request.
10461     *
10462     * !!! ACCESSES CLOSURE VARS:
10463     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10464     */
10465    function sendReq(config, reqData) {
10466      var deferred = $q.defer(),
10467          promise = deferred.promise,
10468          cache,
10469          cachedResp,
10470          reqHeaders = config.headers,
10471          url = buildUrl(config.url, config.paramSerializer(config.params));
10472
10473      $http.pendingRequests.push(config);
10474      promise.then(removePendingReq, removePendingReq);
10475
10476
10477      if ((config.cache || defaults.cache) && config.cache !== false &&
10478          (config.method === 'GET' || config.method === 'JSONP')) {
10479        cache = isObject(config.cache) ? config.cache
10480              : isObject(defaults.cache) ? defaults.cache
10481              : defaultCache;
10482      }
10483
10484      if (cache) {
10485        cachedResp = cache.get(url);
10486        if (isDefined(cachedResp)) {
10487          if (isPromiseLike(cachedResp)) {
10488            // cached request has already been sent, but there is no response yet
10489            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10490          } else {
10491            // serving from cache
10492            if (isArray(cachedResp)) {
10493              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10494            } else {
10495              resolvePromise(cachedResp, 200, {}, 'OK');
10496            }
10497          }
10498        } else {
10499          // put the promise for the non-transformed response into cache as a placeholder
10500          cache.put(url, promise);
10501        }
10502      }
10503
10504
10505      // if we won't have the response in cache, set the xsrf headers and
10506      // send the request to the backend
10507      if (isUndefined(cachedResp)) {
10508        var xsrfValue = urlIsSameOrigin(config.url)
10509            ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10510            : undefined;
10511        if (xsrfValue) {
10512          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10513        }
10514
10515        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10516            config.withCredentials, config.responseType);
10517      }
10518
10519      return promise;
10520
10521
10522      /**
10523       * Callback registered to $httpBackend():
10524       *  - caches the response if desired
10525       *  - resolves the raw $http promise
10526       *  - calls $apply
10527       */
10528      function done(status, response, headersString, statusText) {
10529        if (cache) {
10530          if (isSuccess(status)) {
10531            cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10532          } else {
10533            // remove promise from the cache
10534            cache.remove(url);
10535          }
10536        }
10537
10538        function resolveHttpPromise() {
10539          resolvePromise(response, status, headersString, statusText);
10540        }
10541
10542        if (useApplyAsync) {
10543          $rootScope.$applyAsync(resolveHttpPromise);
10544        } else {
10545          resolveHttpPromise();
10546          if (!$rootScope.$$phase) $rootScope.$apply();
10547        }
10548      }
10549
10550
10551      /**
10552       * Resolves the raw $http promise.
10553       */
10554      function resolvePromise(response, status, headers, statusText) {
10555        //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10556        status = status >= -1 ? status : 0;
10557
10558        (isSuccess(status) ? deferred.resolve : deferred.reject)({
10559          data: response,
10560          status: status,
10561          headers: headersGetter(headers),
10562          config: config,
10563          statusText: statusText
10564        });
10565      }
10566
10567      function resolvePromiseWithResult(result) {
10568        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10569      }
10570
10571      function removePendingReq() {
10572        var idx = $http.pendingRequests.indexOf(config);
10573        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10574      }
10575    }
10576
10577
10578    function buildUrl(url, serializedParams) {
10579      if (serializedParams.length > 0) {
10580        url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10581      }
10582      return url;
10583    }
10584  }];
10585}
10586
10587/**
10588 * @ngdoc service
10589 * @name $xhrFactory
10590 *
10591 * @description
10592 * Factory function used to create XMLHttpRequest objects.
10593 *
10594 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10595 *
10596 * ```
10597 * angular.module('myApp', [])
10598 * .factory('$xhrFactory', function() {
10599 *   return function createXhr(method, url) {
10600 *     return new window.XMLHttpRequest({mozSystem: true});
10601 *   };
10602 * });
10603 * ```
10604 *
10605 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10606 * @param {string} url URL of the request.
10607 */
10608function $xhrFactoryProvider() {
10609  this.$get = function() {
10610    return function createXhr() {
10611      return new window.XMLHttpRequest();
10612    };
10613  };
10614}
10615
10616/**
10617 * @ngdoc service
10618 * @name $httpBackend
10619 * @requires $window
10620 * @requires $document
10621 * @requires $xhrFactory
10622 *
10623 * @description
10624 * HTTP backend used by the {@link ng.$http service} that delegates to
10625 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10626 *
10627 * You should never need to use this service directly, instead use the higher-level abstractions:
10628 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10629 *
10630 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10631 * $httpBackend} which can be trained with responses.
10632 */
10633function $HttpBackendProvider() {
10634  this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10635    return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10636  }];
10637}
10638
10639function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10640  // TODO(vojta): fix the signature
10641  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10642    $browser.$$incOutstandingRequestCount();
10643    url = url || $browser.url();
10644
10645    if (lowercase(method) == 'jsonp') {
10646      var callbackId = '_' + (callbacks.counter++).toString(36);
10647      callbacks[callbackId] = function(data) {
10648        callbacks[callbackId].data = data;
10649        callbacks[callbackId].called = true;
10650      };
10651
10652      var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10653          callbackId, function(status, text) {
10654        completeRequest(callback, status, callbacks[callbackId].data, "", text);
10655        callbacks[callbackId] = noop;
10656      });
10657    } else {
10658
10659      var xhr = createXhr(method, url);
10660
10661      xhr.open(method, url, true);
10662      forEach(headers, function(value, key) {
10663        if (isDefined(value)) {
10664            xhr.setRequestHeader(key, value);
10665        }
10666      });
10667
10668      xhr.onload = function requestLoaded() {
10669        var statusText = xhr.statusText || '';
10670
10671        // responseText is the old-school way of retrieving response (supported by IE9)
10672        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10673        var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10674
10675        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10676        var status = xhr.status === 1223 ? 204 : xhr.status;
10677
10678        // fix status code when it is 0 (0 status is undocumented).
10679        // Occurs when accessing file resources or on Android 4.1 stock browser
10680        // while retrieving files from application cache.
10681        if (status === 0) {
10682          status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10683        }
10684
10685        completeRequest(callback,
10686            status,
10687            response,
10688            xhr.getAllResponseHeaders(),
10689            statusText);
10690      };
10691
10692      var requestError = function() {
10693        // The response is always empty
10694        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
10695        completeRequest(callback, -1, null, null, '');
10696      };
10697
10698      xhr.onerror = requestError;
10699      xhr.onabort = requestError;
10700
10701      if (withCredentials) {
10702        xhr.withCredentials = true;
10703      }
10704
10705      if (responseType) {
10706        try {
10707          xhr.responseType = responseType;
10708        } catch (e) {
10709          // WebKit added support for the json responseType value on 09/03/2013
10710          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
10711          // known to throw when setting the value "json" as the response type. Other older
10712          // browsers implementing the responseType
10713          //
10714          // The json response type can be ignored if not supported, because JSON payloads are
10715          // parsed on the client-side regardless.
10716          if (responseType !== 'json') {
10717            throw e;
10718          }
10719        }
10720      }
10721
10722      xhr.send(isUndefined(post) ? null : post);
10723    }
10724
10725    if (timeout > 0) {
10726      var timeoutId = $browserDefer(timeoutRequest, timeout);
10727    } else if (isPromiseLike(timeout)) {
10728      timeout.then(timeoutRequest);
10729    }
10730
10731
10732    function timeoutRequest() {
10733      jsonpDone && jsonpDone();
10734      xhr && xhr.abort();
10735    }
10736
10737    function completeRequest(callback, status, response, headersString, statusText) {
10738      // cancel timeout and subsequent timeout promise resolution
10739      if (isDefined(timeoutId)) {
10740        $browserDefer.cancel(timeoutId);
10741      }
10742      jsonpDone = xhr = null;
10743
10744      callback(status, response, headersString, statusText);
10745      $browser.$$completeOutstandingRequest(noop);
10746    }
10747  };
10748
10749  function jsonpReq(url, callbackId, done) {
10750    // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
10751    // - fetches local scripts via XHR and evals them
10752    // - adds and immediately removes script elements from the document
10753    var script = rawDocument.createElement('script'), callback = null;
10754    script.type = "text/javascript";
10755    script.src = url;
10756    script.async = true;
10757
10758    callback = function(event) {
10759      removeEventListenerFn(script, "load", callback);
10760      removeEventListenerFn(script, "error", callback);
10761      rawDocument.body.removeChild(script);
10762      script = null;
10763      var status = -1;
10764      var text = "unknown";
10765
10766      if (event) {
10767        if (event.type === "load" && !callbacks[callbackId].called) {
10768          event = { type: "error" };
10769        }
10770        text = event.type;
10771        status = event.type === "error" ? 404 : 200;
10772      }
10773
10774      if (done) {
10775        done(status, text);
10776      }
10777    };
10778
10779    addEventListenerFn(script, "load", callback);
10780    addEventListenerFn(script, "error", callback);
10781    rawDocument.body.appendChild(script);
10782    return callback;
10783  }
10784}
10785
10786var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
10787$interpolateMinErr.throwNoconcat = function(text) {
10788  throw $interpolateMinErr('noconcat',
10789      "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10790      "interpolations that concatenate multiple expressions when a trusted value is " +
10791      "required.  See http://docs.angularjs.org/api/ng.$sce", text);
10792};
10793
10794$interpolateMinErr.interr = function(text, err) {
10795  return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
10796};
10797
10798/**
10799 * @ngdoc provider
10800 * @name $interpolateProvider
10801 *
10802 * @description
10803 *
10804 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
10805 *
10806 * @example
10807<example module="customInterpolationApp">
10808<file name="index.html">
10809<script>
10810  var customInterpolationApp = angular.module('customInterpolationApp', []);
10811
10812  customInterpolationApp.config(function($interpolateProvider) {
10813    $interpolateProvider.startSymbol('//');
10814    $interpolateProvider.endSymbol('//');
10815  });
10816
10817
10818  customInterpolationApp.controller('DemoController', function() {
10819      this.label = "This binding is brought you by // interpolation symbols.";
10820  });
10821</script>
10822<div ng-app="App" ng-controller="DemoController as demo">
10823    //demo.label//
10824</div>
10825</file>
10826<file name="protractor.js" type="protractor">
10827  it('should interpolate binding with custom symbols', function() {
10828    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
10829  });
10830</file>
10831</example>
10832 */
10833function $InterpolateProvider() {
10834  var startSymbol = '{{';
10835  var endSymbol = '}}';
10836
10837  /**
10838   * @ngdoc method
10839   * @name $interpolateProvider#startSymbol
10840   * @description
10841   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
10842   *
10843   * @param {string=} value new value to set the starting symbol to.
10844   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10845   */
10846  this.startSymbol = function(value) {
10847    if (value) {
10848      startSymbol = value;
10849      return this;
10850    } else {
10851      return startSymbol;
10852    }
10853  };
10854
10855  /**
10856   * @ngdoc method
10857   * @name $interpolateProvider#endSymbol
10858   * @description
10859   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10860   *
10861   * @param {string=} value new value to set the ending symbol to.
10862   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10863   */
10864  this.endSymbol = function(value) {
10865    if (value) {
10866      endSymbol = value;
10867      return this;
10868    } else {
10869      return endSymbol;
10870    }
10871  };
10872
10873
10874  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
10875    var startSymbolLength = startSymbol.length,
10876        endSymbolLength = endSymbol.length,
10877        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
10878        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
10879
10880    function escape(ch) {
10881      return '\\\\\\' + ch;
10882    }
10883
10884    function unescapeText(text) {
10885      return text.replace(escapedStartRegexp, startSymbol).
10886        replace(escapedEndRegexp, endSymbol);
10887    }
10888
10889    function stringify(value) {
10890      if (value == null) { // null || undefined
10891        return '';
10892      }
10893      switch (typeof value) {
10894        case 'string':
10895          break;
10896        case 'number':
10897          value = '' + value;
10898          break;
10899        default:
10900          value = toJson(value);
10901      }
10902
10903      return value;
10904    }
10905
10906    /**
10907     * @ngdoc service
10908     * @name $interpolate
10909     * @kind function
10910     *
10911     * @requires $parse
10912     * @requires $sce
10913     *
10914     * @description
10915     *
10916     * Compiles a string with markup into an interpolation function. This service is used by the
10917     * HTML {@link ng.$compile $compile} service for data binding. See
10918     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
10919     * interpolation markup.
10920     *
10921     *
10922     * ```js
10923     *   var $interpolate = ...; // injected
10924     *   var exp = $interpolate('Hello {{name | uppercase}}!');
10925     *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
10926     * ```
10927     *
10928     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
10929     * `true`, the interpolation function will return `undefined` unless all embedded expressions
10930     * evaluate to a value other than `undefined`.
10931     *
10932     * ```js
10933     *   var $interpolate = ...; // injected
10934     *   var context = {greeting: 'Hello', name: undefined };
10935     *
10936     *   // default "forgiving" mode
10937     *   var exp = $interpolate('{{greeting}} {{name}}!');
10938     *   expect(exp(context)).toEqual('Hello !');
10939     *
10940     *   // "allOrNothing" mode
10941     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
10942     *   expect(exp(context)).toBeUndefined();
10943     *   context.name = 'Angular';
10944     *   expect(exp(context)).toEqual('Hello Angular!');
10945     * ```
10946     *
10947     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
10948     *
10949     * ####Escaped Interpolation
10950     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
10951     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
10952     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
10953     * or binding.
10954     *
10955     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
10956     * degree, while also enabling code examples to work without relying on the
10957     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
10958     *
10959     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
10960     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
10961     * interpolation start/end markers with their escaped counterparts.**
10962     *
10963     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
10964     * output when the $interpolate service processes the text. So, for HTML elements interpolated
10965     * by {@link ng.$compile $compile}
10965, or otherwise interpolated with the `mustHaveExpression` parameter
10966     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
10967     * this is typically useful only when user-data is used in rendering a template from the server, or
10968     * when otherwise untrusted data is used by a directive.
10969     *
10970     * <example>
10971     *  <file name="index.html">
10972     *    <div ng-init="username='A user'">
10973     *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
10974     *        </p>
10975     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
10976     *        application, but fails to accomplish their task, because the server has correctly
10977     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
10978     *        characters.</p>
10979     *      <p>Instead, the result of the attempted script injection is visible, and can be removed
10980     *        from the database by an administrator.</p>
10981     *    </div>
10982     *  </file>
10983     * </example>
10984     *
10985     * @param {string} text The text with markup to interpolate.
10986     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
10987     *    embedded expression in order to return an interpolation function. Strings with no
10988     *    embedded expression will return null for the interpolation function.
10989     * @param {string=} trustedContext when provided, the returned function passes the interpolated
10990     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
10991     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
10992     *    provides Strict Contextual Escaping for details.
10993     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
10994     *    unless all embedded expressions evaluate to a value other than `undefined`.
10995     * @returns {function(context)} an interpolation function which is used to compute the
10996     *    interpolated string. The function has these parameters:
10997     *
10998     * - `context`: evaluation context for all expressions embedded in the interpolated text
10999     */
11000    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11001      allOrNothing = !!allOrNothing;
11002      var startIndex,
11003          endIndex,
11004          index = 0,
11005          expressions = [],
11006          parseFns = [],
11007          textLength = text.length,
11008          exp,
11009          concat = [],
11010          expressionPositions = [];
11011
11012      while (index < textLength) {
11013        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11014             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11015          if (index !== startIndex) {
11016            concat.push(unescapeText(text.substring(index, startIndex)));
11017          }
11018          exp = text.substring(startIndex + startSymbolLength, endIndex);
11019          expressions.push(exp);
11020          parseFns.push($parse(exp, parseStringifyInterceptor));
11021          index = endIndex + endSymbolLength;
11022          expressionPositions.push(concat.length);
11023          concat.push('');
11024        } else {
11025          // we did not find an interpolation, so we have to add the remainder to the separators array
11026          if (index !== textLength) {
11027            concat.push(unescapeText(text.substring(index)));
11028          }
11029          break;
11030        }
11031      }
11032
11033      // Concatenating expressions makes it hard to reason about whether some combination of
11034      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
11035      // single expression be used for iframe[src], object[src], etc., we ensure that the value
11036      // that's used is assigned or constructed by some JS code somewhere that is more testable or
11037      // make it obvious that you bound the value to some user controlled value.  This helps reduce
11038      // the load when auditing for XSS issues.
11039      if (trustedContext && concat.length > 1) {
11040          $interpolateMinErr.throwNoconcat(text);
11041      }
11042
11043      if (!mustHaveExpression || expressions.length) {
11044        var compute = function(values) {
11045          for (var i = 0, ii = expressions.length; i < ii; i++) {
11046            if (allOrNothing && isUndefined(values[i])) return;
11047            concat[expressionPositions[i]] = values[i];
11048          }
11049          return concat.join('');
11050        };
11051
11052        var getValue = function(value) {
11053          return trustedContext ?
11054            $sce.getTrusted(trustedContext, value) :
11055            $sce.valueOf(value);
11056        };
11057
11058        return extend(function interpolationFn(context) {
11059            var i = 0;
11060            var ii = expressions.length;
11061            var values = new Array(ii);
11062
11063            try {
11064              for (; i < ii; i++) {
11065                values[i] = parseFns[i](context);
11066              }
11067
11068              return compute(values);
11069            } catch (err) {
11070              $exceptionHandler($interpolateMinErr.interr(text, err));
vendor: 4,275 bytes, lines 11071-11181
11071            }
11072
11073          }, {
11074          // all of these properties are undocumented for now
11075          exp: text, //just for compatibility with regular watchers created via $watch
11076          expressions: expressions,
11077          $$watchDelegate: function(scope, listener) {
11078            var lastValue;
11079            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11080              var currValue = compute(values);
11081              if (isFunction(listener)) {
11082                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11083              }
11084              lastValue = currValue;
11085            });
11086          }
11087        });
11088      }
11089
11090      function parseStringifyInterceptor(value) {
11091        try {
11092          value = getValue(value);
11093          return allOrNothing && !isDefined(value) ? value : stringify(value);
11094        } catch (err) {
11095          $exceptionHandler($interpolateMinErr.interr(text, err));
11096        }
11097      }
11098    }
11099
11100
11101    /**
11102     * @ngdoc method
11103     * @name $interpolate#startSymbol
11104     * @description
11105     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11106     *
11107     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11108     * the symbol.
11109     *
11110     * @returns {string} start symbol.
11111     */
11112    $interpolate.startSymbol = function() {
11113      return startSymbol;
11114    };
11115
11116
11117    /**
11118     * @ngdoc method
11119     * @name $interpolate#endSymbol
11120     * @description
11121     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11122     *
11123     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11124     * the symbol.
11125     *
11126     * @returns {string} end symbol.
11127     */
11128    $interpolate.endSymbol = function() {
11129      return endSymbol;
11130    };
11131
11132    return $interpolate;
11133  }];
11134}
11135
11136function $IntervalProvider() {
11137  this.$get = ['$rootScope', '$window', '$q', '$$q',
11138       function($rootScope,   $window,   $q,   $$q) {
11139    var intervals = {};
11140
11141
11142     /**
11143      * @ngdoc service
11144      * @name $interval
11145      *
11146      * @description
11147      * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11148      * milliseconds.
11149      *
11150      * The return value of registering an interval function is a promise. This promise will be
11151      * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11152      * run indefinitely if `count` is not defined. The value of the notification will be the
11153      * number of iterations that have run.
11154      * To cancel an interval, call `$interval.cancel(promise)`.
11155      *
11156      * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11157      * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11158      * time.
11159      *
11160      * <div class="alert alert-warning">
11161      * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11162      * with them.  In particular they are not automatically destroyed when a controller's scope or a
11163      * directive's element are destroyed.
11164      * You should take this into consideration and make sure to always cancel the interval at the
11165      * appropriate moment.  See the example below for more details on how and when to do this.
11166      * </div>
11167      *
11168      * @param {function()} fn A function that should be called repeatedly.
11169      * @param {number} delay Number of milliseconds between each function call.
11170      * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11171      *   indefinitely.
11172      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11173      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11174      * @param {...*=} Pass additional parameters to the executed function.
11175      * @returns {promise} A promise which will be notified on each iteration.
11176      *
11177      * @example
11178      * <example module="intervalExample">
11179      * <file name="index.html">
11180      *   <script>
11181      *     angular.module('intervalExample', [])
11182      *       .controller('ExampleController', ['$scope', '$interval',
11183      *         function($scope, $interval) {
11184      *           $scope.format = 'M/d/yy h:mm:ss a';
11185      *           $scope.blood_1 = 100;
11186      *           $scope.blood_2 = 120;
11187      *
11188      *           var stop;
11189      *           $scope.fight = function() {
11190      *             // Don't start a new fight if we are already fighting
11191      *             if ( angular.isDefined(stop) ) return;
11192      *
11193      *             stop = $interval(function() {
11194      *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11195      *                 $scope.blood_1 = $scope.blood_1 - 3;
11196      *                 $scope.blood_2 = $scope.blood_2 - 4;
11197      *               } else {
11198      *                 $scope.stopFight();
11199      *               }
11200      *             }, 100);
11201      *           };
11202      *
11203      *           $scope.stopFight = function() {
11204      *             if (angular.isDefined(stop)) {
11205      *               $interval.cancel(stop);
11206      *               stop = undefined;
11207      *             }
11208      *           };
11209      *
11210      *           $scope.resetFight = function() {
11211      *             $scope.blood_1 = 100;
11212      *             $scope.blood_2 = 120;
11213      *           };
11214      *
11215      *           $scope.$on('$destroy', function() {
11216      *             // Make sure that the interval is destroyed too
11217      *             $scope.stopFight();
11218      *           });
11219      *         }])
11220      *       // Register the 'myCurrentTime' directive factory method.
11221      *       // We inject $interval and dateFilter service since the factory method is DI.
11222      *       .directive('myCurrentTime', ['$interval', 'dateFilter',
11223      *         function($interval, dateFilter) {
11224      *           // return the directive link function. (compile function not needed)
11225      *           return function(scope, element, attrs) {
11226      *             var format,  // date format
11227      *                 stopTime; // so that we can cancel the time updates
11228      *
11229      *             // used to update the UI
11230      *             function updateTime() {
11231      *               element.text(dateFilter(new Date(), format));
11232      *             }
11233      *
11234      *             // watch the expression, and update the UI on change.
11235      *             scope.$watch(attrs.myCurrentTime, function(value) {
11236      *               format = value;
11237      *               updateTime();
11238      *             });
11239      *
11240      *             stopTime = $interval(updateTime, 1000);
11241      *
11242      *             // listen on DOM destroy (removal) event, and cancel the next UI update
11243      *             // to prevent updating time after the DOM element was removed.
11244      *             element.on('$destroy', function() {
11245      *               $interval.cancel(stopTime);
11246      *             });
11247      *           }
11248      *         }]);
11249      *   </script>
11250      *
11251      *   <div>
11252      *     <div ng-controller="ExampleController">
11253      *       <label>Date format: <input ng-model="format"></label> <hr/>
11254      *       Current time is: <span my-current-time="format"></span>
11255      *       <hr/>
11256      *       Blood 1 : <font color='red'>{{blood_1}}</font>
11257      *       Blood 2 : <font color='red'>{{blood_2}}</font>
11258      *       <button type="button" data-ng-click="fight()">Fight</button>
11259      *       <button type="button" data-ng-click="stopFight()">StopFight</button>
11260      *       <button type="button" data-ng-click="resetFight()">resetFight</button>
11261      *     </div>
11262      *   </div>
11263      *
11264      * </file>
11265      * </example>
11266      */
11267    function interval(fn, delay, count, invokeApply) {
11268      var hasParams = arguments.length > 4,
11269          args = hasParams ? sliceArgs(arguments, 4) : [],
11270          setInterval = $window.setInterval,
11271          clearInterval = $window.clearInterval,
11272          iteration = 0,
11273          skipApply = (isDefined(invokeApply) && !invokeApply),
11274          deferred = (skipApply ? $$q : $q).defer(),
11275          promise = deferred.promise;
11276
11277      count = isDefined(count) ? count : 0;
11278
11279      promise.then(null, null, (!hasParams) ? fn : function() {
11280        fn.apply(null, args);
11281      });
11282
11283      promise.$$intervalId = setInterval(function tick() {
11284        deferred.notify(iteration++);
11285
11286        if (count > 0 && iteration >= count) {
11287          deferred.resolve(iteration);
11288          clearInterval(promise.$$intervalId);
11289          delete intervals[promise.$$intervalId];
11290        }
11291
11292        if (!skipApply) $rootScope.$apply();
11293
11294      }, delay);
11295
11296      intervals[promise.$$intervalId] = deferred;
11297
11298      return promise;
11299    }
11300
11301
11302     /**
11303      * @ngdoc method
11304      * @name $interval#cancel
11305      *
11306      * @description
11307      * Cancels a task associated with the `promise`.
11308      *
11309      * @param {Promise=} promise returned by the `$interval` function.
11310      * @returns {boolean} Returns `true` if the task was successfully canceled.
11311      */
11312    interval.cancel = function(promise) {
11313      if (promise && promise.$$intervalId in intervals) {
11314        intervals[promise.$$intervalId].reject('canceled');
11315        $window.clearInterval(promise.$$intervalId);
11316        delete intervals[promise.$$intervalId];
11317        return true;
11318      }
11319      return false;
11320    };
11321
11322    return interval;
11323  }];
11324}
11325
11326/**
11327 * @ngdoc service
11328 * @name $locale
11329 *
11330 * @description
11331 * $locale service provides localization rules for various Angular components. As of right now the
11332 * only public api is:
11333 *
11334 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11335 */
11336
11337var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11338    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11339var $locationMinErr = minErr('$location');
11340
11341
11342/**
11343 * Encode path using encodeUriSegment, ignoring forward slashes
11344 *
11345 * @param {string} path Path to encode
11346 * @returns {string}
11347 */
11348function encodePath(path) {
11349  var segments = path.split('/'),
11350      i = segments.length;
11351
11352  while (i--) {
11353    segments[i] = encodeUriSegment(segments[i]);
11354  }
11355
11356  return segments.join('/');
11357}
11358
11359function parseAbsoluteUrl(absoluteUrl, locationObj) {
11360  var parsedUrl = urlResolve(absoluteUrl);
11361
11362  locationObj.$$protocol = parsedUrl.protocol;
11363  locationObj.$$host = parsedUrl.hostname;
11364  locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11365}
11366
11367
11368function parseAppUrl(relativeUrl, locationObj) {
11369  var prefixed = (relativeUrl.charAt(0) !== '/');
11370  if (prefixed) {
11371    relativeUrl = '/' + relativeUrl;
11372  }
11373  var match = urlResolve(relativeUrl);
11374  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11375      match.pathname.substring(1) : match.pathname);
11376  locationObj.$$search = parseKeyValue(match.search);
11377  locationObj.$$hash = decodeURIComponent(match.hash);
11378
11379  // make sure path starts with '/';
11380  if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11381    locationObj.$$path = '/' + locationObj.$$path;
11382  }
11383}
11384
11385
11386/**
11387 *
11388 * @param {string} begin
11389 * @param {string} whole
11390 * @returns {string} returns text from whole after begin or undefined if it does not begin with
11391 *                   expected string.
11392 */
11393function beginsWith(begin, whole) {
11394  if (whole.indexOf(begin) === 0) {
11395    return whole.substr(begin.length);
11396  }
11397}
11398
11399
11400function stripHash(url) {
11401  var index = url.indexOf('#');
11402  return index == -1 ? url : url.substr(0, index);
11403}
11404
11405function trimEmptyHash(url) {
11406  return url.replace(/(#.+)|#$/, '$1');
11407}
11408
11409
11410function stripFile(url) {
11411  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11412}
11413
11414/* return the server only (scheme://host:port) */
11415function serverBase(url) {
11416  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11417}
11418
11419
11420/**
11421 * LocationHtml5Url represents an url
11422 * This object is exposed as $location service when HTML5 mode is enabled and supported
11423 *
11424 * @constructor
11425 * @param {string} appBase application base URL
11426 * @param {string} appBaseNoFile application base URL stripped of any filename
11427 * @param {string} basePrefix url path prefix
11428 */
11429function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11430  this.$$html5 = true;
11431  basePrefix = basePrefix || '';
11432  parseAbsoluteUrl(appBase, this);
11433
11434
11435  /**
11436   * Parse given html5 (regular) url string into properties
11437   * @param {string} url HTML5 url
11438   * @private
11439   */
11440  this.$$parse = function(url) {
11441    var pathUrl = beginsWith(appBaseNoFile, url);
11442    if (!isString(pathUrl)) {
11443      throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11444          appBaseNoFile);
11445    }
11446
11447    parseAppUrl(pathUrl, this);
11448
11449    if (!this.$$path) {
11450      this.$$path = '/';
11451    }
11452
11453    this.$$compose();
11454  };
11455
11456  /**
11457   * Compose url and update `absUrl` property
11458   * @private
11459   */
11460  this.$$compose = function() {
vendor: 4,105 bytes, lines 11461-11583
11461    var search = toKeyValue(this.$$search),
11462        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11463
11464    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11465    this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11466  };
11467
11468  this.$$parseLinkUrl = function(url, relHref) {
11469    if (relHref && relHref[0] === '#') {
11470      // special case for links to hash fragments:
11471      // keep the old url and only replace the hash fragment
11472      this.hash(relHref.slice(1));
11473      return true;
11474    }
11475    var appUrl, prevAppUrl;
11476    var rewrittenUrl;
11477
11478    if (isDefined(appUrl = beginsWith(appBase, url))) {
11479      prevAppUrl = appUrl;
11480      if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11481        rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11482      } else {
11483        rewrittenUrl = appBase + prevAppUrl;
11484      }
11485    } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11486      rewrittenUrl = appBaseNoFile + appUrl;
11487    } else if (appBaseNoFile == url + '/') {
11488      rewrittenUrl = appBaseNoFile;
11489    }
11490    if (rewrittenUrl) {
11491      this.$$parse(rewrittenUrl);
11492    }
11493    return !!rewrittenUrl;
11494  };
11495}
11496
11497
11498/**
11499 * LocationHashbangUrl represents url
11500 * This object is exposed as $location service when developer doesn't opt into html5 mode.
11501 * It also serves as the base class for html5 mode fallback on legacy browsers.
11502 *
11503 * @constructor
11504 * @param {string} appBase application base URL
11505 * @param {string} appBaseNoFile application base URL stripped of any filename
11506 * @param {string} hashPrefix hashbang prefix
11507 */
11508function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
11509
11510  parseAbsoluteUrl(appBase, this);
11511
11512
11513  /**
11514   * Parse given hashbang url into properties
11515   * @param {string} url Hashbang url
11516   * @private
11517   */
11518  this.$$parse = function(url) {
11519    var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11520    var withoutHashUrl;
11521
11522    if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
11523
11524      // The rest of the url starts with a hash so we have
11525      // got either a hashbang path or a plain hash fragment
11526      withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11527      if (isUndefined(withoutHashUrl)) {
11528        // There was no hashbang prefix so we just have a hash fragment
11529        withoutHashUrl = withoutBaseUrl;
11530      }
11531
11532    } else {
11533      // There was no hashbang path nor hash fragment:
11534      // If we are in HTML5 mode we use what is left as the path;
11535      // Otherwise we ignore what is left
11536      if (this.$$html5) {
11537        withoutHashUrl = withoutBaseUrl;
11538      } else {
11539        withoutHashUrl = '';
11540        if (isUndefined(withoutBaseUrl)) {
11541          appBase = url;
11542          this.replace();
11543        }
11544      }
11545    }
11546
11547    parseAppUrl(withoutHashUrl, this);
11548
11549    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11550
11551    this.$$compose();
11552
11553    /*
11554     * In Windows, on an anchor node on documents loaded from
11555     * the filesystem, the browser will return a pathname
11556     * prefixed with the drive name ('/C:/path') when a
11557     * pathname without a drive is set:
11558     *  * a.setAttribute('href', '/foo')
11559     *   * a.pathname === '/C:/foo' //true
11560     *
11561     * Inside of Angular, we're always using pathnames that
11562     * do not include drive names for routing.
11563     */
11564    function removeWindowsDriveName(path, url, base) {
11565      /*
11566      Matches paths for file protocol on windows,
11567      such as /C:/foo/bar, and captures only /foo/bar.
11568      */
11569      var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11570
11571      var firstPathSegmentMatch;
11572
11573      //Get the relative path from the input URL.
11574      if (url.indexOf(base) === 0) {
11575        url = url.replace(base, '');
11576      }
11577
11578      // The input URL intentionally contains a first path segment that ends with a colon.
11579      if (windowsFilePathExp.exec(url)) {
11580        return path;
11581      }
11582
11583      firstPathSegmentMatch = windowsFilePathExp.exec(path);
11584      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11585    }
11586  };
11587
11588  /**
11589   * Compose hashbang url and update `absUrl` property
11590   * @private
11591   */
11592  this.$$compose = function() {
11593    var search = toKeyValue(this.$$search),
11594        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11595
11596    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11597    this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11598  };
11599
11600  this.$$parseLinkUrl = function(url, relHref) {
11601    if (stripHash(appBase) == stripHash(url)) {
11602      this.$$parse(url);
11603      return true;
11604    }
11605    return false;
11606  };
11607}
11608
11609
11610/**
11611 * LocationHashbangUrl represents url
11612 * This object is exposed as $location service when html5 history api is enabled but the browser
11613 * does not support it.
11614 *
11615 * @constructor
11616 * @param {string} appBase application base URL
11617 * @param {string} appBaseNoFile application base URL stripped of any filename
11618 * @param {string} hashPrefix hashbang prefix
11619 */
11620function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
11621  this.$$html5 = true;
11622  LocationHashbangUrl.apply(this, arguments);
11623
11624  this.$$parseLinkUrl = function(url, relHref) {
11625    if (relHref && relHref[0] === '#') {
11626      // special case for links to hash fragments:
11627      // keep the old url and only replace the hash fragment
11628      this.hash(relHref.slice(1));
11629      return true;
11630    }
11631
11632    var rewrittenUrl;
11633    var appUrl;
11634
11635    if (appBase == stripHash(url)) {
11636      rewrittenUrl = url;
11637    } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11638      rewrittenUrl = appBase + hashPrefix + appUrl;
11639    } else if (appBaseNoFile === url + '/') {
11640      rewrittenUrl = appBaseNoFile;
11641    }
11642    if (rewrittenUrl) {
11643      this.$$parse(rewrittenUrl);
11644    }
11645    return !!rewrittenUrl;
11646  };
11647
11648  this.$$compose = function() {
11649    var search = toKeyValue(this.$$search),
11650        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11651
11652    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11653    // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
11654    this.$$absUrl = appBase + hashPrefix + this.$$url;
11655  };
11656
11657}
11658
11659
11660var locationPrototype = {
11661
11662  /**
11663   * Are we in html5 mode?
11664   * @private
11665   */
11666  $$html5: false,
11667
11668  /**
11669   * Has any change been replacing?
11670   * @private
11671   */
11672  $$replace: false,
11673
11674  /**
11675   * @ngdoc method
11676   * @name $location#absUrl
11677   *
11678   * @description
11679   * This method is getter only.
11680   *
11681   * Return full url representation with all segments encoded according to rules specified in
11682   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11683   *
11684   *
11685   * ```js
11686   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11687   * var absUrl = $location.absUrl();
11688   * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11689   * ```
11690   *
11691   * @return {string} full url
11692   */
11693  absUrl: locationGetter('$$absUrl'),
11694
11695  /**
11696   * @ngdoc method
11697   * @name $location#url
11698   *
11699   * @description
11700   * This method is getter / setter.
11701   *
11702   * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
11703   *
11704   * Change path, search and hash, when called with parameter and return `$location`.
11705   *
11706   *
11707   * ```js
11708   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11709   * var url = $location.url();
11710   * // => "/some/path?foo=bar&baz=xoxo"
11711   * ```
11712   *
11713   * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
11714   * @return {string} url
11715   */
11716  url: function(url) {
11717    if (isUndefined(url)) {
11718      return this.$$url;
11719    }
11720
11721    var match = PATH_MATCH.exec(url);
11722    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
11723    if (match[2] || match[1] || url === '') this.search(match[3] || '');
11724    this.hash(match[5] || '');
11725
11726    return this;
11727  },
11728
11729  /**
11730   * @ngdoc method
11731   * @name $location#protocol
11732   *
11733   * @description
11734   * This method is getter only.
11735   *
11736   * Return protocol of current url.
11737   *
11738   *
11739   * ```js
11740   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11741   * var protocol = $location.protocol();
11742   * // => "http"
11743   * ```
11744   *
11745   * @return {string} protocol of current url
11746   */
11747  protocol: locationGetter('$$protocol'),
11748
11749  /**
11750   * @ngdoc method
11751   * @name $location#host
11752   *
11753   * @description
11754   * This method is getter only.
11755   *
11756   * Return host of current url.
11757   *
11758   * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
11759   *
11760   *
11761   * ```js
11762   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11763   * var host = $location.host();
11764   * // => "example.com"
11765   *
11766   * // given url http://user:[email protected]:8080/#/some/path?foo=bar&baz=xoxo
11767   * host = $location.host();
11768   * // => "example.com"
11769   * host = location.host;
11770   * // => "example.com:8080"
11771   * ```
11772   *
11773   * @return {string} host of current url.
11774   */
11775  host: locationGetter('$$host'),
11776
11777  /**
11778   * @ngdoc method
11779   * @name $location#port
11780   *
11781   * @description
11782   * This method is getter only.
11783   *
11784   * Return port of current url.
11785   *
11786   *
11787   * ```js
11788   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11789   * var port = $location.port();
11790   * // => 80
11791   * ```
11792   *
11793   * @return {Number} port
11794   */
11795  port: locationGetter('$$port'),
11796
11797  /**
11798   * @ngdoc method
11799   * @name $location#path
11800   *
11801   * @description
11802   * This method is getter / setter.
11803   *
11804   * Return path of current url when called without any parameter.
11805   *
11806   * Change path when called with parameter and return `$location`.
11807   *
11808   * Note: Path should always begin with forward slash (/), this method will add the forward slash
11809   * if it is missing.
11810   *
11811   *
11812   * ```js
11813   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11814   * var path = $location.path();
11815   * // => "/some/path"
11816   * ```
11817   *
11818   * @param {(string|number)=} path New path
11819   * @return {string} path
11820   */
11821  path: locationGetterSetter('$$path', function(path) {
11822    path = path !== null ? path.toString() : '';
11823    return path.charAt(0) == '/' ? path : '/' + path;
11824  }),
11825
11826  /**
11827   * @ngdoc method
11828   * @name $location#search
11829   *
11830   * @description
11831   * This method is getter / setter.
11832   *
11833   * Return search part (as object) of current url when called without any parameter.
11834   *
11835   * Change search part when called with parameter and return `$location`.
11836   *
11837   *
11838   * ```js
11839   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11840   * var searchObject = $location.search();
11841   * // => {foo: 'bar', baz: 'xoxo'}
11842   *
11843   * // set foo to 'yipee'
11844   * $location.search('foo', 'yipee');
11845   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
11846   * ```
11847   *
11848   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
11849   * hash object.
11850   *
11851   * When called with a single argument the method acts as a setter, setting the `search` component
11852   * of `$location` to the specified value.
11853   *
11854   * If the argument is a hash object containing an array of values, these values will be encoded
11855   * as duplicate search parameters in the url.
11856   *
11857   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
11858   * will override only a single search property.
11859   *
11860   * If `paramValue` is an array, it will override the property of the `search` component of
11861   * `$location` specified via the first argument.
11862   *
11863   * If `paramValue` is `null`, the property specified via the first argument will be deleted.
11864   *
11865   * If `paramValue` is `true`, the property specified via the first argument will be added with no
11866   * value nor trailing equal sign.
11867   *
11868   * @return {Object} If called with no arguments returns the parsed `search` object. If called with
11869   * one or more arguments returns `$location` object itself.
11870   */
11871  search: function(search, paramValue) {
11872    switch (arguments.length) {
11873      case 0:
11874        return this.$$search;
11875      case 1:
11876        if (isString(search) || isNumber(search)) {
11877          search = search.toString();
11878          this.$$search = parseKeyValue(search);
11879        } else if (isObject(search)) {
11880          search = copy(search, {});
11881          // remove object undefined or null properties
vendor: 17,974 bytes, lines 11882-12414
11882          forEach(search, function(value, key) {
11883            if (value == null) delete search[key];
11884          });
11885
11886          this.$$search = search;
11887        } else {
11888          throw $locationMinErr('isrcharg',
11889              'The first argument of the `$location#search()` call must be a string or an object.');
11890        }
11891        break;
11892      default:
11893        if (isUndefined(paramValue) || paramValue === null) {
11894          delete this.$$search[search];
11895        } else {
11896          this.$$search[search] = paramValue;
11897        }
11898    }
11899
11900    this.$$compose();
11901    return this;
11902  },
11903
11904  /**
11905   * @ngdoc method
11906   * @name $location#hash
11907   *
11908   * @description
11909   * This method is getter / setter.
11910   *
11911   * Return hash fragment when called without any parameter.
11912   *
11913   * Change hash fragment when called with parameter and return `$location`.
11914   *
11915   *
11916   * ```js
11917   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
11918   * var hash = $location.hash();
11919   * // => "hashValue"
11920   * ```
11921   *
11922   * @param {(string|number)=} hash New hash fragment
11923   * @return {string} hash
11924   */
11925  hash: locationGetterSetter('$$hash', function(hash) {
11926    return hash !== null ? hash.toString() : '';
11927  }),
11928
11929  /**
11930   * @ngdoc method
11931   * @name $location#replace
11932   *
11933   * @description
11934   * If called, all changes to $location during current `$digest` will be replacing current history
11935   * record, instead of adding new one.
11936   */
11937  replace: function() {
11938    this.$$replace = true;
11939    return this;
11940  }
11941};
11942
11943forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
11944  Location.prototype = Object.create(locationPrototype);
11945
11946  /**
11947   * @ngdoc method
11948   * @name $location#state
11949   *
11950   * @description
11951   * This method is getter / setter.
11952   *
11953   * Return the history state object when called without any parameter.
11954   *
11955   * Change the history state object when called with one parameter and return `$location`.
11956   * The state object is later passed to `pushState` or `replaceState`.
11957   *
11958   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
11959   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
11960   * older browsers (like IE9 or Android < 4.0), don't use this method.
11961   *
11962   * @param {object=} state State object for pushState or replaceState
11963   * @return {object} state
11964   */
11965  Location.prototype.state = function(state) {
11966    if (!arguments.length) {
11967      return this.$$state;
11968    }
11969
11970    if (Location !== LocationHtml5Url || !this.$$html5) {
11971      throw $locationMinErr('nostate', 'History API state support is available only ' +
11972        'in HTML5 mode and only in browsers supporting HTML5 History API');
11973    }
11974    // The user might modify `stateObject` after invoking `$location.state(stateObject)`
11975    // but we're changing the $$state reference to $browser.state() during the $digest
11976    // so the modification window is narrow.
11977    this.$$state = isUndefined(state) ? null : state;
11978
11979    return this;
11980  };
11981});
11982
11983
11984function locationGetter(property) {
11985  return function() {
11986    return this[property];
11987  };
11988}
11989
11990
11991function locationGetterSetter(property, preprocess) {
11992  return function(value) {
11993    if (isUndefined(value)) {
11994      return this[property];
11995    }
11996
11997    this[property] = preprocess(value);
11998    this.$$compose();
11999
12000    return this;
12001  };
12002}
12003
12004
12005/**
12006 * @ngdoc service
12007 * @name $location
12008 *
12009 * @requires $rootElement
12010 *
12011 * @description
12012 * The $location service parses the URL in the browser address bar (based on the
12013 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12014 * available to your application. Changes to the URL in the address bar are reflected into
12015 * $location service and changes to $location are reflected into the browser address bar.
12016 *
12017 * **The $location service:**
12018 *
12019 * - Exposes the current URL in the browser address bar, so you can
12020 *   - Watch and observe the URL.
12021 *   - Change the URL.
12022 * - Synchronizes the URL with the browser when the user
12023 *   - Changes the address bar.
12024 *   - Clicks the back or forward button (or clicks a History link).
12025 *   - Clicks on a link.
12026 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12027 *
12028 * For more information see {@link guide/$location Developer Guide: Using $location}
12029 */
12030
12031/**
12032 * @ngdoc provider
12033 * @name $locationProvider
12034 * @description
12035 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12036 */
12037function $LocationProvider() {
12038  var hashPrefix = '',
12039      html5Mode = {
12040        enabled: false,
12041        requireBase: true,
12042        rewriteLinks: true
12043      };
12044
12045  /**
12046   * @ngdoc method
12047   * @name $locationProvider#hashPrefix
12048   * @description
12049   * @param {string=} prefix Prefix for hash part (containing path and search)
12050   * @returns {*} current value if used as getter or itself (chaining) if used as setter
12051   */
12052  this.hashPrefix = function(prefix) {
12053    if (isDefined(prefix)) {
12054      hashPrefix = prefix;
12055      return this;
12056    } else {
12057      return hashPrefix;
12058    }
12059  };
12060
12061  /**
12062   * @ngdoc method
12063   * @name $locationProvider#html5Mode
12064   * @description
12065   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12066   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12067   *   properties:
12068   *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12069   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12070   *     support `pushState`.
12071   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12072   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12073   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
12074   *     See the {@link guide/$location $location guide for more information}
12075   *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12076   *     enables/disables url rewriting for relative links.
12077   *
12078   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12079   */
12080  this.html5Mode = function(mode) {
12081    if (isBoolean(mode)) {
12082      html5Mode.enabled = mode;
12083      return this;
12084    } else if (isObject(mode)) {
12085
12086      if (isBoolean(mode.enabled)) {
12087        html5Mode.enabled = mode.enabled;
12088      }
12089
12090      if (isBoolean(mode.requireBase)) {
12091        html5Mode.requireBase = mode.requireBase;
12092      }
12093
12094      if (isBoolean(mode.rewriteLinks)) {
12095        html5Mode.rewriteLinks = mode.rewriteLinks;
12096      }
12097
12098      return this;
12099    } else {
12100      return html5Mode;
12101    }
12102  };
12103
12104  /**
12105   * @ngdoc event
12106   * @name $location#$locationChangeStart
12107   * @eventType broadcast on root scope
12108   * @description
12109   * Broadcasted before a URL will change.
12110   *
12111   * This change can be prevented by calling
12112   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12113   * details about event object. Upon successful change
12114   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12115   *
12116   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12117   * the browser supports the HTML5 History API.
12118   *
12119   * @param {Object} angularEvent Synthetic event object.
12120   * @param {string} newUrl New URL
12121   * @param {string=} oldUrl URL that was before it was changed.
12122   * @param {string=} newState New history state object
12123   * @param {string=} oldState History state object that was before it was changed.
12124   */
12125
12126  /**
12127   * @ngdoc event
12128   * @name $location#$locationChangeSuccess
12129   * @eventType broadcast on root scope
12130   * @description
12131   * Broadcasted after a URL was changed.
12132   *
12133   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12134   * the browser supports the HTML5 History API.
12135   *
12136   * @param {Object} angularEvent Synthetic event object.
12137   * @param {string} newUrl New URL
12138   * @param {string=} oldUrl URL that was before it was changed.
12139   * @param {string=} newState New history state object
12140   * @param {string=} oldState History state object that was before it was changed.
12141   */
12142
12143  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12144      function($rootScope, $browser, $sniffer, $rootElement, $window) {
12145    var $location,
12146        LocationMode,
12147        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12148        initialUrl = $browser.url(),
12149        appBase;
12150
12151    if (html5Mode.enabled) {
12152      if (!baseHref && html5Mode.requireBase) {
12153        throw $locationMinErr('nobase',
12154          "$location in HTML5 mode requires a <base> tag to be present!");
12155      }
12156      appBase = serverBase(initialUrl) + (baseHref || '/');
12157      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12158    } else {
12159      appBase = stripHash(initialUrl);
12160      LocationMode = LocationHashbangUrl;
12161    }
12162    var appBaseNoFile = stripFile(appBase);
12163
12164    $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12165    $location.$$parseLinkUrl(initialUrl, initialUrl);
12166
12167    $location.$$state = $browser.state();
12168
12169    var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12170
12171    function setBrowserUrlWithFallback(url, replace, state) {
12172      var oldUrl = $location.url();
12173      var oldState = $location.$$state;
12174      try {
12175        $browser.url(url, replace, state);
12176
12177        // Make sure $location.state() returns referentially identical (not just deeply equal)
12178        // state object; this makes possible quick checking if the state changed in the digest
12179        // loop. Checking deep equality would be too expensive.
12180        $location.$$state = $browser.state();
12181      } catch (e) {
12182        // Restore old values if pushState fails
12183        $location.url(oldUrl);
12184        $location.$$state = oldState;
12185
12186        throw e;
12187      }
12188    }
12189
12190    $rootElement.on('click', function(event) {
12191      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12192      // currently we open nice url link and redirect then
12193
12194      if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12195
12196      var elm = jqLite(event.target);
12197
12198      // traverse the DOM up to find first A tag
12199      while (nodeName_(elm[0]) !== 'a') {
12200        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12201        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12202      }
12203
12204      var absHref = elm.prop('href');
12205      // get the actual href attribute - see
12206      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12207      var relHref = elm.attr('href') || elm.attr('xlink:href');
12208
12209      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12210        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12211        // an animation.
12212        absHref = urlResolve(absHref.animVal).href;
12213      }
12214
12215      // Ignore when url is started with javascript: or mailto:
12216      if (IGNORE_URI_REGEXP.test(absHref)) return;
12217
12218      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12219        if ($location.$$parseLinkUrl(absHref, relHref)) {
12220          // We do a preventDefault for all urls that are part of the angular application,
12221          // in html5mode and also without, so that we are able to abort navigation without
12222          // getting double entries in the location history.
12223          event.preventDefault();
12224          // update location manually
12225          if ($location.absUrl() != $browser.url()) {
12226            $rootScope.$apply();
12227            // hack to work around FF6 bug 684208 when scenario runner clicks on links
12228            $window.angular['ff-684208-preventDefault'] = true;
12229          }
12230        }
12231      }
12232    });
12233
12234
12235    // rewrite hashbang url <> html5 url
12236    if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12237      $browser.url($location.absUrl(), true);
12238    }
12239
12240    var initializing = true;
12241
12242    // update $location when $browser url changes
12243    $browser.onUrlChange(function(newUrl, newState) {
12244
12245      if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12246        // If we are navigating outside of the app then force a reload
12247        $window.location.href = newUrl;
12248        return;
12249      }
12250
12251      $rootScope.$evalAsync(function() {
12252        var oldUrl = $location.absUrl();
12253        var oldState = $location.$$state;
12254        var defaultPrevented;
12255
12256        $location.$$parse(newUrl);
12257        $location.$$state = newState;
12258
12259        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12260            newState, oldState).defaultPrevented;
12261
12262        // if the location was changed by a `$locationChangeStart` handler then stop
12263        // processing this location change
12264        if ($location.absUrl() !== newUrl) return;
12265
12266        if (defaultPrevented) {
12267          $location.$$parse(oldUrl);
12268          $location.$$state = oldState;
12269          setBrowserUrlWithFallback(oldUrl, false, oldState);
12270        } else {
12271          initializing = false;
12272          afterLocationChange(oldUrl, oldState);
12273        }
12274      });
12275      if (!$rootScope.$$phase) $rootScope.$digest();
12276    });
12277
12278    // update browser
12279    $rootScope.$watch(function $locationWatch() {
12280      var oldUrl = trimEmptyHash($browser.url());
12281      var newUrl = trimEmptyHash($location.absUrl());
12282      var oldState = $browser.state();
12283      var currentReplace = $location.$$replace;
12284      var urlOrStateChanged = oldUrl !== newUrl ||
12285        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12286
12287      if (initializing || urlOrStateChanged) {
12288        initializing = false;
12289
12290        $rootScope.$evalAsync(function() {
12291          var newUrl = $location.absUrl();
12292          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12293              $location.$$state, oldState).defaultPrevented;
12294
12295          // if the location was changed by a `$locationChangeStart` handler then stop
12296          // processing this location change
12297          if ($location.absUrl() !== newUrl) return;
12298
12299          if (defaultPrevented) {
12300            $location.$$parse(oldUrl);
12301            $location.$$state = oldState;
12302          } else {
12303            if (urlOrStateChanged) {
12304              setBrowserUrlWithFallback(newUrl, currentReplace,
12305                                        oldState === $location.$$state ? null : $location.$$state);
12306            }
12307            afterLocationChange(oldUrl, oldState);
12308          }
12309        });
12310      }
12311
12312      $location.$$replace = false;
12313
12314      // we don't need to return anything because $evalAsync will make the digest loop dirty when
12315      // there is a change
12316    });
12317
12318    return $location;
12319
12320    function afterLocationChange(oldUrl, oldState) {
12321      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12322        $location.$$state, oldState);
12323    }
12324}];
12325}
12326
12327/**
12328 * @ngdoc service
12329 * @name $log
12330 * @requires $window
12331 *
12332 * @description
12333 * Simple service for logging. Default implementation safely writes the message
12334 * into the browser's console (if present).
12335 *
12336 * The main purpose of this service is to simplify debugging and troubleshooting.
12337 *
12338 * The default is to log `debug` messages. You can use
12339 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12340 *
12341 * @example
12342   <example module="logExample">
12343     <file name="script.js">
12344       angular.module('logExample', [])
12345         .controller('LogController', ['$scope', '$log', function($scope, $log) {
12346           $scope.$log = $log;
12347           $scope.message = 'Hello World!';
12348         }]);
12349     </file>
12350     <file name="index.html">
12351       <div ng-controller="LogController">
12352         <p>Reload this page with open console, enter text and hit the log button...</p>
12353         <label>Message:
12354         <input type="text" ng-model="message" /></label>
12355         <button ng-click="$log.log(message)">log</button>
12356         <button ng-click="$log.warn(message)">warn</button>
12357         <button ng-click="$log.info(message)">info</button>
12358         <button ng-click="$log.error(message)">error</button>
12359         <button ng-click="$log.debug(message)">debug</button>
12360       </div>
12361     </file>
12362   </example>
12363 */
12364
12365/**
12366 * @ngdoc provider
12367 * @name $logProvider
12368 * @description
12369 * Use the `$logProvider` to configure how the application logs messages
12370 */
12371function $LogProvider() {
12372  var debug = true,
12373      self = this;
12374
12375  /**
12376   * @ngdoc method
12377   * @name $logProvider#debugEnabled
12378   * @description
12379   * @param {boolean=} flag enable or disable debug level messages
12380   * @returns {*} current value if used as getter or itself (chaining) if used as setter
12381   */
12382  this.debugEnabled = function(flag) {
12383    if (isDefined(flag)) {
12384      debug = flag;
12385    return this;
12386    } else {
12387      return debug;
12388    }
12389  };
12390
12391  this.$get = ['$window', function($window) {
12392    return {
12393      /**
12394       * @ngdoc method
12395       * @name $log#log
12396       *
12397       * @description
12398       * Write a log message
12399       */
12400      log: consoleLog('log'),
12401
12402      /**
12403       * @ngdoc method
12404       * @name $log#info
12405       *
12406       * @description
12407       * Write an information message
12408       */
12409      info: consoleLog('info'),
12410
12411      /**
12412       * @ngdoc method
12413       * @name $log#warn
12414       *
12415       * @description
12416       * Write a warning message
12417       */
12418      warn: consoleLog('warn'),
12419
12420      /**
12421       * @ngdoc method
12422       * @name $log#error
12423       *
12424       * @description
12425       * Write an error message
12426       */
12427      error: consoleLog('error'),
12428
12429      /**
12430       * @ngdoc method
12431       * @name $log#debug
12432       *
12433       * @description
12434       * Write a debug message
12435       */
12436      debug: (function() {
12437        var fn = consoleLog('debug');
12438
12439        return function() {
12440          if (debug) {
12441            fn.apply(self, arguments);
12442          }
12443        };
12444      }())
12445    };
12446
12447    function formatError(arg) {
12448      if (arg instanceof Error) {
12449        if (arg.stack) {
12450          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12451              ? 'Error: ' + arg.message + '\n' + arg.stack
12452              : arg.stack;
12453        } else if (arg.sourceURL) {
12454          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12455        }
12456      }
12457      return arg;
12458    }
12459
12460    function consoleLog(type) {
12461      var console = $window.console || {},
12462          logFn = console[type] || console.log || noop,
12463          hasApply = false;
12464
12465      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12466      // The reason behind this is that console.log has type "object" in IE8...
12467      try {
12468        hasApply = !!logFn.apply;
12469      } catch (e) {}
12470
12471      if (hasApply) {
12472        return function() {
12473          var args = [];
12474          forEach(arguments, function(arg) {
12475            args.push(formatError(arg));
12476          });
12477          return logFn.apply(console, args);
12478        };
12479      }
12480
12481      // we are IE which either doesn't have window.console => this is noop and we do nothing,
12482      // or we are IE where console.log doesn't have apply so we log at least first 2 args
12483      return function(arg1, arg2) {
12484        logFn(arg1, arg2 == null ? '' : arg2);
12485      };
12486    }
12487  }];
12488}
12489
12490/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12491 *     Any commits to this file should be reviewed with security in mind.  *
12492 *   Changes to this file can potentially create security vulnerabilities. *
12493 *          An approval from 2 Core members with history of modifying      *
12494 *                         this file is required.                          *
12495 *                                                                         *
12496 *  Does the change somehow allow for arbitrary javascript to be executed? *
12497 *    Or allows for someone to change the prototype of built-in objects?   *
12498 *     Or gives undesired access to variables likes document or window?    *
12499 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12500
12501var $parseMinErr = minErr('$parse');
12502
12503// Sandboxing Angular Expressions
12504// ------------------------------
12505// Angular expressions are generally considered safe because these expressions only have direct
12506// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12507// obtaining a reference to native JS functions such as the Function constructor.
12508//
12509// As an example, consider the following Angular expression:
12510//
12511//   {}.toString.constructor('alert("evil JS code")')
12512//
12513// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12514// against the expression language, but not to prevent exploits that were enabled by exposing
12515// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12516// practice and therefore we are not even trying to protect against interaction with an object
12517// explicitly exposed in this way.
12518//
12519// In general, it is not possible to access a Window object from an angular expression unless a
12520// window or some DOM object that has a reference to window is published onto a Scope.
12521// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12522// native objects.
12523//
12524// See https://docs.angularjs.org/guide/security
12525
12526
12527function ensureSafeMemberName(name, fullExpression) {
12528  if (name === "__defineGetter__" || name === "__defineSetter__"
12529      || name === "__lookupGetter__" || name === "__lookupSetter__"
12530      || name === "__proto__") {
12531    throw $parseMinErr('isecfld',
12532        'Attempting to access a disallowed field in Angular expressions! '
12533        + 'Expression: {0}', fullExpression);
12534  }
12535  return name;
12536}
12537
12538function getStringValue(name, fullExpression) {
12539  // From the JavaScript docs:
12540  // Property names must be strings. This means that non-string objects cannot be used
12541  // as keys in an object. Any non-string object, including a number, is typecasted
12542  // into a string via the toString method.
12543  //
12544  // So, to ensure that we are checking the same `name` that JavaScript would use,
12545  // we cast it to a string, if possible.
12546  // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12547  // this is, this will handle objects that misbehave.
12548  name = name + '';
12549  if (!isString(name)) {
12550    throw $parseMinErr('iseccst',
12551        'Cannot convert object to primitive value! '
12552        + 'Expression: {0}', fullExpression);
12553  }
12554  return name;
12555}
12556
12557function ensureSafeObject(obj, fullExpression) {
12558  // nifty check if obj is Function that is fast and works across iframes and other contexts
12559  if (obj) {
12560    if (obj.constructor === obj) {
12561      throw $parseMinErr('isecfn',
12562          'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12563          fullExpression);
12564    } else if (// isWindow(obj)
12565        obj.window === obj) {
12566      throw $parseMinErr('isecwindow',
12567          'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12568          fullExpression);
12569    } else if (// isElement(obj)
12570        obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12571      throw $parseMinErr('isecdom',
12572          'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12573          fullExpression);
12574    } else if (// block Object so that we can't get hold of dangerous Object.* methods
12575        obj === Object) {
12576      throw $parseMinErr('isecobj',
12577          'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12578          fullExpression);
12579    }
12580  }
12581  return obj;
12582}
12583
12584var CALL = Function.prototype.call;
12585var APPLY = Function.prototype.apply;
12586var BIND = Function.prototype.bind;
12587
12588function ensureSafeFunction(obj, fullExpression) {
12589  if (obj) {
12590    if (obj.constructor === obj) {
12591      throw $parseMinErr('isecfn',
12592        'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12593        fullExpression);
12594    } else if (obj === CALL || obj === APPLY || obj === BIND) {
12595      throw $parseMinErr('isecff',
12596        'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12597        fullExpression);
12598    }
12599  }
12600}
12601
12602function ensureSafeAssignContext(obj, fullExpression) {
12603  if (obj) {
12604    if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12605        obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12606      throw $parseMinErr('isecaf',
12607        'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12608    }
12609  }
12610}
12611
12612var OPERATORS = createMap();
12613forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12614var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12615
12616
12617/////////////////////////////////////////
12618
12619
12620/**
12621 * @constructor
12622 */
12623var Lexer = function(options) {
12624  this.options = options;
12625};
12626
12627Lexer.prototype = {
12628  constructor: Lexer,
12629
12630  lex: function(text) {
12631    this.text = text;
12632    this.index = 0;
12633    this.tokens = [];
12634
12635    while (this.index < this.text.length) {
12636      var ch = this.text.charAt(this.index);
12637      if (ch === '"' || ch === "'") {
12638        this.readString(ch);
12639      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12640        this.readNumber();
12641      } else if (this.isIdent(ch)) {
12642        this.readIdent();
12643      } else if (this.is(ch, '(){}[].,;:?')) {
12644        this.tokens.push({index: this.index, text: ch});
12645        this.index++;
12646      } else if (this.isWhitespace(ch)) {
12647        this.index++;
12648      } else {
12649        var ch2 = ch + this.peek();
12650        var ch3 = ch2 + this.peek(2);
12651        var op1 = OPERATORS[ch];
vendor: 48,426 bytes, lines 12652-14123
12652        var op2 = OPERATORS[ch2];
12653        var op3 = OPERATORS[ch3];
12654        if (op1 || op2 || op3) {
12655          var token = op3 ? ch3 : (op2 ? ch2 : ch);
12656          this.tokens.push({index: this.index, text: token, operator: true});
12657          this.index += token.length;
12658        } else {
12659          this.throwError('Unexpected next character ', this.index, this.index + 1);
12660        }
12661      }
12662    }
12663    return this.tokens;
12664  },
12665
12666  is: function(ch, chars) {
12667    return chars.indexOf(ch) !== -1;
12668  },
12669
12670  peek: function(i) {
12671    var num = i || 1;
12672    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12673  },
12674
12675  isNumber: function(ch) {
12676    return ('0' <= ch && ch <= '9') && typeof ch === "string";
12677  },
12678
12679  isWhitespace: function(ch) {
12680    // IE treats non-breaking space as \u00A0
12681    return (ch === ' ' || ch === '\r' || ch === '\t' ||
12682            ch === '\n' || ch === '\v' || ch === '\u00A0');
12683  },
12684
12685  isIdent: function(ch) {
12686    return ('a' <= ch && ch <= 'z' ||
12687            'A' <= ch && ch <= 'Z' ||
12688            '_' === ch || ch === '$');
12689  },
12690
12691  isExpOperator: function(ch) {
12692    return (ch === '-' || ch === '+' || this.isNumber(ch));
12693  },
12694
12695  throwError: function(error, start, end) {
12696    end = end || this.index;
12697    var colStr = (isDefined(start)
12698            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
12699            : ' ' + end);
12700    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
12701        error, colStr, this.text);
12702  },
12703
12704  readNumber: function() {
12705    var number = '';
12706    var start = this.index;
12707    while (this.index < this.text.length) {
12708      var ch = lowercase(this.text.charAt(this.index));
12709      if (ch == '.' || this.isNumber(ch)) {
12710        number += ch;
12711      } else {
12712        var peekCh = this.peek();
12713        if (ch == 'e' && this.isExpOperator(peekCh)) {
12714          number += ch;
12715        } else if (this.isExpOperator(ch) &&
12716            peekCh && this.isNumber(peekCh) &&
12717            number.charAt(number.length - 1) == 'e') {
12718          number += ch;
12719        } else if (this.isExpOperator(ch) &&
12720            (!peekCh || !this.isNumber(peekCh)) &&
12721            number.charAt(number.length - 1) == 'e') {
12722          this.throwError('Invalid exponent');
12723        } else {
12724          break;
12725        }
12726      }
12727      this.index++;
12728    }
12729    this.tokens.push({
12730      index: start,
12731      text: number,
12732      constant: true,
12733      value: Number(number)
12734    });
12735  },
12736
12737  readIdent: function() {
12738    var start = this.index;
12739    while (this.index < this.text.length) {
12740      var ch = this.text.charAt(this.index);
12741      if (!(this.isIdent(ch) || this.isNumber(ch))) {
12742        break;
12743      }
12744      this.index++;
12745    }
12746    this.tokens.push({
12747      index: start,
12748      text: this.text.slice(start, this.index),
12749      identifier: true
12750    });
12751  },
12752
12753  readString: function(quote) {
12754    var start = this.index;
12755    this.index++;
12756    var string = '';
12757    var rawString = quote;
12758    var escape = false;
12759    while (this.index < this.text.length) {
12760      var ch = this.text.charAt(this.index);
12761      rawString += ch;
12762      if (escape) {
12763        if (ch === 'u') {
12764          var hex = this.text.substring(this.index + 1, this.index + 5);
12765          if (!hex.match(/[\da-f]{4}/i)) {
12766            this.throwError('Invalid unicode escape [\\u' + hex + ']');
12767          }
12768          this.index += 4;
12769          string += String.fromCharCode(parseInt(hex, 16));
12770        } else {
12771          var rep = ESCAPE[ch];
12772          string = string + (rep || ch);
12773        }
12774        escape = false;
12775      } else if (ch === '\\') {
12776        escape = true;
12777      } else if (ch === quote) {
12778        this.index++;
12779        this.tokens.push({
12780          index: start,
12781          text: rawString,
12782          constant: true,
12783          value: string
12784        });
12785        return;
12786      } else {
12787        string += ch;
12788      }
12789      this.index++;
12790    }
12791    this.throwError('Unterminated quote', start);
12792  }
12793};
12794
12795var AST = function(lexer, options) {
12796  this.lexer = lexer;
12797  this.options = options;
12798};
12799
12800AST.Program = 'Program';
12801AST.ExpressionStatement = 'ExpressionStatement';
12802AST.AssignmentExpression = 'AssignmentExpression';
12803AST.ConditionalExpression = 'ConditionalExpression';
12804AST.LogicalExpression = 'LogicalExpression';
12805AST.BinaryExpression = 'BinaryExpression';
12806AST.UnaryExpression = 'UnaryExpression';
12807AST.CallExpression = 'CallExpression';
12808AST.MemberExpression = 'MemberExpression';
12809AST.Identifier = 'Identifier';
12810AST.Literal = 'Literal';
12811AST.ArrayExpression = 'ArrayExpression';
12812AST.Property = 'Property';
12813AST.ObjectExpression = 'ObjectExpression';
12814AST.ThisExpression = 'ThisExpression';
12815
12816// Internal use only
12817AST.NGValueParameter = 'NGValueParameter';
12818
12819AST.prototype = {
12820  ast: function(text) {
12821    this.text = text;
12822    this.tokens = this.lexer.lex(text);
12823
12824    var value = this.program();
12825
12826    if (this.tokens.length !== 0) {
12827      this.throwError('is an unexpected token', this.tokens[0]);
12828    }
12829
12830    return value;
12831  },
12832
12833  program: function() {
12834    var body = [];
12835    while (true) {
12836      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12837        body.push(this.expressionStatement());
12838      if (!this.expect(';')) {
12839        return { type: AST.Program, body: body};
12840      }
12841    }
12842  },
12843
12844  expressionStatement: function() {
12845    return { type: AST.ExpressionStatement, expression: this.filterChain() };
12846  },
12847
12848  filterChain: function() {
12849    var left = this.expression();
12850    var token;
12851    while ((token = this.expect('|'))) {
12852      left = this.filter(left);
12853    }
12854    return left;
12855  },
12856
12857  expression: function() {
12858    return this.assignment();
12859  },
12860
12861  assignment: function() {
12862    var result = this.ternary();
12863    if (this.expect('=')) {
12864      result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
12865    }
12866    return result;
12867  },
12868
12869  ternary: function() {
12870    var test = this.logicalOR();
12871    var alternate;
12872    var consequent;
12873    if (this.expect('?')) {
12874      alternate = this.expression();
12875      if (this.consume(':')) {
12876        consequent = this.expression();
12877        return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
12878      }
12879    }
12880    return test;
12881  },
12882
12883  logicalOR: function() {
12884    var left = this.logicalAND();
12885    while (this.expect('||')) {
12886      left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
12887    }
12888    return left;
12889  },
12890
12891  logicalAND: function() {
12892    var left = this.equality();
12893    while (this.expect('&&')) {
12894      left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
12895    }
12896    return left;
12897  },
12898
12899  equality: function() {
12900    var left = this.relational();
12901    var token;
12902    while ((token = this.expect('==','!=','===','!=='))) {
12903      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
12904    }
12905    return left;
12906  },
12907
12908  relational: function() {
12909    var left = this.additive();
12910    var token;
12911    while ((token = this.expect('<', '>', '<=', '>='))) {
12912      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
12913    }
12914    return left;
12915  },
12916
12917  additive: function() {
12918    var left = this.multiplicative();
12919    var token;
12920    while ((token = this.expect('+','-'))) {
12921      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
12922    }
12923    return left;
12924  },
12925
12926  multiplicative: function() {
12927    var left = this.unary();
12928    var token;
12929    while ((token = this.expect('*','/','%'))) {
12930      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
12931    }
12932    return left;
12933  },
12934
12935  unary: function() {
12936    var token;
12937    if ((token = this.expect('+', '-', '!'))) {
12938      return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
12939    } else {
12940      return this.primary();
12941    }
12942  },
12943
12944  primary: function() {
12945    var primary;
12946    if (this.expect('(')) {
12947      primary = this.filterChain();
12948      this.consume(')');
12949    } else if (this.expect('[')) {
12950      primary = this.arrayDeclaration();
12951    } else if (this.expect('{')) {
12952      primary = this.object();
12953    } else if (this.constants.hasOwnProperty(this.peek().text)) {
12954      primary = copy(this.constants[this.consume().text]);
12955    } else if (this.peek().identifier) {
12956      primary = this.identifier();
12957    } else if (this.peek().constant) {
12958      primary = this.constant();
12959    } else {
12960      this.throwError('not a primary expression', this.peek());
12961    }
12962
12963    var next;
12964    while ((next = this.expect('(', '[', '.'))) {
12965      if (next.text === '(') {
12966        primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
12967        this.consume(')');
12968      } else if (next.text === '[') {
12969        primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
12970        this.consume(']');
12971      } else if (next.text === '.') {
12972        primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
12973      } else {
12974        this.throwError('IMPOSSIBLE');
12975      }
12976    }
12977    return primary;
12978  },
12979
12980  filter: function(baseExpression) {
12981    var args = [baseExpression];
12982    var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
12983
12984    while (this.expect(':')) {
12985      args.push(this.expression());
12986    }
12987
12988    return result;
12989  },
12990
12991  parseArguments: function() {
12992    var args = [];
12993    if (this.peekToken().text !== ')') {
12994      do {
12995        args.push(this.expression());
12996      } while (this.expect(','));
12997    }
12998    return args;
12999  },
13000
13001  identifier: function() {
13002    var token = this.consume();
13003    if (!token.identifier) {
13004      this.throwError('is not a valid identifier', token);
13005    }
13006    return { type: AST.Identifier, name: token.text };
13007  },
13008
13009  constant: function() {
13010    // TODO check that it is a constant
13011    return { type: AST.Literal, value: this.consume().value };
13012  },
13013
13014  arrayDeclaration: function() {
13015    var elements = [];
13016    if (this.peekToken().text !== ']') {
13017      do {
13018        if (this.peek(']')) {
13019          // Support trailing commas per ES5.1.
13020          break;
13021        }
13022        elements.push(this.expression());
13023      } while (this.expect(','));
13024    }
13025    this.consume(']');
13026
13027    return { type: AST.ArrayExpression, elements: elements };
13028  },
13029
13030  object: function() {
13031    var properties = [], property;
13032    if (this.peekToken().text !== '}') {
13033      do {
13034        if (this.peek('}')) {
13035          // Support trailing commas per ES5.1.
13036          break;
13037        }
13038        property = {type: AST.Property, kind: 'init'};
13039        if (this.peek().constant) {
13040          property.key = this.constant();
13041        } else if (this.peek().identifier) {
13042          property.key = this.identifier();
13043        } else {
13044          this.throwError("invalid key", this.peek());
13045        }
13046        this.consume(':');
13047        property.value = this.expression();
13048        properties.push(property);
13049      } while (this.expect(','));
13050    }
13051    this.consume('}');
13052
13053    return {type: AST.ObjectExpression, properties: properties };
13054  },
13055
13056  throwError: function(msg, token) {
13057    throw $parseMinErr('syntax',
13058        'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13059          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13060  },
13061
13062  consume: function(e1) {
13063    if (this.tokens.length === 0) {
13064      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13065    }
13066
13067    var token = this.expect(e1);
13068    if (!token) {
13069      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13070    }
13071    return token;
13072  },
13073
13074  peekToken: function() {
13075    if (this.tokens.length === 0) {
13076      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13077    }
13078    return this.tokens[0];
13079  },
13080
13081  peek: function(e1, e2, e3, e4) {
13082    return this.peekAhead(0, e1, e2, e3, e4);
13083  },
13084
13085  peekAhead: function(i, e1, e2, e3, e4) {
13086    if (this.tokens.length > i) {
13087      var token = this.tokens[i];
13088      var t = token.text;
13089      if (t === e1 || t === e2 || t === e3 || t === e4 ||
13090          (!e1 && !e2 && !e3 && !e4)) {
13091        return token;
13092      }
13093    }
13094    return false;
13095  },
13096
13097  expect: function(e1, e2, e3, e4) {
13098    var token = this.peek(e1, e2, e3, e4);
13099    if (token) {
13100      this.tokens.shift();
13101      return token;
13102    }
13103    return false;
13104  },
13105
13106
13107  /* `undefined` is not a constant, it is an identifier,
13108   * but using it as an identifier is not supported
13109   */
13110  constants: {
13111    'true': { type: AST.Literal, value: true },
13112    'false': { type: AST.Literal, value: false },
13113    'null': { type: AST.Literal, value: null },
13114    'undefined': {type: AST.Literal, value: undefined },
13115    'this': {type: AST.ThisExpression }
13116  }
13117};
13118
13119function ifDefined(v, d) {
13120  return typeof v !== 'undefined' ? v : d;
13121}
13122
13123function plusFn(l, r) {
13124  if (typeof l === 'undefined') return r;
13125  if (typeof r === 'undefined') return l;
13126  return l + r;
13127}
13128
13129function isStateless($filter, filterName) {
13130  var fn = $filter(filterName);
13131  return !fn.$stateful;
13132}
13133
13134function findConstantAndWatchExpressions(ast, $filter) {
13135  var allConstants;
13136  var argsToWatch;
13137  switch (ast.type) {
13138  case AST.Program:
13139    allConstants = true;
13140    forEach(ast.body, function(expr) {
13141      findConstantAndWatchExpressions(expr.expression, $filter);
13142      allConstants = allConstants && expr.expression.constant;
13143    });
13144    ast.constant = allConstants;
13145    break;
13146  case AST.Literal:
13147    ast.constant = true;
13148    ast.toWatch = [];
13149    break;
13150  case AST.UnaryExpression:
13151    findConstantAndWatchExpressions(ast.argument, $filter);
13152    ast.constant = ast.argument.constant;
13153    ast.toWatch = ast.argument.toWatch;
13154    break;
13155  case AST.BinaryExpression:
13156    findConstantAndWatchExpressions(ast.left, $filter);
13157    findConstantAndWatchExpressions(ast.right, $filter);
13158    ast.constant = ast.left.constant && ast.right.constant;
13159    ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13160    break;
13161  case AST.LogicalExpression:
13162    findConstantAndWatchExpressions(ast.left, $filter);
13163    findConstantAndWatchExpressions(ast.right, $filter);
13164    ast.constant = ast.left.constant && ast.right.constant;
13165    ast.toWatch = ast.constant ? [] : [ast];
13166    break;
13167  case AST.ConditionalExpression:
13168    findConstantAndWatchExpressions(ast.test, $filter);
13169    findConstantAndWatchExpressions(ast.alternate, $filter);
13170    findConstantAndWatchExpressions(ast.consequent, $filter);
13171    ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13172    ast.toWatch = ast.constant ? [] : [ast];
13173    break;
13174  case AST.Identifier:
13175    ast.constant = false;
13176    ast.toWatch = [ast];
13177    break;
13178  case AST.MemberExpression:
13179    findConstantAndWatchExpressions(ast.object, $filter);
13180    if (ast.computed) {
13181      findConstantAndWatchExpressions(ast.property, $filter);
13182    }
13183    ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13184    ast.toWatch = [ast];
13185    break;
13186  case AST.CallExpression:
13187    allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13188    argsToWatch = [];
13189    forEach(ast.arguments, function(expr) {
13190      findConstantAndWatchExpressions(expr, $filter);
13191      allConstants = allConstants && expr.constant;
13192      if (!expr.constant) {
13193        argsToWatch.push.apply(argsToWatch, expr.toWatch);
13194      }
13195    });
13196    ast.constant = allConstants;
13197    ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13198    break;
13199  case AST.AssignmentExpression:
13200    findConstantAndWatchExpressions(ast.left, $filter);
13201    findConstantAndWatchExpressions(ast.right, $filter);
13202    ast.constant = ast.left.constant && ast.right.constant;
13203    ast.toWatch = [ast];
13204    break;
13205  case AST.ArrayExpression:
13206    allConstants = true;
13207    argsToWatch = [];
13208    forEach(ast.elements, function(expr) {
13209      findConstantAndWatchExpressions(expr, $filter);
13210      allConstants = allConstants && expr.constant;
13211      if (!expr.constant) {
13212        argsToWatch.push.apply(argsToWatch, expr.toWatch);
13213      }
13214    });
13215    ast.constant = allConstants;
13216    ast.toWatch = argsToWatch;
13217    break;
13218  case AST.ObjectExpression:
13219    allConstants = true;
13220    argsToWatch = [];
13221    forEach(ast.properties, function(property) {
13222      findConstantAndWatchExpressions(property.value, $filter);
13223      allConstants = allConstants && property.value.constant;
13224      if (!property.value.constant) {
13225        argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13226      }
13227    });
13228    ast.constant = allConstants;
13229    ast.toWatch = argsToWatch;
13230    break;
13231  case AST.ThisExpression:
13232    ast.constant = false;
13233    ast.toWatch = [];
13234    break;
13235  }
13236}
13237
13238function getInputs(body) {
13239  if (body.length != 1) return;
13240  var lastExpression = body[0].expression;
13241  var candidate = lastExpression.toWatch;
13242  if (candidate.length !== 1) return candidate;
13243  return candidate[0] !== lastExpression ? candidate : undefined;
13244}
13245
13246function isAssignable(ast) {
13247  return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13248}
13249
13250function assignableAST(ast) {
13251  if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13252    return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13253  }
13254}
13255
13256function isLiteral(ast) {
13257  return ast.body.length === 0 ||
13258      ast.body.length === 1 && (
13259      ast.body[0].expression.type === AST.Literal ||
13260      ast.body[0].expression.type === AST.ArrayExpression ||
13261      ast.body[0].expression.type === AST.ObjectExpression);
13262}
13263
13264function isConstant(ast) {
13265  return ast.constant;
13266}
13267
13268function ASTCompiler(astBuilder, $filter) {
13269  this.astBuilder = astBuilder;
13270  this.$filter = $filter;
13271}
13272
13273ASTCompiler.prototype = {
13274  compile: function(expression, expensiveChecks) {
13275    var self = this;
13276    var ast = this.astBuilder.ast(expression);
13277    this.state = {
13278      nextId: 0,
13279      filters: {},
13280      expensiveChecks: expensiveChecks,
13281      fn: {vars: [], body: [], own: {}},
13282      assign: {vars: [], body: [], own: {}},
13283      inputs: []
13284    };
13285    findConstantAndWatchExpressions(ast, self.$filter);
13286    var extra = '';
13287    var assignable;
13288    this.stage = 'assign';
13289    if ((assignable = assignableAST(ast))) {
13290      this.state.computing = 'assign';
13291      var result = this.nextId();
13292      this.recurse(assignable, result);
13293      this.return_(result);
13294      extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13295    }
13296    var toWatch = getInputs(ast.body);
13297    self.stage = 'inputs';
13298    forEach(toWatch, function(watch, key) {
13299      var fnKey = 'fn' + key;
13300      self.state[fnKey] = {vars: [], body: [], own: {}};
13301      self.state.computing = fnKey;
13302      var intoId = self.nextId();
13303      self.recurse(watch, intoId);
13304      self.return_(intoId);
13305      self.state.inputs.push(fnKey);
13306      watch.watchId = key;
13307    });
13308    this.state.computing = 'fn';
13309    this.stage = 'main';
13310    this.recurse(ast);
13311    var fnString =
13312      // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13313      // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13314      '"' + this.USE + ' ' + this.STRICT + '";\n' +
13315      this.filterPrefix() +
13316      'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13317      extra +
13318      this.watchFns() +
13319      'return fn;';
13320
13321    /* jshint -W054 */
13322    var fn = (new Function('$filter',
13323        'ensureSafeMemberName',
13324        'ensureSafeObject',
13325        'ensureSafeFunction',
13326        'getStringValue',
13327        'ensureSafeAssignContext',
13328        'ifDefined',
13329        'plus',
13330        'text',
13331        fnString))(
13332          this.$filter,
13333          ensureSafeMemberName,
13334          ensureSafeObject,
13335          ensureSafeFunction,
13336          getStringValue,
13337          ensureSafeAssignContext,
13338          ifDefined,
13339          plusFn,
13340          expression);
13341    /* jshint +W054 */
13342    this.state = this.stage = undefined;
13343    fn.literal = isLiteral(ast);
13344    fn.constant = isConstant(ast);
13345    return fn;
13346  },
13347
13348  USE: 'use',
13349
13350  STRICT: 'strict',
13351
13352  watchFns: function() {
13353    var result = [];
13354    var fns = this.state.inputs;
13355    var self = this;
13356    forEach(fns, function(name) {
13357      result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13358    });
13359    if (fns.length) {
13360      result.push('fn.inputs=[' + fns.join(',') + '];');
13361    }
13362    return result.join('');
13363  },
13364
13365  generateFunction: function(name, params) {
13366    return 'function(' + params + '){' +
13367        this.varsPrefix(name) +
13368        this.body(name) +
13369        '};';
13370  },
13371
13372  filterPrefix: function() {
13373    var parts = [];
13374    var self = this;
13375    forEach(this.state.filters, function(id, filter) {
13376      parts.push(id + '=$filter(' + self.escape(filter) + ')');
13377    });
13378    if (parts.length) return 'var ' + parts.join(',') + ';';
13379    return '';
13380  },
13381
13382  varsPrefix: function(section) {
13383    return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13384  },
13385
13386  body: function(section) {
13387    return this.state[section].body.join('');
13388  },
13389
13390  recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13391    var left, right, self = this, args, expression;
13392    recursionFn = recursionFn || noop;
13393    if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13394      intoId = intoId || this.nextId();
13395      this.if_('i',
13396        this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13397        this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13398      );
13399      return;
13400    }
13401    switch (ast.type) {
13402    case AST.Program:
13403      forEach(ast.body, function(expression, pos) {
13404        self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13405        if (pos !== ast.body.length - 1) {
13406          self.current().body.push(right, ';');
13407        } else {
13408          self.return_(right);
13409        }
13410      });
13411      break;
13412    case AST.Literal:
13413      expression = this.escape(ast.value);
13414      this.assign(intoId, expression);
13415      recursionFn(expression);
13416      break;
13417    case AST.UnaryExpression:
13418      this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13419      expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13420      this.assign(intoId, expression);
13421      recursionFn(expression);
13422      break;
13423    case AST.BinaryExpression:
13424      this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13425      this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13426      if (ast.operator === '+') {
13427        expression = this.plus(left, right);
13428      } else if (ast.operator === '-') {
13429        expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13430      } else {
13431        expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13432      }
13433      this.assign(intoId, expression);
13434      recursionFn(expression);
13435      break;
13436    case AST.LogicalExpression:
13437      intoId = intoId || this.nextId();
13438      self.recurse(ast.left, intoId);
13439      self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13440      recursionFn(intoId);
13441      break;
13442    case AST.ConditionalExpression:
13443      intoId = intoId || this.nextId();
13444      self.recurse(ast.test, intoId);
13445      self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13446      recursionFn(intoId);
13447      break;
13448    case AST.Identifier:
13449      intoId = intoId || this.nextId();
13450      if (nameId) {
13451        nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13452        nameId.computed = false;
13453        nameId.name = ast.name;
13454      }
13455      ensureSafeMemberName(ast.name);
13456      self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13457        function() {
13458          self.if_(self.stage === 'inputs' || 's', function() {
13459            if (create && create !== 1) {
13460              self.if_(
13461                self.not(self.nonComputedMember('s', ast.name)),
13462                self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13463            }
13464            self.assign(intoId, self.nonComputedMember('s', ast.name));
13465          });
13466        }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13467        );
13468      if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13469        self.addEnsureSafeObject(intoId);
13470      }
13471      recursionFn(intoId);
13472      break;
13473    case AST.MemberExpression:
13474      left = nameId && (nameId.context = this.nextId()) || this.nextId();
13475      intoId = intoId || this.nextId();
13476      self.recurse(ast.object, left, undefined, function() {
13477        self.if_(self.notNull(left), function() {
13478          if (ast.computed) {
13479            right = self.nextId();
13480            self.recurse(ast.property, right);
13481            self.getStringValue(right);
13482            self.addEnsureSafeMemberName(right);
13483            if (create && create !== 1) {
13484              self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13485            }
13486            expression = self.ensureSafeObject(self.computedMember(left, right));
13487            self.assign(intoId, expression);
13488            if (nameId) {
13489              nameId.computed = true;
13490              nameId.name = right;
13491            }
13492          } else {
13493            ensureSafeMemberName(ast.property.name);
13494            if (create && create !== 1) {
13495              self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13496            }
13497            expression = self.nonComputedMember(left, ast.property.name);
13498            if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13499              expression = self.ensureSafeObject(expression);
13500            }
13501            self.assign(intoId, expression);
13502            if (nameId) {
13503              nameId.computed = false;
13504              nameId.name = ast.property.name;
13505            }
13506          }
13507        }, function() {
13508          self.assign(intoId, 'undefined');
13509        });
13510        recursionFn(intoId);
13511      }, !!create);
13512      break;
13513    case AST.CallExpression:
13514      intoId = intoId || this.nextId();
13515      if (ast.filter) {
13516        right = self.filter(ast.callee.name);
13517        args = [];
13518        forEach(ast.arguments, function(expr) {
13519          var argument = self.nextId();
13520          self.recurse(expr, argument);
13521          args.push(argument);
13522        });
13523        expression = right + '(' + args.join(',') + ')';
13524        self.assign(intoId, expression);
13525        recursionFn(intoId);
13526      } else {
13527        right = self.nextId();
13528        left = {};
13529        args = [];
13530        self.recurse(ast.callee, right, left, function() {
13531          self.if_(self.notNull(right), function() {
13532            self.addEnsureSafeFunction(right);
13533            forEach(ast.arguments, function(expr) {
13534              self.recurse(expr, self.nextId(), undefined, function(argument) {
13535                args.push(self.ensureSafeObject(argument));
13536              });
13537            });
13538            if (left.name) {
13539              if (!self.state.expensiveChecks) {
13540                self.addEnsureSafeObject(left.context);
13541              }
13542              expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13543            } else {
13544              expression = right + '(' + args.join(',') + ')';
13545            }
13546            expression = self.ensureSafeObject(expression);
13547            self.assign(intoId, expression);
13548          }, function() {
13549            self.assign(intoId, 'undefined');
13550          });
13551          recursionFn(intoId);
13552        });
13553      }
13554      break;
13555    case AST.AssignmentExpression:
13556      right = this.nextId();
13557      left = {};
13558      if (!isAssignable(ast.left)) {
13559        throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
13560      }
13561      this.recurse(ast.left, undefined, left, function() {
13562        self.if_(self.notNull(left.context), function() {
13563          self.recurse(ast.right, right);
13564          self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13565          self.addEnsureSafeAssignContext(left.context);
13566          expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13567          self.assign(intoId, expression);
13568          recursionFn(intoId || expression);
13569        });
13570      }, 1);
13571      break;
13572    case AST.ArrayExpression:
13573      args = [];
13574      forEach(ast.elements, function(expr) {
13575        self.recurse(expr, self.nextId(), undefined, function(argument) {
13576          args.push(argument);
13577        });
13578      });
13579      expression = '[' + args.join(',') + ']';
13580      this.assign(intoId, expression);
13581      recursionFn(expression);
13582      break;
13583    case AST.ObjectExpression:
13584      args = [];
13585      forEach(ast.properties, function(property) {
13586        self.recurse(property.value, self.nextId(), undefined, function(expr) {
13587          args.push(self.escape(
13588              property.key.type === AST.Identifier ? property.key.name :
13589                ('' + property.key.value)) +
13590              ':' + expr);
13591        });
13592      });
13593      expression = '{' + args.join(',') + '}';
13594      this.assign(intoId, expression);
13595      recursionFn(expression);
13596      break;
13597    case AST.ThisExpression:
13598      this.assign(intoId, 's');
13599      recursionFn('s');
13600      break;
13601    case AST.NGValueParameter:
13602      this.assign(intoId, 'v');
13603      recursionFn('v');
13604      break;
13605    }
13606  },
13607
13608  getHasOwnProperty: function(element, property) {
13609    var key = element + '.' + property;
13610    var own = this.current().own;
13611    if (!own.hasOwnProperty(key)) {
13612      own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13613    }
13614    return own[key];
13615  },
13616
13617  assign: function(id, value) {
13618    if (!id) return;
13619    this.current().body.push(id, '=', value, ';');
13620    return id;
13621  },
13622
13623  filter: function(filterName) {
13624    if (!this.state.filters.hasOwnProperty(filterName)) {
13625      this.state.filters[filterName] = this.nextId(true);
13626    }
13627    return this.state.filters[filterName];
13628  },
13629
13630  ifDefined: function(id, defaultValue) {
13631    return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13632  },
13633
13634  plus: function(left, right) {
13635    return 'plus(' + left + ',' + right + ')';
13636  },
13637
13638  return_: function(id) {
13639    this.current().body.push('return ', id, ';');
13640  },
13641
13642  if_: function(test, alternate, consequent) {
13643    if (test === true) {
13644      alternate();
13645    } else {
13646      var body = this.current().body;
13647      body.push('if(', test, '){');
13648      alternate();
13649      body.push('}');
13650      if (consequent) {
13651        body.push('else{');
13652        consequent();
13653        body.push('}');
13654      }
13655    }
13656  },
13657
13658  not: function(expression) {
13659    return '!(' + expression + ')';
13660  },
13661
13662  notNull: function(expression) {
13663    return expression + '!=null';
13664  },
13665
13666  nonComputedMember: function(left, right) {
13667    return left + '.' + right;
13668  },
13669
13670  computedMember: function(left, right) {
13671    return left + '[' + right + ']';
13672  },
13673
13674  member: function(left, right, computed) {
13675    if (computed) return this.computedMember(left, right);
13676    return this.nonComputedMember(left, right);
13677  },
13678
13679  addEnsureSafeObject: function(item) {
13680    this.current().body.push(this.ensureSafeObject(item), ';');
13681  },
13682
13683  addEnsureSafeMemberName: function(item) {
13684    this.current().body.push(this.ensureSafeMemberName(item), ';');
13685  },
13686
13687  addEnsureSafeFunction: function(item) {
13688    this.current().body.push(this.ensureSafeFunction(item), ';');
13689  },
13690
13691  addEnsureSafeAssignContext: function(item) {
13692    this.current().body.push(this.ensureSafeAssignContext(item), ';');
13693  },
13694
13695  ensureSafeObject: function(item) {
13696    return 'ensureSafeObject(' + item + ',text)';
13697  },
13698
13699  ensureSafeMemberName: function(item) {
13700    return 'ensureSafeMemberName(' + item + ',text)';
13701  },
13702
13703  ensureSafeFunction: function(item) {
13704    return 'ensureSafeFunction(' + item + ',text)';
13705  },
13706
13707  getStringValue: function(item) {
13708    this.assign(item, 'getStringValue(' + item + ',text)');
13709  },
13710
13711  ensureSafeAssignContext: function(item) {
13712    return 'ensureSafeAssignContext(' + item + ',text)';
13713  },
13714
13715  lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13716    var self = this;
13717    return function() {
13718      self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
13719    };
13720  },
13721
13722  lazyAssign: function(id, value) {
13723    var self = this;
13724    return function() {
13725      self.assign(id, value);
13726    };
13727  },
13728
13729  stringEscapeRegex: /[^ a-zA-Z0-9]/g,
13730
13731  stringEscapeFn: function(c) {
13732    return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
13733  },
13734
13735  escape: function(value) {
13736    if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
13737    if (isNumber(value)) return value.toString();
13738    if (value === true) return 'true';
13739    if (value === false) return 'false';
13740    if (value === null) return 'null';
13741    if (typeof value === 'undefined') return 'undefined';
13742
13743    throw $parseMinErr('esc', 'IMPOSSIBLE');
13744  },
13745
13746  nextId: function(skip, init) {
13747    var id = 'v' + (this.state.nextId++);
13748    if (!skip) {
13749      this.current().vars.push(id + (init ? '=' + init : ''));
13750    }
13751    return id;
13752  },
13753
13754  current: function() {
13755    return this.state[this.state.computing];
13756  }
13757};
13758
13759
13760function ASTInterpreter(astBuilder, $filter) {
13761  this.astBuilder = astBuilder;
13762  this.$filter = $filter;
13763}
13764
13765ASTInterpreter.prototype = {
13766  compile: function(expression, expensiveChecks) {
13767    var self = this;
13768    var ast = this.astBuilder.ast(expression);
13769    this.expression = expression;
13770    this.expensiveChecks = expensiveChecks;
13771    findConstantAndWatchExpressions(ast, self.$filter);
13772    var assignable;
13773    var assign;
13774    if ((assignable = assignableAST(ast))) {
13775      assign = this.recurse(assignable);
13776    }
13777    var toWatch = getInputs(ast.body);
13778    var inputs;
13779    if (toWatch) {
13780      inputs = [];
13781      forEach(toWatch, function(watch, key) {
13782        var input = self.recurse(watch);
13783        watch.input = input;
13784        inputs.push(input);
13785        watch.watchId = key;
13786      });
13787    }
13788    var expressions = [];
13789    forEach(ast.body, function(expression) {
13790      expressions.push(self.recurse(expression.expression));
13791    });
13792    var fn = ast.body.length === 0 ? function() {} :
13793             ast.body.length === 1 ? expressions[0] :
13794             function(scope, locals) {
13795               var lastValue;
13796               forEach(expressions, function(exp) {
13797                 lastValue = exp(scope, locals);
13798               });
13799               return lastValue;
13800             };
13801    if (assign) {
13802      fn.assign = function(scope, value, locals) {
13803        return assign(scope, locals, value);
13804      };
13805    }
13806    if (inputs) {
13807      fn.inputs = inputs;
13808    }
13809    fn.literal = isLiteral(ast);
13810    fn.constant = isConstant(ast);
13811    return fn;
13812  },
13813
13814  recurse: function(ast, context, create) {
13815    var left, right, self = this, args, expression;
13816    if (ast.input) {
13817      return this.inputs(ast.input, ast.watchId);
13818    }
13819    switch (ast.type) {
13820    case AST.Literal:
13821      return this.value(ast.value, context);
13822    case AST.UnaryExpression:
13823      right = this.recurse(ast.argument);
13824      return this['unary' + ast.operator](right, context);
13825    case AST.BinaryExpression:
13826      left = this.recurse(ast.left);
13827      right = this.recurse(ast.right);
13828      return this['binary' + ast.operator](left, right, context);
13829    case AST.LogicalExpression:
13830      left = this.recurse(ast.left);
13831      right = this.recurse(ast.right);
13832      return this['binary' + ast.operator](left, right, context);
13833    case AST.ConditionalExpression:
13834      return this['ternary?:'](
13835        this.recurse(ast.test),
13836        this.recurse(ast.alternate),
13837        this.recurse(ast.consequent),
13838        context
13839      );
13840    case AST.Identifier:
13841      ensureSafeMemberName(ast.name, self.expression);
13842      return self.identifier(ast.name,
13843                             self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
13844                             context, create, self.expression);
13845    case AST.MemberExpression:
13846      left = this.recurse(ast.object, false, !!create);
13847      if (!ast.computed) {
13848        ensureSafeMemberName(ast.property.name, self.expression);
13849        right = ast.property.name;
13850      }
13851      if (ast.computed) right = this.recurse(ast.property);
13852      return ast.computed ?
13853        this.computedMember(left, right, context, create, self.expression) :
13854        this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
13855    case AST.CallExpression:
13856      args = [];
13857      forEach(ast.arguments, function(expr) {
13858        args.push(self.recurse(expr));
13859      });
13860      if (ast.filter) right = this.$filter(ast.callee.name);
13861      if (!ast.filter) right = this.recurse(ast.callee, true);
13862      return ast.filter ?
13863        function(scope, locals, assign, inputs) {
13864          var values = [];
13865          for (var i = 0; i < args.length; ++i) {
13866            values.push(args[i](scope, locals, assign, inputs));
13867          }
13868          var value = right.apply(undefined, values, inputs);
13869          return context ? {context: undefined, name: undefined, value: value} : value;
13870        } :
13871        function(scope, locals, assign, inputs) {
13872          var rhs = right(scope, locals, assign, inputs);
13873          var value;
13874          if (rhs.value != null) {
13875            ensureSafeObject(rhs.context, self.expression);
13876            ensureSafeFunction(rhs.value, self.expression);
13877            var values = [];
13878            for (var i = 0; i < args.length; ++i) {
13879              values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
13880            }
13881            value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
13882          }
13883          return context ? {value: value} : value;
13884        };
13885    case AST.AssignmentExpression:
13886      left = this.recurse(ast.left, true, 1);
13887      right = this.recurse(ast.right);
13888      return function(scope, locals, assign, inputs) {
13889        var lhs = left(scope, locals, assign, inputs);
13890        var rhs = right(scope, locals, assign, inputs);
13891        ensureSafeObject(lhs.value, self.expression);
13892        ensureSafeAssignContext(lhs.context);
13893        lhs.context[lhs.name] = rhs;
13894        return context ? {value: rhs} : rhs;
13895      };
13896    case AST.ArrayExpression:
13897      args = [];
13898      forEach(ast.elements, function(expr) {
13899        args.push(self.recurse(expr));
13900      });
13901      return function(scope, locals, assign, inputs) {
13902        var value = [];
13903        for (var i = 0; i < args.length; ++i) {
13904          value.push(args[i](scope, locals, assign, inputs));
13905        }
13906        return context ? {value: value} : value;
13907      };
13908    case AST.ObjectExpression:
13909      args = [];
13910      forEach(ast.properties, function(property) {
13911        args.push({key: property.key.type === AST.Identifier ?
13912                        property.key.name :
13913                        ('' + property.key.value),
13914                   value: self.recurse(property.value)
13915        });
13916      });
13917      return function(scope, locals, assign, inputs) {
13918        var value = {};
13919        for (var i = 0; i < args.length; ++i) {
13920          value[args[i].key] = args[i].value(scope, locals, assign, inputs);
13921        }
13922        return context ? {value: value} : value;
13923      };
13924    case AST.ThisExpression:
13925      return function(scope) {
13926        return context ? {value: scope} : scope;
13927      };
13928    case AST.NGValueParameter:
13929      return function(scope, locals, assign, inputs) {
13930        return context ? {value: assign} : assign;
13931      };
13932    }
13933  },
13934
13935  'unary+': function(argument, context) {
13936    return function(scope, locals, assign, inputs) {
13937      var arg = argument(scope, locals, assign, inputs);
13938      if (isDefined(arg)) {
13939        arg = +arg;
13940      } else {
13941        arg = 0;
13942      }
13943      return context ? {value: arg} : arg;
13944    };
13945  },
13946  'unary-': function(argument, context) {
13947    return function(scope, locals, assign, inputs) {
13948      var arg = argument(scope, locals, assign, inputs);
13949      if (isDefined(arg)) {
13950        arg = -arg;
13951      } else {
13952        arg = 0;
13953      }
13954      return context ? {value: arg} : arg;
13955    };
13956  },
13957  'unary!': function(argument, context) {
13958    return function(scope, locals, assign, inputs) {
13959      var arg = !argument(scope, locals, assign, inputs);
13960      return context ? {value: arg} : arg;
13961    };
13962  },
13963  'binary+': function(left, right, context) {
13964    return function(scope, locals, assign, inputs) {
13965      var lhs = left(scope, locals, assign, inputs);
13966      var rhs = right(scope, locals, assign, inputs);
13967      var arg = plusFn(lhs, rhs);
13968      return context ? {value: arg} : arg;
13969    };
13970  },
13971  'binary-': function(left, right, context) {
13972    return function(scope, locals, assign, inputs) {
13973      var lhs = left(scope, locals, assign, inputs);
13974      var rhs = right(scope, locals, assign, inputs);
13975      var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
13976      return context ? {value: arg} : arg;
13977    };
13978  },
13979  'binary*': function(left, right, context) {
13980    return function(scope, locals, assign, inputs) {
13981      var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
13982      return context ? {value: arg} : arg;
13983    };
13984  },
13985  'binary/': function(left, right, context) {
13986    return function(scope, locals, assign, inputs) {
13987      var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
13988      return context ? {value: arg} : arg;
13989    };
13990  },
13991  'binary%': function(left, right, context) {
13992    return function(scope, locals, assign, inputs) {
13993      var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
13994      return context ? {value: arg} : arg;
13995    };
13996  },
13997  'binary===': function(left, right, context) {
13998    return function(scope, locals, assign, inputs) {
13999      var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14000      return context ? {value: arg} : arg;
14001    };
14002  },
14003  'binary!==': function(left, right, context) {
14004    return function(scope, locals, assign, inputs) {
14005      var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14006      return context ? {value: arg} : arg;
14007    };
14008  },
14009  'binary==': function(left, right, context) {
14010    return function(scope, locals, assign, inputs) {
14011      var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14012      return context ? {value: arg} : arg;
14013    };
14014  },
14015  'binary!=': function(left, right, context) {
14016    return function(scope, locals, assign, inputs) {
14017      var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14018      return context ? {value: arg} : arg;
14019    };
14020  },
14021  'binary<': function(left, right, context) {
14022    return function(scope, locals, assign, inputs) {
14023      var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14024      return context ? {value: arg} : arg;
14025    };
14026  },
14027  'binary>': function(left, right, context) {
14028    return function(scope, locals, assign, inputs) {
14029      var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14030      return context ? {value: arg} : arg;
14031    };
14032  },
14033  'binary<=': function(left, right, context) {
14034    return function(scope, locals, assign, inputs) {
14035      var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14036      return context ? {value: arg} : arg;
14037    };
14038  },
14039  'binary>=': function(left, right, context) {
14040    return function(scope, locals, assign, inputs) {
14041      var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14042      return context ? {value: arg} : arg;
14043    };
14044  },
14045  'binary&&': function(left, right, context) {
14046    return function(scope, locals, assign, inputs) {
14047      var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14048      return context ? {value: arg} : arg;
14049    };
14050  },
14051  'binary||': function(left, right, context) {
14052    return function(scope, locals, assign, inputs) {
14053      var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14054      return context ? {value: arg} : arg;
14055    };
14056  },
14057  'ternary?:': function(test, alternate, consequent, context) {
14058    return function(scope, locals, assign, inputs) {
14059      var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14060      return context ? {value: arg} : arg;
14061    };
14062  },
14063  value: function(value, context) {
14064    return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14065  },
14066  identifier: function(name, expensiveChecks, context, create, expression) {
14067    return function(scope, locals, assign, inputs) {
14068      var base = locals && (name in locals) ? locals : scope;
14069      if (create && create !== 1 && base && !(base[name])) {
14070        base[name] = {};
14071      }
14072      var value = base ? base[name] : undefined;
14073      if (expensiveChecks) {
14074        ensureSafeObject(value, expression);
14075      }
14076      if (context) {
14077        return {context: base, name: name, value: value};
14078      } else {
14079        return value;
14080      }
14081    };
14082  },
14083  computedMember: function(left, right, context, create, expression) {
14084    return function(scope, locals, assign, inputs) {
14085      var lhs = left(scope, locals, assign, inputs);
14086      var rhs;
14087      var value;
14088      if (lhs != null) {
14089        rhs = right(scope, locals, assign, inputs);
14090        rhs = getStringValue(rhs);
14091        ensureSafeMemberName(rhs, expression);
14092        if (create && create !== 1 && lhs && !(lhs[rhs])) {
14093          lhs[rhs] = {};
14094        }
14095        value = lhs[rhs];
14096        ensureSafeObject(value, expression);
14097      }
14098      if (context) {
14099        return {context: lhs, name: rhs, value: value};
14100      } else {
14101        return value;
14102      }
14103    };
14104  },
14105  nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14106    return function(scope, locals, assign, inputs) {
14107      var lhs = left(scope, locals, assign, inputs);
14108      if (create && create !== 1 && lhs && !(lhs[right])) {
14109        lhs[right] = {};
14110      }
14111      var value = lhs != null ? lhs[right] : undefined;
14112      if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14113        ensureSafeObject(value, expression);
14114      }
14115      if (context) {
14116        return {context: lhs, name: right, value: value};
14117      } else {
14118        return value;
14119      }
14120    };
14121  },
14122  inputs: function(input, watchId) {
14123    return function(scope, value, locals, inputs) {
14124      if (inputs) return inputs[watchId];
14125      return input(scope, value, locals);
14126    };
14127  }
14128};
14129
14130/**
14131 * @constructor
14132 */
14133var Parser = function(lexer, $filter, options) {
14134  this.lexer = lexer;
14135  this.$filter = $filter;
14136  this.options = options;
14137  this.ast = new AST(this.lexer);
14138  this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14139                                   new ASTCompiler(this.ast, $filter);
14140};
14141
14142Parser.prototype = {
14143  constructor: Parser,
14144
14145  parse: function(text) {
14146    return this.astCompiler.compile(text, this.options.expensiveChecks);
14147  }
14148};
14149
14150var getterFnCacheDefault = createMap();
14151var getterFnCacheExpensive = createMap();
14152
14153function isPossiblyDangerousMemberName(name) {
14154  return name == 'constructor';
14155}
14156
14157var objectValueOf = Object.prototype.valueOf;
14158
14159function getValueOf(value) {
14160  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14161}
14162
14163///////////////////////////////////
14164
14165/**
14166 * @ngdoc service
14167 * @name $parse
14168 * @kind function
14169 *
14170 * @description
14171 *
14172 * Converts Angular {@link guide/expression expression} into a function.
14173 *
14174 * ```js
14175 *   var getter = $parse('user.name');
14176 *   var setter = getter.assign;
14177 *   var context = {user:{name:'angular'}};
14178 *   var locals = {user:{name:'local'}};
14179 *
14180 *   expect(getter(context)).toEqual('angular');
14181 *   setter(context, 'newValue');
14182 *   expect(context.user.name).toEqual('newValue');
14183 *   expect(getter(context, locals)).toEqual('local');
14184 * ```
14185 *
14186 *
14187 * @param {string} expression String expression to compile.
14188 * @returns {function(context, locals)} a function which represents the compiled expression:
14189 *
14190 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
14191 *      are evaluated against (typically a scope object).
14192 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
14193 *      `context`.
14194 *
14195 *    The returned function also has the following properties:
14196 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14197 *        literal.
14198 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14199 *        constant literals.
14200 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14201 *        set to a function to change its value on the given context.
14202 *
14203 */
14204
14205
14206/**
14207 * @ngdoc provider
14208 * @name $parseProvider
14209 *
14210 * @description
14211 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14212 *  service.
14213 */
14214function $ParseProvider() {
14215  var cacheDefault = createMap();
14216  var cacheExpensive = createMap();
14217
14218  this.$get = ['$filter', function($filter) {
14219    var noUnsafeEval = csp().noUnsafeEval;
14220    var $parseOptions = {
14221          csp: noUnsafeEval,
14222          expensiveChecks: false
14223        },
14224        $parseOptionsExpensive = {
14225          csp: noUnsafeEval,
14226          expensiveChecks: true
14227        };
14228
14229    return function $parse(exp, interceptorFn, expensiveChecks) {
14230      var parsedExpression, oneTime, cacheKey;
14231
14232      switch (typeof exp) {
14233        case 'string':
14234          exp = exp.trim();
14235          cacheKey = exp;
14236
14237          var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14238          parsedExpression = cache[cacheKey];
14239
14240          if (!parsedExpression) {
14241            if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14242              oneTime = true;
14243              exp = exp.substring(2);
14244            }
14245            var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14246            var lexer = new Lexer(parseOptions);
14247            var parser = new Parser(lexer, $filter, parseOptions);
14248            parsedExpression = parser.parse(exp);
14249            if (parsedExpression.constant) {
14250              parsedExpression.$$watchDelegate = constantWatchDelegate;
14251            } else if (oneTime) {
14252              parsedExpression.$$watchDelegate = parsedExpression.literal ?
14253                  oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14254            } else if (parsedExpression.inputs) {
14255              parsedExpression.$$watchDelegate = inputsWatchDelegate;
14256            }
14257            cache[cacheKey] = parsedExpression;
14258          }
14259          return addInterceptor(parsedExpression, interceptorFn);
14260
14261        case 'function':
14262          return addInterceptor(exp, interceptorFn);
14263
14264        default:
14265          return noop;
14266      }
14267    };
14268
14269    function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14270
14271      if (newValue == null || oldValueOfValue == null) { // null/undefined
14272        return newValue === oldValueOfValue;
14273      }
14274
14275      if (typeof newValue === 'object') {
14276
14277        // attempt to convert the value to a primitive type
14278        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14279        //             be cheaply dirty-checked
14280        newValue = getValueOf(newValue);
14281
14282        if (typeof newValue === 'object') {
14283          // objects/arrays are not supported - deep-watching them would be too expensive
14284          return false;
14285        }
14286
14287        // fall-through to the primitive equality check
14288      }
14289
14290      //Primitive or NaN
14291      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14292    }
14293
14294    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14295      var inputExpressions = parsedExpression.inputs;
14296      var lastResult;
14297
14298      if (inputExpressions.length === 1) {
14299        var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14300        inputExpressions = inputExpressions[0];
14301        return scope.$watch(function expressionInputWatch(scope) {
14302          var newInputValue = inputExpressions(scope);
14303          if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14304            lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14305            oldInputValueOf = newInputValue && getValueOf(newInputValue);
14306          }
14307          return lastResult;
14308        }, listener, objectEquality, prettyPrintExpression);
14309      }
14310
14311      var oldInputValueOfValues = [];
14312      var oldInputValues = [];
14313      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14314        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14315        oldInputValues[i] = null;
14316      }
14317
14318      return scope.$watch(function expressionInputsWatch(scope) {
14319        var changed = false;
14320
14321        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14322          var newInputValue = inputExpressions[i](scope);
14323          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14324            oldInputValues[i] = newInputValue;
14325            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14326          }
14327        }
14328
14329        if (changed) {
14330          lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14331        }
14332
14333        return lastResult;
14334      }, listener, objectEquality, prettyPrintExpression);
14335    }
14336
14337    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14338      var unwatch, lastValue;
14339      return unwatch = scope.$watch(function oneTimeWatch(scope) {
14340        return parsedExpression(scope);
14341      }, function oneTimeListener(value, old, scope) {
14342        lastValue = value;
14343        if (isFunction(listener)) {
14344          listener.apply(this, arguments);
14345        }
14346        if (isDefined(value)) {
14347          scope.$$postDigest(function() {
14348            if (isDefined(lastValue)) {
14349              unwatch();
14350            }
14351          });
14352        }
14353      }, objectEquality);
14354    }
14355
14356    function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14357      var unwatch, lastValue;
14358      return unwatch = scope.$watch(function oneTimeWatch(scope) {
14359        return parsedExpression(scope);
14360      }, function oneTimeListener(value, old, scope) {
14361        lastValue = value;
14362        if (isFunction(listener)) {
14363          listener.call(this, value, old, scope);
14364        }
14365        if (isAllDefined(value)) {
14366          scope.$$postDigest(function() {
14367            if (isAllDefined(lastValue)) unwatch();
14368          });
14369        }
14370      }, objectEquality);
14371
14372      function isAllDefined(value) {
14373        var allDefined = true;
14374        forEach(value, function(val) {
14375          if (!isDefined(val)) allDefined = false;
14376        });
14377        return allDefined;
14378      }
14379    }
14380
14381    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14382      var unwatch;
14383      return unwatch = scope.$watch(function constantWatch(scope) {
14384        return parsedExpression(scope);
14385      }, function constantListener(value, old, scope) {
14386        if (isFunction(listener)) {
14387          listener.apply(this, arguments);
14388        }
14389        unwatch();
14390      }, objectEquality);
14391    }
14392
14393    function addInterceptor(parsedExpression, interceptorFn) {
14394      if (!interceptorFn) return parsedExpression;
14395      var watchDelegate = parsedExpression.$$watchDelegate;
14396
14397      var regularWatch =
14398          watchDelegate !== oneTimeLiteralWatchDelegate &&
14399          watchDelegate !== oneTimeWatchDelegate;
14400
14401      var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14402        var value = parsedExpression(scope, locals, assign, inputs);
14403        return interceptorFn(value, scope, locals);
14404      } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14405        var value = parsedExpression(scope, locals, assign, inputs);
14406        var result = interceptorFn(value, scope, locals);
14407        // we only return the interceptor's result if the
14408        // initial value is defined (for bind-once)
14409        return isDefined(value) ? result : value;
14410      };
14411
14412      // Propagate $$watchDelegates other then inputsWatchDelegate
14413      if (parsedExpression.$$watchDelegate &&
14414          parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14415        fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14416      } else if (!interceptorFn.$stateful) {
14417        // If there is an interceptor, but no watchDelegate then treat the interceptor like
14418        // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14419        fn.$$watchDelegate = inputsWatchDelegate;
14420        fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14421      }
14422
14423      return fn;
14424    }
14425  }];
14426}
14427
14428/**
14429 * @ngdoc service
14430 * @name $q
14431 * @requires $rootScope
14432 *
14433 * @description
14434 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14435 * when they are done processing.
14436 *
14437 * This is an implementation of promises/deferred objects inspired by
14438 * [Kris Kowal's Q](https://github.com/kriskowal/q).
14439 *
14440 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14441 * implementations, and the other which resembles ES6 promises to some degree.
14442 *
14443 * # $q constructor
14444 *
14445 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14446 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14447 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14448 *
14449 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14450 * available yet.
14451 *
14452 * It can be used like so:
14453 *
14454 * ```js
14455 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14456 *   // are available in the current lexical scope (they could have been injected or passed in).
14457 *
14458 *   function asyncGreet(name) {
14459 *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
14460 *     return $q(function(resolve, reject) {
14461 *       setTimeout(function() {
14462 *         if (okToGreet(name)) {
14463 *           resolve('Hello, ' + name + '!');
14464 *         } else {
14465 *           reject('Greeting ' + name + ' is not allowed.');
14466 *         }
14467 *       }, 1000);
14468 *     });
14469 *   }
14470 *
14471 *   var promise = asyncGreet('Robin Hood');
14472 *   promise.then(function(greeting) {
14473 *     alert('Success: ' + greeting);
14474 *   }, function(reason) {
14475 *     alert('Failed: ' + reason);
14476 *   });
14477 * ```
14478 *
14479 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14480 *
14481 * However, the more traditional CommonJS-style usage is still available, and documented below.
14482 *
14483 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14484 * interface for interacting with an object that represents the result of an action that is
14485 * performed asynchronously, and may or may not be finished at any given point in time.
14486 *
14487 * From the perspective of dealing with error handling, deferred and promise APIs are to
14488 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14489 *
14490 * ```js
14491 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14492 *   // are available in the current lexical scope (they could have been injected or passed in).
14493 *
14494 *   function asyncGreet(name) {
14495 *     var deferred = $q.defer();
14496 *
14497 *     setTimeout(function() {
14498 *       deferred.notify('About to greet ' + name + '.');
14499 *
14500 *       if (okToGreet(name)) {
14501 *         deferred.resolve('Hello, ' + name + '!');
14502 *       } else {
14503 *         deferred.reject('Greeting ' + name + ' is not allowed.');
14504 *       }
14505 *     }, 1000);
14506 *
14507 *     return deferred.promise;
14508 *   }
14509 *
14510 *   var promise = asyncGreet('Robin Hood');
14511 *   promise.then(function(greeting) {
14512 *     alert('Success: ' + greeting);
14513 *   }, function(reason) {
14514 *     alert('Failed: ' + reason);
14515 *   }, function(update) {
14516 *     alert('Got notification: ' + update);
14517 *   });
14518 * ```
14519 *
14520 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14521 * comes in the way of guarantees that promise and deferred APIs make, see
14522 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14523 *
14524 * Additionally the promise api allows for composition that is very hard to do with the
14525 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14526 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14527 * section on serial or parallel joining of promises.
14528 *
14529 * # The Deferred API
14530 *
14531 * A new instance of deferred is constructed by calling `$q.defer()`.
14532 *
14533 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14534 * that can be used for signaling the successful or unsuccessful completion, as well as the status
14535 * of the task.
14536 *
14537 * **Methods**
14538 *
14539 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14540 *   constructed via `$q.reject`, the promise will be rejected instead.
14541 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14542 *   resolving it with a rejection constructed via `$q.reject`.
14543 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14544 *   multiple times before the promise is either resolved or rejected.
14545 *
14546 * **Properties**
14547 *
14548 * - promise – `{Promise}` – promise object associated with this deferred.
14549 *
14550 *
14551 * # The Promise API
14552 *
14553 * A new promise instance is created when a deferred instance is created and can be retrieved by
14554 * calling `deferred.promise`.
14555 *
14556 * The purpose of the promise object is to allow for interested parties to get access to the result
14557 * of the deferred task when it completes.
14558 *
14559 * **Methods**
14560 *
14561 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14562 *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14563 *   as soon as the result is available. The callbacks are called with a single argument: the result
14564 *   or rejection reason. Additionally, the notify callback may be called zero or more times to
14565 *   provide a progress indication, before the promise is resolved or rejected.
14566 *
14567 *   This method *returns a new promise* which is resolved or rejected via the return value of the
14568 *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14569 *   with the value which is resolved in that promise using
14570 *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14571 *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14572 *   resolved or rejected from the notifyCallback method.
14573 *
14574 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14575 *
14576 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14577 *   but to do so without modifying the final value. This is useful to release resources or do some
14578 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14579 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14580 *   more information.
14581 *
14582 * # Chaining promises
14583 *
14584 * Because calling the `then` method of a promise returns a new derived promise, it is easily
14585 * possible to create a chain of promises:
14586 *
14587 * ```js
14588 *   promiseB = promiseA.then(function(result) {
14589 *     return result + 1;
14590 *   });
14591 *
14592 *   // promiseB will be resolved immediately after promiseA is resolved and its value
14593 *   // will be the result of promiseA incremented by 1
14594 * ```
14595 *
14596 * It is possible to create chains of any length and since a promise can be resolved with another
14597 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14598 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14599 * $http's response interceptors.
14600 *
14601 *
14602 * # Differences between Kris Kowal's Q and $q
14603 *
14604 *  There are two main differences:
14605 *
14606 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14607 *   mechanism in angular, which means faster propagation of resolution or rejection into your
14608 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
14609 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14610 *   all the important functionality needed for common async tasks.
14611 *
14612 *  # Testing
14613 *
14614 *  ```js
14615 *    it('should simulate promise', inject(function($q, $rootScope) {
14616 *      var deferred = $q.defer();
14617 *      var promise = deferred.promise;
14618 *      var resolvedValue;
14619 *
14620 *      promise.then(function(value) { resolvedValue = value; });
14621 *      expect(resolvedValue).toBeUndefined();
14622 *
14623 *      // Simulate resolving of promise
14624 *      deferred.resolve(123);
14625 *      // Note that the 'then' function does not get called synchronously.
14626 *      // This is because we want the promise API to always be async, whether or not
14627 *      // it got called synchronously or asynchronously.
14628 *      expect(resolvedValue).toBeUndefined();
14629 *
14630 *      // Propagate promise resolution to 'then' functions using $apply().
14631 *      $rootScope.$apply();
14632 *      expect(resolvedValue).toEqual(123);
14633 *    }));
14634 *  ```
14635 *
14636 * @param {function(function, function)} resolver Function which is responsible for resolving or
14637 *   rejecting the newly created promise. The first parameter is a function which resolves the
14638 *   promise, the second parameter is a function which rejects the promise.
14639 *
14640 * @returns {Promise} The newly created promise.
14641 */
14642function $QProvider() {
14643
14644  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14645    return qFactory(function(callback) {
14646      $rootScope.$evalAsync(callback);
14647    }, $exceptionHandler);
14648  }];
14649}
14650
14651function $$QProvider() {
14652  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
14653    return qFactory(function(callback) {
14654      $browser.defer(callback);
14655    }, $exceptionHandler);
14656  }];
14657}
14658
14659/**
14660 * Constructs a promise manager.
14661 *
14662 * @param {function(function)} nextTick Function for executing functions in the next turn.
14663 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
14664 *     debugging purposes.
14665 * @returns {object} Promise manager.
14666 */
14667function qFactory(nextTick, exceptionHandler) {
14668  var $qMinErr = minErr('$q', TypeError);
14669  function callOnce(self, resolveFn, rejectFn) {
14670    var called = false;
14671    function wrap(fn) {
14672      return function(value) {
14673        if (called) return;
14674        called = true;
14675        fn.call(self, value);
14676      };
14677    }
14678
14679    return [wrap(resolveFn), wrap(rejectFn)];
14680  }
14681
14682  /**
14683   * @ngdoc method
14684   * @name ng.$q#defer
14685   * @kind function
14686   *
14687   * @description
14688   * Creates a `Deferred` object which represents a task which will finish in the future.
14689   *
14690   * @returns {Deferred} Returns a new instance of deferred.
14691   */
14692  var defer = function() {
14693    return new Deferred();
14694  };
14695
14696  function Promise() {
14697    this.$$state = { status: 0 };
14698  }
14699
14700  extend(Promise.prototype, {
14701    then: function(onFulfilled, onRejected, progressBack) {
14702      if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
14703        return this;
14704      }
14705      var result = new Deferred();
14706
14707      this.$$state.pending = this.$$state.pending || [];
14708      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
14709      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
14710
14711      return result.promise;
14712    },
14713
14714    "catch": function(callback) {
14715      return this.then(null, callback);
14716    },
14717
14718    "finally": function(callback, progressBack) {
14719      return this.then(function(value) {
14720        return handleCallback(value, true, callback);
14721      }, function(error) {
14722        return handleCallback(error, false, callback);
14723      }, progressBack);
14724    }
14725  });
14726
14727  //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
14728  function simpleBind(context, fn) {
14729    return function(value) {
14730      fn.call(context, value);
14731    };
14732  }
14733
14734  function processQueue(state) {
14735    var fn, deferred, pending;
14736
14737    pending = state.pending;
14738    state.processScheduled = false;
14739    state.pending = undefined;
14740    for (var i = 0, ii = pending.length; i < ii; ++i) {
14741      deferred = pending[i][0];
14742      fn = pending[i][state.status];
14743      try {
14744        if (isFunction(fn)) {
14745          deferred.resolve(fn(state.value));
14746        } else if (state.status === 1) {
14747          deferred.resolve(state.value);
14748        } else {
14749          deferred.reject(state.value);
14750        }
14751      } catch (e) {
14752        deferred.reject(e);
14753        exceptionHandler(e);
14754      }
14755    }
14756  }
14757
14758  function scheduleProcessQueue(state) {
14759    if (state.processScheduled || !state.pending) return;
14760    state.processScheduled = true;
14761    nextTick(function() { processQueue(state); });
14762  }
14763
14764  function Deferred() {
14765    this.promise = new Promise();
14766    //Necessary to support unbound execution :/
14767    this.resolve = simpleBind(this, this.resolve);
14768    this.reject = simpleBind(this, this.reject);
14769    this.notify = simpleBind(this, this.notify);
14770  }
14771
14772  extend(Deferred.prototype, {
14773    resolve: function(val) {
14774      if (this.promise.$$state.status) return;
14775      if (val === this.promise) {
14776        this.$$reject($qMinErr(
14777          'qcycle',
14778          "Expected promise to be resolved with value other than itself '{0}'",
14779          val));
14780      } else {
14781        this.$$resolve(val);
14782      }
14783
14784    },
14785
14786    $$resolve: function(val) {
14787      var then, fns;
14788
14789      fns = callOnce(this, this.$$resolve, this.$$reject);
14790      try {
14791        if ((isObject(val) || isFunction(val))) then = val && val.then;
14792        if (isFunction(then)) {
14793          this.promise.$$state.status = -1;
14794          then.call(val, fns[0], fns[1], this.notify);
14795        } else {
14796          this.promise.$$state.value = val;
14797          this.promise.$$state.status = 1;
14798          scheduleProcessQueue(this.promise.$$state);
14799        }
14800      } catch (e) {
14801        fns[1](e);
14802        exceptionHandler(e);
14803      }
14804    },
14805
14806    reject: function(reason) {
14807      if (this.promise.$$state.status) return;
14808      this.$$reject(reason);
14809    },
14810
14811    $$reject: function(reason) {
14812      this.promise.$$state.value = reason;
14813      this.promise.$$state.status = 2;
14814      scheduleProcessQueue(this.promise.$$state);
14815    },
14816
14817    notify: function(progress) {
14818      var callbacks = this.promise.$$state.pending;
14819
14820      if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
14821        nextTick(function() {
14822          var callback, result;
14823          for (var i = 0, ii = callbacks.length; i < ii; i++) {
14824            result = callbacks[i][0];
14825            callback = callbacks[i][3];
14826            try {
14827              result.notify(isFunction(callback) ? callback(progress) : progress);
14828            } catch (e) {
14829              exceptionHandler(e);
14830            }
14831          }
14832        });
14833      }
14834    }
14835  });
14836
14837  /**
14838   * @ngdoc method
14839   * @name $q#reject
14840   * @kind function
14841   *
14842   * @description
14843   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
14844   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
14845   * a promise chain, you don't need to worry about it.
14846   *
14847   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
14848   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
14849   * a promise error callback and you want to forward the error to the promise derived from the
14850   * current promise, you have to "rethrow" the error by returning a rejection constructed via
14851   * `reject`.
14852   *
14853   * ```js
14854   *   promiseB = promiseA.then(function(result) {
14855   *     // success: do something and resolve promiseB
14856   *     //          with the old or a new result
14857   *     return result;
14858   *   }, function(reason) {
14859   *     // error: handle the error if possible and
14860   *     //        resolve promiseB with newPromiseOrValue,
14861   *     //        otherwise forward the rejection to promiseB
14862   *     if (canHandle(reason)) {
14863   *      // handle the error and recover
14864   *      return newPromiseOrValue;
14865   *     }
14866   *     return $q.reject(reason);
14867   *   });
14868   * ```
14869   *
14870   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
14871   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
14872   */
14873  var reject = function(reason) {
14874    var result = new Deferred();
14875    result.reject(reason);
14876    return result.promise;
14877  };
14878
14879  var makePromise = function makePromise(value, resolved) {
14880    var result = new Deferred();
14881    if (resolved) {
14882      result.resolve(value);
14883    } else {
14884      result.reject(value);
14885    }
14886    return result.promise;
14887  };
14888
14889  var handleCallback = function handleCallback(value, isResolved, callback) {
14890    var callbackOutput = null;
14891    try {
14892      if (isFunction(callback)) callbackOutput = callback();
14893    } catch (e) {
14894      return makePromise(e, false);
14895    }
14896    if (isPromiseLike(callbackOutput)) {
14897      return callbackOutput.then(function() {
14898        return makePromise(value, isResolved);
14899      }, function(error) {
14900        return makePromise(error, false);
14901      });
14902    } else {
14903      return makePromise(value, isResolved);
14904    }
14905  };
14906
14907  /**
14908   * @ngdoc method
14909   * @name $q#when
14910   * @kind function
14911   *
14912   * @description
14913   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
14914   * This is useful when you are dealing with an object that might or might not be a promise, or if
14915   * the promise comes from a source that can't be trusted.
14916   *
14917   * @param {*} value Value or a promise
14918   * @param {Function=} successCallback
14919   * @param {Function=} errorCallback
14920   * @param {Function=} progressCallback
14921   * @returns {Promise} Returns a promise of the passed value or promise
14922   */
14923
14924
14925  var when = function(value, callback, errback, progressBack) {
14926    var result = new Deferred();
14927    result.resolve(value);
14928    return result.promise.then(callback, errback, progressBack);
14929  };
14930
14931  /**
14932   * @ngdoc method
14933   * @name $q#resolve
14934   * @kind function
14935   *
14936   * @description
14937   * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
14938   *
14939   * @param {*} value Value or a promise
14940   * @param {Function=} successCallback
14941   * @param {Function=} errorCallback
14942   * @param {Function=} progressCallback
14943   * @returns {Promise} Returns a promise of the passed value or promise
14944   */
14945  var resolve = when;
14946
14947  /**
14948   * @ngdoc method
14949   * @name $q#all
14950   * @kind function
14951   *
14952   * @description
14953   * Combines multiple promises into a single promise that is resolved when all of the input
14954   * promises are resolved.
14955   *
14956   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
14957   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
14958   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
14959   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
14960   *   with the same rejection value.
14961   */
14962
14963  function all(promises) {
14964    var deferred = new Deferred(),
14965        counter = 0,
14966        results = isArray(promises) ? [] : {};
14967
14968    forEach(promises, function(promise, key) {
14969      counter++;
14970      when(promise).then(function(value) {
14971        if (results.hasOwnProperty(key)) return;
14972        results[key] = value;
14973        if (!(--counter)) deferred.resolve(results);
14974      }, function(reason) {
14975        if (results.hasOwnProperty(key)) return;
14976        deferred.reject(reason);
14977      });
14978    });
14979
14980    if (counter === 0) {
14981      deferred.resolve(results);
14982    }
14983
14984    return deferred.promise;
14985  }
14986
14987  var $Q = function Q(resolver) {
14988    if (!isFunction(resolver)) {
14989      throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
14990    }
14991
14992    if (!(this instanceof Q)) {
14993      // More useful when $Q is the Promise itself.
14994      return new Q(resolver);
14995    }
14996
14997    var deferred = new Deferred();
14998
14999    function resolveFn(value) {
15000      deferred.resolve(value);
15001    }
15002
15003    function rejectFn(reason) {
15004      deferred.reject(reason);
15005    }
15006
15007    resolver(resolveFn, rejectFn);
15008
15009    return deferred.promise;
15010  };
15011
15012  $Q.defer = defer;
15013  $Q.reject = reject;
15014  $Q.when = when;
15015  $Q.resolve = resolve;
15016  $Q.all = all;
15017
15018  return $Q;
15019}
15020
15021function $$RAFProvider() { //rAF
15022  this.$get = ['$window', '$timeout', function($window, $timeout) {
15023    var requestAnimationFrame = $window.requestAnimationFrame ||
15024                                $window.webkitRequestAnimationFrame;
15025
15026    var cancelAnimationFrame = $window.cancelAnimationFrame ||
15027                               $window.webkitCancelAnimationFrame ||
15028                               $window.webkitCancelRequestAnimationFrame;
15029
15030    var rafSupported = !!requestAnimationFrame;
15031    var raf = rafSupported
15032      ? function(fn) {
15033          var id = requestAnimationFrame(fn);
15034          return function() {
15035            cancelAnimationFrame(id);
15036          };
15037        }
15038      : function(fn) {
15039          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15040          return function() {
15041            $timeout.cancel(timer);
15042          };
15043        };
15044
15045    raf.supported = rafSupported;
15046
15047    return raf;
15048  }];
15049}
15050
15051/**
15052 * DESIGN NOTES
15053 *
15054 * The design decisions behind the scope are heavily favored for speed and memory consumption.
15055 *
15056 * The typical use of scope is to watch the expressions, which most of the time return the same
15057 * value as last time so we optimize the operation.
15058 *
15059 * Closures construction is expensive in terms of speed as well as memory:
15060 *   - No closures, instead use prototypical inheritance for API
15061 *   - Internal state needs to be stored on scope directly, which means that private state is
15062 *     exposed as $$____ properties
15063 *
15064 * Loop operations are optimized by using while(count--) { ... }
15065 *   - this means that in order to keep the same order of execution as addition we have to add
15066 *     items to the array at the beginning (unshift) instead of at the end (push)
15067 *
15068 * Child scopes are created and removed often
15069 *   - Using an array would be slow since inserts in middle are expensive so we use linked list
15070 *
15071 * There are few watches then a lot of observers. This is why you don't want the observer to be
15072 * implemented in the same way as watch. Watch requires return of initialization function which
15073 * are expensive to construct.
15074 */
15075
15076
15077/**
15078 * @ngdoc provider
15079 * @name $rootScopeProvider
15080 * @description
15081 *
15082 * Provider for the $rootScope service.
15083 */
15084
15085/**
15086 * @ngdoc method
15087 * @name $rootScopeProvider#digestTtl
15088 * @description
15089 *
15090 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15091 * assuming that the model is unstable.
15092 *
15093 * The current default is 10 iterations.
15094 *
15095 * In complex applications it's possible that the dependencies between `$watch`s will result in
15096 * several digest iterations. However if an application needs more than the default 10 digest
15097 * iterations for its model to stabilize then you should investigate what is causing the model to
15098 * continuously change during the digest.
15099 *
15100 * Increasing the TTL could have performance implications, so you should not change it without
15101 * proper justification.
15102 *
15103 * @param {number} limit The number of digest iterations.
15104 */
15105
15106
15107/**
15108 * @ngdoc service
15109 * @name $rootScope
15110 * @description
15111 *
15112 * Every application has a single root {@link ng.$rootScope.Scope scope}.
15113 * All other scopes are descendant scopes of the root scope. Scopes provide separation
15114 * between the model and the view, via a mechanism for watching the model for changes.
15115 * They also provide an event emission/broadcast and subscription facility. See the
15116 * {@link guide/scope developer guide on scopes}.
15117 */
15118function $RootScopeProvider() {
15119  var TTL = 10;
15120  var $rootScopeMinErr = minErr('$rootScope');
15121  var lastDirtyWatch = null;
15122  var applyAsyncId = null;
15123
15124  this.digestTtl = function(value) {
15125    if (arguments.length) {
15126      TTL = value;
15127    }
15128    return TTL;
15129  };
15130
15131  function createChildScopeClass(parent) {
15132    function ChildScope() {
15133      this.$$watchers = this.$$nextSibling =
15134          this.$$childHead = this.$$childTail = null;
15135      this.$$listeners = {};
15136      this.$$listenerCount = {};
15137      this.$$watchersCount = 0;
15138      this.$id = nextUid();
15139      this.$$ChildScope = null;
15140    }
15141    ChildScope.prototype = parent;
15142    return ChildScope;
15143  }
15144
15145  this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
15146      function($injector, $exceptionHandler, $parse, $browser) {
15147
15148    function destroyChildScope($event) {
15149        $event.currentScope.$$destroyed = true;
15150    }
15151
15152    /**
15153     * @ngdoc type
15154     * @name $rootScope.Scope
15155     *
15156     * @description
15157     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15158     * {@link auto.$injector $injector}. Child scopes are created using the
15159     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15160     * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15161     * an in-depth introduction and usage examples.
15162     *
15163     *
15164     * # Inheritance
15165     * A scope can inherit from a parent scope, as in this example:
15166     * ```js
15167         var parent = $rootScope;
15168         var child = parent.$new();
15169
15170         parent.salutation = "Hello";
15171         expect(child.salutation).toEqual('Hello');
15172
15173         child.salutation = "Welcome";
15174         expect(child.salutation).toEqual('Welcome');
15175         expect(parent.salutation).toEqual('Hello');
15176     * ```
15177     *
15178     * When interacting with `Scope` in tests, additional helper methods are available on the
15179     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15180     * details.
15181     *
15182     *
15183     * @param {Object.<string, function()>=} providers Map of service factory which need to be
15184     *                                       provided for the current scope. Defaults to {@link ng}.
15185     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15186     *                              append/override services provided by `providers`. This is handy
15187     *                              when unit-testing and having the need to override a default
15188     *                              service.
15189     * @returns {Object} Newly created scope.
15190     *
15191     */
15192    function Scope() {
15193      this.$id = nextUid();
vendor: 6,561 bytes, lines 15194-15331
15194      this.$$phase = this.$parent = this.$$watchers =
15195                     this.$$nextSibling = this.$$prevSibling =
15196                     this.$$childHead = this.$$childTail = null;
15197      this.$root = this;
15198      this.$$destroyed = false;
15199      this.$$listeners = {};
15200      this.$$listenerCount = {};
15201      this.$$watchersCount = 0;
15202      this.$$isolateBindings = null;
15203    }
15204
15205    /**
15206     * @ngdoc property
15207     * @name $rootScope.Scope#$id
15208     *
15209     * @description
15210     * Unique scope ID (monotonically increasing) useful for debugging.
15211     */
15212
15213     /**
15214      * @ngdoc property
15215      * @name $rootScope.Scope#$parent
15216      *
15217      * @description
15218      * Reference to the parent scope.
15219      */
15220
15221      /**
15222       * @ngdoc property
15223       * @name $rootScope.Scope#$root
15224       *
15225       * @description
15226       * Reference to the root scope.
15227       */
15228
15229    Scope.prototype = {
15230      constructor: Scope,
15231      /**
15232       * @ngdoc method
15233       * @name $rootScope.Scope#$new
15234       * @kind function
15235       *
15236       * @description
15237       * Creates a new child {@link ng.$rootScope.Scope scope}.
15238       *
15239       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15240       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15241       *
15242       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15243       * desired for the scope and its child scopes to be permanently detached from the parent and
15244       * thus stop participating in model change detection and listener notification by invoking.
15245       *
15246       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15247       *         parent scope. The scope is isolated, as it can not see parent scope properties.
15248       *         When creating widgets, it is useful for the widget to not accidentally read parent
15249       *         state.
15250       *
15251       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15252       *                              of the newly created scope. Defaults to `this` scope if not provided.
15253       *                              This is used when creating a transclude scope to correctly place it
15254       *                              in the scope hierarchy while maintaining the correct prototypical
15255       *                              inheritance.
15256       *
15257       * @returns {Object} The newly created child scope.
15258       *
15259       */
15260      $new: function(isolate, parent) {
15261        var child;
15262
15263        parent = parent || this;
15264
15265        if (isolate) {
15266          child = new Scope();
15267          child.$root = this.$root;
15268        } else {
15269          // Only create a child scope class if somebody asks for one,
15270          // but cache it to allow the VM to optimize lookups.
15271          if (!this.$$ChildScope) {
15272            this.$$ChildScope = createChildScopeClass(this);
15273          }
15274          child = new this.$$ChildScope();
15275        }
15276        child.$parent = parent;
15277        child.$$prevSibling = parent.$$childTail;
15278        if (parent.$$childHead) {
15279          parent.$$childTail.$$nextSibling = child;
15280          parent.$$childTail = child;
15281        } else {
15282          parent.$$childHead = parent.$$childTail = child;
15283        }
15284
15285        // When the new scope is not isolated or we inherit from `this`, and
15286        // the parent scope is destroyed, the property `$$destroyed` is inherited
15287        // prototypically. In all other cases, this property needs to be set
15288        // when the parent scope is destroyed.
15289        // The listener needs to be added after the parent is set
15290        if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15291
15292        return child;
15293      },
15294
15295      /**
15296       * @ngdoc method
15297       * @name $rootScope.Scope#$watch
15298       * @kind function
15299       *
15300       * @description
15301       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15302       *
15303       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15304       *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
15305       *   its value when executed multiple times with the same input because it may be executed multiple
15306       *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15307       *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15308       * - The `listener` is called only when the value from the current `watchExpression` and the
15309       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
15310       *   see below). Inequality is determined according to reference inequality,
15311       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15312       *    via the `!==` Javascript operator, unless `objectEquality == true`
15313       *   (see next point)
15314       * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15315       *   according to the {@link angular.equals} function. To save the value of the object for
15316       *   later comparison, the {@link angular.copy} function is used. This therefore means that
15317       *   watching complex objects will have adverse memory and performance implications.
15318       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15319       *   This is achieved by rerunning the watchers until no changes are detected. The rerun
15320       *   iteration limit is 10 to prevent an infinite loop deadlock.
15321       *
15322       *
15323       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15324       * you can register a `watchExpression` function with no `listener`. (Be prepared for
15325       * multiple calls to your `watchExpression` because it will execute multiple times in a
15326       * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
15327       *
15328       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15329       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15330       * watcher. In rare cases, this is undesirable because the listener is called when the result
15331       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15332       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15333       * listener was called due to initialization.
15334       *
15335       *
15336       *
15337       * # Example
15338       * ```js
15339           // let's assume that scope was dependency injected as the $rootScope
15340           var scope = $rootScope;
15341           scope.name = 'misko';
15342           scope.counter = 0;
15343
15344           expect(scope.counter).toEqual(0);
15345           scope.$watch('name', function(newValue, oldValue) {
15346             scope.counter = scope.counter + 1;
15347           });
15348           expect(scope.counter).toEqual(0);
15349
15350           scope.$digest();
15351           // the listener is always called during the first $digest loop after it was registered
15352           expect(scope.counter).toEqual(1);
15353
15354           scope.$digest();
15355           // but now it will not be called unless the value changes
15356           expect(scope.counter).toEqual(1);
15357
15358           scope.name = 'adam';
15359           scope.$digest();
15360           expect(scope.counter).toEqual(2);
15361
15362
15363
15364           // Using a function as a watchExpression
15365           var food;
15366           scope.foodCounter = 0;
15367           expect(scope.foodCounter).toEqual(0);
15368           scope.$watch(
15369             // This function returns the value being watched. It is called for each turn of the $digest loop
15370             function() { return food; },
15371             // This is the change listener, called when the value returned from the above function changes
15372             function(newValue, oldValue) {
15373               if ( newValue !== oldValue ) {
15374                 // Only increment the counter if the value changed
15375                 scope.foodCounter = scope.foodCounter + 1;
15376               }
15377             }
15378           );
15379           // No digest has been run so the counter will be zero
15380           expect(scope.foodCounter).toEqual(0);
15381
15382           // Run the digest but since food has not changed count will still be zero
15383           scope.$digest();
15384           expect(scope.foodCounter).toEqual(0);
15385
15386           // Update food and run digest.  Now the counter will increment
15387           food = 'cheeseburger';
15388           scope.$digest();
15389           expect(scope.foodCounter).toEqual(1);
15390
15391       * ```
15392       *
15393       *
15394       *
15395       * @param {(function()|string)} watchExpression Expression that is evaluated on each
15396       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15397       *    a call to the `listener`.
15398       *
15399       *    - `string`: Evaluated as {@link guide/expression expression}
15400       *    - `function(scope)`: called with current `scope` as a parameter.
15401       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15402       *    of `watchExpression` changes.
15403       *
15404       *    - `newVal` contains the current value of the `watchExpression`
15405       *    - `oldVal` contains the previous value of the `watchExpression`
15406       *    - `scope` refers to the current scope
15407       * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15408       *     comparing for reference equality.
15409       * @returns {function()} Returns a deregistration function for this listener.
15410       */
15411      $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15412        var get = $parse(watchExp);
15413
15414        if (get.$$watchDelegate) {
15415          return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15416        }
15417        var scope = this,
15418            array = scope.$$watchers,
15419            watcher = {
15420              fn: listener,
15421              last: initWatchVal,
15422              get: get,
15423              exp: prettyPrintExpression || watchExp,
15424              eq: !!objectEquality
15425            };
15426
15427        lastDirtyWatch = null;
15428
15429        if (!isFunction(listener)) {
15430          watcher.fn = noop;
15431        }
15432
15433        if (!array) {
15434          array = scope.$$watchers = [];
15435        }
15436        // we use unshift since we use a while loop in $digest for speed.
15437        // the while loop reads in reverse order.
15438        array.unshift(watcher);
15439        incrementWatchersCount(this, 1);
15440
15441        return function deregisterWatch() {
15442          if (arrayRemove(array, watcher) >= 0) {
15443            incrementWatchersCount(scope, -1);
15444          }
15445          lastDirtyWatch = null;
15446        };
15447      },
15448
15449      /**
15450       * @ngdoc method
15451       * @name $rootScope.Scope#$watchGroup
15452       * @kind function
15453       *
15454       * @description
15455       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15456       * If any one expression in the collection changes the `listener` is executed.
15457       *
15458       * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15459       *   call to $digest() to see if any items changes.
15460       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15461       *
15462       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15463       * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15464       *
15465       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15466       *    expression in `watchExpressions` changes
15467       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15468       *    those of `watchExpression`
15469       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15470       *    those of `watchExpression`
15471       *    The `scope` refers to the current scope.
15472       * @returns {function()} Returns a de-registration function for all listeners.
15473       */
15474      $watchGroup: function(watchExpressions, listener) {
15475        var oldValues = new Array(watchExpressions.length);
15476        var newValues = new Array(watchExpressions.length);
15477        var deregisterFns = [];
15478        var self = this;
15479        var changeReactionScheduled = false;
15480        var firstRun = true;
15481
15482        if (!watchExpressions.length) {
15483          // No expressions means we call the listener ASAP
15484          var shouldCall = true;
15485          self.$evalAsync(function() {
15486            if (shouldCall) listener(newValues, newValues, self);
15487          });
15488          return function deregisterWatchGroup() {
15489            shouldCall = false;
15490          };
15491        }
15492
15493        if (watchExpressions.length === 1) {
15494          // Special case size of one
15495          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15496            newValues[0] = value;
15497            oldValues[0] = oldValue;
15498            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15499          });
15500        }
15501
15502        forEach(watchExpressions, function(expr, i) {
15503          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15504            newValues[i] = value;
15505            oldValues[i] = oldValue;
15506            if (!changeReactionScheduled) {
15507              changeReactionScheduled = true;
15508              self.$evalAsync(watchGroupAction);
15509            }
15510          });
15511          deregisterFns.push(unwatchFn);
15512        });
15513
15514        function watchGroupAction() {
15515          changeReactionScheduled = false;
15516
15517          if (firstRun) {
15518            firstRun = false;
15519            listener(newValues, newValues, self);
15520          } else {
15521            listener(newValues, oldValues, self);
15522          }
15523        }
15524
15525        return function deregisterWatchGroup() {
15526          while (deregisterFns.length) {
15527            deregisterFns.shift()();
15528          }
15529        };
15530      },
15531
15532
15533      /**
15534       * @ngdoc method
15535       * @name $rootScope.Scope#$watchCollection
15536       * @kind function
15537       *
15538       * @description
15539       * Shallow watches the properties of an object and fires whenever any of the properties change
15540       * (for arrays, this implies watching the array items; for object maps, this implies watching
15541       * the properties). If a change is detected, the `listener` callback is fired.
15542       *
15543       * - The `obj` collection is observed via standard $watch operation and is examined on every
15544       *   call to $digest() to see if any items have been added, removed, or moved.
15545       * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15546       *   adding, removing, and moving items belonging to an object or array.
15547       *
15548       *
15549       * # Example
15550       * ```js
15551          $scope.names = ['igor', 'matias', 'misko', 'james'];
15552          $scope.dataCount = 4;
15553
15554          $scope.$watchCollection('names', function(newNames, oldNames) {
15555            $scope.dataCount = newNames.length;
15556          });
15557
15558          expect($scope.dataCount).toEqual(4);
15559          $scope.$digest();
15560
15561          //still at 4 ... no changes
15562          expect($scope.dataCount).toEqual(4);
15563
15564          $scope.names.pop();
15565          $scope.$digest();
15566
15567          //now there's been a change
15568          expect($scope.dataCount).toEqual(3);
15569       * ```
15570       *
15571       *
15572       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15573       *    expression value should evaluate to an object or an array which is observed on each
15574       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15575       *    collection will trigger a call to the `listener`.
15576       *
15577       * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15578       *    when a change is detected.
15579       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
15580       *    - The `oldCollection` object is a copy of the former collection data.
15581       *      Due to performance considerations, the`oldCollection` value is computed only if the
15582       *      `listener` function declares two or more arguments.
15583       *    - The `scope` argument refers to the current scope.
15584       *
15585       * @returns {function()} Returns a de-registration function for this listener. When the
15586       *    de-registration function is executed, the internal watch operation is terminated.
15587       */
15588      $watchCollection: function(obj, listener) {
15589        $watchCollectionInterceptor.$stateful = true;
15590
15591        var self = this;
15592        // the current value, updated on each dirty-check run
15593        var newValue;
15594        // a shallow copy of the newValue from the last dirty-check run,
15595        // updated to match newValue during dirty-check run
15596        var oldValue;
15597        // a shallow copy of the newValue from when the last change happened
15598        var veryOldValue;
15599        // only track veryOldValue if the listener is asking for it
15600        var trackVeryOldValue = (listener.length > 1);
15601        var changeDetected = 0;
15602        var changeDetector = $parse(obj, $watchCollectionInterceptor);
15603        var internalArray = [];
15604        var internalObject = {};
15605        var initRun = true;
15606        var oldLength = 0;
15607
15608        function $watchCollectionInterceptor(_value) {
15609          newValue = _value;
15610          var newLength, key, bothNaN, newItem, oldItem;
15611
15612          // If the new value is undefined, then return undefined as the watch may be a one-time watch
15613          if (isUndefined(newValue)) return;
15614
15615          if (!isObject(newValue)) { // if primitive
15616            if (oldValue !== newValue) {
15617              oldValue = newValue;
15618              changeDetected++;
15619            }
15620          } else if (isArrayLike(newValue)) {
15621            if (oldValue !== internalArray) {
15622              // we are transitioning from something which was not an array into array.
15623              oldValue = internalArray;
15624              oldLength = oldValue.length = 0;
15625              changeDetected++;
15626            }
15627
15628            newLength = newValue.length;
15629
15630            if (oldLength !== newLength) {
15631              // if lengths do not match we need to trigger change notification
15632              changeDetected++;
15633              oldValue.length = oldLength = newLength;
15634            }
15635            // copy the items to oldValue and look for changes.
15636            for (var i = 0; i < newLength; i++) {
15637              oldItem = oldValue[i];
15638              newItem = newValue[i];
15639
15640              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
vendor: 5,713 bytes, lines 15641-15792
15641              if (!bothNaN && (oldItem !== newItem)) {
15642                changeDetected++;
15643                oldValue[i] = newItem;
15644              }
15645            }
15646          } else {
15647            if (oldValue !== internalObject) {
15648              // we are transitioning from something which was not an object into object.
15649              oldValue = internalObject = {};
15650              oldLength = 0;
15651              changeDetected++;
15652            }
15653            // copy the items to oldValue and look for changes.
15654            newLength = 0;
15655            for (key in newValue) {
15656              if (hasOwnProperty.call(newValue, key)) {
15657                newLength++;
15658                newItem = newValue[key];
15659                oldItem = oldValue[key];
15660
15661                if (key in oldValue) {
15662                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15663                  if (!bothNaN && (oldItem !== newItem)) {
15664                    changeDetected++;
15665                    oldValue[key] = newItem;
15666                  }
15667                } else {
15668                  oldLength++;
15669                  oldValue[key] = newItem;
15670                  changeDetected++;
15671                }
15672              }
15673            }
15674            if (oldLength > newLength) {
15675              // we used to have more keys, need to find them and destroy them.
15676              changeDetected++;
15677              for (key in oldValue) {
15678                if (!hasOwnProperty.call(newValue, key)) {
15679                  oldLength--;
15680                  delete oldValue[key];
15681                }
15682              }
15683            }
15684          }
15685          return changeDetected;
15686        }
15687
15688        function $watchCollectionAction() {
15689          if (initRun) {
15690            initRun = false;
15691            listener(newValue, newValue, self);
15692          } else {
15693            listener(newValue, veryOldValue, self);
15694          }
15695
15696          // make a copy for the next time a collection is changed
15697          if (trackVeryOldValue) {
15698            if (!isObject(newValue)) {
15699              //primitive
15700              veryOldValue = newValue;
15701            } else if (isArrayLike(newValue)) {
15702              veryOldValue = new Array(newValue.length);
15703              for (var i = 0; i < newValue.length; i++) {
15704                veryOldValue[i] = newValue[i];
15705              }
15706            } else { // if object
15707              veryOldValue = {};
15708              for (var key in newValue) {
15709                if (hasOwnProperty.call(newValue, key)) {
15710                  veryOldValue[key] = newValue[key];
15711                }
15712              }
15713            }
15714          }
15715        }
15716
15717        return this.$watch(changeDetector, $watchCollectionAction);
15718      },
15719
15720      /**
15721       * @ngdoc method
15722       * @name $rootScope.Scope#$digest
15723       * @kind function
15724       *
15725       * @description
15726       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
15727       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
15728       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
15729       * until no more listeners are firing. This means that it is possible to get into an infinite
15730       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
15731       * iterations exceeds 10.
15732       *
15733       * Usually, you don't call `$digest()` directly in
15734       * {@link ng.directive:ngController controllers} or in
15735       * {@link ng.$compileProvider#directive directives}.
15736       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
15737       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
15738       *
15739       * If you want to be notified whenever `$digest()` is called,
15740       * you can register a `watchExpression` function with
15741       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
15742       *
15743       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
15744       *
15745       * # Example
15746       * ```js
15747           var scope = ...;
15748           scope.name = 'misko';
15749           scope.counter = 0;
15750
15751           expect(scope.counter).toEqual(0);
15752           scope.$watch('name', function(newValue, oldValue) {
15753             scope.counter = scope.counter + 1;
15754           });
15755           expect(scope.counter).toEqual(0);
15756
15757           scope.$digest();
15758           // the listener is always called during the first $digest loop after it was registered
15759           expect(scope.counter).toEqual(1);
15760
15761           scope.$digest();
15762           // but now it will not be called unless the value changes
15763           expect(scope.counter).toEqual(1);
15764
15765           scope.name = 'adam';
15766           scope.$digest();
15767           expect(scope.counter).toEqual(2);
15768       * ```
15769       *
15770       */
15771      $digest: function() {
15772        var watch, value, last,
15773            watchers,
15774            length,
15775            dirty, ttl = TTL,
15776            next, current, target = this,
15777            watchLog = [],
15778            logIdx, logMsg, asyncTask;
15779
15780        beginPhase('$digest');
15781        // Check for changes to browser url that happened in sync before the call to $digest
15782        $browser.$$checkUrlChange();
15783
15784        if (this === $rootScope && applyAsyncId !== null) {
15785          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
15786          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
15787          $browser.defer.cancel(applyAsyncId);
15788          flushApplyAsync();
15789        }
15790
15791        lastDirtyWatch = null;
15792
15793        do { // "while dirty" loop
15794          dirty = false;
15795          current = target;
15796
15797          while (asyncQueue.length) {
15798            try {
15799              asyncTask = asyncQueue.shift();
15800              asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
15801            } catch (e) {
15802              $exceptionHandler(e);
15803            }
15804            lastDirtyWatch = null;
15805          }
15806
15807          traverseScopesLoop:
15808          do { // "traverse the scopes" loop
15809            if ((watchers = current.$$watchers)) {
15810              // process our watches
15811              length = watchers.length;
15812              while (length--) {
15813                try {
15814                  watch = watchers[length];
15815                  // Most common watches are on primitives, in which case we can short
15816                  // circuit it with === operator, only when === fails do we use .equals
15817                  if (watch) {
15818                    if ((value = watch.get(current)) !== (last = watch.last) &&
15819                        !(watch.eq
15820                            ? equals(value, last)
15821                            : (typeof value === 'number' && typeof last === 'number'
15822                               && isNaN(value) && isNaN(last)))) {
15823                      dirty = true;
15824                      lastDirtyWatch = watch;
15825                      watch.last = watch.eq ? copy(value, null) : value;
15826                      watch.fn(value, ((last === initWatchVal) ? value : last), current);
15827                      if (ttl < 5) {
15828                        logIdx = 4 - ttl;
15829                        if (!watchLog[logIdx]) watchLog[logIdx] = [];
15830                        watchLog[logIdx].push({
15831                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
15832                          newVal: value,
15833                          oldVal: last
15834                        });
15835                      }
15836                    } else if (watch === lastDirtyWatch) {
15837                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
15838                      // have already been tested.
15839                      dirty = false;
15840                      break traverseScopesLoop;
15841                    }
15842                  }
15843                } catch (e) {
15844                  $exceptionHandler(e);
15845                }
15846              }
15847            }
15848
15849            // Insanity Warning: scope depth-first traversal
15850            // yes, this code is a bit crazy, but it works and we have tests to prove it!
15851            // this piece should be kept in sync with the traversal in $broadcast
15852            if (!(next = ((current.$$watchersCount && current.$$childHead) ||
15853                (current !== target && current.$$nextSibling)))) {
15854              while (current !== target && !(next = current.$$nextSibling)) {
15855                current = current.$parent;
15856              }
15857            }
15858          } while ((current = next));
15859
15860          // `break traverseScopesLoop;` takes us to here
15861
15862          if ((dirty || asyncQueue.length) && !(ttl--)) {
15863            clearPhase();
15864            throw $rootScopeMinErr('infdig',
15865                '{0} $digest() iterations reached. Aborting!\n' +
15866                'Watchers fired in the last 5 iterations: {1}',
15867                TTL, watchLog);
15868          }
15869
15870        } while (dirty || asyncQueue.length);
15871
15872        clearPhase();
15873
15874        while (postDigestQueue.length) {
15875          try {
15876            postDigestQueue.shift()();
15877          } catch (e) {
15878            $exceptionHandler(e);
15879          }
15880        }
15881      },
15882
15883
15884      /**
15885       * @ngdoc event
15886       * @name $rootScope.Scope#$destroy
15887       * @eventType broadcast on scope being destroyed
15888       *
15889       * @description
15890       * Broadcasted when a scope and its children are being destroyed.
15891       *
15892       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
15893       * clean up DOM bindings before an element is removed from the DOM.
15894       */
15895
15896      /**
15897       * @ngdoc method
15898       * @name $rootScope.Scope#$destroy
15899       * @kind function
15900       *
15901       * @description
15902       * Removes the current scope (and all of its children) from the parent scope. Removal implies
15903       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
15904       * propagate to the current scope and its children. Removal also implies that the current
15905       * scope is eligible for garbage collection.
15906       *
15907       * The `$destroy()` is usually used by directives such as
15908       * {@link ng.directive:ngRepeat ngRepeat} for managing the
15909       * unrolling of the loop.
15910       *
15911       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
15912       * Application code can register a `$destroy` event handler that will give it a chance to
15913       * perform any necessary cleanup.
15914       *
15915       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
15916       * clean up DOM bindings before an element is removed from the DOM.
15917       */
15918      $destroy: function() {
15919        // We can't destroy a scope that has been already destroyed.
15920        if (this.$$destroyed) return;
15921        var parent = this.$parent;
15922
15923        this.$broadcast('$destroy');
15924        this.$$destroyed = true;
15925
15926        if (this === $rootScope) {
15927          //Remove handlers attached to window when $rootScope is removed
15928          $browser.$$applicationDestroyed();
15929        }
15930
15931        incrementWatchersCount(this, -this.$$watchersCount);
15932        for (var eventName in this.$$listenerCount) {
15933          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
15934        }
15935
15936        // sever all the references to parent scopes (after this cleanup, the current scope should
15937        // not be retained by any of our references and should be eligible for garbage collection)
15938        if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
15939        if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
15940        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
15941        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
15942
15943        // Disable listeners, watchers and apply/digest methods
15944        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
15945        this.$on = this.$watch = this.$watchGroup = function() { return noop; };
15946        this.$$listeners = {};
15947
15948        // All of the code below is bogus code that works around V8's memory leak via optimized code
15949        // and inline caches.
15950        //
15951        // see:
15952        // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15953        // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15954        // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15955
15956        this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
15957            this.$$childTail = this.$root = this.$$watchers = null;
15958      },
15959
15960      /**
15961       * @ngdoc method
15962       * @name $rootScope.Scope#$eval
15963       * @kind function
15964       *
15965       * @description
15966       * Executes the `expression` on the current scope and returns the result. Any exceptions in
15967       * the expression are propagated (uncaught). This is useful when evaluating Angular
15968       * expressions.
15969       *
15970       * # Example
15971       * ```js
15972           var scope = ng.$rootScope.Scope();
15973           scope.a = 1;
15974           scope.b = 2;
15975
15976           expect(scope.$eval('a+b')).toEqual(3);
15977           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
15978       * ```
15979       *
15980       * @param {(string|function())=} expression An angular expression to be executed.
15981       *
15982       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
15983       *    - `function(scope)`: execute the function with the current `scope` parameter.
15984       *
15985       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
15986       * @returns {*} The result of evaluating the expression.
15987       */
15988      $eval: function(expr, locals) {
15989        return $parse(expr)(this, locals);
15990      },
15991
15992      /**
15993       * @ngdoc method
15994       * @name $rootScope.Scope#$evalAsync
15995       * @kind function
15996       *
15997       * @description
15998       * Executes the expression on the current scope at a later point in time.
15999       *
16000       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16001       * that:
16002       *
16003       *   - it will execute after the function that scheduled the evaluation (preferably before DOM
16004       *     rendering).
16005       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16006       *     `expression` execution.
16007       *
16008       * Any exceptions from the execution of the expression are forwarded to the
16009       * {@link ng.$exceptionHandler $exceptionHandler} service.
16010       *
16011       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16012       * will be scheduled. However, it is encouraged to always call code that changes the model
16013       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16014       *
16015       * @param {(string|function())=} expression An angular expression to be executed.
16016       *
16017       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16018       *    - `function(scope)`: execute the function with the current `scope` parameter.
16019       *
16020       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16021       */
16022      $evalAsync: function(expr, locals) {
16023        // if we are outside of an $digest loop and this is the first time we are scheduling async
16024        // task also schedule async auto-flush
16025        if (!$rootScope.$$phase && !asyncQueue.length) {
16026          $browser.defer(function() {
16027            if (asyncQueue.length) {
16028              $rootScope.$digest();
16029            }
16030          });
16031        }
16032
16033        asyncQueue.push({scope: this, expression: expr, locals: locals});
16034      },
16035
16036      $$postDigest: function(fn) {
16037        postDigestQueue.push(fn);
16038      },
16039
16040      /**
16041       * @ngdoc method
16042       * @name $rootScope.Scope#$apply
16043       * @kind function
16044       *
16045       * @description
16046       * `$apply()` is used to execute an expression in angular from outside of the angular
16047       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16048       * Because we are calling into the angular framework we need to perform proper scope life
16049       * cycle of {@link ng.$exceptionHandler exception handling},
16050       * {@link ng.$rootScope.Scope#$digest executing watches}.
16051       *
16052       * ## Life cycle
16053       *
16054       * # Pseudo-Code of `$apply()`
16055       * ```js
16056           function $apply(expr) {
16057             try {
16058               return $eval(expr);
16059             } catch (e) {
16060               $exceptionHandler(e);
16061             } finally {
16062               $root.$digest();
16063             }
16064           }
16065       * ```
16066       *
16067       *
16068       * Scope's `$apply()` method transitions through the following stages:
16069       *
16070       * 1. The {@link guide/expression expression} is executed using the
16071       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
16072       * 2. Any exceptions from the execution of the expression are forwarded to the
16073       *    {@link ng.$exceptionHandler $exceptionHandler} service.
16074       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16075       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16076       *
16077       *
16078       * @param {(string|function())=} exp An angular expression to be executed.
16079       *
16080       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16081       *    - `function(scope)`: execute the function with current `scope` parameter.
16082       *
16083       * @returns {*} The result of evaluating the expression.
16084       */
16085      $apply: function(expr) {
16086        try {
16087          beginPhase('$apply');
16088          try {
16089            return this.$eval(expr);
16090          } finally {
16091            clearPhase();
16092          }
16093        } catch (e) {
16094          $exceptionHandler(e);
16095        } finally {
16096          try {
16097            $rootScope.$digest();
16098          } catch (e) {
16099            $exceptionHandler(e);
16100            throw e;
16101          }
16102        }
16103      },
16104
16105      /**
16106       * @ngdoc method
16107       * @name $rootScope.Scope#$applyAsync
16108       * @kind function
16109       *
16110       * @description
16111       * Schedule the invocation of $apply to occur at a later time. The actual time difference
16112       * varies across browsers, but is typically around ~10 milliseconds.
16113       *
16114       * This can be used to queue up multiple expressions which need to be evaluated in the same
16115       * digest.
16116       *
16117       * @param {(string|function())=} exp An angular expression to be executed.
16118       *
16119       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16120       *    - `function(scope)`: execute the function with current `scope` parameter.
16121       */
16122      $applyAsync: function(expr) {
16123        var scope = this;
16124        expr && applyAsyncQueue.push($applyAsyncExpression);
16125        scheduleApplyAsync();
16126
16127        function $applyAsyncExpression() {
16128          scope.$eval(expr);
16129        }
16130      },
16131
16132      /**
16133       * @ngdoc method
16134       * @name $rootScope.Scope#$on
16135       * @kind function
16136       *
16137       * @description
16138       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16139       * discussion of event life cycle.
16140       *
16141       * The event listener function format is: `function(event, args...)`. The `event` object
16142       * passed into the listener has the following attributes:
16143       *
16144       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16145       *     `$broadcast`-ed.
16146       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16147       *     event propagates through the scope hierarchy, this property is set to null.
16148       *   - `name` - `{string}`: name of the event.
16149       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16150       *     further event propagation (available only for events that were `$emit`-ed).
16151       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16152       *     to true.
16153       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16154       *
16155       * @param {string} name Event name to listen on.
16156       * @param {function(event, ...args)} listener Function to call when the event is emitted.
16157       * @returns {function()} Returns a deregistration function for this listener.
16158       */
16159      $on: function(name, listener) {
16160        var namedListeners = this.$$listeners[name];
16161        if (!namedListeners) {
16162          this.$$listeners[name] = namedListeners = [];
16163        }
16164        namedListeners.push(listener);
16165
16166        var current = this;
16167        do {
16168          if (!current.$$listenerCount[name]) {
16169            current.$$listenerCount[name] = 0;
16170          }
16171          current.$$listenerCount[name]++;
16172        } while ((current = current.$parent));
16173
16174        var self = this;
16175        return function() {
16176          var indexOfListener = namedListeners.indexOf(listener);
16177          if (indexOfListener !== -1) {
16178            namedListeners[indexOfListener] = null;
16179            decrementListenerCount(self, 1, name);
16180          }
16181        };
16182      },
16183
16184
16185      /**
16186       * @ngdoc method
16187       * @name $rootScope.Scope#$emit
16188       * @kind function
16189       *
16190       * @description
16191       * Dispatches an event `name` upwards through the scope hierarchy notifying the
16192       * registered {@link ng.$rootScope.Scope#$on} listeners.
16193       *
16194       * The event life cycle starts at the scope on which `$emit` was called. All
16195       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16196       * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16197       * registered listeners along the way. The event will stop propagating if one of the listeners
16198       * cancels it.
16199       *
16200       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16201       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16202       *
16203       * @param {string} name Event name to emit.
16204       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16205       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16206       */
16207      $emit: function(name, args) {
16208        var empty = [],
16209            namedListeners,
16210            scope = this,
16211            stopPropagation = false,
16212            event = {
16213              name: name,
16214              targetScope: scope,
16215              stopPropagation: function() {stopPropagation = true;},
16216              preventDefault: function() {
16217                event.defaultPrevented = true;
16218              },
16219              defaultPrevented: false
16220            },
16221            listenerArgs = concat([event], arguments, 1),
16222            i, length;
16223
16224        do {
16225          namedListeners = scope.$$listeners[name] || empty;
16226          event.currentScope = scope;
16227          for (i = 0, length = namedListeners.length; i < length; i++) {
16228
16229            // if listeners were deregistered, defragment the array
16230            if (!namedListeners[i]) {
16231              namedListeners.splice(i, 1);
16232              i--;
16233              length--;
16234              continue;
16235            }
16236            try {
16237              //allow all listeners attached to the current scope to run
16238              namedListeners[i].apply(null, listenerArgs);
16239            } catch (e) {
16240              $exceptionHandler(e);
16241            }
16242          }
16243          //if any listener on the current scope stops propagation, prevent bubbling
16244          if (stopPropagation) {
16245            event.currentScope = null;
16246            return event;
16247          }
16248          //traverse upwards
16249          scope = scope.$parent;
16250        } while (scope);
16251
16252        event.currentScope = null;
16253
16254        return event;
16255      },
16256
16257
16258      /**
16259       * @ngdoc method
16260       * @name $rootScope.Scope#$broadcast
16261       * @kind function
16262       *
16263       * @description
16264       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16265       * registered {@link ng.$rootScope.Scope#$on} listeners.
16266       *
16267       * The event life cycle starts at the scope on which `$broadcast` was called. All
16268       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16269       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16270       * scope and calls all registered listeners along the way. The event cannot be canceled.
16271       *
16272       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16273       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16274       *
16275       * @param {string} name Event name to broadcast.
16276       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16277       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16278       */
16279      $broadcast: function(name, args) {
16280        var target = this,
16281            current = target,
16282            next = target,
16283            event = {
16284              name: name,
16285              targetScope: target,
16286              preventDefault: function() {
16287                event.defaultPrevented = true;
16288              },
16289              defaultPrevented: false
16290            };
16291
16292        if (!target.$$listenerCount[name]) return event;
16293
16294        var listenerArgs = concat([event], arguments, 1),
16295            listeners, i, length;
16296
16297        //down while you can, then up and next sibling or up and next sibling until back at root
16298        while ((current = next)) {
16299          event.currentScope = current;
16300          listeners = current.$$listeners[name] || [];
16301          for (i = 0, length = listeners.length; i < length; i++) {
16302            // if listeners were deregistered, defragment the array
16303            if (!listeners[i]) {
16304              listeners.splice(i, 1);
16305              i--;
16306              length--;
16307              continue;
16308            }
16309
16310            try {
16311              listeners[i].apply(null, listenerArgs);
16312            } catch (e) {
16313              $exceptionHandler(e);
16314            }
16315          }
16316
16317          // Insanity Warning: scope depth-first traversal
16318          // yes, this code is a bit crazy, but it works and we have tests to prove it!
16319          // this piece should be kept in sync with the traversal in $digest
16320          // (though it differs due to having the extra check for $$listenerCount)
16321          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16322              (current !== target && current.$$nextSibling)))) {
16323            while (current !== target && !(next = current.$$nextSibling)) {
16324              current = current.$parent;
16325            }
16326          }
16327        }
16328
16329        event.currentScope = null;
16330        return event;
16331      }
16332    };
16333
16334    var $rootScope = new Scope();
16335
16336    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16337    var asyncQueue = $rootScope.$$asyncQueue = [];
16338    var postDigestQueue = $rootScope.$$postDigestQueue = [];
16339    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16340
16341    return $rootScope;
16342
16343
16344    function beginPhase(phase) {
16345      if ($rootScope.$$phase) {
16346        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16347      }
16348
16349      $rootScope.$$phase = phase;
16350    }
16351
16352    function clearPhase() {
16353      $rootScope.$$phase = null;
16354    }
16355
16356    function incrementWatchersCount(current, count) {
16357      do {
16358        current.$$watchersCount += count;
16359      } while ((current = current.$parent));
16360    }
16361
16362    function decrementListenerCount(current, count, name) {
16363      do {
16364        current.$$listenerCount[name] -= count;
16365
16366        if (current.$$listenerCount[name] === 0) {
16367          delete current.$$listenerCount[name];
16368        }
16369      } while ((current = current.$parent));
16370    }
16371
16372    /**
16373     * function used as an initial value for watchers.
16374     * because it's unique we can easily tell it apart from other values
16375     */
16376    function initWatchVal() {}
16377
16378    function flushApplyAsync() {
vendor: 5,601 bytes, lines 16379-16524
16379      while (applyAsyncQueue.length) {
16380        try {
16381          applyAsyncQueue.shift()();
16382        } catch (e) {
16383          $exceptionHandler(e);
16384        }
16385      }
16386      applyAsyncId = null;
16387    }
16388
16389    function scheduleApplyAsync() {
16390      if (applyAsyncId === null) {
16391        applyAsyncId = $browser.defer(function() {
16392          $rootScope.$apply(flushApplyAsync);
16393        });
16394      }
16395    }
16396  }];
16397}
16398
16399/**
16400 * @description
16401 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16402 */
16403function $$SanitizeUriProvider() {
16404  var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16405    imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16406
16407  /**
16408   * @description
16409   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16410   * urls during a[href] sanitization.
16411   *
16412   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16413   *
16414   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16415   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16416   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16417   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16418   *
16419   * @param {RegExp=} regexp New regexp to whitelist urls with.
16420   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16421   *    chaining otherwise.
16422   */
16423  this.aHrefSanitizationWhitelist = function(regexp) {
16424    if (isDefined(regexp)) {
16425      aHrefSanitizationWhitelist = regexp;
16426      return this;
16427    }
16428    return aHrefSanitizationWhitelist;
16429  };
16430
16431
16432  /**
16433   * @description
16434   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16435   * urls during img[src] sanitization.
16436   *
16437   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16438   *
16439   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16440   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16441   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16442   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16443   *
16444   * @param {RegExp=} regexp New regexp to whitelist urls with.
16445   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16446   *    chaining otherwise.
16447   */
16448  this.imgSrcSanitizationWhitelist = function(regexp) {
16449    if (isDefined(regexp)) {
16450      imgSrcSanitizationWhitelist = regexp;
16451      return this;
16452    }
16453    return imgSrcSanitizationWhitelist;
16454  };
16455
16456  this.$get = function() {
16457    return function sanitizeUri(uri, isImage) {
16458      var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16459      var normalizedVal;
16460      normalizedVal = urlResolve(uri).href;
16461      if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16462        return 'unsafe:' + normalizedVal;
16463      }
16464      return uri;
16465    };
16466  };
16467}
16468
16469/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16470 *     Any commits to this file should be reviewed with security in mind.  *
16471 *   Changes to this file can potentially create security vulnerabilities. *
16472 *          An approval from 2 Core members with history of modifying      *
16473 *                         this file is required.                          *
16474 *                                                                         *
16475 *  Does the change somehow allow for arbitrary javascript to be executed? *
16476 *    Or allows for someone to change the prototype of built-in objects?   *
16477 *     Or gives undesired access to variables likes document or window?    *
16478 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16479
16480var $sceMinErr = minErr('$sce');
16481
16482var SCE_CONTEXTS = {
16483  HTML: 'html',
16484  CSS: 'css',
16485  URL: 'url',
16486  // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16487  // url.  (e.g. ng-include, script src, templateUrl)
16488  RESOURCE_URL: 'resourceUrl',
16489  JS: 'js'
16490};
16491
16492// Helper functions follow.
16493
16494function adjustMatcher(matcher) {
16495  if (matcher === 'self') {
16496    return matcher;
16497  } else if (isString(matcher)) {
16498    // Strings match exactly except for 2 wildcards - '*' and '**'.
16499    // '*' matches any character except those from the set ':/.?&'.
16500    // '**' matches any character (like .* in a RegExp).
16501    // More than 2 *'s raises an error as it's ill defined.
16502    if (matcher.indexOf('***') > -1) {
16503      throw $sceMinErr('iwcard',
16504          'Illegal sequence *** in string matcher.  String: {0}', matcher);
16505    }
16506    matcher = escapeForRegexp(matcher).
16507                  replace('\\*\\*', '.*').
16508                  replace('\\*', '[^:/.?&;]*');
16509    return new RegExp('^' + matcher + '$');
16510  } else if (isRegExp(matcher)) {
16511    // The only other type of matcher allowed is a Regexp.
16512    // Match entire URL / disallow partial matches.
16513    // Flags are reset (i.e. no global, ignoreCase or multiline)
16514    return new RegExp('^' + matcher.source + '$');
16515  } else {
16516    throw $sceMinErr('imatcher',
16517        'Matchers may only be "self", string patterns or RegExp objects');
16518  }
16519}
16520
16521
16522function adjustMatchers(matchers) {
16523  var adjustedMatchers = [];
16524  if (isDefined(matchers)) {
16525    forEach(matchers, function(matcher) {
16526      adjustedMatchers.push(adjustMatcher(matcher));
16527    });
16528  }
16529  return adjustedMatchers;
16530}
16531
16532
16533/**
16534 * @ngdoc service
16535 * @name $sceDelegate
16536 * @kind function
16537 *
16538 * @description
16539 *
16540 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16541 * Contextual Escaping (SCE)} services to AngularJS.
16542 *
16543 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16544 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
16545 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16546 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16547 * work because `$sce` delegates to `$sceDelegate` for these operations.
16548 *
16549 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16550 *
16551 * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
16552 * can override it completely to change the behavior of `$sce`, the common case would
16553 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16554 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16555 * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16556 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16557 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16558 */
16559
16560/**
16561 * @ngdoc provider
16562 * @name $sceDelegateProvider
16563 * @description
16564 *
16565 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16566 * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
16567 * that the URLs used for sourcing Angular templates are safe.  Refer {@link
16568 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16569 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16570 *
16571 * For the general details about this service in Angular, read the main page for {@link ng.$sce
16572 * Strict Contextual Escaping (SCE)}.
16573 *
16574 * **Example**:  Consider the following case. <a name="example"></a>
16575 *
16576 * - your app is hosted at url `http://myapp.example.com/`
16577 * - but some of your templates are hosted on other domains you control such as
16578 *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
16579 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16580 *
16581 * Here is what a secure configuration for this scenario might look like:
16582 *
16583 * ```
16584 *  angular.module('myApp', []).config(function($sceDelegateProvider) {
16585 *    $sceDelegateProvider.resourceUrlWhitelist([
16586 *      // Allow same origin resource loads.
16587 *      'self',
16588 *      // Allow loading from our assets domain.  Notice the difference between * and **.
16589 *      'http://srv*.assets.example.com/**'
16590 *    ]);
16591 *
16592 *    // The blacklist overrides the whitelist so the open redirect here is blocked.
16593 *    $sceDelegateProvider.resourceUrlBlacklist([
16594 *      'http://myapp.example.com/clickThru**'
16595 *    ]);
16596 *  });
16597 * ```
16598 */
16599
16600function $SceDelegateProvider() {
16601  this.SCE_CONTEXTS = SCE_CONTEXTS;
16602
16603  // Resource URLs can also be trusted by policy.
16604  var resourceUrlWhitelist = ['self'],
16605      resourceUrlBlacklist = [];
16606
16607  /**
16608   * @ngdoc method
16609   * @name $sceDelegateProvider#resourceUrlWhitelist
16610   * @kind function
16611   *
16612   * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16613   *     provided.  This must be an array or null.  A snapshot of this array is used so further
16614   *     changes to the array are ignored.
16615   *
16616   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16617   *     allowed in this array.
16618   *
16619   *     Note: **an empty whitelist array will block all URLs**!
16620   *
16621   * @return {Array} the currently set whitelist array.
16622   *
16623   * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
16624   * same origin resource requests.
16625   *
16626   * @description
16627   * Sets/Gets the whitelist of trusted resource URLs.
16628   */
16629  this.resourceUrlWhitelist = function(value) {
16630    if (arguments.length) {
16631      resourceUrlWhitelist = adjustMatchers(value);
16632    }
16633    return resourceUrlWhitelist;
16634  };
16635
16636  /**
16637   * @ngdoc method
16638   * @name $sceDelegateProvider#resourceUrlBlacklist
16639   * @kind function
16640   *
16641   * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
16642   *     provided.  This must be an array or null.  A snapshot of this array is used so further
16643   *     changes to the array are ignored.
16644   *
16645   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16646   *     allowed in this array.
16647   *
16648   *     The typical usage for the blacklist is to **block
16649   *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
16650   *     these would otherwise be trusted but actually return content from the redirected domain.
16651   *
16652   *     Finally, **the blacklist overrides the whitelist** and has the final say.
16653   *
16654   * @return {Array} the currently set blacklist array.
16655   *
16656   * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
16657   * is no blacklist.)
16658   *
16659   * @description
16660   * Sets/Gets the blacklist of trusted resource URLs.
16661   */
16662
16663  this.resourceUrlBlacklist = function(value) {
16664    if (arguments.length) {
16665      resourceUrlBlacklist = adjustMatchers(value);
16666    }
16667    return resourceUrlBlacklist;
16668  };
16669
16670  this.$get = ['$injector', function($injector) {
16671
16672    var htmlSanitizer = function htmlSanitizer(html) {
16673      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16674    };
16675
16676    if ($injector.has('$sanitize')) {
16677      htmlSanitizer = $injector.get('$sanitize');
16678    }
16679
16680
16681    function matchUrl(matcher, parsedUrl) {
16682      if (matcher === 'self') {
16683        return urlIsSameOrigin(parsedUrl);
16684      } else {
16685        // definitely a regex.  See adjustMatchers()
16686        return !!matcher.exec(parsedUrl.href);
16687      }
16688    }
16689
16690    function isResourceUrlAllowedByPolicy(url) {
16691      var parsedUrl = urlResolve(url.toString());
16692      var i, n, allowed = false;
16693      // Ensure that at least one item from the whitelist allows this url.
16694      for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
16695        if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
16696          allowed = true;
16697          break;
16698        }
16699      }
16700      if (allowed) {
16701        // Ensure that no item from the blacklist blocked this url.
16702        for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
16703          if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
16704            allowed = false;
16705            break;
16706          }
16707        }
16708      }
16709      return allowed;
16710    }
16711
16712    function generateHolderType(Base) {
16713      var holderType = function TrustedValueHolderType(trustedValue) {
16714        this.$$unwrapTrustedValue = function() {
16715          return trustedValue;
16716        };
16717      };
16718      if (Base) {
16719        holderType.prototype = new Base();
16720      }
16721      holderType.prototype.valueOf = function sceValueOf() {
16722        return this.$$unwrapTrustedValue();
16723      };
16724      holderType.prototype.toString = function sceToString() {
16725        return this.$$unwrapTrustedValue().toString();
16726      };
16727      return holderType;
16728    }
16729
16730    var trustedValueHolderBase = generateHolderType(),
16731        byType = {};
16732
16733    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
16734    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
16735    byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
16736    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
16737    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
16738
16739    /**
16740     * @ngdoc method
16741     * @name $sceDelegate#trustAs
16742     *
16743     * @description
16744     * Returns an object that is trusted by angular for use in specified strict
16745     * contextual escaping contexts (such as ng-bind-html, ng-include, any src
16746     * attribute interpolation, any dom event binding attribute interpolation
16747     * such as for onclick,  etc.) that uses the provided value.
16748     * See {@link ng.$sce $sce} for enabling strict contextual escaping.
16749     *
16750     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
16751     *   resourceUrl, html, js and css.
16752     * @param {*} value The value that that should be considered trusted/safe.
16753     * @returns {*} A value that can be used to stand in for the provided `value` in places
16754     * where Angular expects a $sce.trustAs() return value.
16755     */
16756    function trustAs(type, trustedValue) {
16757      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16758      if (!Constructor) {
16759        throw $sceMinErr('icontext',
vendor: 5,127 bytes, lines 16760-16883
16760            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
16761            type, trustedValue);
16762      }
16763      if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
16764        return trustedValue;
16765      }
16766      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
16767      // mutable objects, we ensure here that the value passed in is actually a string.
16768      if (typeof trustedValue !== 'string') {
16769        throw $sceMinErr('itype',
16770            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
16771            type);
16772      }
16773      return new Constructor(trustedValue);
16774    }
16775
16776    /**
16777     * @ngdoc method
16778     * @name $sceDelegate#valueOf
16779     *
16780     * @description
16781     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
16782     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
16783     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
16784     *
16785     * If the passed parameter is not a value that had been returned by {@link
16786     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
16787     *
16788     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
16789     *      call or anything else.
16790     * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
16791     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
16792     *     `value` unchanged.
16793     */
16794    function valueOf(maybeTrusted) {
16795      if (maybeTrusted instanceof trustedValueHolderBase) {
16796        return maybeTrusted.$$unwrapTrustedValue();
16797      } else {
16798        return maybeTrusted;
16799      }
16800    }
16801
16802    /**
16803     * @ngdoc method
16804     * @name $sceDelegate#getTrusted
16805     *
16806     * @description
16807     * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
16808     * returns the originally supplied value if the queried context type is a supertype of the
16809     * created type.  If this condition isn't satisfied, throws an exception.
16810     *
16811     * @param {string} type The kind of context in which this value is to be used.
16812     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
16813     *     `$sceDelegate.trustAs`} call.
16814     * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
16815     *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
16816     */
16817    function getTrusted(type, maybeTrusted) {
16818      if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
16819        return maybeTrusted;
16820      }
16821      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16822      if (constructor && maybeTrusted instanceof constructor) {
16823        return maybeTrusted.$$unwrapTrustedValue();
16824      }
16825      // If we get here, then we may only take one of two actions.
16826      // 1. sanitize the value for the requested type, or
16827      // 2. throw an exception.
16828      if (type === SCE_CONTEXTS.RESOURCE_URL) {
16829        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
16830          return maybeTrusted;
16831        } else {
16832          throw $sceMinErr('insecurl',
16833              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
16834              maybeTrusted.toString());
16835        }
16836      } else if (type === SCE_CONTEXTS.HTML) {
16837        return htmlSanitizer(maybeTrusted);
16838      }
16839      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16840    }
16841
16842    return { trustAs: trustAs,
16843             getTrusted: getTrusted,
16844             valueOf: valueOf };
16845  }];
16846}
16847
16848
16849/**
16850 * @ngdoc provider
16851 * @name $sceProvider
16852 * @description
16853 *
16854 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
16855 * -   enable/disable Strict Contextual Escaping (SCE) in a module
16856 * -   override the default implementation with a custom delegate
16857 *
16858 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
16859 */
16860
16861/* jshint maxlen: false*/
16862
16863/**
16864 * @ngdoc service
16865 * @name $sce
16866 * @kind function
16867 *
16868 * @description
16869 *
16870 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
16871 *
16872 * # Strict Contextual Escaping
16873 *
16874 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
16875 * contexts to result in a value that is marked as safe to use for that context.  One example of
16876 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
16877 * to these contexts as privileged or SCE contexts.
16878 *
16879 * As of version 1.2, Angular ships with SCE enabled by default.
16880 *
16881 * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
16882 * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
16883 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
16884 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
16885 * to the top of your HTML document.
16886 *
16887 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
16888 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
16889 *
16890 * Here's an example of a binding in a privileged context:
16891 *
16892 * ```
16893 * <input ng-model="userHtml" aria-label="User input">
16894 * <div ng-bind-html="userHtml"></div>
16895 * ```
16896 *
16897 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
16898 * disabled, this application allows the user to render arbitrary HTML into the DIV.
16899 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
16900 * bindings.  (HTML is just one example of a context where rendering user controlled input creates
16901 * security vulnerabilities.)
16902 *
16903 * For the case of HTML, you might use a library, either on the client side, or on the server side,
16904 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
16905 *
16906 * How would you ensure that every place that used these types of bindings was bound to a value that
16907 * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
16908 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
16909 * properties/fields and forgot to update the binding to the sanitized value?
16910 *
16911 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
16912 * determine that something explicitly says it's safe to use a value for binding in that
16913 * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
16914 * for those values that you can easily tell are safe - because they were received from your server,
16915 * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
16916 * allowing only the files in a specific directory to do this.  Ensuring that the internal API
16917 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
16918 *
16919 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
16920 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
16921 * obtain values that will be accepted by SCE / privileged contexts.
16922 *
16923 *
16924 * ## How does it work?
16925 *
16926 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
16927 * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
16928 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
16929 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
16930 *
16931 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
16932 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
16933 * simplified):
16934 *
16935 * ```
16936 * var ngBindHtmlDirective = ['$sce', function($sce) {
16937 *   return function(scope, element, attr) {
16938 *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
16939 *       element.html(value || '');
16940 *     });
16941 *   };
16942 * }];
16943 * ```
16944 *
16945 * ## Impact on loading templates
16946 *
16947 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
16948 * `templateUrl`'s specified by {@link guide/directive directives}.
16949 *
16950 * By default, Angular only loads templates from the same domain and protocol as the application
16951 * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
16952 * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
16953 * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
16954 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
16955 *
16956 * *Please note*:
16957 * The browser's
16958 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
16959 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
16960 * policy apply in addition to this and may further restrict whether the template is successfully
16961 * loaded.  This means that without the right CORS policy, loading templates from a different domain
16962 * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
16963 * browsers.
16964 *
16965 * ## This feels like too much overhead
16966 *
16967 * It's important to remember that SCE only applies to interpolation expressions.
16968 *
16969 * If your expressions are constant literals, they're automatically trusted and you don't need to
16970 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
16971 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
16972 *
16973 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
16974 * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
16975 *
16976 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
16977 * templates in `ng-include` from your application's domain without having to even know about SCE.
16978 * It blocks loading templates from other domains or loading templates over http from an https
16979 * served document.  You can change these by setting your own custom {@link
16980 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
16981 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
16982 *
16983 * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
16984 * application that's secure and can be audited to verify that with much more ease than bolting
16985 * security onto an application later.
16986 *
16987 * <a name="contexts"></a>
16988 * ## What trusted context types are supported?
16989 *
16990 * | Context             | Notes          |
16991 * |---------------------|----------------|
16992 * | `$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. |
16993 * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
16994 * | `$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. |
16995 * | `$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. |
16996 * | `$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. |
16997 *
16998 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
16999 *
17000 *  Each element in these arrays must be one of the following:
17001 *
17002 *  - **'self'**
17003 *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
17004 *      domain** as the application document using the **same protocol**.
17005 *  - **String** (except the special value `'self'`)
17006 *    - The string is matched against the full *normalized / absolute URL* of the resource
17007 *      being tested (substring matches are not good enough.)
17008 *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
17009 *      match themselves.
17010 *    - `*`: matches zero or more occurrences of any character other than one of the following 6
17011 *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
17012 *      in a whitelist.
17013 *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
17014 *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
17015 *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17016 *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
17017 *      http://foo.example.com/templates/**).
17018 *  - **RegExp** (*see caveat below*)
17019 *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
17020 *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
17021 *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17022 *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
17023 *      small number of cases.  A `.` character in the regex used when matching the scheme or a
17024 *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
17025 *      is highly recommended to use the string patterns and only fall back to regular expressions
17026 *      as a last resort.
17027 *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
17028 *      matched against the **entire** *normalized / absolute URL* of the resource being tested
17029 *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
17030 *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17031 *    - If you are generating your JavaScript from some other templating engine (not
17032 *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17033 *      remember to escape your regular expression (and be aware that you might need more than
17034 *      one level of escaping depending on your templating engine and the way you interpolated
17035 *      the value.)  Do make use of your platform's escaping mechanism as it might be good
17036 *      enough before coding your own.  E.g. Ruby has
17037 *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17038 *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17039 *      Javascript lacks a similar built in function for escaping.  Take a look at Google
17040 *      Closure library's [goog.string.regExpEscape(s)](
17041 *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17042 *
17043 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17044 *
17045 * ## Show me an example using SCE.
17046 *
17047 * <example module="mySceApp" deps="angular-sanitize.js">
17048 * <file name="index.html">
17049 *   <div ng-controller="AppController as myCtrl">
17050 *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17051 *     <b>User comments</b><br>
17052 *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17053 *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
17054 *     exploit.
17055 *     <div class="well">
17056 *       <div ng-repeat="userComment in myCtrl.userComments">
17057 *         <b>{{userComment.name}}</b>:
17058 *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17059 *         <br>
17060 *       </div>
17061 *     </div>
17062 *   </div>
17063 * </file>
17064 *
17065 * <file name="script.js">
17066 *   angular.module('mySceApp', ['ngSanitize'])
17067 *     .controller('AppController', ['$http', '$templateCache', '$sce',
17068 *       function($http, $templateCache, $sce) {
17069 *         var self = this;
17070 *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17071 *           self.userComments = userComments;
17072 *         });
17073 *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
17074 *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17075 *             'sanitization.&quot;">Hover over this text.</span>');
17076 *       }]);
17077 * </file>
17078 *
17079 * <file name="test_data.json">
17080 * [
17081 *   { "name": "Alice",
17082 *     "htmlComment":
17083 *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17084 *   },
17085 *   { "name": "Bob",
17086 *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
17087 *   }
17088 * ]
17089 * </file>
17090 *
17091 * <file name="protractor.js" type="protractor">
17092 *   describe('SCE doc demo', function() {
17093 *     it('should sanitize untrusted values', function() {
17094 *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17095 *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
17096 *     });
17097 *
17098 *     it('should NOT sanitize explicitly trusted values', function() {
17099 *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17100 *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17101 *           'sanitization.&quot;">Hover over this text.</span>');
17102 *     });
17103 *   });
17104 * </file>
17105 * </example>
17106 *
17107 *
17108 *
17109 * ## Can I disable SCE completely?
17110 *
17111 * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
17112 * for little coding overhead.  It will be much harder to take an SCE disabled application and
17113 * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
17114 * for cases where you have a lot of existing code that was written before SCE was introduced and
17115 * you're migrating them a module at a time.
17116 *
17117 * That said, here's how you can completely disable SCE:
17118 *
17119 * ```
17120 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17121 *   // Completely disable SCE.  For demonstration purposes only!
17122 *   // Do not use in new projects.
17123 *   $sceProvider.enabled(false);
17124 * });
17125 * ```
17126 *
17127 */
17128/* jshint maxlen: 100 */
17129
17130function $SceProvider() {
17131  var enabled = true;
17132
17133  /**
17134   * @ngdoc method
17135   * @name $sceProvider#enabled
17136   * @kind function
17137   *
17138   * @param {boolean=} value If provided, then enables/disables SCE.
17139   * @return {boolean} true if SCE is enabled, false otherwise.
17140   *
17141   * @description
17142   * Enables/disables SCE and returns the current value.
17143   */
17144  this.enabled = function(value) {
17145    if (arguments.length) {
17146      enabled = !!value;
17147    }
17148    return enabled;
17149  };
17150
17151
17152  /* Design notes on the default implementation for SCE.
17153   *
17154   * The API contract for the SCE delegate
17155   * -------------------------------------
17156   * The SCE delegate object must provide the following 3 methods:
17157   *
17158   * - trustAs(contextEnum, value)
17159   *     This method is used to tell the SCE service that the provided value is OK to use in the
17160   *     contexts specified by contextEnum.  It must return an object that will be accepted by
17161   *     getTrusted() for a compatible contextEnum and return this value.
17162   *
17163   * - valueOf(value)
17164   *     For values that were not produced by trustAs(), return them as is.  For values that were
17165   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
17166   *     trustAs is wrapping the given values into some type, this operation unwraps it when given
17167   *     such a value.
17168   *
17169   * - getTrusted(contextEnum, value)
17170   *     This function should return the a value that is safe to use in the context specified by
17171   *     contextEnum or throw and exception otherwise.
17172   *
17173   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17174   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
17175   * instance, an implementation could maintain a registry of all trusted objects by context.  In
17176   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
17177   * return the same object passed in if it was found in the registry under a compatible context or
17178   * throw an exception otherwise.  An implementation might only wrap values some of the time based
17179   * on some criteria.  getTrusted() might return a value and not throw an exception for special
17180   * constants or objects even if not wrapped.  All such implementations fulfill this contract.
17181   *
17182   *
17183   * A note on the inheritance model for SCE contexts
17184   * ------------------------------------------------
17185   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
17186   * is purely an implementation details.
17187   *
17188   * The contract is simply this:
17189   *
17190   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17191   *     will also succeed.
17192   *
17193   * Inheritance happens to capture this in a natural way.  In some future, we
17194   * may not use inheritance anymore.  That is OK because no code outside of
17195   * sce.js and sceSpecs.js would need to be aware of this detail.
17196   */
17197
17198  this.$get = ['$parse', '$sceDelegate', function(
17199                $parse,   $sceDelegate) {
17200    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
17201    // the "expression(javascript expression)" syntax which is insecure.
17202    if (enabled && msie < 8) {
17203      throw $sceMinErr('iequirks',
17204        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17205        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17206        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
17207    }
17208
17209    var sce = shallowCopy(SCE_CONTEXTS);
17210
17211    /**
17212     * @ngdoc method
17213     * @name $sce#isEnabled
17214     * @kind function
17215     *
17216     * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
17217     * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17218     *
17219     * @description
17220     * Returns a boolean indicating if SCE is enabled.
17221     */
17222    sce.isEnabled = function() {
17223      return enabled;
17224    };
17225    sce.trustAs = $sceDelegate.trustAs;
17226    sce.getTrusted = $sceDelegate.getTrusted;
17227    sce.valueOf = $sceDelegate.valueOf;
17228
17229    if (!enabled) {
17230      sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17231      sce.valueOf = identity;
17232    }
17233
17234    /**
17235     * @ngdoc method
17236     * @name $sce#parseAs
17237     *
17238     * @description
17239     * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
17240     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
17241     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17242     * *result*)}
17243     *
17244     * @param {string} type The kind of SCE context in which this result will be used.
17245     * @param {string} expression String expression to compile.
17246     * @returns {function(context, locals)} a function which represents the compiled expression:
17247     *
17248     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17249     *      are evaluated against (typically a scope object).
17250     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17251     *      `context`.
17252     */
17253    sce.parseAs = function sceParseAs(type, expr) {
17254      var parsed = $parse(expr);
17255      if (parsed.literal && parsed.constant) {
17256        return parsed;
17257      } else {
17258        return $parse(expr, function(value) {
17259          return sce.getTrusted(type, value);
17260        });
17261      }
17262    };
17263
17264    /**
17265     * @ngdoc method
17266     * @name $sce#trustAs
17267     *
17268     * @description
17269     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
17270     * returns an object that is trusted by angular for use in specified strict contextual
17271     * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17272     * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
17273     * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
17274     * escaping.
17275     *
17276     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17277     *   resourceUrl, html, js and css.
17278     * @param {*} value The value that that should be considered trusted/safe.
17279     * @returns {*} A value that can be used to stand in for the provided `value` in places
17280     * where Angular expects a $sce.trustAs() return value.
17281     */
17282
17283    /**
17284     * @ngdoc method
17285     * @name $sce#trustAsHtml
17286     *
17287     * @description
17288     * Shorthand method.  `$sce.trustAsHtml(value)` →
17289     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17290     *
17291     * @param {*} value The value to trustAs.
17292     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17293     *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
17294     *     only accept expressions that are either literal constants or are the
17295     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17296     */
17297
17298    /**
17299     * @ngdoc method
17300     * @name $sce#trustAsUrl
17301     *
17302     * @description
17303     * Shorthand method.  `$sce.trustAsUrl(value)` →
17304     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17305     *
17306     * @param {*} value The value to trustAs.
17307     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17308     *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
17309     *     only accept expressions that are either literal constants or are the
17310     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17311     */
17312
17313    /**
17314     * @ngdoc method
17315     * @name $sce#trustAsResourceUrl
17316     *
17317     * @description
17318     * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
17319     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17320     *
17321     * @param {*} value The value to trustAs.
17322     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17323     *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
17324     *     only accept expressions that are either literal constants or are the return
17325     *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
17326     */
17327
17328    /**
17329     * @ngdoc method
17330     * @name $sce#trustAsJs
17331     *
17332     * @description
17333     * Shorthand method.  `$sce.trustAsJs(value)` →
17334     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17335     *
17336     * @param {*} value The value to trustAs.
17337     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17338     *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
17339     *     only accept expressions that are either literal constants or are the
17340     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17341     */
17342
17343    /**
17344     * @ngdoc method
17345     * @name $sce#getTrusted
17346     *
17347     * @description
17348     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
17349     * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17350     * originally supplied value if the queried context type is a supertype of the created type.
17351     * If this condition isn't satisfied, throws an exception.
17352     *
17353     * @param {string} type The kind of context in which this value is to be used.
17354     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17355     *                         call.
17356     * @returns {*} The value the was originally provided to
17357     *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17358     *              Otherwise, throws an exception.
17359     */
17360
17361    /**
17362     * @ngdoc method
17363     * @name $sce#getTrustedHtml
17364     *
17365     * @description
17366     * Shorthand method.  `$sce.getTrustedHtml(value)` →
17367     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17368     *
17369     * @param {*} value The value to pass to `$sce.getTrusted`.
17370     * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17371     */
17372
17373    /**
17374     * @ngdoc method
17375     * @name $sce#getTrustedCss
17376     *
17377     * @description
17378     * Shorthand method.  `$sce.getTrustedCss(value)` →
17379     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17380     *
17381     * @param {*} value The value to pass to `$sce.getTrusted`.
17382     * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17383     */
17384
17385    /**
17386     * @ngdoc method
17387     * @name $sce#getTrustedUrl
17388     *
17389     * @description
17390     * Shorthand method.  `$sce.getTrustedUrl(value)` →
17391     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17392     *
17393     * @param {*} value The value to pass to `$sce.getTrusted`.
17394     * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17395     */
17396
17397    /**
17398     * @ngdoc method
17399     * @name $sce#getTrustedResourceUrl
17400     *
17401     * @description
17402     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
17403     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17404     *
17405     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17406     * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17407     */
17408
17409    /**
17410     * @ngdoc method
17411     * @name $sce#getTrustedJs
17412     *
17413     * @description
17414     * Shorthand method.  `$sce.getTrustedJs(value)` →
17415     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17416     *
17417     * @param {*} value The value to pass to `$sce.getTrusted`.
17418     * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17419     */
17420
17421    /**
17422     * @ngdoc method
17423     * @name $sce#parseAsHtml
17424     *
17425     * @description
17426     * Shorthand method.  `$sce.parseAsHtml(expression string)` →
17427     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17428     *
17429     * @param {string} expression String expression to compile.
17430     * @returns {function(context, locals)} a function which represents the compiled expression:
17431     *
17432     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17433     *      are evaluated against (typically a scope object).
17434     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17435     *      `context`.
17436     */
17437
17438    /**
17439     * @ngdoc method
17440     * @name $sce#parseAsCss
17441     *
17442     * @description
17443     * Shorthand method.  `$sce.parseAsCss(value)` →
17444     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17445     *
17446     * @param {string} expression String expression to compile.
17447     * @returns {function(context, locals)} a function which represents the compiled expression:
17448     *
17449     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17450     *      are evaluated against (typically a scope object).
17451     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17452     *      `context`.
17453     */
17454
17455    /**
17456     * @ngdoc method
17457     * @name $sce#parseAsUrl
17458     *
17459     * @description
17460     * Shorthand method.  `$sce.parseAsUrl(value)` →
17461     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17462     *
17463     * @param {string} expression String expression to compile.
17464     * @returns {function(context, locals)} a function which represents the compiled expression:
17465     *
17466     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17467     *      are evaluated against (typically a scope object).
17468     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17469     *      `context`.
17470     */
17471
17472    /**
17473     * @ngdoc method
17474     * @name $sce#parseAsResourceUrl
17475     *
17476     * @description
17477     * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
17478     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17479     *
17480     * @param {string} expression String expression to compile.
17481     * @returns {function(context, locals)} a function which represents the compiled expression:
17482     *
17483     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17484     *      are evaluated against (typically a scope object).
17485     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17486     *      `context`.
17487     */
17488
17489    /**
17490     * @ngdoc method
17491     * @name $sce#parseAsJs
17492     *
17493     * @description
17494     * Shorthand method.  `$sce.parseAsJs(value)` →
17495     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17496     *
17497     * @param {string} expression String expression to compile.
17498     * @returns {function(context, locals)} a function which represents the compiled expression:
17499     *
17500     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17501     *      are evaluated against (typically a scope object).
17502     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17503     *      `context`.
17504     */
17505
17506    // Shorthand delegations.
17507    var parse = sce.parseAs,
17508        getTrusted = sce.getTrusted,
17509        trustAs = sce.trustAs;
17510
17511    forEach(SCE_CONTEXTS, function(enumValue, name) {
17512      var lName = lowercase(name);
17513      sce[camelCase("parse_as_" + lName)] = function(expr) {
17514        return parse(enumValue, expr);
17515      };
17516      sce[camelCase("get_trusted_" + lName)] = function(value) {
17517        return getTrusted(enumValue, value);
17518      };
17519      sce[camelCase("trust_as_" + lName)] = function(value) {
17520        return trustAs(enumValue, value);
17521      };
17522    });
17523
17524    return sce;
17525  }];
17526}
17527
17528/**
17529 * !!! This is an undocumented "private" service !!!
17530 *
17531 * @name $sniffer
17532 * @requires $window
17533 * @requires $document
17534 *
17535 * @property {boolean} history Does the browser support html5 history api ?
17536 * @property {boolean} transitions Does the browser support CSS transition events ?
17537 * @property {boolean} animations Does the browser support CSS animation events ?
17538 *
17539 * @description
17540 * This is very simple implementation of testing browser's features.
17541 */
17542function $SnifferProvider() {
17543  this.$get = ['$window', '$document', function($window, $document) {
17544    var eventSupport = {},
17545        android =
17546          toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17547        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17548        document = $document[0] || {},
17549        vendorPrefix,
17550        vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17551        bodyStyle = document.body && document.body.style,
17552        transitions = false,
17553        animations = false,
17554        match;
17555
17556    if (bodyStyle) {
17557      for (var prop in bodyStyle) {
17558        if (match = vendorRegex.exec(prop)) {
17559          vendorPrefix = match[0];
17560          vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17561          break;
17562        }
17563      }
17564
17565      if (!vendorPrefix) {
17566        vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17567      }
17568
17569      transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17570      animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17571
17572      if (android && (!transitions ||  !animations)) {
17573        transitions = isString(bodyStyle.webkitTransition);
17574        animations = isString(bodyStyle.webkitAnimation);
17575      }
17576    }
17577
17578
17579    return {
17580      // Android has history.pushState, but it does not update location correctly
17581      // so let's not use the history API at all.
17582      // http://code.google.com/p/android/issues/detail?id=17471
17583      // https://github.com/angular/angular.js/issues/904
17584
17585      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17586      // so let's not use the history API also
17587      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17588      // jshint -W018
17589      history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17590      // jshint +W018
17591      hasEvent: function(event) {
17592        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17593        // it. In particular the event is not fired when backspace or delete key are pressed or
17594        // when cut operation is performed.
17595        // IE10+ implements 'input' event but it erroneously fires under various situations,
17596        // e.g. when placeholder changes, or a form is focused.
17597        if (event === 'input' && msie <= 11) return false;
17598
17599        if (isUndefined(eventSupport[event])) {
17600          var divElm = document.createElement('div');
17601          eventSupport[event] = 'on' + event in divElm;
17602        }
17603
17604        return eventSupport[event];
17605      },
17606      csp: csp(),
17607      vendorPrefix: vendorPrefix,
17608      transitions: transitions,
17609      animations: animations,
17610      android: android
17611    };
17612  }];
17613}
17614
17615var $compileMinErr = minErr('$compile');
17616
17617/**
17618 * @ngdoc service
17619 * @name $templateRequest
17620 *
17621 * @description
17622 * The `$templateRequest` service runs security checks then downloads the provided template using
17623 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
17624 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
17625 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
17626 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
17627 * when `tpl` is of type string and `$templateCache` has the matching entry.
17628 *
17629 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
17630 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
17631 *
17632 * @return {Promise} a promise for the HTTP response data of the given URL.
17633 *
17634 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
17635 */
17636function $TemplateRequestProvider() {
17637  this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
17638    function handleRequestFn(tpl, ignoreRequestError) {
17639      handleRequestFn.totalPendingRequests++;
17640
17641      // We consider the template cache holds only trusted templates, so
17642      // there's no need to go through whitelisting again for keys that already
17643      // are included in there. This also makes Angular accept any script
17644      // directive, no matter its name. However, we still need to unwrap trusted
17645      // types.
17646      if (!isString(tpl) || !$templateCache.get(tpl)) {
17647        tpl = $sce.getTrustedResourceUrl(tpl);
17648      }
17649
17650      var transformResponse = $http.defaults && $http.defaults.transformResponse;
17651
17652      if (isArray(transformResponse)) {
17653        transformResponse = transformResponse.filter(function(transformer) {
17654          return transformer !== defaultHttpResponseTransform;
17655        });
17656      } else if (transformResponse === defaultHttpResponseTransform) {
17657        transformResponse = null;
17658      }
17659
17660      var httpOptions = {
17661        cache: $templateCache,
17662        transformResponse: transformResponse
17663      };
17664
17665      return $http.get(tpl, httpOptions)
17666        ['finally'](function() {
17667          handleRequestFn.totalPendingRequests--;
17668        })
17669        .then(function(response) {
17670          $templateCache.put(tpl, response.data);
17671          return response.data;
17672        }, handleError);
17673
17674      function handleError(resp) {
17675        if (!ignoreRequestError) {
17676          throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
17677            tpl, resp.status, resp.statusText);
17678        }
17679        return $q.reject(resp);
17680      }
17681    }
17682
17683    handleRequestFn.totalPendingRequests = 0;
17684
17685    return handleRequestFn;
17686  }];
17687}
17688
vendor: 5,594 bytes, lines 17689-17842
17689function $$TestabilityProvider() {
17690  this.$get = ['$rootScope', '$browser', '$location',
17691       function($rootScope,   $browser,   $location) {
17692
17693    /**
17694     * @name $testability
17695     *
17696     * @description
17697     * The private $$testability service provides a collection of methods for use when debugging
17698     * or by automated test and debugging tools.
17699     */
17700    var testability = {};
17701
17702    /**
17703     * @name $$testability#findBindings
17704     *
17705     * @description
17706     * Returns an array of elements that are bound (via ng-bind or {{}})
17707     * to expressions matching the input.
17708     *
17709     * @param {Element} element The element root to search from.
17710     * @param {string} expression The binding expression to match.
17711     * @param {boolean} opt_exactMatch If true, only returns exact matches
17712     *     for the expression. Filters and whitespace are ignored.
17713     */
17714    testability.findBindings = function(element, expression, opt_exactMatch) {
17715      var bindings = element.getElementsByClassName('ng-binding');
17716      var matches = [];
17717      forEach(bindings, function(binding) {
17718        var dataBinding = angular.element(binding).data('$binding');
17719        if (dataBinding) {
17720          forEach(dataBinding, function(bindingName) {
17721            if (opt_exactMatch) {
17722              var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
17723              if (matcher.test(bindingName)) {
17724                matches.push(binding);
17725              }
17726            } else {
17727              if (bindingName.indexOf(expression) != -1) {
17728                matches.push(binding);
17729              }
17730            }
17731          });
17732        }
17733      });
17734      return matches;
17735    };
17736
17737    /**
17738     * @name $$testability#findModels
17739     *
17740     * @description
17741     * Returns an array of elements that are two-way found via ng-model to
17742     * expressions matching the input.
17743     *
17744     * @param {Element} element The element root to search from.
17745     * @param {string} expression The model expression to match.
17746     * @param {boolean} opt_exactMatch If true, only returns exact matches
17747     *     for the expression.
17748     */
17749    testability.findModels = function(element, expression, opt_exactMatch) {
17750      var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
17751      for (var p = 0; p < prefixes.length; ++p) {
17752        var attributeEquals = opt_exactMatch ? '=' : '*=';
17753        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
17754        var elements = element.querySelectorAll(selector);
17755        if (elements.length) {
17756          return elements;
17757        }
17758      }
17759    };
17760
17761    /**
17762     * @name $$testability#getLocation
17763     *
17764     * @description
17765     * Shortcut for getting the location in a browser agnostic way. Returns
17766     *     the path, search, and hash. (e.g. /path?a=b#hash)
17767     */
17768    testability.getLocation = function() {
17769      return $location.url();
17770    };
17771
17772    /**
17773     * @name $$testability#setLocation
17774     *
17775     * @description
17776     * Shortcut for navigating to a location without doing a full page reload.
17777     *
17778     * @param {string} url The location url (path, search and hash,
17779     *     e.g. /path?a=b#hash) to go to.
17780     */
17781    testability.setLocation = function(url) {
17782      if (url !== $location.url()) {
17783        $location.url(url);
17784        $rootScope.$digest();
17785      }
17786    };
17787
17788    /**
17789     * @name $$testability#whenStable
17790     *
17791     * @description
17792     * Calls the callback when $timeout and $http requests are completed.
17793     *
17794     * @param {function} callback
17795     */
17796    testability.whenStable = function(callback) {
17797      $browser.notifyWhenNoOutstandingRequests(callback);
17798    };
17799
17800    return testability;
17801  }];
17802}
17803
17804function $TimeoutProvider() {
17805  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
17806       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
17807
17808    var deferreds = {};
17809
17810
17811     /**
17812      * @ngdoc service
17813      * @name $timeout
17814      *
17815      * @description
17816      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
17817      * block and delegates any exceptions to
17818      * {@link ng.$exceptionHandler $exceptionHandler} service.
17819      *
17820      * The return value of calling `$timeout` is a promise, which will be resolved when
17821      * the delay has passed and the timeout function, if provided, is executed.
17822      *
17823      * To cancel a timeout request, call `$timeout.cancel(promise)`.
17824      *
17825      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
17826      * synchronously flush the queue of deferred functions.
17827      *
17828      * If you only want a promise that will be resolved after some specified delay
17829      * then you can call `$timeout` without the `fn` function.
17830      *
17831      * @param {function()=} fn A function, whose execution should be delayed.
17832      * @param {number=} [delay=0] Delay in milliseconds.
17833      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
17834      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
17835      * @param {...*=} Pass additional parameters to the executed function.
17836      * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
17837      *   promise will be resolved with is the return value of the `fn` function.
17838      *
17839      */
17840    function timeout(fn, delay, invokeApply) {
17841      if (!isFunction(fn)) {
17842        invokeApply = delay;
17843        delay = fn;
17844        fn = noop;
17845      }
17846
17847      var args = sliceArgs(arguments, 3),
17848          skipApply = (isDefined(invokeApply) && !invokeApply),
17849          deferred = (skipApply ? $$q : $q).defer(),
17850          promise = deferred.promise,
17851          timeoutId;
17852
17853      timeoutId = $browser.defer(function() {
17854        try {
17855          deferred.resolve(fn.apply(null, args));
17856        } catch (e) {
17857          deferred.reject(e);
17858          $exceptionHandler(e);
17859        }
17860        finally {
17861          delete deferreds[promise.$$timeoutId];
17862        }
17863
17864        if (!skipApply) $rootScope.$apply();
17865      }, delay);
17866
17867      promise.$$timeoutId = timeoutId;
17868      deferreds[timeoutId] = deferred;
17869
17870      return promise;
17871    }
17872
17873
17874     /**
17875      * @ngdoc method
17876      * @name $timeout#cancel
17877      *
17878      * @description
17879      * Cancels a task associated with the `promise`. As a result of this, the promise will be
17880      * resolved with a rejection.
17881      *
17882      * @param {Promise=} promise Promise returned by the `$timeout` function.
17883      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
17884      *   canceled.
17885      */
17886    timeout.cancel = function(promise) {
17887      if (promise && promise.$$timeoutId in deferreds) {
17888        deferreds[promise.$$timeoutId].reject('canceled');
17889        delete deferreds[promise.$$timeoutId];
17890        return $browser.defer.cancel(promise.$$timeoutId);
17891      }
17892      return false;
17893    };
17894
17895    return timeout;
17896  }];
17897}
17898
17899// NOTE:  The usage of window and document instead of $window and $document here is
17900// deliberate.  This service depends on the specific behavior of anchor nodes created by the
17901// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
17902// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
17903// doesn't know about mocked locations and resolves URLs to the real document - which is
17904// exactly the behavior needed here.  There is little value is mocking these out for this
17905// service.
17906var urlParsingNode = document.createElement("a");
17907var originUrl = urlResolve(window.location.href);
17908
17909
17910/**
17911 *
17912 * Implementation Notes for non-IE browsers
17913 * ----------------------------------------
17914 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
17915 * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
17916 * URL will be resolved into an absolute URL in the context of the application document.
17917 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
17918 * properties are all populated to reflect the normalized URL.  This approach has wide
17919 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
17920 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
17921 *
17922 * Implementation Notes for IE
17923 * ---------------------------
17924 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
17925 * browsers.  However, the parsed components will not be set if the URL assigned did not specify
17926 * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
17927 * work around that by performing the parsing in a 2nd step by taking a previously normalized
17928 * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
17929 * properties such as protocol, hostname, port, etc.
17930 *
17931 * References:
17932 *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
17933 *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
17934 *   http://url.spec.whatwg.org/#urlutils
17935 *   https://github.com/angular/angular.js/pull/2902
17936 *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
17937 *
17938 * @kind function
17939 * @param {string} url The URL to be parsed.
17940 * @description Normalizes and parses a URL.
17941 * @returns {object} Returns the normalized URL as a dictionary.
17942 *
17943 *   | member name   | Description    |
17944 *   |---------------|----------------|
17945 *   | href          | A normalized version of the provided URL if it was not an absolute URL |
17946 *   | protocol      | The protocol including the trailing colon                              |
17947 *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
17948 *   | search        | The search params, minus the question mark                             |
17949 *   | hash          | The hash string, minus the hash symbol
17950 *   | hostname      | The hostname
17951 *   | port          | The port, without ":"
17952 *   | pathname      | The pathname, beginning with "/"
17953 *
17954 */
17955function urlResolve(url) {
17956  var href = url;
17957
17958  if (msie) {
17959    // Normalize before parse.  Refer Implementation Notes on why this is
17960    // done in two steps on IE.
17961    urlParsingNode.setAttribute("href", href);
17962    href = urlParsingNode.href;
17963  }
17964
17965  urlParsingNode.setAttribute('href', href);
17966
17967  // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
17968  return {
17969    href: urlParsingNode.href,
17970    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
17971    host: urlParsingNode.host,
17972    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
17973    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
17974    hostname: urlParsingNode.hostname,
17975    port: urlParsingNode.port,
17976    pathname: (urlParsingNode.pathname.charAt(0) === '/')
17977      ? urlParsingNode.pathname
17978      : '/' + urlParsingNode.pathname
17979  };
17980}
17981
17982/**
17983 * Parse a request URL and determine whether this is a same-origin request as the application document.
17984 *
17985 * @param {string|object} requestUrl The url of the request as a string that will be resolved
17986 * or a parsed URL object.
17987 * @returns {boolean} Whether the request is for the same origin as the application document.
17988 */
17989function urlIsSameOrigin(requestUrl) {
17990  var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
17991  return (parsed.protocol === originUrl.protocol &&
17992          parsed.host === originUrl.host);
17993}
17994
17995/**
17996 * @ngdoc service
17997 * @name $window
17998 *
17999 * @description
18000 * A reference to the browser's `window` object. While `window`
18001 * is globally available in JavaScript, it causes testability problems, because
18002 * it is a global variable. In angular we always refer to it through the
18003 * `$window` service, so it may be overridden, removed or mocked for testing.
18004 *
18005 * Expressions, like the one defined for the `ngClick` directive in the example
18006 * below, are evaluated with respect to the current scope.  Therefore, there is
18007 * no risk of inadvertently coding in a dependency on a global value in such an
18008 * expression.
18009 *
18010 * @example
18011   <example module="windowExample">
18012     <file name="index.html">
18013       <script>
18014         angular.module('windowExample', [])
18015           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18016             $scope.greeting = 'Hello, World!';
18017             $scope.doGreeting = function(greeting) {
18018               $window.alert(greeting);
18019             };
18020           }]);
18021       </script>
18022       <div ng-controller="ExampleController">
18023         <input type="text" ng-model="greeting" aria-label="greeting" />
18024         <button ng-click="doGreeting(greeting)">ALERT</button>
18025       </div>
18026     </file>
18027     <file name="protractor.js" type="protractor">
18028      it('should display the greeting in the input box', function() {
18029       element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18030       // If we click the button it will block the test runner
18031       // element(':button').click();
18032      });
18033     </file>
18034   </example>
18035 */
18036function $WindowProvider() {
18037  this.$get = valueFn(window);
18038}
18039
18040/**
18041 * @name $$cookieReader
18042 * @requires $document
18043 *
18044 * @description
18045 * This is a private service for reading cookies used by $http and ngCookies
18046 *
18047 * @return {Object} a key/value map of the current cookies
18048 */
18049function $$CookieReader($document) {
18050  var rawDocument = $document[0] || {};
18051  var lastCookies = {};
18052  var lastCookieString = '';
18053
18054  function safeDecodeURIComponent(str) {
18055    try {
18056      return decodeURIComponent(str);
18057    } catch (e) {
18058      return str;
18059    }
18060  }
18061
18062  return function() {
18063    var cookieArray, cookie, i, index, name;
18064    var currentCookieString = rawDocument.cookie || '';
18065
18066    if (currentCookieString !== lastCookieString) {
18067      lastCookieString = currentCookieString;
18068      cookieArray = lastCookieString.split('; ');
18069      lastCookies = {};
18070
18071      for (i = 0; i < cookieArray.length; i++) {
18072        cookie = cookieArray[i];
18073        index = cookie.indexOf('=');
18074        if (index > 0) { //ignore nameless cookies
18075          name = safeDecodeURIComponent(cookie.substring(0, index));
18076          // the first value that is seen for a cookie is the most
18077          // specific one.  values for the same cookie name that
18078          // follow are for less specific paths.
18079          if (isUndefined(lastCookies[name])) {
18080            lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18081          }
18082        }
18083      }
18084    }
18085    return lastCookies;
18086  };
18087}
18088
18089$$CookieReader.$inject = ['$document'];
18090
18091function $$CookieReaderProvider() {
18092  this.$get = $$CookieReader;
18093}
18094
18095/* global currencyFilter: true,
18096 dateFilter: true,
18097 filterFilter: true,
18098 jsonFilter: true,
18099 limitToFilter: true,
18100 lowercaseFilter: true,
18101 numberFilter: true,
18102 orderByFilter: true,
18103 uppercaseFilter: true,
18104 */
18105
18106/**
18107 * @ngdoc provider
18108 * @name $filterProvider
18109 * @description
18110 *
18111 * Filters are just functions which transform input to an output. However filters need to be
18112 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18113 * annotated with dependencies and is responsible for creating a filter function.
18114 *
18115 * <div class="alert alert-warning">
18116 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18117 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18118 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18119 * (`myapp_subsection_filterx`).
18120 * </div>
18121 *
18122 * ```js
18123 *   // Filter registration
18124 *   function MyModule($provide, $filterProvider) {
18125 *     // create a service to demonstrate injection (not always needed)
18126 *     $provide.value('greet', function(name){
18127 *       return 'Hello ' + name + '!';
18128 *     });
18129 *
18130 *     // register a filter factory which uses the
18131 *     // greet service to demonstrate DI.
18132 *     $filterProvider.register('greet', function(greet){
18133 *       // return the filter function which uses the greet service
18134 *       // to generate salutation
18135 *       return function(text) {
18136 *         // filters need to be forgiving so check input validity
18137 *         return text && greet(text) || text;
18138 *       };
18139 *     });
18140 *   }
18141 * ```
18142 *
18143 * The filter function is registered with the `$injector` under the filter name suffix with
18144 * `Filter`.
18145 *
18146 * ```js
18147 *   it('should be the same instance', inject(
18148 *     function($filterProvider) {
18149 *       $filterProvider.register('reverse', function(){
18150 *         return ...;
18151 *       });
18152 *     },
18153 *     function($filter, reverseFilter) {
18154 *       expect($filter('reverse')).toBe(reverseFilter);
18155 *     });
18156 * ```
18157 *
18158 *
18159 * For more information about how angular filters work, and how to create your own filters, see
18160 * {@link guide/filter Filters} in the Angular Developer Guide.
18161 */
18162
18163/**
18164 * @ngdoc service
18165 * @name $filter
18166 * @kind function
18167 * @description
18168 * Filters are used for formatting data displayed to the user.
18169 *
18170 * The general syntax in templates is as follows:
18171 *
18172 *         {{ expression [| filter_name[:parameter_value] ... ] }}
18173 *
18174 * @param {String} name Name of the filter function to retrieve
18175 * @return {Function} the filter function
18176 * @example
18177   <example name="$filter" module="filterExample">
18178     <file name="index.html">
18179       <div ng-controller="MainCtrl">
18180        <h3>{{ originalText }}</h3>
18181        <h3>{{ filteredText }}</h3>
18182       </div>
18183     </file>
18184
18185     <file name="script.js">
18186      angular.module('filterExample', [])
18187      .controller('MainCtrl', function($scope, $filter) {
18188        $scope.originalText = 'hello';
18189        $scope.filteredText = $filter('uppercase')($scope.originalText);
18190      });
18191     </file>
18192   </example>
18193  */
18194$FilterProvider.$inject = ['$provide'];
18195function $FilterProvider($provide) {
18196  var suffix = 'Filter';
18197
18198  /**
18199   * @ngdoc method
18200   * @name $filterProvider#register
18201   * @param {string|Object} name Name of the filter function, or an object map of filters where
18202   *    the keys are the filter names and the values are the filter factories.
18203   *
18204   *    <div class="alert alert-warning">
18205   *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18206   *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18207   *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18208   *    (`myapp_subsection_filterx`).
18209   *    </div>
18210    * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18211   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18212   *    of the registered filter instances.
18213   */
18214  function register(name, factory) {
18215    if (isObject(name)) {
18216      var filters = {};
18217      forEach(name, function(filter, key) {
18218        filters[key] = register(key, filter);
18219      });
18220      return filters;
18221    } else {
18222      return $provide.factory(name + suffix, factory);
18223    }
18224  }
18225  this.register = register;
18226
18227  this.$get = ['$injector', function($injector) {
18228    return function(name) {
18229      return $injector.get(name + suffix);
18230    };
18231  }];
18232
18233  ////////////////////////////////////////
18234
18235  /* global
18236    currencyFilter: false,
18237    dateFilter: false,
18238    filterFilter: false,
18239    jsonFilter: false,
18240    limitToFilter: false,
18241    lowercaseFilter: false,
18242    numberFilter: false,
18243    orderByFilter: false,
18244    uppercaseFilter: false,
18245  */
18246
18247  register('currency', currencyFilter);
18248  register('date', dateFilter);
18249  register('filter', filterFilter);
18250  register('json', jsonFilter);
18251  register('limitTo', limitToFilter);
18252  register('lowercase', lowercaseFilter);
18253  register('number', numberFilter);
18254  register('orderBy', orderByFilter);
18255  register('uppercase', uppercaseFilter);
18256}
18257
18258/**
18259 * @ngdoc filter
18260 * @name filter
18261 * @kind function
18262 *
18263 * @description
18264 * Selects a subset of items from `array` and returns it as a new array.
18265 *
18266 * @param {Array} array The source array.
18267 * @param {string|Object|function()} expression The predicate to be used for selecting items from
18268 *   `array`.
18269 *
18270 *   Can be one of:
18271 *
18272 *   - `string`: The string is used for matching against the contents of the `array`. All strings or
18273 *     objects with string properties in `array` that match this string will be returned. This also
18274 *     applies to nested object properties.
18275 *     The predicate can be negated by prefixing the string with `!`.
18276 *
18277 *   - `Object`: A pattern object can be used to filter specific properties on objects contained
18278 *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18279 *     which have property `name` containing "M" and property `phone` containing "1". A special
18280 *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18281 *     property of the object or its nested object properties. That's equivalent to the simple
18282 *     substring match with a `string` as described above. The predicate can be negated by prefixing
18283 *     the string with `!`.
18284 *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
18285 *     not containing "M".
18286 *
18287 *     Note that a named property will match properties on the same level only, while the special
18288 *     `$` property will match properties on the same level or deeper. E.g. an array item like
18289 *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18290 *     **will** be matched by `{$: 'John'}`.
18291 *
18292 *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18293 *     The function is called for each element of the array, with the element, its index, and
18294 *     the entire array itself as arguments.
18295 *
18296 *     The final result is an array of those elements that the predicate returned true for.
18297 *
18298 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18299 *     determining if the expected value (from the filter expression) and actual value (from
18300 *     the object in the array) should be considered a match.
18301 *
18302 *   Can be one of:
18303 *
18304 *   - `function(actual, expected)`:
18305 *     The function will be given the object value and the predicate value to compare and
18306 *     should return true if both values should be considered equal.
18307 *
18308 *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18309 *     This is essentially strict comparison of expected and actual.
18310 *
18311 *   - `false|undefined`: A short hand for a function which will look for a substring match in case
18312 *     insensitive way.
18313 *
18314 *     Primitive values are converted to strings. Objects are not compared against primitives,
18315 *     unless they have a custom `toString` method (e.g. `Date` objects).
18316 *
18317 * @example
18318   <example>
18319     <file name="index.html">
18320       <div ng-init="friends = [{name:'John', phone:'555-1276'},
18321                                {name:'Mary', phone:'800-BIG-MARY'},
18322                                {name:'Mike', phone:'555-4321'},
18323                                {name:'Adam', phone:'555-5678'},
18324                                {name:'Julie', phone:'555-8765'},
18325                                {name:'Juliette', phone:'555-5678'}]"></div>
18326
18327       <label>Search: <input ng-model="searchText"></label>
18328       <table id="searchTextResults">
18329         <tr><th>
18329Name</th><th>Phone</th></tr>
18330         <tr ng-repeat="friend in friends | filter:searchText">
18331           <td>{{friend.name}}</td>
18332           <td>{{friend.phone}}</td>
18333         </tr>
18334       </table>
18335       <hr>
18336       <label>Any: <input ng-model="search.$"></label> <br>
18337       <label>Name only <input ng-model="search.name"></label><br>
18338       <label>Phone only <input ng-model="search.phone"></label><br>
18339       <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18340       <table id="searchObjResults">
18341         <tr><th>Name</th><th>Phone</th></tr>
18342         <tr ng-repeat="friendObj in friends | filter:search:strict">
18343           <td>{{friendObj.name}}</td>
18344           <td>{{friendObj.phone}}</td>
18345         </tr>
18346       </table>
18347     </file>
18348     <file name="protractor.js" type="protractor">
18349       var expectFriendNames = function(expectedNames, key) {
18350         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18351           arr.forEach(function(wd, i) {
18352             expect(wd.getText()).toMatch(expectedNames[i]);
18353           });
18354         });
18355       };
18356
18357       it('should search across all fields when filtering with a string', function() {
18358         var searchText = element(by.model('searchText'));
18359         searchText.clear();
18360         searchText.sendKeys('m');
18361         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18362
18363         searchText.clear();
18364         searchText.sendKeys('76');
18365         expectFriendNames(['John', 'Julie'], 'friend');
18366       });
18367
18368       it('should search in specific fields when filtering with a predicate object', function() {
18369         var searchAny = element(by.model('search.$'));
18370         searchAny.clear();
18371         searchAny.sendKeys('i');
18372         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18373       });
18374       it('should use a equal comparison when comparator is true', function() {
18375         var searchName = element(by.model('search.name'));
18376         var strict = element(by.model('strict'));
18377         searchName.clear();
18378         searchName.sendKeys('Julie');
18379         strict.click();
18380         expectFriendNames(['Julie'], 'friendObj');
18381       });
18382     </file>
18383   </example>
18384 */
18385function filterFilter() {
18386  return function(array, expression, comparator) {
18387    if (!isArrayLike(array)) {
18388      if (array == null) {
18389        return array;
18390      } else {
18391        throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18392      }
18393    }
18394
18395    var expressionType = getTypeForFilter(expression);
18396    var predicateFn;
18397    var matchAgainstAnyProp;
18398
18399    switch (expressionType) {
18400      case 'function':
18401        predicateFn = expression;
18402        break;
18403      case 'boolean':
18404      case 'null':
18405      case 'number':
18406      case 'string':
18407        matchAgainstAnyProp = true;
18408        //jshint -W086
18409      case 'object':
18410        //jshint +W086
18411        predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18412        break;
18413      default:
18414        return array;
18415    }
18416
18417    return Array.prototype.filter.call(array, predicateFn);
18418  };
18419}
18420
18421// Helper functions for `filterFilter`
18422function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18423  var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18424  var predicateFn;
18425
18426  if (comparator === true) {
18427    comparator = equals;
18428  } else if (!isFunction(comparator)) {
18429    comparator = function(actual, expected) {
18430      if (isUndefined(actual)) {
18431        // No substring matching against `undefined`
18432        return false;
18433      }
18434      if ((actual === null) || (expected === null)) {
18435        // No substring matching against `null`; only match against `null`
18436        return actual === expected;
18437      }
18438      if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18439        // Should not compare primitives against objects, unless they have custom `toString` method
18440        return false;
18441      }
18442
18443      actual = lowercase('' + actual);
18444      expected = lowercase('' + expected);
18445      return actual.indexOf(expected) !== -1;
18446    };
18447  }
18448
18449  predicateFn = function(item) {
18450    if (shouldMatchPrimitives && !isObject(item)) {
18451      return deepCompare(item, expression.$, comparator, false);
18452    }
18453    return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18454  };
18455
18456  return predicateFn;
18457}
18458
18459function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18460  var actualType = getTypeForFilter(actual);
18461  var expectedType = getTypeForFilter(expected);
18462
18463  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18464    return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18465  } else if (isArray(actual)) {
18466    // In case `actual` is an array, consider it a match
18467    // if ANY of it's items matches `expected`
18468    return actual.some(function(item) {
18469      return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18470    });
18471  }
18472
18473  switch (actualType) {
18474    case 'object':
18475      var key;
18476      if (matchAgainstAnyProp) {
18477        for (key in actual) {
18478          if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18479            return true;
18480          }
18481        }
18482        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18483      } else if (expectedType === 'object') {
18484        for (key in expected) {
18485          var expectedVal = expected[key];
18486          if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18487            continue;
18488          }
18489
18490          var matchAnyProperty = key === '$';
18491          var actualVal = matchAnyProperty ? actual : actual[key];
18492          if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18493            return false;
18494          }
18495        }
18496        return true;
18497      } else {
18498        return comparator(actual, expected);
18499      }
18500      break;
18501    case 'function':
18502      return false;
18503    default:
18504      return comparator(actual, expected);
18505  }
18506}
18507
18508// Used for easily differentiating between `null` and actual `object`
18509function getTypeForFilter(val) {
18510  return (val === null) ? 'null' : typeof val;
18511}
18512
18513/**
18514 * @ngdoc filter
18515 * @name currency
18516 * @kind function
18517 *
18518 * @description
18519 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18520 * symbol for current locale is used.
18521 *
18522 * @param {number} amount Input to filter.
18523 * @param {string=} symbol Currency symbol or identifier to be displayed.
18524 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18525 * @returns {string} Formatted number.
18526 *
18527 *
18528 * @example
18529   <example module="currencyExample">
18530     <file name="index.html">
18531       <script>
18532         angular.module('currencyExample', [])
18533           .controller('ExampleController', ['$scope', function($scope) {
18534             $scope.amount = 1234.56;
18535           }]);
18536       </script>
18537       <div ng-controller="ExampleController">
18538         <input type="number" ng-model="amount" aria-label="amount"> <br>
18539         default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18540         custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18541         no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18542       </div>
18543     </file>
18544     <file name="protractor.js" type="protractor">
18545       it('should init with 1234.56', function() {
18546         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18547         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18548         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18549       });
18550       it('should update', function() {
18551         if (browser.params.browser == 'safari') {
18552           // Safari does not understand the minus key. See
18553           // https://github.com/angular/protractor/issues/481
18554           return;
18555         }
18556         element(by.model('amount')).clear();
18557         element(by.model('amount')).sendKeys('-1234');
18558         expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
18559         expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
18560         expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
18561       });
18562     </file>
18563   </example>
18564 */
18565currencyFilter.$inject = ['$locale'];
18566function currencyFilter($locale) {
18567  var formats = $locale.NUMBER_FORMATS;
18568  return function(amount, currencySymbol, fractionSize) {
18569    if (isUndefined(currencySymbol)) {
18570      currencySymbol = formats.CURRENCY_SYM;
18571    }
18572
18573    if (isUndefined(fractionSize)) {
18574      fractionSize = formats.PATTERNS[1].maxFrac;
18575    }
18576
18577    // if null or undefined pass it through
18578    return (amount == null)
18579        ? amount
18580        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18581            replace(/\u00A4/g, currencySymbol);
18582  };
18583}
18584
18585/**
18586 * @ngdoc filter
18587 * @name number
18588 * @kind function
18589 *
18590 * @description
18591 * Formats a number as text.
18592 *
18593 * If the input is null or undefined, it will just be returned.
18594 * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
18595 * If the input is not a number an empty string is returned.
18596 *
18597 *
18598 * @param {number|string} number Number to format.
18599 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18600 * If this is not provided then the fraction size is computed from the current locale's number
18601 * formatting pattern. In the case of the default locale, it will be 3.
18602 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
18603 *
18604 * @example
18605   <example module="numberFilterExample">
18606     <file name="index.html">
18607       <script>
18608         angular.module('numberFilterExample', [])
18609           .controller('ExampleController', ['$scope', function($scope) {
18610             $scope.val = 1234.56789;
18611           }]);
18612       </script>
18613       <div ng-controller="ExampleController">
18614         <label>Enter number: <input ng-model='val'></label><br>
18615         Default formatting: <span id='number-default'>{{val | number}}</span><br>
18616         No fractions: <span>{{val | number:0}}</span><br>
18617         Negative number: <span>{{-val | number:4}}</span>
18618       </div>
18619     </file>
18620     <file name="protractor.js" type="protractor">
18621       it('should format numbers', function() {
18622         expect(element(by.id('number-default')).getText()).toBe('1,234.568');
18623         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
18624         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
18625       });
18626
18627       it('should update', function() {
18628         element(by.model('val')).clear();
18629         element(by.model('val')).sendKeys('3374.333');
18630         expect(element(by.id('number-default')).getText()).toBe('3,374.333');
18631         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
18632         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
18633      });
18634     </file>
18635   </example>
18636 */
18637
18638
18639numberFilter.$inject = ['$locale'];
18640function numberFilter($locale) {
18641  var formats = $locale.NUMBER_FORMATS;
18642  return function(number, fractionSize) {
18643
18644    // if null or undefined pass it through
18645    return (number == null)
18646        ? number
18647        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
18648                       fractionSize);
18649  };
18650}
18651
18652var DECIMAL_SEP = '.';
18653function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
18654  if (isObject(number)) return '';
18655
18656  var isNegative = number < 0;
18657  number = Math.abs(number);
18658
18659  var isInfinity = number === Infinity;
18660  if (!isInfinity && !isFinite(number)) return '';
18661
18662  var numStr = number + '',
18663      formatedText = '',
18664      hasExponent = false,
18665      parts = [];
18666
18667  if (isInfinity) formatedText = '\u221e';
18668
18669  if (!isInfinity && numStr.indexOf('e') !== -1) {
18670    var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
18671    if (match && match[2] == '-' && match[3] > fractionSize + 1) {
18672      number = 0;
18673    } else {
18674      formatedText = numStr;
18675      hasExponent = true;
18676    }
18677  }
18678
18679  if (!isInfinity && !hasExponent) {
18680    var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
18681
18682    // determine fractionSize if it is not specified
18683    if (isUndefined(fractionSize)) {
18684      fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
18685    }
18686
18687    // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
18688    // inspired by:
18689    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
18690    number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
18691
18692    var fraction = ('' + number).split(DECIMAL_SEP);
18693    var whole = fraction[0];
18694    fraction = fraction[1] || '';
18695
18696    var i, pos = 0,
18697        lgroup = pattern.lgSize,
18698        group = pattern.gSize;
18699
18700    if (whole.length >= (lgroup + group)) {
18701      pos = whole.length - lgroup;
18702      for (i = 0; i < pos; i++) {
18703        if ((pos - i) % group === 0 && i !== 0) {
18704          formatedText += groupSep;
18705        }
18706        formatedText += whole.charAt(i);
18707      }
18708    }
18709
18710    for (i = pos; i < whole.length; i++) {
18711      if ((whole.length - i) % lgroup === 0 && i !== 0) {
18712        formatedText += groupSep;
18713      }
18714      formatedText += whole.charAt(i);
18715    }
18716
18717    // format fraction part.
18718    while (fraction.length < fractionSize) {
18719      fraction += '0';
18720    }
18721
18722    if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
18723  } else {
18724    if (fractionSize > 0 && number < 1) {
18725      formatedText = number.toFixed(fractionSize);
18726      number = parseFloat(formatedText);
18727      formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
18728    }
18729  }
18730
18731  if (number === 0) {
18732    isNegative = false;
18733  }
18734
18735  parts.push(isNegative ? pattern.negPre : pattern.posPre,
18736             formatedText,
18737             isNegative ? pattern.negSuf : pattern.posSuf);
18738  return parts.join('');
18739}
18740
18741function padNumber(num, digits, trim) {
18742  var neg = '';
18743  if (num < 0) {
18744    neg =  '-';
18745    num = -num;
18746  }
18747  num = '' + num;
18748  while (num.length < digits) num = '0' + num;
18749  if (trim) {
18750    num = num.substr(num.length - digits);
18751  }
18752  return neg + num;
18753}
18754
18755
18756function dateGetter(name, size, offset, trim) {
18757  offset = offset || 0;
18758  return function(date) {
18759    var value = date['get' + name]();
18760    if (offset > 0 || value > -offset) {
18761      value += offset;
18762    }
18763    if (value === 0 && offset == -12) value = 12;
18764    return padNumber(value, size, trim);
18765  };
18766}
18767
18768function dateStrGetter(name, shortForm) {
18769  return function(date, formats) {
18770    var value = date['get' + name]();
18771    var get = uppercase(shortForm ? ('SHORT' + name) : name);
18772
18773    return formats[get][value];
18774  };
18775}
18776
18777function timeZoneGetter(date, formats, offset) {
18778  var zone = -1 * offset;
18779  var paddedZone = (zone >= 0) ? "+" : "";
18780
18781  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
18782                padNumber(Math.abs(zone % 60), 2);
18783
18784  return paddedZone;
18785}
18786
18787function getFirstThursdayOfYear(year) {
18788    // 0 = index of January
18789    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
18790    // 4 = index of Thursday (+1 to account for 1st = 5)
18791    // 11 = index of *next* Thursday (+1 account for 1st = 12)
18792    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
18793}
18794
18795function getThursdayThisWeek(datetime) {
18796    return new Date(datetime.getFullYear(), datetime.getMonth(),
18797      // 4 = index of Thursday
18798      datetime.getDate() + (4 - datetime.getDay()));
18799}
18800
18801function weekGetter(size) {
18802   return function(date) {
18803      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
18804         thisThurs = getThursdayThisWeek(date);
18805
18806      var diff = +thisThurs - +firstThurs,
18807         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
18808
18809      return padNumber(result, size);
18810   };
18811}
18812
18813function ampmGetter(date, formats) {
18814  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
18815}
18816
18817function eraGetter(date, formats) {
vendor: 5,270 bytes, lines 18818-18930
18818  return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
18819}
18820
18821function longEraGetter(date, formats) {
18822  return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
18823}
18824
18825var DATE_FORMATS = {
18826  yyyy: dateGetter('FullYear', 4),
18827    yy: dateGetter('FullYear', 2, 0, true),
18828     y: dateGetter('FullYear', 1),
18829  MMMM: dateStrGetter('Month'),
18830   MMM: dateStrGetter('Month', true),
18831    MM: dateGetter('Month', 2, 1),
18832     M: dateGetter('Month', 1, 1),
18833    dd: dateGetter('Date', 2),
18834     d: dateGetter('Date', 1),
18835    HH: dateGetter('Hours', 2),
18836     H: dateGetter('Hours', 1),
18837    hh: dateGetter('Hours', 2, -12),
18838     h: dateGetter('Hours', 1, -12),
18839    mm: dateGetter('Minutes', 2),
18840     m: dateGetter('Minutes', 1),
18841    ss: dateGetter('Seconds', 2),
18842     s: dateGetter('Seconds', 1),
18843     // while ISO 8601 requires fractions to be prefixed with `.` or `,`
18844     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
18845   sss: dateGetter('Milliseconds', 3),
18846  EEEE: dateStrGetter('Day'),
18847   EEE: dateStrGetter('Day', true),
18848     a: ampmGetter,
18849     Z: timeZoneGetter,
18850    ww: weekGetter(2),
18851     w: weekGetter(1),
18852     G: eraGetter,
18853     GG: eraGetter,
18854     GGG: eraGetter,
18855     GGGG: longEraGetter
18856};
18857
18858var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
18859    NUMBER_STRING = /^\-?\d+$/;
18860
18861/**
18862 * @ngdoc filter
18863 * @name date
18864 * @kind function
18865 *
18866 * @description
18867 *   Formats `date` to a string based on the requested `format`.
18868 *
18869 *   `format` string can be composed of the following elements:
18870 *
18871 *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
18872 *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
18873 *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
18874 *   * `'MMMM'`: Month in year (January-December)
18875 *   * `'MMM'`: Month in year (Jan-Dec)
18876 *   * `'MM'`: Month in year, padded (01-12)
18877 *   * `'M'`: Month in year (1-12)
18878 *   * `'dd'`: Day in month, padded (01-31)
18879 *   * `'d'`: Day in month (1-31)
18880 *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
18881 *   * `'EEE'`: Day in Week, (Sun-Sat)
18882 *   * `'HH'`: Hour in day, padded (00-23)
18883 *   * `'H'`: Hour in day (0-23)
18884 *   * `'hh'`: Hour in AM/PM, padded (01-12)
18885 *   * `'h'`: Hour in AM/PM, (1-12)
18886 *   * `'mm'`: Minute in hour, padded (00-59)
18887 *   * `'m'`: Minute in hour (0-59)
18888 *   * `'ss'`: Second in minute, padded (00-59)
18889 *   * `'s'`: Second in minute (0-59)
18890 *   * `'sss'`: Millisecond in second, padded (000-999)
18891 *   * `'a'`: AM/PM marker
18892 *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
18893 *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
18894 *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
18895 *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
18896 *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
18897 *
18898 *   `format` string can also be one of the following predefined
18899 *   {@link guide/i18n localizable formats}:
18900 *
18901 *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
18902 *     (e.g. Sep 3, 2010 12:05:08 PM)
18903 *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
18904 *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
18905 *     (e.g. Friday, September 3, 2010)
18906 *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
18907 *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
18908 *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
18909 *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
18910 *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
18911 *
18912 *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
18913 *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
18914 *   (e.g. `"h 'o''clock'"`).
18915 *
18916 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
18917 *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
18918 *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
18919 *    specified in the string input, the time is considered to be in the local timezone.
18920 * @param {string=} format Formatting rules (see Description). If not specified,
18921 *    `mediumDate` is used.
18922 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
18923 *    continental US time zone abbreviations, but for general use, use a time zone offset, for
18924 *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
18925 *    If not specified, the timezone of the browser will be used.
18926 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
18927 *
18928 * @example
18929   <example>
18930     <file name="index.html">
vendor: 4,190 bytes, lines 18931-19043
18931       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
18932           <span>{{1288323623006 | date:'medium'}}</span><br>
18933       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
18934          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
18935       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
18936          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
18937       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
18938          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
18939     </file>
18940     <file name="protractor.js" type="protractor">
18941       it('should format date', function() {
18942         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
18943            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
18944         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
18945            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
18946         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
18947            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
18948         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
18949            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
18950       });
18951     </file>
18952   </example>
18953 */
18954dateFilter.$inject = ['$locale'];
18955function dateFilter($locale) {
18956
18957
18958  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
18959                     // 1        2       3         4          5          6          7          8  9     10      11
18960  function jsonStringToDate(string) {
18961    var match;
18962    if (match = string.match(R_ISO8601_STR)) {
18963      var date = new Date(0),
18964          tzHour = 0,
18965          tzMin  = 0,
18966          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
18967          timeSetter = match[8] ? date.setUTCHours : date.setHours;
18968
18969      if (match[9]) {
18970        tzHour = toInt(match[9] + match[10]);
18971        tzMin = toInt(match[9] + match[11]);
18972      }
18973      dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
18974      var h = toInt(match[4] || 0) - tzHour;
18975      var m = toInt(match[5] || 0) - tzMin;
18976      var s = toInt(match[6] || 0);
18977      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
18978      timeSetter.call(date, h, m, s, ms);
18979      return date;
18980    }
18981    return string;
18982  }
18983
18984
18985  return function(date, format, timezone) {
18986    var text = '',
18987        parts = [],
18988        fn, match;
18989
18990    format = format || 'mediumDate';
18991    format = $locale.DATETIME_FORMATS[format] || format;
18992    if (isString(date)) {
18993      date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
18994    }
18995
18996    if (isNumber(date)) {
18997      date = new Date(date);
18998    }
18999
19000    if (!isDate(date) || !isFinite(date.getTime())) {
19001      return date;
19002    }
19003
19004    while (format) {
19005      match = DATE_FORMATS_SPLIT.exec(format);
19006      if (match) {
19007        parts = concat(parts, match, 1);
19008        format = parts.pop();
19009      } else {
19010        parts.push(format);
19011        format = null;
19012      }
19013    }
19014
19015    var dateTimezoneOffset = date.getTimezoneOffset();
19016    if (timezone) {
19017      dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
19018      date = convertTimezoneToLocal(date, timezone, true);
19019    }
19020    forEach(parts, function(value) {
19021      fn = DATE_FORMATS[value];
19022      text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19023                 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19024    });
19025
19026    return text;
19027  };
19028}
19029
19030
19031/**
19032 * @ngdoc filter
19033 * @name json
19034 * @kind function
19035 *
19036 * @description
19037 *   Allows you to convert a JavaScript object into JSON string.
19038 *
19039 *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
19040 *   the binding is automatically converted to JSON.
19041 *
19042 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19043 * @param {number=}
19043 spacing The number of spaces to use per indentation, defaults to 2.
19044 * @returns {string} JSON string.
19045 *
19046 *
19047 * @example
19048   <example>
19049     <file name="index.html">
19050       <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19051       <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19052     </file>
19053     <file name="protractor.js" type="protractor">
19054       it('should jsonify filtered objects', function() {
19055         expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
19056         expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
19057       });
19058     </file>
19059   </example>
19060 *
19061 */
19062function jsonFilter() {
19063  return function(object, spacing) {
19064    if (isUndefined(spacing)) {
19065        spacing = 2;
19066    }
19067    return toJson(object, spacing);
19068  };
19069}
19070
19071
19072/**
19073 * @ngdoc filter
19074 * @name lowercase
19075 * @kind function
19076 * @description
19077 * Converts string to lowercase.
19078 * @see angular.lowercase
19079 */
19080var lowercaseFilter = valueFn(lowercase);
19081
19082
19083/**
19084 * @ngdoc filter
19085 * @name uppercase
19086 * @kind function
19087 * @description
19088 * Converts string to uppercase.
19089 * @see angular.uppercase
19090 */
19091var uppercaseFilter = valueFn(uppercase);
19092
19093/**
19094 * @ngdoc filter
19095 * @name limitTo
19096 * @kind function
19097 *
19098 * @description
19099 * Creates a new array or string containing only a specified number of elements. The elements
19100 * are taken from either the beginning or the end of the source array, string or number, as specified by
19101 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19102 * converted to a string.
19103 *
19104 * @param {Array|string|number} input Source array, string or number to be limited.
19105 * @param {string|number} limit The length of the returned array or string. If the `limit` number
19106 *     is positive, `limit` number of items from the beginning of the source array/string are copied.
19107 *     If the number is negative, `limit` number  of items from the end of the source array/string
19108 *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19109 *     the input will be returned unchanged.
19110 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19111 *     indicates an offset from the end of `input`. Defaults to `0`.
19112 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19113 *     had less than `limit` elements.
19114 *
19115 * @example
19116   <example module="limitToExample">
19117     <file name="index.html">
19118       <script>
19119         angular.module('limitToExample', [])
19120           .controller('ExampleController', ['$scope', function($scope) {
19121             $scope.numbers = [1,2,3,4,5,6,7,8,9];
19122             $scope.letters = "abcdefghi";
19123             $scope.longNumber = 2345432342;
19124             $scope.numLimit = 3;
19125             $scope.letterLimit = 3;
19126             $scope.longNumberLimit = 3;
19127           }]);
19128       </script>
19129       <div ng-controller="ExampleController">
19130         <label>
19131            Limit {{numbers}} to:
19132            <input type="number" step="1" ng-model="numLimit">
19133         </label>
19134         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
19135         <label>
19136            Limit {{letters}} to:
19137            <input type="number" step="1" ng-model="letterLimit">
19138         </label>
19139         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
19140         <label>
19141            Limit {{longNumber}} to:
19142            <input type="number" step="1" ng-model="longNumberLimit">
19143         </label>
19144         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
19145       </div>
19146     </file>
19147     <file name="protractor.js" type="protractor">
19148       var numLimitInput = element(by.model('numLimit'));
19149       var letterLimitInput = element(by.model('letterLimit'));
19150       var longNumberLimitInput = element(by.model('longNumberLimit'));
19151       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
19152       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
19153       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
19154
19155       it('should limit the number array to first three items', function() {
19156         expect(numLimitInput.getAttribute('value')).toBe('3');
19157         expect(letterLimitInput.getAttribute('value')).toBe('3');
19158         expect(longNumberLimitInput.getAttribute('value')).toBe('3');
19159         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
19160         expect(limitedLetters.getText()).toEqual('Output letters: abc');
19161         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
19162       });
19163
19164       // There is a bug in safari and protractor that doesn't like the minus key
19165       // it('should update the output when -3 is entered', function() {
19166       //   numLimitInput.clear();
19167       //   numLimitInput.sendKeys('-3');
19168       //   letterLimitInput.clear();
19169       //   letterLimitInput.sendKeys('-3');
19170       //   longNumberLimitInput.clear();
19171       //   longNumberLimitInput.sendKeys('-3');
19172       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19173       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19174       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19175       // });
19176
19177       it('should not exceed the maximum size of input array', function() {
19178         numLimitInput.clear();
19179         numLimitInput.sendKeys('100');
19180         letterLimitInput.clear();
19181         letterLimitInput.sendKeys('100');
19182         longNumberLimitInput.clear();
19183         longNumberLimitInput.sendKeys('100');
19184         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19185         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19186         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19187       });
19188     </file>
19189   </example>
19190*/
19191function limitToFilter() {
19192  return function(input, limit, begin) {
19193    if (Math.abs(Number(limit)) === Infinity) {
19194      limit = Number(limit);
19195    } else {
19196      limit = toInt(limit);
19197    }
19198    if (isNaN(limit)) return input;
19199
19200    if (isNumber(input)) input = input.toString();
19201    if (!isArray(input) && !isString(input)) return input;
19202
19203    begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19204    begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin;
19205
19206    if (limit >= 0) {
19207      return input.slice(begin, begin + limit);
19208    } else {
19209      if (begin === 0) {
19210        return input.slice(limit, input.length);
19211      } else {
19212        return input.slice(Math.max(0, begin + limit), begin);
19213      }
19214    }
19215  };
19216}
19217
19218/**
19219 * @ngdoc filter
19220 * @name orderBy
19221 * @kind function
19222 *
19223 * @description
19224 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19225 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19226 * as expected, make sure they are actually being saved as numbers and not strings.
19227 *
19228 * @param {Array} array The array to sort.
19229 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19230 *    used by the comparator to determine the order of elements.
19231 *
19232 *    Can be one of:
19233 *
19234 *    - `function`: Getter function. The result of this function will be sorted using the
19235 *      `<`, `===`, `>` operator.
19236 *    - `string`: An Angular expression. The result of this expression is used to compare elements
19237 *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19238 *      3 first characters of a property called `name`). The result of a constant expression
19239 *      is interpreted as a property name to be used in comparisons (for example `"special name"`
19240 *      to sort object by the value of their `special name` property). An expression can be
19241 *      optionally prefixed with `+` or `-` to control ascending or descending sort order
19242 *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19243 *      element itself is used to compare where sorting.
19244 *    - `Array`: An array of function or string predicates. The first predicate in the array
19245 *      is used for sorting, but when two items are equivalent, the next predicate is used.
19246 *
19247 *    If the predicate is missing or empty then it defaults to `'+'`.
19248 *
19249 * @param {boolean=} reverse Reverse the order of the array.
19250 * @returns {Array} Sorted copy of the source array.
19251 *
19252 *
19253 * @example
19254 * The example below demonstrates a simple ngRepeat, where the data is sorted
19255 * by age in descending order (predicate is set to `'-age'`).
19256 * `reverse` is not set, which means it defaults to `false`.
19257   <example module="orderByExample">
19258     <file name="index.html">
19259       <script>
19260         angular.module('orderByExample', [])
19261           .controller('ExampleController', ['$scope', function($scope) {
19262             $scope.friends =
19263                 [{name:'John', phone:'555-1212', age:10},
19264                  {name:'Mary', phone:'555-9876', age:19},
19265                  {name:'Mike', phone:'555-4321', age:21},
19266                  {name:'Adam', phone:'555-5678', age:35},
19267                  {name:'Julie', phone:'555-8765', age:29}];
19268           }]);
19269       </script>
19270       <div ng-controller="ExampleController">
19271         <table class="friend">
19272           <tr>
19273             <th>Name</th>
19274             <th>Phone Number</th>
19275             <th>Age</th>
19276           </tr>
19277           <tr ng-repeat="friend in friends | orderBy:'-age'">
19278             <td>{{friend.name}}</td>
19279             <td>{{friend.phone}}</td>
19280             <td>{{friend.age}}</td>
19281           </tr>
19282         </table>
19283       </div>
19284     </file>
19285   </example>
19286 *
19287 * The predicate and reverse parameters can be controlled dynamically through scope properties,
19288 * as shown in the next example.
19289 * @example
19290   <example module="orderByExample">
19291     <file name="index.html">
19292       <script>
19293         angular.module('orderByExample', [])
19294           .controller('ExampleController', ['$scope', function($scope) {
19295             $scope.friends =
19296                 [{name:'John', phone:'555-1212', age:10},
19297                  {name:'Mary', phone:'555-9876', age:19},
19298                  {name:'Mike', phone:'555-4321', age:21},
19299                  {name:'Adam', phone:'555-5678', age:35},
19300                  {name:'Julie', phone:'555-8765', age:29}];
19301             $scope.predicate = 'age';
19302             $scope.reverse = true;
19303             $scope.order = function(predicate) {
19304               $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19305               $scope.predicate = predicate;
19306             };
19307           }]);
19308       </script>
19309       <style type="text/css">
19310         .sortorder:after {
19311           content: '\25b2';
19312         }
19313         .sortorder.reverse:after {
19314           content: '\25bc';
19315         }
19316       </style>
19317       <div ng-controller="ExampleController">
19318         <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19319         <hr/>
19320         [ <a href="" ng-click="predicate=''">unsorted</a> ]
19321         <table class="friend">
19322           <tr>
19323             <th>
19324               <a href="" ng-click="order('name')">Name</a>
19325               <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19326             </th>
19327             <th>
19328               <a href="" ng-click="order('phone')">Phone Number</a>
19329               <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19330             </th>
19331             <th>
19332               <a href="" ng-click="order('age')">Age</a>
19333               <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19334             </th>
19335           </tr>
19336           <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19337             <td>{{friend.name}}</td>
19338             <td>{{friend.phone}}</td>
19339             <td>{{friend.age}}</td>
19340           </tr>
19341         </table>
19342       </div>
19343     </file>
19344   </example>
19345 *
19346 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19347 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19348 * desired parameters.
19349 *
19350 * Example:
19351 *
19352 * @example
19353  <example module="orderByExample">
19354    <file name="index.html">
19355      <div ng-controller="ExampleController">
19356        <table class="friend">
19357          <tr>
19358            <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
19359              (<a href="" ng-click="order('-name',false)">^</a>)</th>
19360            <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
19361            <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
19362          </tr>
19363          <tr ng-repeat="friend in friends">
19364            <td>{{friend.name}}</td>
19365            <td>{{friend.phone}}</td>
19366            <td>{{friend.age}}</td>
19367          </tr>
19368        </table>
19369      </div>
19370    </file>
19371
19372    <file name="script.js">
19373      angular.module('orderByExample', [])
19374        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19375          var orderBy = $filter('orderBy');
19376          $scope.friends = [
19377            { name: 'John',    phone: '555-1212',    age: 10 },
19378            { name: 'Mary',    phone: '555-9876',    age: 19 },
19379            { name: 'Mike',    phone: '555-4321',    age: 21 },
19380            { name: 'Adam',    phone: '555-5678',    age: 35 },
19381            { name: 'Julie',   phone: '555-8765',    age: 29 }
19382          ];
19383          $scope.order = function(predicate, reverse) {
19384            $scope.friends = orderBy($scope.friends, predicate, reverse);
19385          };
19386          $scope.order('-age',false);
19387        }]);
19388    </file>
19389</example>
19390 */
19391orderByFilter.$inject = ['$parse'];
19392function orderByFilter($parse) {
19393  return function(array, sortPredicate, reverseOrder) {
19394
19395    if (!(isArrayLike(array))) return array;
19396
19397    if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
19398    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19399
19400    var predicates = processPredicates(sortPredicate, reverseOrder);
19401    // Add a predicate at the end that evaluates to the element index. This makes the
19402    // sort stable as it works as a tie-breaker when all the input predicates cannot
19403    // distinguish between two elements.
19404    predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
19405
19406    // The next three lines are a version of a Swartzian Transform idiom from Perl
19407    // (sometimes called the Decorate-Sort-Undecorate idiom)
19408    // See https://en.wikipedia.org/wiki/Schwartzian_transform
19409    var compareValues = Array.prototype.map.call(array, getComparisonObject);
19410    compareValues.sort(doComparison);
19411    array = compareValues.map(function(item) { return item.value; });
19412
19413    return array;
19414
19415    function getComparisonObject(value, index) {
19416      return {
19417        value: value,
19418        predicateValues: predicates.map(function(predicate) {
19419          return getPredicateValue(predicate.get(value), index);
19420        })
19421      };
19422    }
19423
19424    function doComparison(v1, v2) {
19425      var result = 0;
19426      for (var index=0, length = predicates.length; index < length; ++index) {
19427        result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
19428        if (result) break;
19429      }
19430      return result;
19431    }
19432  };
19433
19434  function processPredicates(sortPredicate, reverseOrder) {
19435    reverseOrder = reverseOrder ? -1 : 1;
19436    return sortPredicate.map(function(predicate) {
19437      var descending = 1, get = identity;
19438
19439      if (isFunction(predicate)) {
19440        get = predicate;
19441      } else if (isString(predicate)) {
19442        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19443          descending = predicate.charAt(0) == '-' ? -1 : 1;
19444          predicate = predicate.substring(1);
19445        }
19446        if (predicate !== '') {
19447          get = $parse(predicate);
19448          if (get.constant) {
19449            var key = get();
19450            get = function(value) { return value[key]; };
19451          }
19452        }
19453      }
19454      return { get: get, descending: descending * reverseOrder };
19455    });
19456  }
19457
19458  function isPrimitive(value) {
19459    switch (typeof value) {
19460      case 'number': /* falls through */
19461      case 'boolean': /* falls through */
19462      case 'string':
19463        return true;
19464      default:
19465        return false;
19466    }
19467  }
19468
19469  function objectValue(value, index) {
19470    // If `valueOf` is a valid function use that
19471    if (typeof value.valueOf === 'function') {
19472      value = value.valueOf();
19473      if (isPrimitive(value)) return value;
19474    }
19475    // If `toString` is a valid function and not the one from `Object.prototype` use that
19476    if (hasCustomToString(value)) {
19477      value = value.toString();
19478      if (isPrimitive(value)) return value;
19479    }
19480    // We have a basic object so we use the position of the object in the collection
19481    return index;
19482  }
19483
19484  function getPredicateValue(value, index) {
19485    var type = typeof value;
19486    if (value === null) {
19487      type = 'string';
19488      value = 'null';
19489    } else if (type === 'string') {
19490      value = value.toLowerCase();
19491    } else if (type === 'object') {
19492      value = objectValue(value, index);
19493    }
19494    return { value: value, type: type };
19495  }
19496
19497  function compare(v1, v2) {
19498    var result = 0;
19499    if (v1.type === v2.type) {
19500      if (v1.value !== v2.value) {
19501        result = v1.value < v2.value ? -1 : 1;
19502      }
19503    } else {
19504      result = v1.type < v2.type ? -1 : 1;
19505    }
19506    return result;
19507  }
19508}
19509
19510function ngDirective(directive) {
19511  if (isFunction(directive)) {
19512    directive = {
19513      link: directive
19514    };
19515  }
19516  directive.restrict = directive.restrict || 'AC';
19517  return valueFn(directive);
19518}
19519
19520/**
19521 * @ngdoc directive
19522 * @name a
19523 * @restrict E
19524 *
19525 * @description
19526 * Modifies the default behavior of the html A tag so that the default action is prevented when
19527 * the href attribute is empty.
19528 *
19529 * This change permits the easy creation of action links with the `ngClick` directive
19530 * without changing the location or causing page reloads, e.g.:
19531 * `<a href="" ng-click="list.addItem()">Add Item</a>`
19532 */
19533var htmlAnchorDirective = valueFn({
19534  restrict: 'E',
19535  compile: function(element, attr) {
19536    if (!attr.href && !attr.xlinkHref) {
19537      return function(scope, element) {
19538        // If the linked element is not an anchor tag anymore, do nothing
19539        if (element[0].nodeName.toLowerCase() !== 'a') return;
19540
19541        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
19542        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
19543                   'xlink:href' : 'href';
19544        element.on('click', function(event) {
19545          // if we have no href url, then don't navigate anywhere.
19546          if (!element.attr(href)) {
19547            event.preventDefault();
19548          }
19549        });
19550      };
19551    }
19552  }
19553});
19554
19555/**
19556 * @ngdoc directive
19557 * @name ngHref
19558 * @restrict A
19559 * @priority 99
19560 *
19561 * @description
19562 * Using Angular markup like `{{hash}}` in an href attribute will
19563 * make the link go to the wrong URL if the user clicks it before
19564 * Angular has a chance to replace the `{{hash}}` markup with its
19565 * value. Until Angular replaces the markup the link will be broken
19566 * and will most likely return a 404 error. The `ngHref` directive
19567 * solves this problem.
19568 *
19569 * The wrong way to write it:
19570 * ```html
19571 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19572 * ```
19573 *
19574 * The correct way to write it:
19575 * ```html
19576 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19577 * ```
19578 *
19579 * @element A
19580 * @param {template} ngHref any string which can contain `{{}}` markup.
19581 *
19582 * @example
19583 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
19584 * in links and their different behaviors:
19585    <example>
19586      <file name="index.html">
19587        <input ng-model="value" /><br />
19588        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
19589        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
19590        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
19591        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
19592        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
19593        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
19594      </file>
19595      <file name="protractor.js" type="protractor">
19596        it('should execute ng-click but not reload when href without value', function() {
19597          element(by.id('link-1')).click();
19598          expect(element(by.model('value')).getAttribute('value')).toEqual('1');
19599          expect(element(by.id('link-1')).getAttribute('href')).toBe('');
19600        });
19601
19602        it('should execute ng-click but not reload when href empty string', function() {
19603          element(by.id('link-2')).click();
19604          expect(element(by.model('value')).getAttribute('value')).toEqual('2');
19605          expect(element(by.id('link-2')).getAttribute('href')).toBe('');
19606        });
19607
19608        it('should execute ng-click and change url when ng-href specified', function() {
19609          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
19610
19611          element(by.id('link-3')).click();
19612
19613          // At this point, we navigate away from an Angular page, so we need
19614          // to use browser.driver to get the base webdriver.
19615
19616          browser.wait(function() {
19617            return browser.driver.getCurrentUrl().then(function(url) {
19618              return url.match(/\/123$/);
19619            });
19620          }, 5000, 'page should navigate to /123');
19621        });
19622
19623        it('should execute ng-click but not reload when href empty string and name specified', function() {
19624          element(by.id('link-4')).click();
19625          expect(element(by.model('value')).getAttribute('value')).toEqual('4');
19626          expect(element(by.id('link-4')).getAttribute('href')).toBe('');
19627        });
19628
19629        it('should execute ng-click but not reload when no href but name specified', function() {
19630          element(by.id('link-5')).click();
19631          expect(element(by.model('value')).getAttribute('value')).toEqual('5');
19632          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
19633        });
19634
19635        it('should only change url when only ng-href', function() {
19636          element(by.model('value')).clear();
19637          element(by.model('value')).sendKeys('6');
19638          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
19639
19640          element(by.id('link-6')).click();
19641
19642          // At this point, we navigate away from an Angular page, so we need
19643          // to use browser.driver to get the base webdriver.
19644          browser.wait(function() {
19645            return browser.driver.getCurrentUrl().then(function(url) {
19646              return url.match(/\/6$/);
19647            });
19648          }, 5000, 'page should navigate to /6');
19649        });
19650      </file>
19651    </example>
19652 */
19653
19654/**
19655 * @ngdoc directive
19656 * @name ngSrc
19657 * @restrict A
19658 * @priority 99
19659 *
19660 * @description
19661 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
19662 * work right: The browser will fetch from the URL with the literal
19663 * text `{{hash}}` until Angular replaces the expression inside
19664 * `{{hash}}`. The `ngSrc` directive solves this problem.
19665 *
19666 * The buggy way to write it:
19667 * ```html
19668 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
19669 * ```
19670 *
19671 * The correct way to write it:
19672 * ```html
19673 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
19674 * ```
19675 *
19676 * @element IMG
19677 * @param {template} ngSrc any string which can contain `{{}}` markup.
19678 */
19679
19680/**
19681 * @ngdoc directive
19682 * @name ngSrcset
19683 * @restrict A
19684 * @priority 99
19685 *
19686 * @description
19687 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
19688 * work right: The browser will fetch from the URL with the literal
19689 * text `{{hash}}` until Angular replaces the expression inside
19690 * `{{hash}}`. The `ngSrcset` directive solves this problem.
19691 *
19692 * The buggy way to write it:
19693 * ```html
19694 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
19695 * ```
19696 *
19697 * The correct way to write it:
19698 * ```html
19699 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
19700 * ```
19701 *
19702 * @element IMG
19703 * @param {template} ngSrcset any string which can contain `{{}}` markup.
19704 */
19705
19706/**
19707 * @ngdoc directive
19708 * @name ngDisabled
19709 * @restrict A
19710 * @priority 100
19711 *
19712 * @description
19713 *
19714 * This directive sets the `disabled` attribute on the element if the
19715 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
19716 *
19717 * A special directive is necessary because we cannot use interpolation inside the `disabled`
19718 * attribute.  The following example would make the button enabled on Chrome/Firefox
19719 * but not on older IEs:
19720 *
19721 * ```html
19722 * <!-- See below for an example of ng-disabled being used correctly -->
19723 * <div ng-init="isDisabled = false">
19724 *  <button disabled="{{isDisabled}}">Disabled</button>
19725 * </div>
19726 * ```
19727 *
19728 * This is because the HTML specification does not require browsers to preserve the values of
19729 * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
19730 * If we put an Angular interpolation expression into such an attribute then the
19731 * binding information would be lost when the browser removes the attribute.
19732 *
19733 * @example
19734    <example>
19735      <file name="index.html">
19736        <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
19737        <button ng-model="button" ng-disabled="checked">Button</button>
19738      </file>
19739      <file name="protractor.js" type="protractor">
19740        it('should toggle button', function() {
19741          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
19742          element(by.model('checked')).click();
19743          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
19744        });
19745      </file>
19746    </example>
19747 *
19748 * @element INPUT
19749 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
19750 *     then the `disabled` attribute will be set on the element
19751 */
19752
19753
19754/**
19755 * @ngdoc directive
19756 * @name ngChecked
19757 * @restrict A
19758 * @priority 100
19759 *
19760 * @description
19761 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
19762 *
19763 * Note that this directive should not be used together with {@link ngModel `ngModel`},
19764 * as this can lead to unexpected behavior.
19765 *
19766 * ### Why do we need `ngChecked`?
19767 *
19768 * The HTML specification does not require browsers to preserve the values of boolean attributes
19769 * such as checked. (Their presence means true and their absence means false.)
19770 * If we put an Angular interpolation expression into such an attribute then the
19771 * binding information would be lost when the browser removes the attribute.
19772 * The `ngChecked` directive solves this problem for the `checked` attribute.
19773 * This complementary directive is not removed by the browser and so provides
19774 * a permanent reliable place to store the binding information.
19775 * @example
19776    <example>
19777      <file name="index.html">
19778        <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
19779        <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
19780      </file>
19781      <file name="protractor.js" type="protractor">
19782        it('should check both checkBoxes', function() {
19783          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
19784          element(by.model('master')).click();
19785          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
19786        });
19787      </file>
19788    </example>
19789 *
19790 * @element INPUT
19791 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
19792 *     then the `checked` attribute will be set on the element
19793 */
19794
19795
19796/**
19797 * @ngdoc directive
19798 * @name ngReadonly
19799 * @restrict A
19800 * @priority 100
19801 *
19802 * @description
19803 * The HTML specification does not require browsers to preserve the values of boolean attributes
19804 * such as readonly. (Their presence means true and their absence means false.)
19805 * If we put an Angular interpolation expression into such an attribute then the
19806 * binding information would be lost when the browser removes the attribute.
19807 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
19808 * This complementary directive is not removed by the browser and so provides
19809 * a permanent reliable place to store the binding information.
19810 * @example
19811    <example>
19812      <file name="index.html">
19813        <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
19814        <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
19815      </file>
19816      <file name="protractor.js" type="protractor">
19817        it('should toggle readonly attr', function() {
19818          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
19819          element(by.model('checked')).click();
19820          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
19821        });
19822      </file>
19823    </example>
19824 *
19825 * @element INPUT
19826 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
19827 *     then special attribute "readonly" will be set on the element
19828 */
19829
19830
19831/**
19832 * @ngdoc directive
19833 * @name ngSelected
19834 * @restrict A
19835 * @priority 100
19836 *
19837 * @description
19838 * The HTML specification does not require browsers to preserve the values of boolean attributes
19839 * such as selected. (Their presence means true and their absence means false.)
19840 * If we put an Angular interpolation expression into such an attribute then the
19841 * binding information would be lost when the browser removes the attribute.
19842 * The `ngSelected` directive solves this problem for the `selected` attribute.
19843 * This complementary directive is not removed by the browser and so provides
19844 * a permanent reliable place to store the binding information.
19845 *
19846 * @example
19847    <example>
19848      <file name="index.html">
19849        <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
19850        <select aria-label="ngSelected demo">
19851          <option>Hello!</option>
19852          <option id="greet" ng-selected="selected">Greetings!</option>
19853        </select>
19854      </file>
19855      <file name="protractor.js" type="protractor">
19856        it('should select Greetings!', function() {
19857          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
19858          element(by.model('selected')).click();
19859          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
19860        });
19861      </file>
19862    </example>
19863 *
19864 * @element OPTION
19865 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
19866 *     then special attribute "selected" will be set on the element
19867 */
19868
19869/**
19870 * @ngdoc directive
19871 * @name ngOpen
19872 * @restrict A
19873 * @priority 100
19874 *
19875 * @description
19876 * The HTML specification does not require browsers to preserve the values of boolean attributes
19877 * such as open. (Their presence means true and their absence means false.)
19878 * If we put an Angular interpolation expression into such an attribute then the
19879 * binding information would be lost when the browser removes the attribute.
19880 * The `ngOpen` directive solves this problem for the `open` attribute.
19881 * This complementary directive is not removed by the browser and so provides
19882 * a permanent reliable place to store the binding information.
19883 * @example
19884     <example>
19885       <file name="index.html">
19886         <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
19887         <details id="details" ng-open="open">
19888            <summary>Show/Hide me</summary>
19889         </details>
19890       </file>
19891       <file name="protractor.js" type="protractor">
19892         it('should toggle open', function() {
19893           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
19894           element(by.model('open')).click();
19895           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
19896         });
19897       </file>
19898     </example>
19899 *
19900 * @element DETAILS
19901 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
19902 *     then special attribute "open" will be set on the element
19903 */
19904
19905var ngAttributeAliasDirectives = {};
19906
19907// boolean attrs are evaluated
19908forEach(BOOLEAN_ATTR, function(propName, attrName) {
19909  // binding to multiple is not supported
19910  if (propName == "multiple") return;
19911
19912  function defaultLinkFn(scope, element, attr) {
19913    scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
19914      attr.$set(attrName, !!value);
19915    });
19916  }
19917
19918  var normalized = directiveNormalize('ng-' + attrName);
19919  var linkFn = defaultLinkFn;
19920
19921  if (propName === 'checked') {
19922    linkFn = function(scope, element, attr) {
19923      // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
19924      if (attr.ngModel !== attr[normalized]) {
19925        defaultLinkFn(scope, element, attr);
19926      }
19927    };
19928  }
19929
19930  ngAttributeAliasDirectives[normalized] = function() {
19931    return {
19932      restrict: 'A',
19933      priority: 100,
19934      link: linkFn
19935    };
19936  };
19937});
19938
19939// aliased input attrs are evaluated
19940forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
19941  ngAttributeAliasDirectives[ngAttr] = function() {
19942    return {
19943      priority: 100,
19944      link: function(scope, element, attr) {
19945        //special case ngPattern when a literal regular expression value
19946        //is used as the expression (this way we don't have to watch anything).
19947        if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
19948          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
19949          if (match) {
19950            attr.$set("ngPattern", new RegExp(match[1], match[2]));
19951            return;
19952          }
19953        }
19954
19955        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
19956          attr.$set(ngAttr, value);
19957        });
19958      }
19959    };
19960  };
19961});
19962
19963// ng-src, ng-srcset, ng-href are interpolated
19964forEach(['src', 'srcset', 'href'], function(attrName) {
19965  var normalized = directiveNormalize('ng-' + attrName);
19966  ngAttributeAliasDirectives[normalized] = function() {
19967    return {
19968      priority: 99, // it needs to run after the attributes are interpolated
19969      link: function(scope, element, attr) {
19970        var propName = attrName,
19971            name = attrName;
19972
19973        if (attrName === 'href' &&
19974            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
19975          name = 'xlinkHref';
19976          attr.$attr[name] = 'xlink:href';
19977          propName = null;
19978        }
19979
19980        attr.$observe(normalized, function(value) {
19981          if (!value) {
19982            if (attrName === 'href') {
19983              attr.$set(name, null);
19984            }
19985            return;
19986          }
19987
19988          attr.$set(name, value);
19989
19990          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
19991          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
19992          // to set the property as well to achieve the desired effect.
19993          // we use attr[attrName] value since $set can sanitize the url.
19994          if (msie && propName) element.prop(propName, attr[name]);
19995        });
19996      }
19997    };
19998  };
19999});
20000
20001/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20002 */
20003var nullFormCtrl = {
20004  $addControl: noop,
20005  $$renameControl: nullFormRenameControl,
20006  $removeControl: noop,
20007  $setValidity: noop,
20008  $setDirty: noop,
20009  $setPristine: noop,
20010  $setSubmitted: noop
20011},
20012SUBMITTED_CLASS = 'ng-submitted';
20013
20014function nullFormRenameControl(control, name) {
20015  control.$name = name;
20016}
20017
20018/**
20019 * @ngdoc type
20020 * @name form.FormController
20021 *
20022 * @property {boolean} $pristine True if user has not interacted with the form yet.
20023 * @property {boolean} $dirty True if user has already interacted with the form.
20024 * @property {boolean} $valid True if all of the containing forms and controls are valid.
20025 * @property {boolean}
20025 $invalid True if at least one containing control or form is invalid.
20026 * @property {boolean} $pending True if at least one containing control or form is pending.
20027 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20028 *
20029 * @property {Object} $error Is an object hash, containing references to controls or
20030 *  forms with failing validators, where:
20031 *
20032 *  - keys are validation tokens (error names),
20033 *  - values are arrays of controls or forms that have a failing validator for given error name.
20034 *
20035 *  Built-in validation tokens:
20036 *
20037 *  - `email`
20038 *  - `max`
20039 *  - `maxlength`
20040 *  - `min`
20041 *  - `minlength`
20042 *  - `number`
20043 *  - `pattern`
20044 *  - `required`
20045 *  - `url`
20046 *  - `date`
20047 *  - `datetimelocal`
20048 *  - `time`
20049 *  - `week`
20050 *  - `month`
20051 *
20052 * @description
20053 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20054 * such as being valid/invalid or dirty/pristine.
20055 *
20056 * Each {@link ng.directive:form form} directive creates an instance
20057 * of `FormController`.
20058 *
20059 */
20060//asks for $scope to fool the BC controller module
20061FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20062function FormController(element, attrs, $scope, $animate, $interpolate) {
20063  var form = this,
20064      controls = [];
20065
20066  // init state
20067  form.$error = {};
20068  form.$$success = {};
20069  form.$pending = undefined;
20070  form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20071  form.$dirty = false;
20072  form.$pristine = true;
20073  form.$valid = true;
20074  form.$invalid = false;
20075  form.$submitted = false;
20076  form.$$parentForm = nullFormCtrl;
20077
20078  /**
20079   * @ngdoc method
20080   * @name form.FormController#$rollbackViewValue
20081   *
20082   * @description
20083   * Rollback all form controls pending updates to the `$modelValue`.
20084   *
20085   * Updates may be pending by a debounced event or because the input is waiting for a some future
20086   * event defined in `ng-model-options`. This method is typically needed by the reset button of
20087   * a form that uses `ng-model-options` to pend updates.
20088   */
20089  form.$rollbackViewValue = function() {
20090    forEach(controls, function(control) {
20091      control.$rollbackViewValue();
20092    });
20093  };
20094
20095  /**
20096   * @ngdoc method
20097   * @name form.FormController#$commitViewValue
20098   *
20099   * @description
20100   * Commit all form controls pending updates to the `$modelValue`.
20101   *
20102   * Updates may be pending by a debounced event or because the input is waiting for a some future
20103   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20104   * usually handles calling this in response to input events.
20105   */
20106  form.$commitViewValue = function() {
20107    forEach(controls, function(control) {
20108      control.$commitViewValue();
20109    });
20110  };
20111
20112  /**
20113   * @ngdoc method
20114   * @name form.FormController#$addControl
20115   * @param {object} control control object, either a {@link form.FormController} or an
20116   * {@link ngModel.NgModelController}
20117   *
20118   * @description
20119   * Register a control with the form. Input elements using ngModelController do this automatically
20120   * when they are linked.
20121   *
20122   * Note that the current state of the control will not be reflected on the new parent form. This
20123   * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20124   * state.
20125   *
20126   * However, if the method is used programmatically, for example by adding dynamically created controls,
20127   * or controls that have been previously removed without destroying their corresponding DOM element,
20128   * it's the developers responsiblity to make sure the current state propagates to the parent form.
20129   *
20130   * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20131   * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20132   */
20133  form.$addControl = function(control) {
20134    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
20135    // and not added to the scope.  Now we throw an error.
20136    assertNotHasOwnProperty(control.$name, 'input');
20137    controls.push(control);
20138
20139    if (control.$name) {
20140      form[control.$name] = control;
20141    }
20142
20143    control.$$parentForm = form;
20144  };
20145
20146  // Private API: rename a form control
20147  form.$$renameControl = function(control, newName) {
20148    var oldName = control.$name;
20149
20150    if (form[oldName] === control) {
20151      delete form[oldName];
20152    }
20153    form[newName] = control;
20154    control.$name = newName;
20155  };
20156
20157  /**
20158   * @ngdoc method
20159   * @name form.FormController#$removeControl
20160   * @param {object} control control object, either a {@link form.FormController} or an
20161   * {@link ngModel.NgModelController}
20162   *
20163   * @description
20164   * Deregister a control from the form.
20165   *
20166   * Input elements using ngModelController do this automatically when they are destroyed.
20167   *
20168   * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20169   * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20170   * different from case to case. For example, removing the only `$dirty` control from a form may or
20171   * may not mean that the form is still `$dirty`.
20172   */
20173  form.$removeControl = function(control) {
20174    if (control.$name && form[control.$name] === control) {
20175      delete form[control.$name];
20176    }
20177    forEach(form.$pending, function(value, name) {
20178      form.$setValidity(name, null, control);
20179    });
20180    forEach(form.$error, function(value, name) {
20181      form.$setValidity(name, null, control);
20182    });
20183    forEach(form.$$success, function(value, name) {
20184      form.$setValidity(name, null, control);
20185    });
20186
20187    arrayRemove(controls, control);
20188    control.$$parentForm = nullFormCtrl;
20189  };
20190
20191
20192  /**
20193   * @ngdoc method
20194   * @name form.FormController#$setValidity
20195   *
20196   * @description
20197   * Sets the validity of a form control.
20198   *
20199   * This method will also propagate to parent forms.
20200   */
20201  addSetValidityMethod({
20202    ctrl: this,
20203    $element: element,
20204    set: function(object, property, controller) {
20205      var list = object[property];
20206      if (!list) {
20207        object[property] = [controller];
20208      } else {
20209        var index = list.indexOf(controller);
20210        if (index === -1) {
20211          list.push(controller);
20212        }
20213      }
20214    },
20215    unset: function(object, property, controller) {
20216      var list = object[property];
20217      if (!list) {
20218        return;
20219      }
20220      arrayRemove(list, controller);
20221      if (list.length === 0) {
20222        delete object[property];
20223      }
20224    },
20225    $animate: $animate
20226  });
20227
20228  /**
20229   * @ngdoc method
20230   * @name form.FormController#$setDirty
20231   *
20232   * @description
20233   * Sets the form to a dirty state.
20234   *
20235   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20236   * state (ng-dirty class). This method will also propagate to parent forms.
20237   */
20238  form.$setDirty = function() {
20239    $animate.removeClass(element, PRISTINE_CLASS);
20240    $animate.addClass(element, DIRTY_CLASS);
20241    form.$dirty = true;
20242    form.$pristine = false;
20243    form.$$parentForm.$setDirty();
20244  };
20245
20246  /**
20247   * @ngdoc method
20248   * @name form.FormController#$setPristine
20249   *
20250   * @description
20251   * Sets the form to its pristine state.
20252   *
20253   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20254   * state (ng-pristine class). This method will also propagate to all the controls contained
20255   * in this form.
20256   *
20257   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20258   * saving or resetting it.
20259   */
20260  form.$setPristine = function() {
20261    $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20262    form.$dirty = false;
20263    form.$pristine = true;
20264    form.$submitted = false;
20265    forEach(controls, function(control) {
20266      control.$setPristine();
20267    });
20268  };
20269
20270  /**
20271   * @ngdoc method
20272   * @name form.FormController#$setUntouched
20273   *
20274   * @description
20275   * Sets the form to its untouched state.
20276   *
20277   * This method can be called to remove the 'ng-touched' class and set the form controls to their
20278   * untouched state (ng-untouched class).
20279   *
20280   * Setting a form controls back to their untouched state is often useful when setting the form
20281   * back to its pristine state.
20282   */
20283  form.$setUntouched = function() {
20284    forEach(controls, function(control) {
20285      control.$setUntouched();
20286    });
20287  };
20288
20289  /**
20290   * @ngdoc method
20291   * @name form.FormController#$setSubmitted
20292   *
20293   * @description
20294   * Sets the form to its submitted state.
20295   */
20296  form.$setSubmitted = function() {
20297    $animate.addClass(element, SUBMITTED_CLASS);
20298    form.$submitted = true;
20299    form.$$parentForm.$setSubmitted();
20300  };
20301}
20302
20303/**
20304 * @ngdoc directive
20305 * @name ngForm
20306 * @restrict EAC
20307 *
20308 * @description
20309 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20310 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20311 * sub-group of controls needs to be determined.
20312 *
20313 * Note: the purpose of `ngForm` is to group controls,
20314 * but not to be a replacement for the `<form>` tag with all of its capabilities
20315 * (e.g. posting to the server, ...).
20316 *
20317 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20318 *                       related scope, under this name.
20319 *
20320 */
20321
20322 /**
20323 * @ngdoc directive
20324 * @name form
20325 * @restrict E
20326 *
20327 * @description
20328 * Directive that instantiates
20329 * {@link form.FormController FormController}.
20330 *
20331 * If the `name` attribute is specified, the form controller is published onto the current scope under
20332 * this name.
20333 *
20334 * # Alias: {@link ng.directive:ngForm `ngForm`}
20335 *
20336 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20337 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20338 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
20339 * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
20340 * using Angular validation directives in forms that are dynamically generated using the
20341 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
20342 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
20343 * `ngForm` directive and nest these in an outer `form` element.
20344 *
20345 *
20346 * # CSS classes
20347 *  - `ng-valid` is set if the form is valid.
20348 *  - `ng-invalid` is set if the form is invalid.
20349 *  - `ng-pending` is set if the form is pending.
20350 *  - `ng-pristine` is set if the form is pristine.
20351 *  - `ng-dirty` is set if the form is dirty.
20352 *  - `ng-submitted` is set if the form was submitted.
20353 *
20354 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20355 *
20356 *
20357 * # Submitting a form and preventing the default action
20358 *
20359 * Since the role of forms in client-side Angular applications is different than in classical
20360 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20361 * page reload that sends the data to the server. Instead some javascript logic should be triggered
20362 * to handle the form submission in an application-specific way.
20363 *
20364 * For this reason, Angular prevents the default action (form submission to the server) unless the
20365 * `<form>` element has an `action` attribute specified.
20366 *
20367 * You can use one of the following two ways to specify what javascript method should be called when
20368 * a form is submitted:
20369 *
20370 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20371 * - {@link ng.directive:ngClick ngClick} directive on the first
20372  *  button or input field of type submit (input[type=submit])
20373 *
20374 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20375 * or {@link ng.directive:ngClick ngClick} directives.
20376 * This is because of the following form submission rules in the HTML specification:
20377 *
20378 * - If a form has only one input field then hitting enter in this field triggers form submit
20379 * (`ngSubmit`)
20380 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20381 * doesn't trigger submit
20382 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20383 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20384 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20385 *
20386 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20387 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20388 * to have access to the updated model.
20389 *
20390 * ## Animation Hooks
20391 *
20392 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20393 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20394 * other validations that are performed within the form. Animations in ngForm are similar to how
20395 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20396 * as JS animations.
20397 *
20398 * The following example shows a simple way to utilize CSS transitions to style a form element
20399 * that has been rendered as invalid after it has been validated:
20400 *
20401 * <pre>
20402 * //be sure to include ngAnimate as a module to hook into more
20403 * //advanced animations
20404 * .my-form {
20405 *   transition:0.5s linear all;
20406 *   background: white;
20407 * }
20408 * .my-form.ng-invalid {
20409 *   background: red;
20410 *   color:white;
20411 * }
20412 * </pre>
20413 *
20414 * @example
20415    <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20416      <file name="index.html">
20417       <script>
20418         angular.module('formExample', [])
20419           .controller('FormController', ['$scope', function($scope) {
20420             $scope.userType = 'guest';
20421           }]);
20422       </script>
20423       <style>
20424        .my-form {
20425          transition:all linear 0.5s;
20426          background: transparent;
20427        }
20428        .my-form.ng-invalid {
20429          background: red;
20430        }
20431       </style>
20432       <form name="myForm" ng-controller="FormController" class="my-form">
20433         userType: <input name="input" ng-model="userType" required>
20434         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20435         <code>userType = {{userType}}</code><br>
20436         <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20437         <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20438         <code>myForm.$valid = {{myForm.$valid}}</code><br>
20439         <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20440        </form>
20441      </file>
20442      <file name="protractor.js" type="protractor">
20443        it('should initialize to model', function() {
20444          var userType = element(by.binding('userType'));
20445          var valid = element(by.binding('myForm.input.$valid'));
20446
20447          expect(userType.getText()).toContain('guest');
20448          expect(valid.getText()).toContain('true');
20449        });
20450
20451        it('should be invalid if empty', function() {
20452          var userType = element(by.binding('userType'));
20453          var valid = element(by.binding('myForm.input.$valid'));
20454          var userInput = element(by.model('userType'));
20455
20456          userInput.clear();
20457          userInput.sendKeys('');
20458
20459          expect(userType.getText()).toEqual('userType =');
20460          expect(valid.getText()).toContain('false');
20461        });
20462      </file>
20463    </example>
20464 *
20465 * @param {string=} name Name of the form. If specified, the form controller will be published into
20466 *                       related scope, under this name.
20467 */
20468var formDirectiveFactory = function(isNgForm) {
20469  return ['$timeout', '$parse', function($timeout, $parse) {
20470    var formDirective = {
20471      name: 'form',
20472      restrict: isNgForm ? 'EAC' : 'E',
20473      require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20474      controller: FormController,
20475      compile: function ngFormCompile(formElement, attr) {
20476        // Setup initial state of the control
20477        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20478
20479        var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20480
20481        return {
20482          pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20483            var controller = ctrls[0];
20484
20485            // if `action` attr is not present on the form, prevent the default action (submission)
20486            if (!('action' in attr)) {
20487              // we can't use jq events because if a form is destroyed during submission the default
20488              // action is not prevented. see #1238
20489              //
20490              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20491              // page reload if the form was destroyed by submission of the form via a click handler
20492              // on a button in the form. Looks like an IE9 specific bug.
20493              var handleFormSubmission = function(event) {
20494                scope.$apply(function() {
20495                  controller.$commitViewValue();
20496                  controller.$setSubmitted();
20497                });
20498
20499                event.preventDefault();
20500              };
20501
20502              addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20503
20504              // unregister the preventDefault listener so that we don't not leak memory but in a
20505              // way that will achieve the prevention of the default action.
20506              formElement.on('$destroy', function() {
20507                $timeout(function() {
20508                  removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20509                }, 0, false);
20510              });
20511            }
20512
20513            var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20514            parentFormCtrl.$addControl(controller);
20515
20516            var setter = nameAttr ? getSetter(controller.$name) : noop;
20517
20518            if (nameAttr) {
20519              setter(scope, controller);
20520              attr.$observe(nameAttr, function(newValue) {
20521                if (controller.$name === newValue) return;
20522                setter(scope, undefined);
20523                controller.$$parentForm.$$renameControl(controller, newValue);
20524                setter = getSetter(controller.$name);
20525                setter(scope, controller);
20526              });
20527            }
20528            formElement.on('$destroy', function() {
20529              controller.$$parentForm.$removeControl(controller);
20530              setter(scope, undefined);
20531              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
20532            });
20533          }
20534        };
20535      }
20536    };
20537
20538    return formDirective;
20539
20540    function getSetter(expression) {
20541      if (expression === '') {
20542        //create an assignable expression, so forms with an empty name can be renamed later
20543        return $parse('this[""]').assign;
20544      }
20545      return $parse(expression).assign || noop;
20546    }
20547  }];
20548};
20549
20550var formDirective = formDirectiveFactory();
20551var ngFormDirective = formDirectiveFactory(true);
20552
20553/* global VALID_CLASS: false,
20554  INVALID_CLASS: false,
20555  PRISTINE_CLASS: false,
20556  DIRTY_CLASS: false,
20557  UNTOUCHED_CLASS: false,
20558  TOUCHED_CLASS: false,
20559  ngModelMinErr: false,
20560*/
20561
20562// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
20563var 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)/;
20564var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
20565var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
20566var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
20567var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
20568var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20569var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
20570var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
20571var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20572
20573var inputType = {
20574
20575  /**
20576   * @ngdoc input
20577   * @name input[text]
20578   *
20579   * @description
20580   * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
20581   *
20582   *
20583   * @param {string} ngModel Assignable angular expression to data-bind to.
20584   * @param {string=} name Property name of the form under which the control is published.
20585   * @param {string=} required Adds `required` validation error key if the value is not entered.
20586   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20587   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20588   *    `required` when you want to data-bind to the `required` attribute.
20589   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20590   *    minlength.
20591   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20592   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
20593   *    any length.
20594   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
20595   *    that contains the regular expression body that will be converted to a regular expression
20596   *    as in the ngPattern directive.
20597   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
20598   *    a RegExp found by evaluating the Angular expression given in the attribute value.
20599   *    If the expression evaluates to a RegExp object, then this is used directly.
20600   *    If the expression evaluates to a string, then it will be converted to a RegExp
20601   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
20602   *    `new RegExp('^abc$')`.<br />
20603   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
20604   *    start at the index of the last search's match, thus not taking the whole input value into
20605   *    account.
20606   * @param {string=} ngChange Angular expression to be executed when input changes due to user
20607   *    interaction with the input element.
20608   * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20609   *    This parameter is ignored for input[type=password] controls, which will never trim the
20610   *    input.
20611   *
20612   * @example
20613      <example name="text-input-directive" module="textInputExample">
20614        <file name="index.html">
20615         <script>
20616           angular.module('textInputExample', [])
20617             .controller('ExampleController', ['$scope', function($scope) {
20618               $scope.example = {
20619                 text: 'guest',
20620                 word: /^\s*\w*\s*$/
20621               };
20622             }]);
20623         </script>
20624         <form name="myForm" ng-controller="ExampleController">
20625           <label>Single word:
20626             <input type="text" name="input" ng-model="example.text"
20627                    ng-pattern="example.word" required ng-trim="false">
20628           </label>
20629           <div role="alert">
20630             <span class="error" ng-show="myForm.input.$error.required">
20631               Required!</span>
20632             <span class="error" ng-show="myForm.input.$error.pattern">
20633               Single word only!</span>
20634           </div>
20635           <tt>text = {{example.text}}</tt><br/>
20636           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20637           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20638           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20639           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20640          </form>
20641        </file>
20642        <file name="protractor.js" type="protractor">
20643          var text = element(by.binding('example.text'));
20644          var valid = element(by.binding('myForm.input.$valid'));
20645          var input = element(by.model('example.text'));
20646
20647          it('should initialize to model', function() {
20648            expect(text.getText()).toContain('guest');
20649            expect(valid.getText()).toContain('true');
20650          });
20651
20652          it('should be invalid if empty', function() {
20653            input.clear();
20654            input.sendKeys('');
20655
20656            expect(text.getText()).toEqual('text =');
20657            expect(valid.getText()).toContain('false');
20658          });
20659
20660          it('should be invalid if multi word', function() {
20661            input.clear();
20662            input.sendKeys('hello world');
20663
20664            expect(valid.getText()).toContain('false');
20665          });
20666        </file>
20667      </example>
20668   */
20669  'text': textInputType,
20670
20671    /**
20672     * @ngdoc input
20673     * @name input[date]
20674     *
20675     * @description
20676     * Input with date validation and transformation. In browsers that do not yet support
20677     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
20678     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
20679     * modern browsers do not yet support this input type, it is important to provide cues to users on the
20680     * expected input format via a placeholder or label.
20681     *
20682     * The model must always be a Date object, otherwise Angular will throw an error.
20683     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20684     *
20685     * The timezone to be used to read/write the `Date` instance in the model can be defined using
20686     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20687     *
20688     * @param {string} ngModel Assignable angular expression to data-bind to.
20689     * @param {string=} name Property name of the form under which the control is published.
20690     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20691     *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20692     *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
20693     *   constraint validation.
20694     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20695     *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20696     *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
20697     *   constraint validation.
20698     * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
20699     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20700     * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
20701     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20702     * @param {string=} required Sets `required` validation error key if the value is not entered.
20703     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20704     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20705     *    `required` when you want to data-bind to the `required` attribute.
20706     * @param {string=} ngChange Angular expression to be executed when input changes due to user
20707     *    interaction with the input element.
20708     *
20709     * @example
20710     <example name="date-input-directive" module="dateInputExample">
20711     <file name="index.html">
20712       <script>
20713          angular.module('dateInputExample', [])
20714            .controller('DateController', ['$scope', function($scope) {
20715              $scope.example = {
20716                value: new Date(2013, 9, 22)
20717              };
20718            }]);
20719       </script>
20720       <form name="myForm" ng-controller="DateController as dateCtrl">
20721          <label for="exampleInput">Pick a date in 2013:</label>
20722          <input type="date" id="exampleInput" name="input" ng-model="example.value"
20723              placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
20724          <div role="alert">
20725            <span class="error" ng-show="myForm.input.$error.required">
20726                Required!</span>
20727            <span class="error" ng-show="myForm.input.$error.date">
20728                Not a valid date!</span>
20729           </div>
20730           <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
20731           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20732           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20733           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20734           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20735       </form>
20736     </file>
20737     <file name="protractor.js" type="protractor">
20738        var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
20739        var valid = element(by.binding('myForm.input.$valid'));
20740        var input = element(by.model('example.value'));
20741
20742        // currently protractor/webdriver does not support
20743        // sending keys to all known HTML5 input controls
20744        // for various browsers (see https://github.com/angular/protractor/issues/562).
20745        function setInput(val) {
20746          // set the value of the element and force validation.
20747          var scr = "var ipt = document.getElementById('exampleInput'); " +
20748          "ipt.value = '" + val + "';" +
20749          "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20750          browser.executeScript(scr);
20751        }
20752
20753        it('should initialize to model', function() {
20754          expect(value.getText()).toContain('2013-10-22');
20755          expect(valid.getText()).toContain('myForm.input.$valid = true');
20756        });
20757
20758        it('should be invalid if empty', function() {
20759          setInput('');
20760          expect(value.getText()).toEqual('value =');
20761          expect(valid.getText()).toContain('myForm.input.$valid = false');
20762        });
20763
20764        it('should be invalid if over max', function() {
20765          setInput('2015-01-01');
20766          expect(value.getText()).toContain('');
20767          expect(valid.getText()).toContain('myForm.input.$valid = false');
20768        });
20769     </file>
20770     </example>
20771     */
20772  'date': createDateInputType('date', DATE_REGEXP,
20773         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
20774         'yyyy-MM-dd'),
20775
20776   /**
20777    * @ngdoc input
20778    * @name input[datetime-local]
20779    *
20780    * @description
20781    * Input with datetime validation and transformation. In browsers that do not yet support
20782    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20783    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
20784    *
20785    * The model must always be a Date object, otherwise Angular will throw an error.
20786    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20787    *
20788    * The timezone to be used to read/write the `Date` instance in the model can be defined using
20789    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20790    *
20791    * @param {string} ngModel Assignable angular expression to data-bind to.
20792    * @param {string=} name Property name of the form under which the control is published.
20793    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20794    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20795    *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20796    *   Note that `min` will also add native HTML5 constraint validation.
20797    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20798    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20799    *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20800    *   Note that `max` will also add native HTML5 constraint validation.
20801    * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
20802    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20803    * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
20804    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20805    * @param {string=} required Sets `required` validation error key if the value is not entered.
20806    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20807    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20808    *    `required` when you want to data-bind to the `required` attribute.
20809    * @param {string=} ngChange Angular expression to be executed when input changes due to user
20810    *    interaction with the input element.
20811    *
20812    * @example
20813    <example name="datetimelocal-input-directive" module="dateExample">
20814    <file name="index.html">
20815      <script>
20816        angular.module('dateExample', [])
20817          .controller('DateController', ['$scope', function($scope) {
20818            $scope.example = {
20819              value: new Date(2010, 11, 28, 14, 57)
20820            };
20821          }]);
20822      </script>
20823      <form name="myForm" ng-controller="DateController as dateCtrl">
20824        <label for="exampleInput">Pick a date between in 2013:</label>
20825        <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
20826            placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
20827        <div role="alert">
20828          <span class="error" ng-show="myForm.input.$error.required">
20829              Required!</span>
20830          <span class="error" ng-show="myForm.input.$error.datetimelocal">
20831              Not a valid date!</span>
20832        </div>
20833        <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
20834        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20835        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20836        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20837        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20838      </form>
20839    </file>
20840    <file name="protractor.js" type="protractor">
20841      var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
20842      var valid = element(by.binding('myForm.input.$valid'));
20843      var input = element(by.model('example.value'));
20844
20845      // currently protractor/webdriver does not support
20846      // sending keys to all known HTML5 input controls
20847      // for various browsers (https://github.com/angular/protractor/issues/562).
20848      function setInput(val) {
20849        // set the value of the element and force validation.
20850        var scr = "var ipt = document.getElementById('exampleInput'); " +
20851        "ipt.value = '" + val + "';" +
20852        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20853        browser.executeScript(scr);
20854      }
20855
20856      it('should initialize to model', function() {
20857        expect(value.getText()).toContain('2010-12-28T14:57:00');
20858        expect(valid.getText()).toContain('myForm.input.$valid = true');
20859      });
20860
20861      it('should be invalid if empty', function() {
20862        setInput('');
20863        expect(value.getText()).toEqual('value =');
20864        expect(valid.getText()).toContain('myForm.input.$valid = false');
20865      });
20866
20867      it('should be invalid if over max', function() {
20868        setInput('2015-01-01T23:59:00');
20869        expect(value.getText()).toContain('');
20870        expect(valid.getText()).toContain('myForm.input.$valid = false');
20871      });
20872    </file>
20873    </example>
20874    */
20875  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
20876      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
20877      'yyyy-MM-ddTHH:mm:ss.sss'),
20878
20879  /**
20880   * @ngdoc input
20881   * @name input[time]
20882   *
20883   * @description
20884   * Input with time validation and transformation. In browsers that do not yet support
20885   * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20886   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
20887   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
20888   *
20889   * The model must always be a Date object, otherwise Angular will throw an error.
20890   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20891   *
20892   * The timezone to be used to read/write the `Date` instance in the model can be defined using
20893   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20894   *
20895   * @param {string} ngModel Assignable angular expression to data-bind to.
20896   * @param {string=} name Property name of the form under which the control is published.
20897   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20898   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
20899   *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
20900   *   native HTML5 constraint validation.
20901   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20902   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
20903   *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
20904   *   native HTML5 constraint validation.
20905   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
20906   *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20907   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
20908   *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20909   * @param {string=} required Sets `required` validation error key if the value is not entered.
20910   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20911   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20912   *    `required` when you want to data-bind to the `required` attribute.
20913   * @param {string=} ngChange Angular expression to be executed when input changes due to user
20914   *    interaction with the input element.
20915   *
20916   * @example
20917   <example name="time-input-directive" module="timeExample">
20918   <file name="index.html">
20919     <script>
20920      angular.module('timeExample', [])
20921        .controller('DateController', ['$scope', function($scope) {
20922          $scope.example = {
20923            value: new Date(1970, 0, 1, 14, 57, 0)
20924          };
20925        }]);
20926     </script>
20927     <form name="myForm" ng-controller="DateController as dateCtrl">
20928        <label for="exampleInput">Pick a between 8am and 5pm:</label>
20929        <input type="time" id="exampleInput" name="input" ng-model="example.value"
20930            placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
20931        <div role="alert">
20932          <span class="error" ng-show="myForm.input.$error.required">
20933              Required!</span>
20934          <span class="error" ng-show="myForm.input.$error.time">
20935              Not a valid date!</span>
20936        </div>
20937        <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
20938        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20939        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20940        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20941        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20942     </form>
20943   </file>
20944   <file name="protractor.js" type="protractor">
20945      var value = element(by.binding('example.value | date: "HH:mm:ss"'));
20946      var valid = element(by.binding('myForm.input.$valid'));
20947      var input = element(by.model('example.value'));
20948
20949      // currently protractor/webdriver does not support
20950      // sending keys to all known HTML5 input controls
20951      // for various browsers (https://github.com/angular/protractor/issues/562).
20952      function setInput(val) {
20953        // set the value of the element and force validation.
20954        var scr = "var ipt = document.getElementById('exampleInput'); " +
20955        "ipt.value = '" + val + "';" +
20956        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20957        browser.executeScript(scr);
20958      }
20959
20960      it('should initialize to model', function() {
20961        expect(value.getText()).toContain('14:57:00');
20962        expect(valid.getText()).toContain('myForm.input.$valid = true');
20963      });
20964
20965      it('should be invalid if empty', function() {
20966        setInput('');
20967        expect(value.getText()).toEqual('value =');
20968        expect(valid.getText()).toContain('myForm.input.$valid = false');
20969      });
20970
20971      it('should be invalid if over max', function() {
20972        setInput('23:59:00');
20973        expect(value.getText()).toContain('');
20974        expect(valid.getText()).toContain('myForm.input.$valid = false');
20975      });
20976   </file>
20977   </example>
20978   */
20979  'time': createDateInputType('time', TIME_REGEXP,
20980      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
20981     'HH:mm:ss.sss'),
20982
20983   /**
20984    * @ngdoc input
20985    * @name input[week]
20986    *
20987    * @description
20988    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
20989    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20990    * week format (yyyy-W##), for example: `2013-W02`.
20991    *
20992    * The model must always be a Date object, otherwise Angular will throw an error.
20993    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20994    *
20995    * The timezone to be used to read/write the `Date` instance in the model can be defined using
20996    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20997    *
20998    * @param {string} ngModel Assignable angular expression to data-bind to.
20999    * @param {string=} name Property name of the form under which the control is published.
21000    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21001    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21002    *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21003    *   native HTML5 constraint validation.
21004    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21005    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21006    *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21007    *   native HTML5 constraint validation.
21008    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21009    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21010    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21011    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21012    * @param {string=} required Sets `required` validation error key if the value is not entered.
21013    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21014    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21015    *    `required` when you want to data-bind to the `required` attribute.
21016    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21017    *    interaction with the input element.
21018    *
21019    * @example
21020    <example name="week-input-directive" module="weekExample">
21021    <file name="index.html">
21022      <script>
21023      angular.module('weekExample', [])
21024        .controller('DateController', ['$scope', function($scope) {
21025          $scope.example = {
21026            value: new Date(2013, 0, 3)
21027          };
21028        }]);
21029      </script>
21030      <form name="myForm" ng-controller="DateController as dateCtrl">
21031        <label>Pick a date between in 2013:
21032          <input id="exampleInput" type="week" name="input" ng-model="example.value"
21033                 placeholder="YYYY-W##" min="2012-W32"
21034                 max="2013-W52" required />
21035        </label>
21036        <div role="alert">
21037          <span class="error" ng-show="myForm.input.$error.required">
21038              Required!</span>
21039          <span class="error" ng-show="myForm.input.$error.week">
21040              Not a valid date!</span>
21041        </div>
21042        <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21043        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21044        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21045        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21046        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21047      </form>
21048    </file>
21049    <file name="protractor.js" type="protractor">
21050      var value = element(by.binding('example.value | date: "yyyy-Www"'));
21051      var valid = element(by.binding('myForm.input.$valid'));
21052      var input = element(by.model('example.value'));
21053
21054      // currently protractor/webdriver does not support
21055      // sending keys to all known HTML5 input controls
21056      // for various browsers (https://github.com/angular/protractor/issues/562).
21057      function setInput(val) {
21058        // set the value of the element and force validation.
21059        var scr = "var ipt = document.getElementById('exampleInput'); " +
21060        "ipt.value = '" + val + "';" +
21061        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21062        browser.executeScript(scr);
21063      }
21064
21065      it('should initialize to model', function() {
21066        expect(value.getText()).toContain('2013-W01');
21067        expect(valid.getText()).toContain('myForm.input.$valid = true');
21068      });
21069
21070      it('should be invalid if empty', function() {
21071        setInput('');
21072        expect(value.getText()).toEqual('value =');
21073        expect(valid.getText()).toContain('myForm.input.$valid = false');
21074      });
21075
21076      it('should be invalid if over max', function() {
21077        setInput('2015-W01');
21078        expect(value.getText()).toContain('');
21079        expect(valid.getText()).toContain('myForm.input.$valid = false');
21080      });
21081    </file>
21082    </example>
21083    */
21084  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21085
21086  /**
21087   * @ngdoc input
21088   * @name input[month]
21089   *
21090   * @description
21091   * Input with month validation and transformation. In browsers that do not yet support
21092   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21093   * month format (yyyy-MM), for example: `2009-01`.
21094   *
21095   * The model must always be a Date object, otherwise Angular will throw an error.
21096   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21097   * If the model is not set to the first of the month, the next view to model update will set it
21098   * to the first of the month.
21099   *
21100   * The timezone to be used to read/write the `Date` instance in the model can be defined using
21101   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21102   *
21103   * @param {string} ngModel Assignable angular expression to data-bind to.
21104   * @param {string=} name Property name of the form under which the control is published.
21105   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21106   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21107   *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21108   *   native HTML5 constraint validation.
21109   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21110   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21111   *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21112   *   native HTML5 constraint validation.
21113   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21114   *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21115   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21116   *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21117
21118   * @param {string=} required Sets `required` validation error key if the value is not entered.
21119   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21120   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21121   *    `required` when you want to data-bind to the `required` attribute.
21122   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21123   *    interaction with the input element.
21124   *
21125   * @example
21126   <example name="month-input-directive" module="monthExample">
21127   <file name="index.html">
21128     <script>
21129      angular.module('monthExample', [])
21130        .controller('DateController', ['$scope', function($scope) {
21131          $scope.example = {
21132            value: new Date(2013, 9, 1)
21133          };
21134        }]);
21135     </script>
21136     <form name="myForm" ng-controller="DateController as dateCtrl">
21137       <label for="exampleInput">Pick a month in 2013:</label>
21138       <input id="exampleInput" type="month" name="input" ng-model="example.value"
21139          placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
21140       <div role="alert">
21141         <span class="error" ng-show="myForm.input.$error.required">
21142            Required!</span>
21143         <span class="error" ng-show="myForm.input.$error.month">
21144            Not a valid month!</span>
21145       </div>
21146       <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
21147       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21148       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21149       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21150       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21151     </form>
21152   </file>
21153   <file name="protractor.js" type="protractor">
21154      var value = element(by.binding('example.value | date: "yyyy-MM"'));
21155      var valid = element(by.binding('myForm.input.$valid'));
21156      var input = element(by.model('example.value'));
21157
21158      // currently protractor/webdriver does not support
21159      // sending keys to all known HTML5 input controls
21160      // for various browsers (https://github.com/angular/protractor/issues/562).
21161      function setInput(val) {
21162        // set the value of the element and force validation.
21163        var scr = "var ipt = document.getElementById('exampleInput'); " +
21164        "ipt.value = '" + val + "';" +
21165        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21166        browser.executeScript(scr);
21167      }
21168
21169      it('should initialize to model', function() {
21170        expect(value.getText()).toContain('2013-10');
21171        expect(valid.getText()).toContain('myForm.input.$valid = true');
21172      });
21173
21174      it('should be invalid if empty', function() {
21175        setInput('');
21176        expect(value.getText()).toEqual('value =');
21177        expect(valid.getText()).toContain('myForm.input.$valid = false');
21178      });
21179
21180      it('should be invalid if over max', function() {
21181        setInput('2015-01');
21182        expect(value.getText()).toContain('');
21183        expect(valid.getText()).toContain('myForm.input.$valid = false');
21184      });
21185   </file>
21186   </example>
21187   */
21188  'month': createDateInputType('month', MONTH_REGEXP,
21189     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
21190     'yyyy-MM'),
21191
21192  /**
21193   * @ngdoc input
21194   * @name input[number]
21195   *
21196   * @description
21197   * Text input with number validation and transformation. Sets the `number` validation
21198   * error if not a valid number.
21199   *
21200   * <div class="alert alert-warning">
21201   * The model must always be of type `number` otherwise Angular will throw an error.
21202   * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
21203   * error docs for more information and an example of how to convert your model if necessary.
21204   * </div>
21205   *
21206   * ## Issues with HTML5 constraint validation
21207   *
21208   * In browsers that follow the
21209   * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
21210   * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
21211   * If a non-number is entered in the input, the browser will report the value as an empty string,
21212   * which means the view / model values in `ngModel` and subsequently the scope value
21213   * will also be an empty string.
21214   *
21215   *
21216   * @param {string} ngModel Assignable angular expression to data-bind to.
21217   * @param {string=} name Property name of the form under which the control is published.
21218   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21219   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21220   * @param {string=} required Sets `required` validation error key if the value is not entered.
21221   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21222   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21223   *    `required` when you want to data-bind to the `required` attribute.
21224   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21225   *    minlength.
21226   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21227   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21228   *    any length.
21229   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21230   *    that contains the regular expression body that will be converted to a regular expression
21231   *    as in the ngPattern directive.
21232   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21233   *    a RegExp found by evaluating the Angular expression given in the attribute value.
21234   *    If the expression evaluates to a RegExp object, then this is used directly.
21235   *    If the expression evaluates to a string, then it will be converted to a RegExp
21236   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21237   *    `new RegExp('^abc$')`.<br />
21238   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21239   *    start at the index of the last search's match, thus not taking the whole input value into
21240   *    account.
21241   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21242   *    interaction with the input element.
21243   *
21244   * @example
21245      <example name="number-input-directive" module="numberExample">
21246        <file name="index.html">
21247         <script>
21248           angular.module('numberExample', [])
21249             .controller('ExampleController', ['$scope', function($scope) {
21250               $scope.example = {
21251                 value: 12
21252               };
21253             }]);
21254         </script>
21255         <form name="myForm" ng-controller="ExampleController">
21256           <label>Number:
21257             <input type="number" name="input" ng-model="example.value"
21258                    min="0" max="99" required>
21259          </label>
21260           <div role="alert">
21261             <span class="error" ng-show="myForm.input.$error.required">
21262               Required!</span>
21263             <span class="error" ng-show="myForm.input.$error.number">
21264               Not valid number!</span>
21265           </div>
21266           <tt>value = {{example.value}}</tt><br/>
21267           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21268           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21269           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21270           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21271          </form>
21272        </file>
21273        <file name="protractor.js" type="protractor">
21274          var value = element(by.binding('example.value'));
21275          var valid = element(by.binding('myForm.input.$valid'));
21276          var input = element(by.model('example.value'));
21277
21278          it('should initialize to model', function() {
21279            expect(value.getText()).toContain('12');
21280            expect(valid.getText()).toContain('true');
21281          });
21282
21283          it('should be invalid if empty', function() {
21284            input.clear();
21285            input.sendKeys('');
21286            expect(value.getText()).toEqual('value =');
21287            expect(valid.getText()).toContain('false');
21288          });
21289
21290          it('should be invalid if over max', function() {
21291            input.clear();
21292            input.sendKeys('123');
21293            expect(value.getText()).toEqual('value =');
21294            expect(valid.getText()).toContain('false');
21295          });
21296        </file>
21297      </example>
21298   */
21299  'number': numberInputType,
21300
21301
21302  /**
21303   * @ngdoc input
21304   * @name input[url]
21305   *
21306   * @description
21307   * Text input with URL validation. Sets the `url` validation error key if the content is not a
21308   * valid URL.
21309   *
21310   * <div class="alert alert-warning">
21311   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21312   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21313   * the built-in validators (see the {@link guide/forms Forms guide})
21314   * </div>
21315   *
21316   * @param {string} ngModel Assignable angular expression to data-bind to.
21317   * @param {string=} name Property name of the form under which the control is published.
21318   * @param {string=} required Sets `required` validation error key if the value is not entered.
21319   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21320   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21321   *    `required` when you want to data-bind to the `required` attribute.
21322   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21323   *    minlength.
21324   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21325   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21326   *    any length.
21327   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21328   *    that contains the regular expression body that will be converted to a regular expression
21329   *    as in the ngPattern directive.
21330   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21331   *    a RegExp found by evaluating the Angular expression given in the attribute value.
21332   *    If the expression evaluates to a RegExp object, then this is used directly.
21333   *    If the expression evaluates to a string, then it will be converted to a RegExp
21334   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21335   *    `new RegExp('^abc$')`.<br />
21336   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21337   *    start at the index of the last search's match, thus not taking the whole input value into
21338   *    account.
21339   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21340   *    interaction with the input element.
21341   *
21342   * @example
21343      <example name="url-input-directive" module="urlExample">
21344        <file name="index.html">
21345         <script>
21346           angular.module('urlExample', [])
21347             .controller('ExampleController', ['$scope', function($scope) {
21348               $scope.url = {
21349                 text: 'http://google.com'
21350               };
21351             }]);
21352         </script>
21353         <form name="myForm" ng-controller="ExampleController">
21354           <label>URL:
21355             <input type="url" name="input" ng-model="url.text" required>
21356           <label>
21357           <div role="alert">
21358             <span class="error" ng-show="myForm.input.$error.required">
21359               Required!</span>
21360             <span class="error" ng-show="myForm.input.$error.url">
21361               Not valid url!</span>
21362           </div>
21363           <tt>text = {{url.text}}</tt><br/>
21364           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21365           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21366           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21367           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21368           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21369          </form>
21370        </file>
21371        <file name="protractor.js" type="protractor">
21372          var text = element(by.binding('url.text'));
21373          var valid = element(by.binding('myForm.input.$valid'));
21374          var input = element(by.model('url.text'));
21375
21376          it('should initialize to model', function() {
21377            expect(text.getText()).toContain('http://google.com');
21378            expect(valid.getText()).toContain('true');
21379          });
21380
21381          it('should be invalid if empty', function() {
21382            input.clear();
21383            input.sendKeys('');
21384
21385            expect(text.getText()).toEqual('text =');
21386            expect(valid.getText()).toContain('false');
21387          });
21388
21389          it('should be invalid if not url', function() {
21390            input.clear();
21391            input.sendKeys('box');
21392
21393            expect(valid.getText()).toContain('false');
21394          });
21395        </file>
21396      </example>
21397   */
21398  'url': urlInputType,
21399
21400
21401  /**
21402   * @ngdoc input
21403   * @name input[email]
21404   *
21405   * @description
21406   * Text input with email validation. Sets the `email` validation error key if not a valid email
21407   * address.
21408   *
21409   * <div class="alert alert-warning">
21410   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21411   * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21412   * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21413   * </div>
21414   *
21415   * @param {string} ngModel Assignable angular expression to data-bind to.
21416   * @param {string=} name Property name of the form under which the control is published.
21417   * @param {string=} required Sets `required` validation error key if the value is not entered.
21418   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21419   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21420   *    `required` when you want to data-bind to the `required` attribute.
21421   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21422   *    minlength.
21423   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21424   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21425   *    any length.
21426   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21427   *    that contains the regular expression body that will be converted to a regular expression
21428   *    as in the ngPattern directive.
21429   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21430   *    a RegExp found by evaluating the Angular expression given in the attribute value.
21431   *    If the expression evaluates to a RegExp object, then this is used directly.
21432   *    If the expression evaluates to a string, then it will be converted to a RegExp
21433   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21434   *    `new RegExp('^abc$')`.<br />
21435   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21436   *    start at the index of the last search's match, thus not taking the whole input value into
21437   *    account.
21438   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21439   *    interaction with the input element.
21440   *
21441   * @example
21442      <example name="email-input-directive" module="emailExample">
21443        <file name="index.html">
21444         <script>
21445           angular.module('emailExample', [])
21446             .controller('ExampleController', ['$scope', function($scope) {
21447               $scope.email = {
21448                 text: '[email protected]'
21449               };
21450             }]);
21451         </script>
21452           <form name="myForm" ng-controller="ExampleController">
21453             <label>Email:
21454               <input type="email" name="input" ng-model="email.text" required>
21455             </label>
21456             <div role="alert">
21457               <span class="error" ng-show="myForm.input.$error.required">
21458                 Required!</span>
21459               <span class="error" ng-show="myForm.input.$error.email">
21460                 Not valid email!</span>
21461             </div>
21462             <tt>text = {{email.text}}</tt><br/>
21463             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21464             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21465             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21466             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21467             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21468           </form>
21469         </file>
21470        <file name="protractor.js" type="protractor">
21471          var text = element(by.binding('email.text'));
21472          var valid = element(by.binding('myForm.input.$valid'));
21473          var input = element(by.model('email.text'));
21474
21475          it('should initialize to model', function() {
21476            expect(text.getText()).toContain('[email protected]');
21477            expect(valid.getText()).toContain('true');
21478          });
21479
21480          it('should be invalid if empty', function() {
21481            input.clear();
21482            input.sendKeys('');
21483            expect(text.getText()).toEqual('text =');
21484            expect(valid.getText()).toContain('false');
21485          });
21486
21487          it('should be invalid if not email', function() {
21488            input.clear();
21489            input.sendKeys('xxx');
21490
21491            expect(valid.getText()).toContain('false');
21492          });
21493        </file>
21494      </example>
21495   */
21496  'email': emailInputType,
21497
21498
21499  /**
21500   * @ngdoc input
21501   * @name input[radio]
21502   *
21503   * @description
21504   * HTML radio button.
21505   *
21506   * @param {string} ngModel Assignable angular expression to data-bind to.
21507   * @param {string} value The value to which the `ngModel` expression should be set when selected.
21508   *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
21509   *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
21510   * @param {string=} name Property name of the form under which the control is published.
21511   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21512   *    interaction with the input element.
21513   * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
21514   *    is selected. Should be used instead of the `value` attribute if you need
21515   *    a non-string `ngModel` (`boolean`, `array`, ...).
21516   *
21517   * @example
21518      <example name="radio-input-directive" module="radioExample">
21519        <file name="index.html">
21520         <script>
21521           angular.module('radioExample', [])
21522             .controller('ExampleController', ['$scope', function($scope) {
21523               $scope.color = {
21524                 name: 'blue'
21525               };
21526               $scope.specialValue = {
21527                 "id": "12345",
21528                 "value": "green"
21529               };
21530             }]);
21531         </script>
21532         <form name="myForm" ng-controller="ExampleController">
21533           <label>
21534             <input type="radio" ng-model="color.name" value="red">
21535             Red
21536           </label><br/>
21537           <label>
21538             <input type="radio" ng-model="color.name" ng-value="specialValue">
21539             Green
21540           </label><br/>
21541           <label>
21542             <input type="radio" ng-model="color.name" value="blue">
21543             Blue
21544           </label><br/>
21545           <tt>color = {{color.name | json}}</tt><br/>
21546          </form>
21547          Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
21548        </file>
21549        <file name="protractor.js" type="protractor">
21550          it('should change state', function() {
21551            var color = element(by.binding('color.name'));
21552
21553            expect(color.getText()).toContain('blue');
21554
21555            element.all(by.model('color.name')).get(0).click();
21556
21557            expect(color.getText()).toContain('red');
21558          });
21559        </file>
21560      </example>
21561   */
21562  'radio': radioInputType,
21563
21564
21565  /**
21566   * @ngdoc input
21567   * @name input[checkbox]
21568   *
21569   * @description
21570   * HTML checkbox.
21571   *
21572   * @param {string} ngModel Assignable angular expression to data-bind to.
21573   * @param {string=} name Property name of the form under which the control is published.
21574   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
21575   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
21576   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21577   *    interaction with the input element.
21578   *
21579   * @example
21580      <example name="checkbox-input-directive" module="checkboxExample">
21581        <file name="index.html">
21582         <script>
21583           angular.module('checkboxExample', [])
21584             .controller('ExampleController', ['$scope', function($scope) {
21585               $scope.checkboxModel = {
21586                value1 : true,
21587                value2 : 'YES'
21588              };
21589             }]);
21590         </script>
21591         <form name="myForm" ng-controller="ExampleController">
21592           <label>Value1:
21593             <input type="checkbox" ng-model="checkboxModel.value1">
21594           </label><br/>
21595           <label>Value2:
21596             <input type="checkbox" ng-model="checkboxModel.value2"
21597                    ng-true-value="'YES'" ng-false-value="'NO'">
21598            </label><br/>
21599           <tt>value1 = {{checkboxModel.value1}}</tt><br/>
21600           <tt>value2 = {{checkboxModel.value2}}</tt><br/>
21601          </form>
21602        </file>
21603        <file name="protractor.js" type="protractor">
21604          it('should change state', function() {
21605            var value1 = element(by.binding('checkboxModel.value1'));
21606            var value2 = element(by.binding('checkboxModel.value2'));
21607
21608            expect(value1.getText()).toContain('true');
21609            expect(value2.getText()).toContain('YES');
21610
21611            element(by.model('checkboxModel.value1')).click();
21612            element(by.model('checkboxModel.value2')).click();
21613
21614            expect(value1.getText()).toContain('false');
21615            expect(value2.getText()).toContain('NO');
21616          });
21617        </file>
21618      </example>
21619   */
21620  'checkbox': checkboxInputType,
21621
21622  'hidden': noop,
21623  'button': noop,
21624  'submit': noop,
21625  'reset': noop,
21626  'file': noop
21627};
21628
21629function stringBasedInputType(ctrl) {
21630  ctrl.$formatters.push(function(value) {
21631    return ctrl.$isEmpty(value) ? value : value.toString();
21632  });
21633}
21634
21635function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21636  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21637  stringBasedInputType(ctrl);
21638}
21639
21640function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21641  var type = lowercase(element[0].type);
21642
21643  // In composition mode, users are still inputing intermediate text buffer,
21644  // hold the listener until composition is done.
21645  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
21646  if (!$sniffer.android) {
21647    var composing = false;
21648
21649    element.on('compositionstart', function(data) {
21650      composing = true;
21651    });
21652
21653    element.on('compositionend', function() {
21654      composing = false;
21655      listener();
21656    });
21657  }
21658
21659  var listener = function(ev) {
21660    if (timeout) {
21661      $browser.defer.cancel(timeout);
21662      timeout = null;
21663    }
21664    if (composing) return;
21665    var value = element.val(),
21666        event = ev && ev.type;
21667
21668    // By default we will trim the value
21669    // If the attribute ng-trim exists we will avoid trimming
21670    // If input type is 'password', the value is never trimmed
21671    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
21672      value = trim(value);
21673    }
21674
21675    // If a control is suffering from bad input (due to native validators), browsers discard its
21676    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
21677    // control's value is the same empty value twice in a row.
21678    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
21679      ctrl.$setViewValue(value, event);
21680    }
21681  };
21682
21683  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
21684  // input event on backspace, delete or cut
21685  if ($sniffer.hasEvent('input')) {
21686    element.on('input', listener);
21687  } else {
21688    var timeout;
21689
21690    var deferListener = function(ev, input, origValue) {
21691      if (!timeout) {
21692        timeout = $browser.defer(function() {
21693          timeout = null;
21694          if (!input || input.value !== origValue) {
21695            listener(ev);
21696          }
21697        });
21698      }
21699    };
21700
21701    element.on('keydown', function(event) {
21702      var key = event.keyCode;
21703
21704      // ignore
21705      //    command            modifiers                   arrows
21706      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
21707
21708      deferListener(event, this, this.value);
21709    });
21710
21711    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
21712    if ($sniffer.hasEvent('paste')) {
21713      element.on('paste cut', deferListener);
21714    }
21715  }
21716
21717  // if user paste into input using mouse on older browser
21718  // or form autocomplete on newer browser, we need "change" event to catch it
21719  element.on('change', listener);
21720
21721  ctrl.$render = function() {
21722    // Workaround for Firefox validation #12102.
21723    var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
21724    if (element.val() !== value) {
21725      element.val(value);
21726    }
21727  };
21728}
21729
21730function weekParser(isoWeek, existingDate) {
21731  if (isDate(isoWeek)) {
21732    return isoWeek;
21733  }
21734
21735  if (isString(isoWeek)) {
21736    WEEK_REGEXP.lastIndex = 0;
21737    var parts = WEEK_REGEXP.exec(isoWeek);
21738    if (parts) {
21739      var year = +parts[1],
21740          week = +parts[2],
21741          hours = 0,
21742          minutes = 0,
21743          seconds = 0,
21744          milliseconds = 0,
21745          firstThurs = getFirstThursdayOfYear(year),
21746          addDays = (week - 1) * 7;
21747
21748      if (existingDate) {
21749        hours = existingDate.getHours();
21750        minutes = existingDate.getMinutes();
21751        seconds = existingDate.getSeconds();
21752        milliseconds = existingDate.getMilliseconds();
21753      }
21754
21755      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
21756    }
21757  }
21758
21759  return NaN;
21760}
21761
21762function createDateParser(regexp, mapping) {
21763  return function(iso, date) {
21764    var parts, map;
21765
21766    if (isDate(iso)) {
21767      return iso;
21768    }
21769
21770    if (isString(iso)) {
21771      // When a date is JSON'ified to wraps itself inside of an extra
21772      // set of double quotes. This makes the date parsing code unable
21773      // to match the date string and parse it as a date.
21774      if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
21775        iso = iso.substring(1, iso.length - 1);
21776      }
21777      if (ISO_DATE_REGEXP.test(iso)) {
21778        return new Date(iso);
21779      }
21780      regexp.lastIndex = 0;
21781      parts = regexp.exec(iso);
21782
21783      if (parts) {
21784        parts.shift();
21785        if (date) {
21786          map = {
21787            yyyy: date.getFullYear(),
21788            MM: date.getMonth() + 1,
21789            dd: date.getDate(),
21790            HH: date.getHours(),
21791            mm: date.getMinutes(),
21792            ss: date.getSeconds(),
21793            sss: date.getMilliseconds() / 1000
21794          };
21795        } else {
21796          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
21797        }
21798
21799        forEach(parts, function(part, index) {
21800          if (index < mapping.length) {
21801            map[mapping[index]] = +part;
21802          }
21803        });
21804        return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
21805      }
21806    }
21807
21808    return NaN;
21809  };
21810}
21811
21812function createDateInputType(type, regexp, parseDate, format) {
21813  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
21814    badInputChecker(scope, element, attr, ctrl);
21815    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21816    var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
21817    var previousDate;
21818
21819    ctrl.$$parserName = type;
21820    ctrl.$parsers.push(function(value) {
21821      if (ctrl.$isEmpty(value)) return null;
21822      if (regexp.test(value)) {
21823        // Note: We cannot read ctrl.$modelValue, as there might be a different
21824        // parser/formatter in the processing chain so that the model
21825        // contains some different data format!
21826        var parsedDate = parseDate(value, previousDate);
21827        if (timezone) {
21828          parsedDate = convertTimezoneToLocal(parsedDate, timezone);
21829        }
21830        return parsedDate;
21831      }
21832      return undefined;
21833    });
21834
21835    ctrl.$formatters.push(function(value) {
21836      if (value && !isDate(value)) {
21837        throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
21838      }
21839      if (isValidDate(value)) {
21840        previousDate = value;
21841        if (previousDate && timezone) {
21842          previousDate = convertTimezoneToLocal(previousDate, timezone, true);
21843        }
21844        return $filter('date')(value, format, timezone);
21845      } else {
21846        previousDate = null;
21847        return '';
21848      }
21849    });
21850
21851    if (isDefined(attr.min) || attr.ngMin) {
21852      var minVal;
21853      ctrl.$validators.min = function(value) {
21854        return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
21855      };
21856      attr.$observe('min', function(val) {
21857        minVal = parseObservedDateValue(val);
21858        ctrl.$validate();
21859      });
21860    }
21861
21862    if (isDefined(attr.max) || attr.ngMax) {
21863      var maxVal;
21864      ctrl.$validators.max = function(value) {
21865        return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
21866      };
21867      attr.$observe('max', function(val) {
21868        maxVal = parseObservedDateValue(val);
21869        ctrl.$validate();
21870      });
21871    }
21872
21873    function isValidDate(value) {
21874      // Invalid Date: getTime() returns NaN
21875      return value && !(value.getTime && value.getTime() !== value.getTime());
21876    }
21877
21878    function parseObservedDateValue(val) {
21879      return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
21880    }
21881  };
21882}
21883
21884function badInputChecker(scope, element, attr, ctrl) {
21885  var node = element[0];
21886  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
21887  if (nativeValidation) {
21888    ctrl.$parsers.push(function(value) {
21889      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
21890      // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
21891      // - also sets validity.badInput (should only be validity.typeMismatch).
21892      // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
21893      // - can ignore this case as we can still read out the erroneous email...
21894      return validity.badInput && !validity.typeMismatch ? undefined : value;
21895    });
21896  }
21897}
21898
21899function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21900  badInputChecker(scope, element, attr, ctrl);
21901  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21902
21903  ctrl.$$parserName = 'number';
21904  ctrl.$parsers.push(function(value) {
21905    if (ctrl.$isEmpty(value))      return null;
21906    if (NUMBER_REGEXP.test(value)) return parseFloat(value);
21907    return undefined;
21908  });
21909
21910  ctrl.$formatters.push(function(value) {
21911    if (!ctrl.$isEmpty(value)) {
21912      if (!isNumber(value)) {
21913        throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
21914      }
21915      value = value.toString();
21916    }
21917    return value;
21918  });
21919
21920  if (isDefined(attr.min) || attr.ngMin) {
21921    var minVal;
21922    ctrl.$validators.min = function(value) {
21923      return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
21924    };
21925
21926    attr.$observe('min', function(val) {
21927      if (isDefined(val) && !isNumber(val)) {
21928        val = parseFloat(val, 10);
21929      }
21930      minVal = isNumber(val) && !isNaN(val) ? val : undefined;
21931      // TODO(matsko): implement validateLater to reduce number of validations
21932      ctrl.$validate();
21933    });
21934  }
21935
21936  if (isDefined(attr.max) || attr.ngMax) {
21937    var maxVal;
21938    ctrl.$validators.max = function(value) {
21939      return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
21940    };
21941
21942    attr.$observe('max', function(val) {
21943      if (isDefined(val) && !isNumber(val)) {
21944        val = parseFloat(val, 10);
21945      }
21946      maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
21947      // TODO(matsko): implement validateLater to reduce number of validations
21948      ctrl.$validate();
21949    });
21950  }
21951}
21952
21953function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21954  // Note: no badInputChecker here by purpose as `url` is only a validation
21955  // in browsers, i.e. we can always read out input.value even if it is not valid!
21956  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21957  stringBasedInputType(ctrl);
21958
21959  ctrl.$$parserName = 'url';
21960  ctrl.$validators.url = function(modelValue, viewValue) {
21961    var value = modelValue || viewValue;
21962    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
21963  };
21964}
21965
21966function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21967  // Note: no badInputChecker here by purpose as `url` is only a validation
21968  // in browsers, i.e. we can always read out input.value even if it is not val
vendor: 6,806 bytes, lines 21968-22124
21968id!
21969  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21970  stringBasedInputType(ctrl);
21971
21972  ctrl.$$parserName = 'email';
21973  ctrl.$validators.email = function(modelValue, viewValue) {
21974    var value = modelValue || viewValue;
21975    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
21976  };
21977}
21978
21979function radioInputType(scope, element, attr, ctrl) {
21980  // make the name unique, if not defined
21981  if (isUndefined(attr.name)) {
21982    element.attr('name', nextUid());
21983  }
21984
21985  var listener = function(ev) {
21986    if (element[0].checked) {
21987      ctrl.$setViewValue(attr.value, ev && ev.type);
21988    }
21989  };
21990
21991  element.on('click', listener);
21992
21993  ctrl.$render = function() {
21994    var value = attr.value;
21995    element[0].checked = (value == ctrl.$viewValue);
21996  };
21997
21998  attr.$observe('value', ctrl.$render);
21999}
22000
22001function parseConstantExpr($parse, context, name, expression, fallback) {
22002  var parseFn;
22003  if (isDefined(expression)) {
22004    parseFn = $parse(expression);
22005    if (!parseFn.constant) {
22006      throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22007                                   '`{1}`.', name, expression);
22008    }
22009    return parseFn(context);
22010  }
22011  return fallback;
22012}
22013
22014function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22015  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22016  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22017
22018  var listener = function(ev) {
22019    ctrl.$setViewValue(element[0].checked, ev && ev.type);
22020  };
22021
22022  element.on('click', listener);
22023
22024  ctrl.$render = function() {
22025    element[0].checked = ctrl.$viewValue;
22026  };
22027
22028  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22029  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22030  // it to a boolean.
22031  ctrl.$isEmpty = function(value) {
22032    return value === false;
22033  };
22034
22035  ctrl.$formatters.push(function(value) {
22036    return equals(value, trueValue);
22037  });
22038
22039  ctrl.$parsers.push(function(value) {
22040    return value ? trueValue : falseValue;
22041  });
22042}
22043
22044
22045/**
22046 * @ngdoc directive
22047 * @name textarea
22048 * @restrict E
22049 *
22050 * @description
22051 * HTML textarea element control with angular data-binding. The data-binding and validation
22052 * properties of this element are exactly the same as those of the
22053 * {@link ng.directive:input input element}.
22054 *
22055 * @param {string} ngModel Assignable angular expression to data-bind to.
22056 * @param {string=} name Property name of the form under which the control is published.
22057 * @param {string=} required Sets `required` validation error key if the value is not entered.
22058 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22059 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22060 *    `required` when you want to data-bind to the `required` attribute.
22061 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22062 *    minlength.
22063 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22064 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22065 *    length.
22066 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22067 *    a RegExp found by evaluating the Angular expression given in the attribute value.
22068 *    If the expression evaluates to a RegExp object, then this is used directly.
22069 *    If the expression evaluates to a string, then it will be converted to a RegExp
22070 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22071 *    `new RegExp('^abc$')`.<br />
22072 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22073 *    start at the index of the last search's match, thus not taking the whole input value into
22074 *    account.
22075 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22076 *    interaction with the input element.
22077 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22078 */
22079
22080
22081/**
22082 * @ngdoc directive
22083 * @name input
22084 * @restrict E
22085 *
22086 * @description
22087 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22088 * input state control, and validation.
22089 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22090 *
22091 * <div class="alert alert-warning">
22092 * **Note:** Not every feature offered is available for all input types.
22093 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22094 * </div>
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=} required Sets `required` validation error key if the value is not entered.
22099 * @param {boolean=} ngRequired Sets `required` attribute if set to true
22100 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22101 *    minlength.
22102 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22103 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22104 *    length.
22105 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22106 *    a RegExp found by evaluating the Angular expression given in the attribute value.
22107 *    If the expression evaluates to a RegExp object, then this is used directly.
22108 *    If the expression evaluates to a string, then it will be converted to a RegExp
22109 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22110 *    `new RegExp('^abc$')`.<br />
22111 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22112 *    start at the index of the last search's match, thus not taking the whole input value into
22113 *    account.
22114 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22115 *    interaction with the input element.
22116 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22117 *    This parameter is ignored for input[type=password] controls, which will never trim the
22118 *    input.
22119 *
22120 * @example
22121    <example name="input-directive" module="inputExample">
22122      <file name="index.html">
22123       <script>
22124          angular.module('inputExample', [])
22125            .controller('ExampleController', ['$scope', function($scope) {
22126              $scope.user = {name: 'guest', last: 'visitor'};
22127            }]);
22128       </script>
22129       <div ng-controller="ExampleController">
22130         <form name="myForm">
22131           <label>
22132              User name:
22133              <input type="text" name="userName" ng-model="user.name" required>
22134           </label>
22135           <div role="alert">
22136             <span class="error" ng-show="myForm.userName.$error.required">
22137              Required!</span>
22138           </div>
22139           <label>
22140              Last name:
22141              <input type="text" name="lastName" ng-model="user.last"
22142              ng-minlength="3" ng-maxlength="10">
22143           </label>
22144           <div role="alert">
22145             <span class="error" ng-show="myForm.lastName.$error.minlength">
22146               Too short!</span>
22147             <span class="error" ng-show="myForm.lastName.$error.maxlength">
22148               Too long!</span>
22149           </div>
22150         </form>
22151         <hr>
22152         <tt>user = {{user}}</tt><br/>
22153         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
22154         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
22155         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
22156         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
22157         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22158         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22159         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
22160         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
22161       </div>
22162      </file>
22163      <file name="protractor.js" type="protractor">
22164        var user = element(by.exactBinding('user'));
22165        var userNameValid = element(by.binding('myForm.userName.$valid'));
22166        var lastNameValid = element(by.binding('myForm.lastName.$valid'));
22167        var lastNameError = element(by.binding('myForm.lastName.$error'));
22168        var formValid = element(by.binding('myForm.$valid'));
22169        var userNameInput = element(by.model('user.name'));
22170        var userLastInput = element(by.model('user.last'));
22171
22172        it('should initialize to model', function() {
22173          expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
22174          expect(userNameValid.getText()).toContain('true');
22175          expect(formValid.getText()).toContain('true');
22176        });
22177
22178        it('should be invalid if empty when required', function() {
22179          userNameInput.clear();
22180          userNameInput.sendKeys('');
22181
22182          expect(user.getText()).toContain('{"last":"visitor"}');
22183          expect(userNameValid.getText()).toContain('false');
22184          expect(formValid.getText()).toContain('false');
22185        });
22186
22187        it('should be valid if empty when min length is set', function() {
22188          userLastInput.clear();
22189          userLastInput.sendKeys('');
22190
22191          expect(user.getText()).toContain('{"name":"guest","last":""}');
22192          expect(lastNameValid.getText()).toContain('true');
22193          expect(formValid.getText()).toContain('true');
22194        });
22195
22196        it('should be invalid if less than required min length', function() {
22197          userLastInput.clear();
22198          userLastInput.sendKeys('xx');
22199
22200          expect(user.getText()).toContain('{"name":"guest"}');
22201          expect(lastNameValid.getText()).toContain('false');
22202          expect(lastNameError.getText()).toContain('minlength');
22203          expect(formValid.getText()).toContain('false');
22204        });
22205
22206        it('should be invalid if longer than max length', function() {
22207          userLastInput.clear();
22208          userLastInput.sendKeys('some ridiculously long name');
22209
22210          expect(user.getText()).toContain('{"name":"guest"}');
22211          expect(lastNameValid.getText()).toContain('false');
22212          expect(lastNameError.getText()).toContain('maxlength');
22213          expect(formValid.getText()).toContain('false');
22214        });
22215      </file>
22216    </example>
22217 */
22218var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
22219    function($browser, $sniffer, $filter, $parse) {
22220  return {
22221    restrict: 'E',
22222    require: ['?ngModel'],
22223    link: {
22224      pre: function(scope, element, attr, ctrls) {
22225        if (ctrls[0]) {
22226          (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
22227                                                              $browser, $filter, $parse);
22228        }
22229      }
22230    }
22231  };
22232}];
22233
22234
22235
22236var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
22237/**
22238 * @ngdoc directive
22239 * @name ngValue
22240 *
22241 * @description
22242 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
22243 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
22244 * the bound value.
22245 *
22246 * `ngValue` is useful when dynamically generating lists of radio buttons using
22247 * {@link ngRepeat `ngRepeat`}, as shown below.
22248 *
22249 * Likewise, `ngValue` can be used to generate `<option>` elements for
22250 * the {@link select `select`} element. In that case however, only strings are supported
22251 * for the `value `attribute, so the resulting `ngModel` will always be a string.
22252 * Support for `select` models with non-string values is available via `ngOptions`.
22253 *
22254 * @element input
22255 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
22256 *   of the `input` element
22257 *
22258 * @example
22259    <example name="ngValue-directive" module="valueExample">
22260      <file name="index.html">
22261       <script>
22262          angular.module('valueExample', [])
22263            .controller('ExampleController', ['$scope', function($scope) {
22264              $scope.names = ['pizza', 'unicorns', 'robots'];
22265              $scope.my = { favorite: 'unicorns' };
22266            }]);
22267       </script>
22268        <form ng-controller="ExampleController">
22269          <h2>Which is your favorite?</h2>
22270            <label ng-repeat="name in names" for="{{name}}">
22271              {{name}}
22272              <input type="radio"
22273                     ng-model="my.favorite"
22274                     ng-value="name"
22275                     id="{{name}}"
22276                     name="favorite">
22277            </label>
22278          <div>You chose {{my.favorite}}</div>
22279        </form>
22280      </file>
22281      <file name="protractor.js" type="protractor">
22282        var favorite = element(by.binding('my.favorite'));
22283
22284        it('should initialize to model', function() {
22285          expect(favorite.getText()).toContain('unicorns');
22286        });
22287        it('should bind the values to the inputs', function() {
22288          element.all(by.model('my.favorite')).get(0).click();
22289          expect(favorite.getText()).toContain('pizza');
22290        });
22291      </file>
22292    </example>
22293 */
22294var ngValueDirective = function() {
22295  return {
22296    restrict: 'A',
22297    priority: 100,
22298    compile: function(tpl, tplAttr) {
22299      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22300        return function ngValueConstantLink(scope, elm, attr) {
22301          attr.$set('value', scope.$eval(attr.ngValue));
22302        };
22303      } else {
22304        return function ngValueLink(scope, elm, attr) {
22305          scope.$watch(attr.ngValue, function valueWatchAction(value) {
22306            attr.$set('value', value);
22307          });
22308        };
22309      }
22310    }
22311  };
22312};
22313
22314/**
22315 * @ngdoc directive
22316 * @name ngBind
22317 * @restrict AC
22318 *
22319 * @description
22320 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22321 * with the value of a given expression, and to update the text content when the value of that
22322 * expression changes.
22323 *
22324 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22325 * `{{ expression }}` which is similar but less verbose.
22326 *
22327 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22328 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22329 * element attribute, it makes the bindings invisible to the user while the page is loading.
22330 *
22331 * An alternative solution to this problem would be using the
22332 * {@link ng.directive:ngCloak ngCloak} directive.
22333 *
22334 *
22335 * @element ANY
22336 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22337 *
22338 * @example
22339 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22340   <example module="bindExample">
22341     <file name="index.html">
22342       <script>
22343         angular.module('bindExample', [])
22344           .controller('ExampleController', ['$scope', function($scope) {
22345             $scope.name = 'Whirled';
22346           }]);
22347       </script>
22348       <div ng-controller="ExampleController">
22349         <label>Enter name: <input type="text" ng-model="name"></label><br>
22350         Hello <span ng-bind="name"></span>!
22351       </div>
22352     </file>
22353     <file name="protractor.js" type="protractor">
22354       it('should check ng-bind', function() {
22355         var nameInput = element(by.model('name'));
22356
22357         expect(element(by.binding('name')).getText()).toBe('Whirled');
22358         nameInput.clear();
22359         nameInput.sendKeys('world');
22360         expect(element(by.binding('name')).getText()).toBe('world');
22361       });
22362     </file>
22363   </example>
22364 */
22365var ngBindDirective = ['$compile', function($compile) {
22366  return {
22367    restrict: 'AC',
22368    compile: function ngBindCompile(templateElement) {
22369      $compile.$$addBindingClass(templateElement);
22370      return function ngBindLink(scope, element, attr) {
22371        $compile.$$addBindingInfo(element, attr.ngBind);
22372        element = element[0];
22373        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22374          element.textContent = isUndefined(value) ? '' : value;
22375        });
22376      };
22377    }
22378  };
22379}];
22380
22381
22382/**
22383 * @ngdoc directive
22384 * @name ngBindTemplate
22385 *
22386 * @description
22387 * The `ngBindTemplate` directive specifies that the element
22388 * text content should be replaced with the interpolation of the template
22389 * in the `ngBindTemplate` attribute.
22390 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22391 * expressions. This directive is needed since some HTML elements
22392 * (such as TITLE and OPTION) cannot contain SPAN elements.
22393 *
22394 * @element ANY
22395 * @param {string} ngBindTemplate template of form
22396 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22397 *
22398 * @example
22399 * Try it here: enter text in text box and watch the greeting change.
22400   <example module="bindExample">
22401     <file name="index.html">
22402       <script>
22403         angular.module('bindExample', [])
22404           .controller('ExampleController', ['$scope', function($scope) {
22405             $scope.salutation = 'Hello';
22406             $scope.name = 'World';
22407           }]);
22408       </script>
22409       <div ng-controller="ExampleController">
22410        <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22411        <label>Name: <input type="text" ng-model="name"></label><br>
22412        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22413       </div>
22414     </file>
22415     <file name="protractor.js" type="protractor">
22416       it('should check ng-bind', function() {
22417         var salutationElem = element(by.binding('salutation'));
22418         var salutationInput = element(by.model('salutation'));
22419         var nameInput = element(by.model('name'));
22420
22421         expect(salutationElem.getText()).toBe('Hello World!');
22422
22423         salutationInput.clear();
22424         salutationInput.sendKeys('Greetings');
22425         nameInput.clear();
22426         nameInput.sendKeys('user');
22427
22428         expect(salutationElem.getText()).toBe('Greetings user!');
22429       });
22430     </file>
22431   </example>
22432 */
22433var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22434  return {
22435    compile: function ngBindTemplateCompile(templateElement) {
22436      $compile.$$addBindingClass(templateElement);
22437      return function ngBindTemplateLink(scope, element, attr) {
22438        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22439        $compile.$$addBindingInfo(element, interpolateFn.expressions);
22440        element = element[0];
22441        attr.$observe('ngBindTemplate', function(value) {
22442          element.textContent = isUndefined(value) ? '' : value;
22443        });
22444      };
22445    }
22446  };
22447}];
22448
22449
22450/**
22451 * @ngdoc directive
22452 * @name ngBindHtml
22453 *
22454 * @description
22455 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22456 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22457 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22458 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22459 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22460 *
22461 * You may also bypass sanitization for values you know are safe. To do so, bind to
22462 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
22463 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22464 *
22465 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22466 * will have an exception (instead of an exploit.)
22467 *
22468 * @element ANY
22469 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22470 *
22471 * @example
22472
22473   <example module="bindHtmlExample" deps="angular-sanitize.js">
22474     <file name="index.html">
22475       <div ng-controller="ExampleController">
22476        <p ng-bind-html="myHTML"></p>
22477       </div>
22478     </file>
22479
22480     <file name="script.js">
22481       angular.module('bindHtmlExample', ['ngSanitize'])
22482         .controller('ExampleController', ['$scope', function($scope) {
22483           $scope.myHTML =
22484              'I am an <code>HTML</code>string with ' +
22485              '<a href="#">links!</a> and other <em>stuff</em>';
22486         }]);
22487     </file>
22488
22489     <file name="protractor.js" type="protractor">
22490       it('should check ng-bind-html', function() {
22491         expect(element(by.binding('myHTML')).getText()).toBe(
22492             'I am an HTMLstring with links! and other stuff');
22493       });
22494     </file>
22495   </example>
22496 */
22497var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
22498  return {
22499    restrict: 'A',
22500    compile: function ngBindHtmlCompile(tElement, tAttrs) {
22501      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
22502      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
22503        return (value || '').toString();
22504      });
22505      $compile.$$addBindingClass(tElement);
22506
22507      return function ngBindHtmlLink(scope, element, attr) {
22508        $compile.$$addBindingInfo(element, attr.ngBindHtml);
22509
22510        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
22511          // we re-evaluate the expr because we want a TrustedValueHolderType
22512          // for $sce, not a string
22513          element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
22514        });
22515      };
22516    }
22517  };
22518}];
22519
22520/**
22521 * @ngdoc directive
22522 * @name ngChange
22523 *
22524 * @description
22525 * Evaluate the given expression when the user changes the input.
22526 * The expression is evaluated immediately, unlike the JavaScript onchange event
22527 * which only triggers at the end of a change (usually, when the user leaves the
22528 * form element or presses the return key).
22529 *
22530 * The `ngChange` expression is only evaluated when a change in the input value causes
22531 * a new value to be committed to the model.
22532 *
22533 * It will not be evaluated:
22534 * * if the value returned from the `$parsers` transformation pipeline has not changed
22535 * * if the input has continued to be invalid since the model will stay `null`
22536 * * if the model is changed programmatically and not by a change to the input value
22537 *
22538 *
22539 * Note, this directive requires `ngModel` to be present.
22540 *
22541 * @element input
22542 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
22543 * in input value.
22544 *
22545 * @example
22546 * <example name="ngChange-directive" module="changeExample">
22547 *   <file name="index.html">
22548 *     <script>
22549 *       angular.module('changeExample', [])
22550 *         .controller('ExampleController', ['$scope', function($scope) {
22551 *           $scope.counter = 0;
22552 *           $scope.change = function() {
22553 *             $scope.counter++;
22554 *           };
22555 *         }]);
22556 *     </script>
22557 *     <div ng-controller="ExampleController">
22558 *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
22559 *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
22560 *       <label for="ng-change-example2">Confirmed</label><br />
22561 *       <tt>debug = {{confirmed}}</tt><br/>
22562 *       <tt>counter = {{counter}}</tt><br/>
22563 *     </div>
22564 *   </file>
22565 *   <file name="protractor.js" type="protractor">
22566 *     var counter = element(by.binding('counter'));
22567 *     var debug = element(by.binding('confirmed'));
22568 *
22569 *     it('should evaluate the expression if changing from view', function() {
22570 *       expect(counter.getText()).toContain('0');
22571 *
22572 *       element(by.id('ng-change-example1')).click();
22573 *
22574 *       expect(counter.getText()).toContain('1');
22575 *       expect(debug.getText()).toContain('true');
22576 *     });
22577 *
22578 *     it('should not evaluate the expression if changing from model', function() {
22579 *       element(by.id('ng-change-example2')).click();
22580
22581 *       expect(counter.getText()).toContain('0');
22582 *       expect(debug.getText()).toContain('true');
22583 *     });
22584 *   </file>
22585 * </example>
22586 */
22587var ngChangeDirective = valueFn({
22588  restrict: 'A',
22589  require: 'ngModel',
22590  link: function(scope, element, attr, ctrl) {
22591    ctrl.$viewChangeListeners.push(function() {
22592      scope.$eval(attr.ngChange);
22593    });
22594  }
22595});
22596
22597function classDirective(name, selector) {
22598  name = 'ngClass' + name;
22599  return ['$animate', function($animate) {
22600    return {
22601      restrict: 'AC',
22602      link: function(scope, element, attr) {
22603        var oldVal;
22604
22605        scope.$watch(attr[name], ngClassWatchAction, true);
22606
22607        attr.$observe('class', function(value) {
22608          ngClassWatchAction(scope.$eval(attr[name]));
22609        });
22610
22611
22612        if (name !== 'ngClass') {
22613          scope.$watch('$index', function($index, old$index) {
22614            // jshint bitwise: false
22615            var mod = $index & 1;
22616            if (mod !== (old$index & 1)) {
22617              var classes = arrayClasses(scope.$eval(attr[name]));
22618              mod === selector ?
22619                addClasses(classes) :
22620                removeClasses(classes);
22621            }
22622          });
22623        }
22624
22625        function addClasses(classes) {
22626          var newClasses = digestClassCounts(classes, 1);
22627          attr.$addClass(newClasses);
22628        }
22629
22630        function removeClasses(classes) {
22631          var newClasses = digestClassCounts(classes, -1);
22632          attr.$removeClass(newClasses);
22633        }
22634
22635        function digestClassCounts(classes, count) {
22636          // Use createMap() to prevent class assumptions involving property
22637          // names in Object.prototype
22638          var classCounts = element.data('$classCounts') || createMap();
22639          var classesToUpdate = [];
22640          forEach(classes, function(className) {
22641            if (count > 0 || classCounts[className]) {
22642              classCounts[className] = (classCounts[className] || 0) + count;
22643              if (classCounts[className] === +(count > 0)) {
22644                classesToUpdate.push(className);
22645              }
22646            }
22647          });
22648          element.data('$classCounts', classCounts);
22649          return classesToUpdate.join(' ');
22650        }
22651
22652        function updateClasses(oldClasses, newClasses) {
22653          var toAdd = arrayDifference(newClasses, oldClasses);
22654          var toRemove = arrayDifference(oldClasses, newClasses);
22655          toAdd = digestClassCounts(toAdd, 1);
22656          toRemove = digestClassCounts(toRemove, -1);
22657          if (toAdd && toAdd.length) {
22658            $animate.addClass(element, toAdd);
22659          }
22660          if (toRemove && toRemove.length) {
22661            $animate.removeClass(element, toRemove);
22662          }
22663        }
22664
22665        function ngClassWatchAction(newVal) {
22666          if (selector === true || scope.$index % 2 === selector) {
22667            var newClasses = arrayClasses(newVal || []);
22668            if (!oldVal) {
22669              addClasses(newClasses);
22670            } else if (!equals(newVal,oldVal)) {
22671              var oldClasses = arrayClasses(oldVal);
22672              updateClasses(oldClasses, newClasses);
22673            }
22674          }
22675          oldVal = shallowCopy(newVal);
22676        }
22677      }
22678    };
22679
22680    function arrayDifference(tokens1, tokens2) {
22681      var values = [];
22682
22683      outer:
22684      for (var i = 0; i < tokens1.length; i++) {
22685        var token = tokens1[i];
22686        for (var j = 0; j < tokens2.length; j++) {
22687          if (token == tokens2[j]) continue outer;
22688        }
22689        values.push(token);
22690      }
22691      return values;
22692    }
22693
22694    function arrayClasses(classVal) {
22695      var classes = [];
22696      if (isArray(classVal)) {
22697        forEach(classVal, function(v) {
22698          classes = classes.concat(arrayClasses(v));
22699        });
22700        return classes;
22701      } else if (isString(classVal)) {
22702        return classVal.split(' ');
22703      } else if (isObject(classVal)) {
22704        forEach(classVal, function(v, k) {
22705          if (v) {
22706            classes = classes.concat(k.split(' '));
22707          }
22708        });
22709        return classes;
22710      }
22711      return classVal;
22712    }
22713  }];
22714}
22715
22716/**
22717 * @ngdoc directive
22718 * @name ngClass
22719 * @restrict AC
22720 *
22721 * @description
22722 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
22723 * an expression that represents all classes to be added.
22724 *
22725 * The directive operates in three different ways, depending on which of three types the expression
22726 * evaluates to:
22727 *
22728 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
22729 * names.
22730 *
22731 * 2. If the expression evaluates to an object, then for each key-value pair of the
22732 * object with a truthy value the corresponding key is used as a class name.
22733 *
22734 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
22735 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
22736 * to give you more control over what CSS classes appear. See the code below for an example of this.
22737 *
22738 *
22739 * The directive won't add duplicate classes if a particular class was already set.
22740 *
22741 * When the expression changes, the previously added classes are removed and only then are the
22742 * new classes added.
22743 *
22744 * @animations
22745 * **add** - happens just before the class is applied to the elements
22746 *
22747 * **remove** - happens just before the class is removed from the element
22748 *
22749 * @element ANY
22750 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
22751 *   of the evaluation can be a string representing space delimited class
22752 *   names, an array, or a map of class names to boolean values. In the case of a map, the
22753 *   names of the properties whose values are truthy will be added as css classes to the
22754 *   element.
22755 *
22756 * @example Example that demonstrates basic bindings via ngClass directive.
22757   <example>
22758     <file name="index.html">
22759       <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
22760       <label>
22761          <input type="checkbox" ng-model="deleted">
22762          deleted (apply "strike" class)
22763       </label><br>
22764       <label>
22765          <input type="checkbox" ng-model="important">
22766          important (apply "bold" class)
22767       </label><br>
22768       <label>
22769          <input type="checkbox" ng-model="error">
22770          error (apply "has-error" class)
22771       </label>
22772       <hr>
22773       <p ng-class="style">Using String Syntax</p>
22774       <input type="text" ng-model="style"
22775              placeholder="Type: bold strike red" aria-label="Type: bold strike red">
22776       <hr>
22777       <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
22778       <input ng-model="style1"
22779              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
22780       <input ng-model="style2"
22781              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
22782       <input ng-model="style3"
22783              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
22784       <hr>
22785       <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
22786       <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
22787       <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
22788     </file>
22789     <file name="style.css">
22790       .strike {
22791           text-decoration: line-through;
22792       }
22793       .bold {
22794           font-weight: bold;
22795       }
22796       .red {
22797           color: red;
22798       }
22799       .has-error {
22800           color: red;
22801           background-color: yellow;
22802       }
22803       .orange {
22804           color: orange;
22805       }
22806     </file>
22807     <file name="protractor.js" type="protractor">
22808       var ps = element.all(by.css('p'));
22809
22810       it('should let you toggle the class', function() {
22811
22812         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
22813         expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
22814
22815         element(by.model('important')).click();
22816         expect(ps.first().getAttribute('class')).toMatch(/bold/);
22817
22818         element(by.model('error')).click();
22819         expect(ps.first().getAttribute('class')).toMatch(/has-error/);
22820       });
22821
22822       it('should let you toggle string example', function() {
22823         expect(ps.get(1).getAttribute('class')).toBe('');
22824         element(by.model('style')).clear();
22825         element(by.model('style')).sendKeys('red');
22826         expect(ps.get(1).getAttribute('class')).toBe('red');
22827       });
22828
22829       it('array example should have 3 classes', function() {
22830         expect(ps.get(2).getAttribute('class')).toBe('');
22831         element(by.model('style1')).sendKeys('bold');
22832         element(by.model('style2')).sendKeys('strike');
22833         element(by.model('style3')).sendKeys('red');
22834         expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
22835       });
22836
22837       it('array with map example should have 2 classes', function() {
22838         expect(ps.last().getAttribute('class')).toBe('');
22839         element(by.model('style4')).sendKeys('bold');
22840         element(by.model('warning')).click();
22841         expect(ps.last().getAttribute('class')).toBe('bold orange');
22842       });
22843     </file>
22844   </example>
22845
22846   ## Animations
22847
22848   The example below demonstrates how to perform animations using ngClass.
22849
22850   <example module="ngAnimate" deps="angular-animate.js" animations="true">
22851     <file name="index.html">
22852      <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
22853      <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
22854      <br>
22855      <span class="base-class" ng-class="myVar">Sample Text</span>
22856     </file>
22857     <file name="style.css">
22858       .base-class {
22859         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22860       }
22861
22862       .base-class.my-class {
22863         color: red;
22864         font-size:3em;
22865       }
22866     </file>
22867     <file name="protractor.js" type="protractor">
22868       it('should check ng-class', function() {
22869         expect(element(by.css('.base-class')).getAttribute('class')).not.
22870           toMatch(/my-class/);
22871
22872         element(by.id('setbtn')).click();
22873
22874         expect(element(by.css('.base-class')).getAttribute('class')).
22875           toMatch(/my-class/);
22876
22877         element(by.id('clearbtn')).click();
22878
22879         expect(element(by.css('.base-class')).getAttribute('class')).not.
22880           toMatch(/my-class/);
22881       });
22882     </file>
22883   </example>
22884
22885
22886   ## ngClass and pre-existing CSS3 Transitions/Animations
22887   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
22888   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
22889   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
22890   to view the step by step details of {@link $animate#addClass $animate.addClass} and
22891   {@link $animate#removeClass $animate.removeClass}.
22892 */
22893var ngClassDirective = classDirective('', true);
22894
22895/**
22896 * @ngdoc directive
22897 * @name ngClassOdd
22898 * @restrict AC
22899 *
22900 * @description
22901 * The `ngClassOdd` and `ngClassEven` directives work exactly as
22902 * {@link ng.directive:ngClass ngClass}, except they work in
22903 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
22904 *
22905 * This directive can be applied only within the scope of an
22906 * {@link ng.directive:ngRepeat ngRepeat}.
22907 *
22908 * @element ANY
22909 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
22910 *   of the evaluation can be a string representing space delimited class names or an array.
22911 *
22912 * @example
22913   <example>
22914     <file name="index.html">
22915        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
22916          <li ng-repeat="name in names">
22917           <span ng-class-odd="'odd'" ng-class-even="'even'">
22918             {{name}}
22919           </span>
22920          </li>
22921        </ol>
22922     </file>
22923     <file name="style.css">
22924       .odd {
22925         color: red;
22926       }
22927       .even {
22928         color: blue;
22929       }
22930     </file>
22931     <file name="protractor.js" type="protractor">
22932       it('should check ng-class-odd and ng-class-even', function() {
22933         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
22934           toMatch(/odd/);
22935         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
22936           toMatch(/even/);
22937       });
22938     </file>
22939   </example>
22940 */
22941var ngClassOddDirective = classDirective('Odd', 0);
22942
22943/**
22944 * @ngdoc directive
22945 * @name ngClassEven
22946 * @restrict AC
22947 *
22948 * @description
22949 * The `ngClassOdd` and `ngClassEven` directives work exactly as
22950 * {@link ng.directive:ngClass ngClass}, except they work in
22951 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
22952 *
22953 * This directive can be applied only within the scope of an
22954 * {@link ng.directive:ngRepeat ngRepeat}.
22955 *
22956 * @element ANY
22957 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
22958 *   result of the evaluation can be a string representing space delimited class names or an array.
22959 *
22960 * @example
22961   <example>
22962     <file name="index.html">
22963        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
22964          <li ng-repeat="name in names">
22965           <span ng-class-odd="'odd'" ng-class-even="'even'">
22966             {{name}} &nbsp; &nbsp; &nbsp;
22967           </span>
22968          </li>
22969        </ol>
22970     </file>
22971     <file name="style.css">
22972       .odd {
22973         color: red;
22974       }
22975       .even {
22976         color: blue;
22977       }
22978     </file>
22979     <file name="protractor.js" type="protractor">
22980       it('should check ng-class-odd and ng-class-even', function() {
22981         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
22982           toMatch(/odd/);
22983         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
22984           toMatch(/even/);
22985       });
22986     </file>
22987   </example>
22988 */
22989var ngClassEvenDirective = classDirective('Even', 1);
22990
22991/**
22992 * @ngdoc directive
22993 * @name ngCloak
22994 * @restrict AC
22995 *
22996 * @description
22997 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
22998 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
22999 * directive to avoid the undesirable flicker effect caused by the html template display.
23000 *
23001 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23002 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23003 * of the browser view.
23004 *
23005 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23006 * `angular.min.js`.
23007 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23008 *
23009 * ```css
23010 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23011 *   display: none !important;
23012 * }
23013 * ```
23014 *
23015 * When this css rule is loaded by the browser, all html elements (including their children) that
23016 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23017 * during the compilation of the template it deletes the `ngCloak` element attribute, making
23018 * the compiled element visible.
23019 *
23020 * For the best result, the `angular.js` script must be loaded in the head section of the html
23021 * document; alternatively, the css rule above must be included in the external stylesheet of the
23022 * application.
23023 *
23024 * @element ANY
23025 *
23026 * @example
23027   <example>
23028     <file name="index.html">
23029        <div id="template1" ng-cloak>{{ 'hello' }}</div>
23030        <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23031     </file>
23032     <file name="protractor.js" type="protractor">
23033       it('should remove the template directive and css class', function() {
23034         expect($('#template1').getAttribute('ng-cloak')).
23035           toBeNull();
23036         expect($('#template2').getAttribute('ng-cloak')).
23037           toBeNull();
23038       });
23039     </file>
23040   </example>
23041 *
23042 */
23043var ngCloakDirective = ngDirective({
23044  compile: function(element, attr) {
23045    attr.$set('ngCloak', undefined);
23046    element.removeClass('ng-cloak');
23047  }
23048});
23049
23050/**
23051 * @ngdoc directive
23052 * @name ngController
23053 *
23054 * @description
23055 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23056 * supports the principles behind the Model-View-Controller design pattern.
23057 *
23058 * MVC components in angular:
23059 *
23060 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23061 *   are accessed through bindings.
23062 * * View — The template (HTML with data bindings) that is rendered into the View.
23063 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23064 *   logic behind the application to decorate the scope with functions and values
23065 *
23066 * Note that you can also attach controllers to the DOM by declaring it in a route definition
23067 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23068 * again using `ng-controller` in the template itself.  This will cause the controller to be attached
23069 * and executed twice.
23070 *
23071 * @element ANY
23072 * @scope
23073 * @priority 500
23074 * @param {expression} ngController Name of a constructor function registered with the current
23075 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23076 * that on the current scope evaluates to a constructor function.
23077 *
23078 * The controller instance can be published into a scope property by specifying
23079 * `ng-controller="as propertyName"`.
23080 *
23081 * If the current `$controllerProvider` is configured to use globals (via
23082 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23083 * also be the name of a globally accessible constructor function (not recommended).
23084 *
23085 * @example
23086 * Here is a simple form for editing user contact information. Adding, remov
23086ing, clearing, and
23087 * greeting are methods declared on the controller (see source tab). These methods can
23088 * easily be called from the angular markup. Any changes to the data are automatically reflected
23089 * in the View without the need for a manual update.
23090 *
23091 * Two different declaration styles are included below:
23092 *
23093 * * one binds methods and properties directly onto the controller using `this`:
23094 * `ng-controller="SettingsController1 as settings"`
23095 * * one injects `$scope` into the controller:
23096 * `ng-controller="SettingsController2"`
23097 *
23098 * The second option is more common in the Angular community, and is generally used in boilerplates
23099 * and in this guide. However, there are advantages to binding properties directly to the controller
23100 * and avoiding scope.
23101 *
23102 * * Using `controller as` makes it obvious which controller you are accessing in the template when
23103 * multiple controllers apply to an element.
23104 * * If you are writing your controllers as classes you have easier access to the properties and
23105 * methods, which will appear on the scope, from inside the controller code.
23106 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23107 * inheritance masking primitives.
23108 *
23109 * This example demonstrates the `controller as` syntax.
23110 *
23111 * <example name="ngControllerAs" module="controllerAsExample">
23112 *   <file name="index.html">
23113 *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23114 *      <label>Name: <input type="text" ng-model="settings.name"/></label>
23115 *      <button ng-click="settings.greet()">greet</button><br/>
23116 *      Contact:
23117 *      <ul>
23118 *        <li ng-repeat="contact in settings.contacts">
23119 *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23120 *             <option>phone</option>
23121 *             <option>email</option>
23122 *          </select>
23123 *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23124 *          <button ng-click="settings.clearContact(contact)">clear</button>
23125 *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
23126 *        </li>
23127 *        <li><button ng-click="settings.addContact()">add</button></li>
23128 *     </ul>
23129 *    </div>
23130 *   </file>
23131 *   <file name="app.js">
23132 *    angular.module('controllerAsExample', [])
23133 *      .controller('SettingsController1', SettingsController1);
23134 *
23135 *    function SettingsController1() {
23136 *      this.name = "John Smith";
23137 *      this.contacts = [
23138 *        {type: 'phone', value: '408 555 1212'},
23139 *        {type: 'email', value: '[email protected]'} ];
23140 *    }
23141 *
23142 *    SettingsController1.prototype.greet = function() {
23143 *      alert(this.name);
23144 *    };
23145 *
23146 *    SettingsController1.prototype.addContact = function() {
23147 *      this.contacts.push({type: 'email', value: '[email protected]'});
23148 *    };
23149 *
23150 *    SettingsController1.prototype.removeContact = function(contactToRemove) {
23151 *     var index = this.contacts.indexOf(contactToRemove);
23152 *      this.contacts.splice(index, 1);
23153 *    };
23154 *
23155 *    SettingsController1.prototype.clearContact = function(contact) {
23156 *      contact.type = 'phone';
23157 *      contact.value = '';
23158 *    };
23159 *   </file>
23160 *   <file name="protractor.js" type="protractor">
23161 *     it('should check controller as', function() {
23162 *       var container = element(by.id('ctrl-as-exmpl'));
23163 *         expect(container.element(by.model('settings.name'))
23164 *           .getAttribute('value')).toBe('John Smith');
23165 *
23166 *       var firstRepeat =
23167 *           container.element(by.repeater('contact in settings.contacts').row(0));
23168 *       var secondRepeat =
23169 *           container.element(by.repeater('contact in settings.contacts').row(1));
23170 *
23171 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23172 *           .toBe('408 555 1212');
23173 *
23174 *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23175 *           .toBe('[email protected]');
23176 *
23177 *       firstRepeat.element(by.buttonText('clear')).click();
23178 *
23179 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23180 *           .toBe('');
23181 *
23182 *       container.element(by.buttonText('add')).click();
23183 *
23184 *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
23185 *           .element(by.model('contact.value'))
23186 *           .getAttribute('value'))
23187 *           .toBe('[email protected]');
23188 *     });
23189 *   </file>
23190 * </example>
23191 *
23192 * This example demonstrates the "attach to `$scope`" style of controller.
23193 *
23194 * <example name="ngController" module="controllerExample">
23195 *  <file name="index.html">
23196 *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
23197 *     <label>Name: <input type="text" ng-model="name"/></label>
23198 *     <button ng-click="greet()">greet</button><br/>
23199 *     Contact:
23200 *     <ul>
23201 *       <li ng-repeat="contact in contacts">
23202 *         <select ng-model="contact.type" id="select_{{$index}}">
23203 *            <option>phone</option>
23204 *            <option>email</option>
23205 *         </select>
23206 *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23207 *         <button ng-click="clearContact(contact)">clear</button>
23208 *         <button ng-click="removeContact(contact)">X</button>
23209 *       </li>
23210 *       <li>[ <button ng-click="addContact()">add</button> ]</li>
23211 *    </ul>
23212 *   </div>
23213 *  </file>
23214 *  <file name="app.js">
23215 *   angular.module('controllerExample', [])
23216 *     .controller('SettingsController2', ['$scope', SettingsController2]);
23217 *
23218 *   function SettingsController2($scope) {
23219 *     $scope.name = "John Smith";
23220 *     $scope.contacts = [
23221 *       {type:'phone', value:'408 555 1212'},
23222 *       {type:'email', value:'[email protected]'} ];
23223 *
23224 *     $scope.greet = function() {
23225 *       alert($scope.name);
23226 *     };
23227 *
23228 *     $scope.addContact = function() {
23229 *       $scope.contacts.push({type:'email', value:'[email protected]'});
23230 *     };
23231 *
23232 *     $scope.removeContact = function(contactToRemove) {
23233 *       var index = $scope.contacts.indexOf(contactToRemove);
23234 *       $scope.contacts.splice(index, 1);
23235 *     };
23236 *
23237 *     $scope.clearContact = function(contact) {
23238 *       contact.type = 'phone';
23239 *       contact.value = '';
23240 *     };
23241 *   }
23242 *  </file>
23243 *  <file name="protractor.js" type="protractor">
23244 *    it('should check controller', function() {
23245 *      var container = element(by.id('ctrl-exmpl'));
23246 *
23247 *      expect(container.element(by.model('name'))
23248 *          .getAttribute('value')).toBe('John Smith');
23249 *
23250 *      var firstRepeat =
23251 *          container.element(by.repeater('contact in contacts').row(0));
23252 *      var secondRepeat =
23253 *          container.element(by.repeater('contact in contacts').row(1));
23254 *
23255 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23256 *          .toBe('408 555 1212');
23257 *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23258 *          .toBe('[email protected]');
23259 *
23260 *      firstRepeat.element(by.buttonText('clear')).click();
23261 *
23262 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23263 *          .toBe('');
23264 *
23265 *      container.element(by.buttonText('add')).click();
23266 *
23267 *      expect(container.element(by.repeater('contact in contacts').row(2))
23268 *          .element(by.model('contact.value'))
23269 *          .getAttribute('value'))
23270 *          .toBe('[email protected]');
23271 *    });
23272 *  </file>
23273 *</example>
23274
23275 */
23276var ngControllerDirective = [function() {
23277  return {
23278    restrict: 'A',
23279    scope: true,
23280    controller: '@',
23281    priority: 500
23282  };
23283}];
23284
23285/**
23286 * @ngdoc directive
23287 * @name ngCsp
23288 *
23289 * @element html
23290 * @description
23291 *
23292 * Angular has some features that can break certain
23293 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
23294 *
23295 * If you intend to implement these rules then you must tell Angular not to use these features.
23296 *
23297 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23298 *
23299 *
23300 * The following rules affect Angular:
23301 *
23302 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
23303 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
23304 * increase in the speed of evaluating Angular expressions.
23305 *
23306 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
23307 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
23308 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
23309 * `angular-csp.css` in your HTML manually.
23310 *
23311 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
23312 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
23313 * however, triggers a CSP error to be logged in the console:
23314 *
23315 * ```
23316 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23317 * script in the following Content Security Policy directive: "default-src 'self'". Note that
23318 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23319 * ```
23320 *
23321 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23322 * directive on an element of the HTML document that appears before the `<script>` tag that loads
23323 * the `angular.js` file.
23324 *
23325 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23326 *
23327 * You can specify which of the CSP related Angular features should be deactivated by providing
23328 * a value for the `ng-csp` attribute. The options are as follows:
23329 *
23330 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
23331 *
23332 * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
23333 *
23334 * You can use these values in the following combinations:
23335 *
23336 *
23337 * * No declaration means that Angular will assume that you can do inline styles, but it will do
23338 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
23339 * of Angular.
23340 *
23341 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
23342 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
23343 * of Angular.
23344 *
23345 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
23346 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
23347 *
23348 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
23349 * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
23350 *
23351 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
23352 * styles nor use eval, which is the same as an empty: ng-csp.
23353 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
23354 *
23355 * @example
23356 * This example shows how to apply the `ngCsp` directive to the `html` tag.
23357   ```html
23358     <!doctype html>
23359     <html ng-app ng-csp>
23360     ...
23361     ...
23362     </html>
23363   ```
23364  * @example
23365      // Note: the suffix `.csp` in the example name triggers
23366      // csp mode in our http server!
23367      <example name="example.csp" module="cspExample" ng-csp="true">
23368        <file name="index.html">
23369          <div ng-controller="MainController as ctrl">
23370            <div>
23371              <button ng-click="ctrl.inc()" id="inc">Increment</button>
23372              <span id="counter">
23373                {{ctrl.counter}}
23374              </span>
23375            </div>
23376
23377            <div>
23378              <button ng-click="ctrl.evil()" id="evil">Evil</button>
23379              <span id="evilError">
23380                {{ctrl.evilError}}
23381              </span>
23382            </div>
23383          </div>
23384        </file>
23385        <file name="script.js">
23386           angular.module('cspExample', [])
23387             .controller('MainController', function() {
23388                this.counter = 0;
23389                this.inc = function() {
23390                  this.counter++;
23391                };
23392                this.evil = function() {
23393                  // jshint evil:true
23394                  try {
23395                    eval('1+2');
23396                  } catch (e) {
23397                    this.evilError = e.message;
23398                  }
23399                };
23400              });
23401        </file>
23402        <file name="protractor.js" type="protractor">
23403          var util, webdriver;
23404
23405          var incBtn = element(by.id('inc'));
23406          var counter = element(by.id('counter'));
23407          var evilBtn = element(by.id('evil'));
23408          var evilError = element(by.id('evilError'));
23409
23410          function getAndClearSevereErrors() {
23411            return browser.manage().logs().get('browser').then(function(browserLog) {
23412              return browserLog.filter(function(logEntry) {
23413                return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23414              });
23415            });
23416          }
23417
23418          function clearErrors() {
23419            getAndClearSevereErrors();
23420          }
23421
23422          function expectNoErrors() {
23423            getAndClearSevereErrors().then(function(filteredLog) {
23424              expect(filteredLog.length).toEqual(0);
23425              if (filteredLog.length) {
23426                console.log('browser console errors: ' + util.inspect(filteredLog));
23427              }
23428            });
23429          }
23430
23431          function expectError(regex) {
23432            getAndClearSevereErrors().then(function(filteredLog) {
23433              var found = false;
23434              filteredLog.forEach(function(log) {
23435                if (log.message.match(regex)) {
23436                  found = true;
23437                }
23438              });
23439              if (!found) {
23440                throw new Error('expected an error that matches ' + regex);
23441              }
23442            });
23443          }
23444
23445          beforeEach(function() {
23446            util = require('util');
23447            webdriver = require('protractor/node_modules/selenium-webdriver');
23448          });
23449
23450          // For now, we only test on Chrome,
23451          // as Safari does not load the page with Protractor's injected scripts,
23452          // and Firefox webdriver always disables content security policy (#6358)
23453          if (browser.params.browser !== 'chrome') {
23454            return;
23455          }
23456
23457          it('should not report errors when the page is loaded', function() {
23458            // clear errors so we are not dependent on previous tests
23459            clearErrors();
23460            // Need to reload the page as the page is already loaded when
23461            // we come here
23462            browser.driver.getCurrentUrl().then(function(url) {
23463              browser.get(url);
23464            });
23465            expectNoErrors();
23466          });
23467
23468          it('should evaluate expressions', function() {
23469            expect(counter.getText()).toEqual('0');
23470            incBtn.click();
23471            expect(counter.getText()).toEqual('1');
23472            expectNoErrors();
23473          });
23474
23475          it('should throw and report an error when using "eval"', function() {
23476            evilBtn.click();
23477            expect(evilError.getText()).toMatch(/Content Security Policy/);
23478            expectError(/Content Security Policy/);
23479          });
23480        </file>
23481      </example>
23482  */
23483
23484// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
23485// bootstrap the system (before $parse is instantiated), for this reason we just have
23486// the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
23487
23488/**
23489 * @ngdoc directive
23490 * @name ngClick
23491 *
23492 * @description
23493 * The ngClick directive allows you to specify custom behavior when
23494 * an element is clicked.
23495 *
23496 * @element ANY
23497 * @priority 0
23498 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
23499 * click. ({@link guide/expression#-event- Event object is available as `$event`})
23500 *
23501 * @example
23502   <example>
23503     <file name="index.html">
23504      <button ng-click="count = count + 1" ng-init="count=0">
23505        Increment
23506      </button>
23507      <span>
23508        count: {{count}}
23509      </span>
23510     </file>
23511     <file name="protractor.js" type="protractor">
23512       it('should check ng-click', function() {
23513         expect(element(by.binding('count')).getText()).toMatch('0');
23514         element(by.css('button')).click();
23515         expect(element(by.binding('count')).getText()).toMatch('1');
23516       });
23517     </file>
23518   </example>
23519 */
23520/*
23521 * A collection of directives that allows creation of custom event handlers that are defined as
23522 * angular expressions and are compiled and executed within the current scope.
23523 */
23524var ngEventDirectives = {};
23525
23526// For events that might fire synchronously during DOM manipulation
23527// we need to execute their event handlers asynchronously using $evalAsync,
23528// so that they are not executed in an inconsistent state.
23529var forceAsyncEvents = {
23530  'blur': true,
23531  'focus': true
23532};
23533forEach(
23534  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
23535  function(eventName) {
23536    var directiveName = directiveNormalize('ng-' + eventName);
23537    ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
23538      return {
23539        restrict: 'A',
23540        compile: function($element, attr) {
23541          // We expose the powerful $event object on the scope that provides access to the Window,
23542          // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
23543          // checks at the cost of speed since event handler expressions are not executed as
23544          // frequently as regular change detection.
23545          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
23546          return function ngEventHandler(scope, element) {
23547            element.on(eventName, function(event) {
23548              var callback = function() {
23549                fn(scope, {$event:event});
23550              };
23551              if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
23552                scope.$evalAsync(callback);
23553              } else {
23554                scope.$apply(callback);
23555              }
23556            });
23557          };
23558        }
23559      };
23560    }];
23561  }
23562);
23563
23564/**
23565 * @ngdoc directive
23566 * @name ngDblclick
23567 *
23568 * @description
23569 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
23570 *
23571 * @element ANY
23572 * @priority 0
23573 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
23574 * a dblclick. (The Event object is available as `$event`)
23575 *
23576 * @example
23577   <example>
23578     <file name="index.html">
23579      <button ng-dblclick="count = count + 1" ng-init="count=0">
23580        Increment (on double click)
23581      </button>
23582      count: {{count}}
23583     </file>
23584   </example>
23585 */
23586
23587
23588/**
23589 * @ngdoc directive
23590 * @name ngMousedown
23591 *
23592 * @description
23593 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
23594 *
23595 * @element ANY
23596 * @priority 0
23597 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
23598 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
23599 *
23600 * @example
23601   <example>
23602     <file name="index.html">
23603      <button ng-mousedown="count = count + 1" ng-init="count=0">
23604        Increment (on mouse down)
23605      </button>
23606      count: {{count}}
23607     </file>
23608   </example>
23609 */
23610
23611
23612/**
23613 * @ngdoc directive
23614 * @name ngMouseup
23615 *
23616 * @description
23617 * Specify custom behavior on mouseup event.
23618 *
23619 * @element ANY
23620 * @priority 0
23621 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
23622 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
23623 *
23624 * @example
23625   <example>
23626     <file name="index.html">
23627      <button ng-mouseup="count = count + 1" ng-init="count=0">
23628        Increment (on mouse up)
23629      </button>
23630      count: {{count}}
23631     </file>
23632   </example>
23633 */
23634
23635/**
23636 * @ngdoc directive
23637 * @name ngMouseover
23638 *
23639 * @description
23640 * Specify custom behavior on mouseover event.
23641 *
23642 * @element ANY
23643 * @priority 0
23644 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
23645 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
23646 *
23647 * @example
23648   <example>
23649     <file name="index.html">
23650      <button ng-mouseover="count = count + 1" ng-init="count=0">
23651        Increment (when mouse is over)
23652      </button>
23653      count: {{count}}
23654     </file>
23655   </example>
23656 */
23657
23658
23659/**
23660 * @ngdoc directive
23661 * @name ngMouseenter
23662 *
23663 * @description
23664 * Specify custom behavior on mouseenter event.
23665 *
23666 * @element ANY
23667 * @priority 0
23668 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
23669 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
23670 *
23671 * @example
23672   <example>
23673     <file name="index.html">
23674      <button ng-mouseenter="count = count + 1" ng-init="count=0">
23675        Increment (when mouse enters)
23676      </button>
23677      count: {{count}}
23678     </file>
23679   </example>
23680 */
23681
23682
23683/**
23684 * @ngdoc directive
23685 * @name ngMouseleave
23686 *
23687 * @description
23688 * Specify custom behavior on mouseleave event.
23689 *
23690 * @element ANY
23691 * @priority 0
23692 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
23693 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
23694 *
23695 * @example
23696   <example>
23697     <file name="index.html">
23698      <button ng-mouseleave="count = count + 1" ng-init="count=0">
23699        Increment (when mouse leaves)
23700      </button>
23701      count: {{count}}
23702     </file>
23703   </example>
23704 */
23705
23706
23707/**
23708 * @ngdoc directive
23709 * @name ngMousemove
23710 *
23711 * @description
23712 * Specify custom behavior on mousemove event.
23713 *
23714 * @element ANY
23715 * @priority 0
23716 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
23717 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
23718 *
23719 * @example
23720   <example>
23721     <file name="index.html">
23722      <button ng-mousemove="count = count + 1" ng-init="count=0">
23723        Increment (when mouse moves)
23724      </button>
23725      count: {{count}}
23726     </file>
23727   </example>
23728 */
23729
23730
23731/**
23732 * @ngdoc directive
23733 * @name ngKeydown
23734 *
23735 * @description
23736 * Specify custom behavior on keydown event.
23737 *
23738 * @element ANY
23739 * @priority 0
23740 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
23741 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23742 *
23743 * @example
23744   <example>
23745     <file name="index.html">
23746      <input ng-keydown="count = count + 1" ng-init="count=0">
23747      key down count: {{count}}
23748     </file>
23749   </example>
23750 */
23751
23752
23753/**
23754 * @ngdoc directive
23755 * @name ngKeyup
23756 *
23757 * @description
23758 * Specify custom behavior on keyup event.
23759 *
23760 * @element ANY
23761 * @priority 0
23762 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
23763 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23764 *
23765 * @example
23766   <example>
23767     <file name="index.html">
23768       <p>Typing in the input box below updates the key count</p>
23769       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
23770
23771       <p>Typing in the input box below updates the keycode</p>
23772       <input ng-keyup="event=$event">
23773       <p>event keyCode: {{ event.keyCode }}</p>
23774       <p>event altKey: {{ event.altKey }}</p>
23775     </file>
23776   </example>
23777 */
23778
23779
23780/**
23781 * @ngdoc directive
23782 * @name ngKeypress
23783 *
23784 * @description
23785 * Specify custom behavior on keypress event.
23786 *
23787 * @element ANY
23788 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
23789 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
23790 * and can be interrogated for keyCode, altKey, etc.)
23791 *
23792 * @example
23793   <example>
23794     <file name="index.html">
23795      <input ng-keypress="count = count + 1" ng-init="count=0">
23796      key press count: {{count}}
23797     </file>
23798   </example>
23799 */
23800
23801
23802/**
23803 * @ngdoc directive
23804 * @name ngSubmit
23805 *
23806 * @description
23807 * Enables binding angular expressions to onsubmit events.
23808 *
23809 * Additionally it prevents the default action (which for form means 
23809sending the request to the
23810 * server and reloading the current page), but only if the form does not contain `action`,
23811 * `data-action`, or `x-action` attributes.
23812 *
23813 * <div class="alert alert-warning">
23814 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
23815 * `ngSubmit` handlers together. See the
23816 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
23817 * for a detailed discussion of when `ngSubmit` may be triggered.
23818 * </div>
23819 *
23820 * @element form
23821 * @priority 0
23822 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
23823 * ({@link guide/expression#-event- Event object is available as `$event`})
23824 *
23825 * @example
23826   <example module="submitExample">
23827     <file name="index.html">
23828      <script>
23829        angular.module('submitExample', [])
23830          .controller('ExampleController', ['$scope', function($scope) {
23831            $scope.list = [];
23832            $scope.text = 'hello';
23833            $scope.submit = function() {
23834              if ($scope.text) {
23835                $scope.list.push(this.text);
23836                $scope.text = '';
23837              }
23838            };
23839          }]);
23840      </script>
23841      <form ng-submit="submit()" ng-controller="ExampleController">
23842        Enter text and hit enter:
23843        <input type="text" ng-model="text" name="text" />
23844        <input type="submit" id="submit" value="Submit" />
23845        <pre>list={{list}}</pre>
23846      </form>
23847     </file>
23848     <file name="protractor.js" type="protractor">
23849       it('should check ng-submit', function() {
23850         expect(element(by.binding('list')).getText()).toBe('list=[]');
23851         element(by.css('#submit')).click();
23852         expect(element(by.binding('list')).getText()).toContain('hello');
23853         expect(element(by.model('text')).getAttribute('value')).toBe('');
23854       });
23855       it('should ignore empty strings', function() {
23856         expect(element(by.binding('list')).getText()).toBe('list=[]');
23857         element(by.css('#submit')).click();
23858         element(by.css('#submit')).click();
23859         expect(element(by.binding('list')).getText()).toContain('hello');
23860        });
23861     </file>
23862   </example>
23863 */
23864
23865/**
23866 * @ngdoc directive
23867 * @name ngFocus
23868 *
23869 * @description
23870 * Specify custom behavior on focus event.
23871 *
23872 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
23873 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
23874 * during an `$apply` to ensure a consistent state.
23875 *
23876 * @element window, input, select, textarea, a
23877 * @priority 0
23878 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
23879 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
23880 *
23881 * @example
23882 * See {@link ng.directive:ngClick ngClick}
23883 */
23884
23885/**
23886 * @ngdoc directive
23887 * @name ngBlur
23888 *
23889 * @description
23890 * Specify custom behavior on blur event.
23891 *
23892 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
23893 * an element has lost focus.
23894 *
23895 * Note: As the `blur` event is executed synchronously also during DOM manipulations
23896 * (e.g. removing a focussed input),
23897 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
23898 * during an `$apply` to ensure a consistent state.
23899 *
23900 * @element window, input, select, textarea, a
23901 * @priority 0
23902 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
23903 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
23904 *
23905 * @example
23906 * See {@link ng.directive:ngClick ngClick}
23907 */
23908
23909/**
23910 * @ngdoc directive
23911 * @name ngCopy
23912 *
23913 * @description
23914 * Specify custom behavior on copy event.
23915 *
23916 * @element window, input, select, textarea, a
23917 * @priority 0
23918 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
23919 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
23920 *
23921 * @example
23922   <example>
23923     <file name="index.html">
23924      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
23925      copied: {{copied}}
23926     </file>
23927   </example>
23928 */
23929
23930/**
23931 * @ngdoc directive
23932 * @name ngCut
23933 *
23934 * @description
23935 * Specify custom behavior on cut event.
23936 *
23937 * @element window, input, select, textarea, a
23938 * @priority 0
23939 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
23940 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
23941 *
23942 * @example
23943   <example>
23944     <file name="index.html">
23945      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
23946      cut: {{cut}}
23947     </file>
23948   </example>
23949 */
23950
23951/**
23952 * @ngdoc directive
23953 * @name ngPaste
23954 *
23955 * @description
23956 * Specify custom behavior on paste event.
23957 *
23958 * @element window, input, select, textarea, a
23959 * @priority 0
23960 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
23961 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
23962 *
23963 * @example
23964   <example>
23965     <file name="index.html">
23966      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
23967      pasted: {{paste}}
23968     </file>
23969   </example>
23970 */
23971
23972/**
23973 * @ngdoc directive
23974 * @name ngIf
23975 * @restrict A
23976 * @multiElement
23977 *
23978 * @description
23979 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
23980 * {expression}. If the expression assigned to `ngIf` evaluates to a false
23981 * value then the element is removed from the DOM, otherwise a clone of the
23982 * element is reinserted into the DOM.
23983 *
23984 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
23985 * element in the DOM rather than changing its visibility via the `display` css property.  A common
23986 * case when this difference is significant is when using css selectors that rely on an element's
23987 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classe
23987s.
23988 *
23989 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
23990 * is created when the element is restored.  The scope created within `ngIf` inherits from
23991 * its parent scope using
23992 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
23993 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
23994 * a javascript primitive defined in the parent scope. In this case any modifications made to the
23995 * variable within the child scope will override (hide) the value in the parent scope.
23996 *
23997 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
23998 * is if an element's class attribute is directly modified after it's compiled, using something like
23999 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24000 * the added class will be lost because the original compiled state is used to regenerate the element.
24001 *
24002 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24003 * and `leave` effects.
24004 *
24005 * @animations
24006 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24007 * leave - happens just before the `ngIf` contents are removed from the DOM
24008 *
24009 * @element ANY
24010 * @scope
24011 * @priority 600
24012 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24013 *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
24014 *     element is added to the DOM tree.
24015 *
24016 * @example
24017  <example module="ngAnimate" deps="angular-animate.js" animations="true">
24018    <file name="index.html">
24019      <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24020      Show when checked:
24021      <span ng-if="checked" class="animate-if">
24022        This is removed when the checkbox is unchecked.
24023      </span>
24024    </file>
24025    <file name="animations.css">
24026      .animate-if {
24027        background:white;
24028        border:1px solid black;
24029        padding:10px;
24030      }
24031
24032      .animate-if.ng-enter, .animate-if.ng-leave {
24033        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24034      }
24035
24036      .animate-if.ng-enter,
24037      .animate-if.ng-leave.ng-leave-active {
24038        opacity:0;
24039      }
24040
24041      .animate-if.ng-leave,
24042      .animate-if.ng-enter.ng-enter-active {
24043        opacity:1;
24044      }
24045    </file>
24046  </example>
24047 */
24048var ngIfDirective = ['$animate', function($animate) {
24049  return {
24050    multiElement: true,
24051    transclude: 'element',
24052    priority: 600,
24053    terminal: true,
24054    restrict: 'A',
24055    $$tlb: true,
24056    link: function($scope, $element, $attr, ctrl, $transclude) {
24057        var block, childScope, previousElements;
24058        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24059
24060          if (value) {
24061            if (!childScope) {
24062              $transclude(function(clone, newScope) {
24063                childScope = newScope;
24064                clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24065                // Note: We only need the first/last node of the cloned nodes.
24066                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24067                // by a directive with templateUrl when its template arrives.
24068                block = {
24069                  clone: clone
24070                };
24071                $animate.enter(clone, $element.parent(), $element);
24072              });
24073            }
24074          } else {
24075            if (previousElements) {
24076              previousElements.remove();
24077              previousElements = null;
24078            }
24079            if (childScope) {
24080              childScope.$destroy();
24081              childScope = null;
24082            }
24083            if (block) {
24084              previousElements = getBlockNodes(block.clone);
24085              $animate.leave(previousElements).then(function() {
24086                previousElements = null;
24087              });
24088              block = null;
24089            }
24090          }
24091        });
24092    }
24093  };
24094}];
24095
24096/**
24097 * @ngdoc directive
24098 * @name ngInclude
24099 * @restrict ECA
24100 *
24101 * @description
24102 * Fetches, compiles and includes an external HTML fragment.
24103 *
24104 * By default, the template URL is restricted to the same domain and protocol as the
24105 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24106 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24107 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24108 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24109 * ng.$sce Strict Contextual Escaping}.
24110 *
24111 * In addition, the browser's
24112 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24113 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24114 * policy may further restrict whether the template is successfully loaded.
24115 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24116 * access on some browsers.
24117 *
24118 * @animations
24119 * enter - animation is used to bring new content into the browser.
24120 * leave - animation is used to animate existing content away.
24121 *
24122 * The enter and leave animation occur concurrently.
24123 *
24124 * @scope
24125 * @priority 400
24126 *
24127 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24128 *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24129 * @param {string=} onload Expression to evaluate when a new partial is loaded.
24130 *
24131 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24132 *                  $anchorScroll} to scroll the viewport after the content is loaded.
24133 *
24134 *                  - If the attribute is not set, disable scrolling.
24135 *                  - If the attribute is set without value, enable scrolling.
24136 *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
24137 *
24138 * @example
24139  <example module="includeExample" deps="angular-animate.js" animations="true">
24140    <file name="index.html">
24141     <div ng-controller="ExampleController">
24142       <select ng-model="template" ng-options="t.name for t in templates">
24143        <option value="">(blank)</option>
24144       </select>
24145       url of the template: <code>{{template.url}}</code>
24146       <hr/>
24147       <div class="slide-animate-container">
24148         <div class="slide-animate" ng-include="template.url"></div>
24149       </div>
24150     </div>
24151    </file>
24152    <file name="script.js">
24153      angular.module('includeExample', ['ngAnimate'])
24154        .controller('ExampleController', ['$scope', function($scope) {
24155          $scope.templates =
24156            [ { name: 'template1.html', url: 'template1.html'},
24157              { name: 'template2.html', url: 'template2.html'} ];
24158          $scope.template = $scope.templates[0];
24159        }]);
24160     </file>
24161    <file name="template1.html">
24162      Content of template1.html
24163    </file>
24164    <file name="template2.html">
24165      Content of template2.html
24166    </file>
24167    <file name="animations.css">
24168      .slide-animate-container {
24169        position:relative;
24170        background:white;
24171        border:1px solid black;
24172        height:40px;
24173        overflow:hidden;
24174      }
24175
24176      .slide-animate {
24177        padding:10px;
24178      }
24179
24180      .slide-animate.ng-enter, .slide-animate.ng-leave {
24181        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24182
24183        position:absolute;
24184        top:0;
24185        left:0;
24186        right:0;
24187        bottom:0;
24188        display:block;
24189        padding:10px;
24190      }
24191
24192      .slide-animate.ng-enter {
24193        top:-50px;
24194      }
24195      .slide-animate.ng-enter.ng-enter-active {
24196        top:0;
24197      }
24198
24199      .slide-animate.ng-leave {
24200        top:0;
24201      }
24202      .slide-animate.ng-leave.ng-leave-active {
24203        top:50px;
24204      }
24205    </file>
24206    <file name="protractor.js" type="protractor">
24207      var templateSelect = element(by.model('template'));
24208      var includeElem = element(by.css('[ng-include]'));
24209
24210      it('should load template1.html', function() {
24211        expect(includeElem.getText()).toMatch(/Content of template1.html/);
24212      });
24213
24214      it('should load template2.html', function() {
24215        if (browser.params.browser == 'firefox') {
24216          // Firefox can't handle using selects
24217          // See https://github.com/angular/protractor/issues/480
24218          return;
24219        }
24220        templateSelect.click();
24221        templateSelect.all(by.css('option')).get(2).click();
24222        expect(includeElem.getText()).toMatch(/Content of template2.html/);
24223      });
24224
24225      it('should change to blank', function() {
24226        if (browser.params.browser == 'firefox') {
24227          // Firefox can't handle using selects
24228          return;
24229        }
24230        templateSelect.click();
24231        templateSelect.all(by.css('option')).get(0).click();
24232        expect(includeElem.isPresent()).toBe(false);
24233      });
24234    </file>
24235  </example>
24236 */
24237
24238
24239/**
24240 * @ngdoc event
24241 * @name ngInclude#$includeContentRequested
24242 * @eventType emit on the scope ngInclude was declared in
24243 * @description
24244 * Emitted every time the ngInclude content is requested.
24245 *
24246 * @param {Object} angularEvent Synthetic event object.
24247 * @param {String} src URL of content to load.
24248 */
24249
24250
24251/**
24252 * @ngdoc event
24253 * @name ngInclude#$includeContentLoaded
24254 * @eventType emit on the current ngInclude scope
24255 * @description
24256 * Emitted every time the ngInclude content is reloaded.
24257 *
24258 * @param {Object} angularEvent Synthetic event object.
24259 * @param {String} src URL of content to load.
24260 */
24261
24262
24263/**
24264 * @ngdoc event
24265 * @name ngInclude#$includeContentError
24266 * @eventType emit on the scope ngInclude was declared in
24267 * @description
24268 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
24269 *
24270 * @param {Object} angularEvent Synthetic event object.
24271 * @param {String} src URL of content to load.
24272 */
24273var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
24274                  function($templateRequest,   $anchorScroll,   $animate) {
24275  return {
24276    restrict: 'ECA',
24277    priority: 400,
24278    terminal: true,
24279    transclude: 'element',
24280    controller: angular.noop,
24281    compile: function(element, attr) {
24282      var srcExp = attr.ngInclude || attr.src,
24283          onloadExp = attr.onload || '',
24284          autoScrollExp = attr.autoscroll;
24285
24286      return function(scope, $element, $attr, ctrl, $transclude) {
24287        var changeCounter = 0,
24288            currentScope,
24289            previousElement,
24290            currentElement;
24291
24292        var cleanupLastIncludeContent = function() {
24293          if (previousElement) {
24294            previousElement.remove();
24295            previousElement = null;
24296          }
24297          if (currentScope) {
24298            currentScope.$destroy();
24299            currentScope = null;
24300          }
24301          if (currentElement) {
24302            $animate.leave(currentElement).then(function() {
24303              previousElement = null;
24304            });
24305            previousElement = currentElement;
24306            currentElement = null;
24307          }
24308        };
24309
24310        scope.$watch(srcExp, function ngIncludeWatchAction(src) {
24311          var afterAnimation = function() {
24312            if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24313              $anchorScroll();
24314            }
24315          };
24316          var thisChangeId = ++changeCounter;
24317
24318          if (src) {
24319            //set the 2nd param to true to ignore the template request error so that the inner
24320            //contents and scope can be cleaned up.
24321            $templateRequest(src, true).then(function(response) {
24322              if (thisChangeId !== changeCounter) return;
24323              var newScope = scope.$new();
24324              ctrl.template = response;
24325
24326              // Note: This will also link all children of ng-include that were contained in the original
24327              // html. If that content contains controllers, ... they could pollute/change the scope.
24328              // However, using ng-include on an element with additional content does not make sense...
24329              // Note: We can't remove them in the cloneAttchFn of $transclude as that
24330              // function is called before linking the content, which would apply child
24331              // directives to non existing elements.
24332              var clone = $transclude(newScope, function(clone) {
24333                cleanupLastIncludeContent();
24334                $animate.enter(clone, null, $element).then(afterAnimation);
24335              });
24336
24337              currentScope = newScope;
24338              currentElement = clone;
24339
24340              currentScope.$emit('$includeContentLoaded', src);
24341              scope.$eval(onloadExp);
24342            }, function() {
24343              if (thisChangeId === changeCounter) {
24344                cleanupLastIncludeContent();
vendor: 2,913 bytes, lines 24345-24427
24345                scope.$emit('$includeContentError', src);
24346              }
24347            });
24348            scope.$emit('$includeContentRequested', src);
24349          } else {
24350            cleanupLastIncludeContent();
24351            ctrl.template = null;
24352          }
24353        });
24354      };
24355    }
24356  };
24357}];
24358
24359// This directive is called during the $transclude call of the first `ngInclude` directive.
24360// It will replace and compile the content of the element with the loaded template.
24361// We need this directive so that the element content is already filled when
24362// the link function of another directive on the same element as ngInclude
24363// is called.
24364var ngIncludeFillContentDirective = ['$compile',
24365  function($compile) {
24366    return {
24367      restrict: 'ECA',
24368      priority: -400,
24369      require: 'ngInclude',
24370      link: function(scope, $element, $attr, ctrl) {
24371        if (/SVG/.test($element[0].toString())) {
24372          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24373          // support innerHTML, so detect this here and try to generate the contents
24374          // specially.
24375          $element.empty();
24376          $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24377              function namespaceAdaptedClone(clone) {
24378            $element.append(clone);
24379          }, {futureParentElement: $element});
24380          return;
24381        }
24382
24383        $element.html(ctrl.template);
24384        $compile($element.contents())(scope);
24385      }
24386    };
24387  }];
24388
24389/**
24390 * @ngdoc directive
24391 * @name ngInit
24392 * @restrict AC
24393 *
24394 * @description
24395 * The `ngInit` directive allows you to evaluate an expression in the
24396 * current scope.
24397 *
24398 * <div class="alert alert-danger">
24399 * This directive can be abused to add unnecessary amounts of logic into your templates.
24400 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24401 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24402 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24403 * rather than `ngInit` to initialize values on a scope.
24404 * </div>
24405 *
24406 * <div class="alert alert-warning">
24407 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24408 * sure you have parentheses to ensure correct operator precedence:
24409 * <pre class="prettyprint">
24410 * `<div ng-init="test1 = ($index | toString)"></div>`
24411 * </pre>
24412 * </div>
24413 *
24414 * @priority 450
24415 *
24416 * @element ANY
24417 * @param {expression} ngInit {@link guide/expression Expression} to eval.
24418 *
24419 * @example
24420   <example module="initExample">
24421     <file name="index.html">
24422   <script>
24423     angular.module('initExample', [])
24424       .controller('ExampleController', ['$scope', function($scope) {
24425         $scope.list = [['a', 'b'], ['c', 'd']];
24426       }]);
24427   </script>
24428   <div ng-controller="ExampleController">
24429     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24430       <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24431          <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24432       </div>
24433     </div>
24434   </div>
24435     </file>
24436     <file name="protractor.js" type="protractor">
24437       it('should alias index positions', function() {
24438         var elements = element.all(by.css('.example-init'));
24439         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24440         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24441         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24442         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24443       });
24444     </file>
24445   </example>
24446 */
24447var ngInitDirective = ngDirective({
24448  priority: 450,
24449  compile: function() {
24450    return {
24451      pre: function(scope, element, attrs) {
24452        scope.$eval(attrs.ngInit);
24453      }
24454    };
24455  }
24456});
24457
24458/**
24459 * @ngdoc directive
24460 * @name ngList
24461 *
24462 * @description
24463 * Text input that converts between a delimited string and an array of strings. The default
24464 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24465 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24466 *
24467 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
24468 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
24469 *   list item is respected. This implies that the user of the directive is responsible for
24470 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
24471 *   tab or newline character.
24472 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
24473 *   when joining the list items back together) and whitespace around each list item is stripped
24474 *   before it is added to the model.
24475 *
24476 * ### Example with Validation
24477 *
24478 * <example name="ngList-directive" module="listExample">
24479 *   <file name="app.js">
24480 *      angular.module('listExample', [])
24481 *        .controller('ExampleController', ['$scope', function($scope) {
24482 *          $scope.names = ['morpheus', 'neo', 'trinity'];
24483 *        }]);
24484 *   </file>
24485 *   <file name="index.html">
24486 *    <form name="myForm" ng-controller="ExampleController">
24487 *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
24488 *      <span role="alert">
24489 *        <span class="error" ng-show="myForm.namesInput.$error.required">
24490 *        Required!</span>
24491 *      </span>
24492 *      <br>
24493 *      <tt>names = {{names}}</tt><br/>
24494 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
24495 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
24496 *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24497 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24498 *     </form>
24499 *   </file>
24500 *   <file name="protractor.js" type="protractor">
24501 *     var listInput = element(by.model('names'));
24502 *     var names = element(by.exactBinding('names'));
24503 *     var valid = element(by.binding('myForm.namesInput.$valid'));
24504 *     var error = element(by.css('span.error'));
24505 *
24506 *     it('should initialize to model', function() {
24507 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
24508 *       expect(valid.getText()).toContain('true');
24509 *       expect(error.getCssValue('display')).toBe('none');
24510 *     });
24511 *
24512 *     it('should be invalid if empty', function() {
24513 *       listInput.clear();
24514 *       listInput.sendKeys('');
24515 *
24516 *       expect(names.getText()).toContain('');
24517 *       expect(valid.getText()).toContain('false');
24518 *       expect(error.getCssValue('display')).not.toBe('none');
24519 *     });
24520 *   </file>
24521 * </example>
24522 *
24523 * ### Example - splitting on newline
24524 * <example name="ngList-directive-newlines">
24525 *   <file name="index.html">
24526 *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
24527 *    <pre>{{ list | json }}</pre>
24528 *   </file>
24529 *   <file name="protractor.js" type="protractor">
24530 *     it("should split the text by newlines", function() {
24531 *       var listInput = element(by.model('list'));
24532 *       var output = element(by.binding('list | json'));
24533 *       listInput.sendKeys('abc\ndef\nghi');
24534 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
24535 *     });
24536 *   </file>
24537 * </example>
24538 *
24539 * @element input
24540 * @param {string=} ngList optional delimiter that should be used to split the value.
24541 */
24542var ngListDirective = function() {
24543  return {
24544    restrict: 'A',
24545    priority: 100,
24546    require: 'ngModel',
24547    link: function(scope, element, attr, ctrl) {
24548      // We want to control whitespace trimming so we use this convoluted approach
24549      // to access the ngList attribute, which doesn't pre-trim the attribute
24550      var ngList = element.attr(attr.$attr.ngList) || ', ';
24551      var trimValues = attr.ngTrim !== 'false';
24552      var separator = trimValues ? trim(ngList) : ngList;
24553
24554      var parse = function(viewValue) {
24555        // If the viewValue is invalid (say required but empty) it will be `undefined`
24556        if (isUndefined(viewValue)) return;
24557
24558        var list = [];
24559
24560        if (viewValue) {
24561          forEach(viewValue.split(separator), function(value) {
24562            if (value) list.push(trimValues ? trim(value) : value);
24563          });
24564        }
24565
24566        return list;
24567      };
24568
24569      ctrl.$parsers.push(parse);
24570      ctrl.$formatters.push(function(value) {
24571        if (isArray(value)) {
24572          return value.join(ngList);
24573        }
24574
24575        return undefined;
24576      });
24577
24578      // Override the standard $isEmpty because an empty array means the input is empty.
24579      ctrl.$isEmpty = function(value) {
24580        return !value || !value.length;
24581      };
24582    }
24583  };
24584};
24585
24586/* global VALID_CLASS: true,
24587  INVALID_CLASS: true,
24588  PRISTINE_CLASS: true,
24589  DIRTY_CLASS: true,
24590  UNTOUCHED_CLASS: true,
24591  TOUCHED_CLASS: true,
24592*/
24593
24594var VALID_CLASS = 'ng-valid',
24595    INVALID_CLASS = 'ng-invalid',
24596    PRISTINE_CLASS = 'ng-pristine',
24597    DIRTY_CLASS = 'ng-dirty',
24598    UNTOUCHED_CLASS = 'ng-untouched',
24599    TOUCHED_CLASS = 'ng-touched',
24600    PENDING_CLASS = 'ng-pending';
24601
24602var ngModelMinErr = minErr('ngModel');
24603
24604/**
24605 * @ngdoc type
24606 * @name ngModel.NgModelController
24607 *
24608 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
24609 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
24610 * is set.
24611 * @property {*} $modelValue The value in the model that the control is bound to.
24612 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
24613       the control reads value from the DOM. The functions are called in array order, each passing
24614       its return value through to the next. The last return value is forwarded to the
24615       {@link ngModel.NgModelController#$validators `$validators`} collection.
24616
24617Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
24618`$viewValue`}.
24619
24620Returning `undefined` from a parser means a parse error occurred. In that case,
24621no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
24622will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
24623is set to `true`. The parse error is stored in `ngModel.$error.parse`.
24624
24625 *
24626 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
24627       the model value changes. The functions are called in reverse array order, each passing the value through to the
24628       next. The last return value is used as the actual DOM value.
24629       Used to format / convert values for display in the control.
24630 * ```js
24631 * function formatter(value) {
24632 *   if (value) {
24633 *     return value.toUpperCase();
24634 *   }
24635 * }
24636 * ngModel.$formatters.push(formatter);
24637 * ```
24638 *
24639 * @property {Object.<string, function>} $validators A collection of validators that are applied
24640 *      whenever the model value changes. The key value within the object refers to the name of the
24641 *      validator while the function refers to the validation operation. The validation operation is
24642 *      provided with the model value as an argument and must return a true or false value depending
24643 *      on the response of that validation.
24644 *
24645 * ```js
24646 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
24647 *   var value = modelValue || viewValue;
24648 *   return /[0-9]+/.test(value) &&
24649 *          /[a-z]+/.test(value) &&
24650 *          /[A-Z]+/.test(value) &&
24651 *          /\W+/.test(value);
24652 * };
24653 * ```
24654 *
24655 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
24656 *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
24657 *      is expected to return a promise when it is run during the model validation process. Once the promise
24658 *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
24659 *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
24660 *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
24661 *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
24662 *      will only run once all synchronous validators have passed.
24663 *
24664 * Please note that if $http is used then it is important that the server returns a success HTTP response code
24665 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
24666 *
24667 * ```js
24668 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
24669 *   var value = modelValue || viewValue;
24670 *
24671 *   // Lookup user by username
24672 *   return $http.get('/api/users/' + value).
24673 *      then(function resolved() {
24674 *        //username exists, this means validation fails
24675 *        return $q.reject('exists');
24676 *      }, function rejected() {
24677 *        //username does not exist, therefore this validation passes
24678 *        return true;
24679 *      });
24680 * };
24681 * ```
24682 *
24683 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
24684 *     view value has changed. It is called with no arguments, and its return value is ignored.
24685 *     This can be used in place of additional $watches against the model value.
24686 *
24687 * @property {Object} $error An object hash with all failing validator ids as keys.
24688 * @property {Object} $pending An object hash with all pending validator ids as keys.
24689 *
24690 * @property {boolean} $untouched True if control has not lost focus yet.
24691 * @property {boolean} $touched True if control has lost focus.
24692 * @property {boolean} $pristine True if user has not interacted with the control yet.
24693 * @property {boolean} $dirty True if user has already interacted with the control.
24694 * @property {boolean} $valid True if there is no error.
24695 * @property {boolean}
24695 $invalid True if at least one error on the control.
24696 * @property {string} $name The name attribute of the control.
24697 *
24698 * @description
24699 *
24700 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
24701 * The controller contains services for data-binding, validation, CSS updates, and value formatting
24702 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
24703 * listening to DOM events.
24704 * Such DOM related logic should be provided by other directives which make use of
24705 * `NgModelController` for data-binding to control elements.
24706 * Angular provides this DOM logic for most {@link input `input`} elements.
24707 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
24708 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
24709 *
24710 * @example
24711 * ### Custom Control Example
24712 * This example shows how to use `NgModelController` with a custom control to achieve
24713 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
24714 * collaborate together to achieve the desired result.
24715 *
24716 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
24717 * contents be edited in place by the user.
24718 *
24719 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
24720 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
24721 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
24722 * that content using the `$sce` service.
24723 *
24724 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
24725    <file name="style.css">
24726      [contenteditable] {
24727        border: 1px solid black;
24728        background-color: white;
24729        min-height: 20px;
24730      }
24731
24732      .ng-invalid {
24733        border: 1px solid red;
24734      }
24735
24736    </file>
24737    <file name="script.js">
24738      angular.module('customControl', ['ngSanitize']).
24739        directive('contenteditable', ['$sce', function($sce) {
24740          return {
24741            restrict: 'A', // only activate on element attribute
24742            require: '?ngModel', // get a hold of NgModelController
24743            link: function(scope, element, attrs, ngModel) {
24744              if (!ngModel) return; // do nothing if no ng-model
24745
24746              // Specify how UI should be updated
24747              ngModel.$render = function() {
24748                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
24749              };
24750
24751              // Listen for change events to enable binding
24752              element.on('blur keyup change', function() {
24753                scope.$evalAsync(read);
24754              });
24755              read(); // initialize
24756
24757              // Write data to the model
24758              function read() {
24759                var html = element.html();
24760                // When we clear the content editable the browser leaves a <br> behind
24761                // If strip-br attribute is provided then we strip this out
24762                if ( attrs.stripBr && html == '<br>' ) {
24763                  html = '';
24764                }
24765                ngModel.$setViewValue(html);
24766              }
24767            }
24768          };
24769        }]);
24770    </file>
24771    <file name="index.html">
24772      <form name="myForm">
24773       <div contenteditable
24774            name="myWidget" ng-model="userContent"
24775            strip-br="true"
24776            required>Change me!</div>
24777        <span ng-show="myForm.myWidget.$error.required">Required!</span>
24778       <hr>
24779       <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
24780      </form>
24781    </file>
24782    <file name="protractor.js" type="protractor">
24783    it('should data-bind and become invalid', function() {
24784      if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
24785        // SafariDriver can't handle contenteditable
24786        // and Firefox driver can't clear contenteditables very well
24787        return;
24788      }
24789      var contentEditable = element(by.css('[contenteditable]'));
24790      var content = 'Change me!';
24791
24792      expect(contentEditable.getText()).toEqual(content);
24793
24794      contentEditable.clear();
24795      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
24796      expect(contentEditable.getText()).toEqual('');
24797      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
24798    });
24799    </file>
24800 * </example>
24801 *
24802 *
24803 */
24804var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
24805    function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
24806  this.$viewValue = Number.NaN;
24807  this.$modelValue = Number.NaN;
24808  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
24809  this.$validators = {};
24810  this.$asyncValidators = {};
24811  this.$parsers = [];
24812  this.$formatters = [];
24813  this.$viewChangeListeners = [];
24814  this.$untouched = true;
24815  this.$touched = false;
24816  this.$pristine = true;
24817  this.$dirty = false;
24818  this.$valid = true;
24819  this.$invalid = false;
24820  this.$error = {}; // keep invalid keys here
24821  this.$$success = {}; // keep valid keys here
24822  this.$pending = undefined; // keep pending keys here
24823  this.$name = $interpolate($attr.name || '', false)($scope);
24824  this.$$parentForm = nullFormCtrl;
24825
24826  var parsedNgModel = $parse($attr.ngModel),
24827      parsedNgModelAssign = parsedNgModel.assign,
24828      ngModelGet = parsedNgModel,
24829      ngModelSet = parsedNgModelAssign,
24830      pendingDebounce = null,
24831      parserValid,
24832      ctrl = this;
24833
24834  this.$$setOptions = function(options) {
24835    ctrl.$options = options;
24836    if (options && options.getterSetter) {
24837      var invokeModelGetter = $parse($attr.ngModel + '()'),
24838          invokeModelSetter = $parse($attr.ngModel + '($$$p)');
24839
24840      ngModelGet = function($scope) {
24841        var modelValue = parsedNgModel($scope);
24842        if (isFunction(modelValue)) {
24843          modelValue = invokeModelGetter($scope);
24844        }
24845        return modelValue;
24846      };
24847      ngModelSet = function($scope, newValue) {
24848        if (isFunction(parsedNgModel($scope))) {
24849          invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
24850        } else {
24851          parsedNgModelAssign($scope, ctrl.$modelValue);
24852        }
24853      };
24854    } else if (!parsedNgModel.assign) {
24855      throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
24856          $attr.ngModel, startingTag($element));
24857    }
24858  };
24859
24860  /**
24861   * @ngdoc method
24862   * @name ngModel.NgModelController#$render
24863   *
24864   * @description
24865   * Called when the view needs to be updated. It is expected that the user of the ng-model
24866   * directive will implement this method.
24867   *
24868   * The `$render()` method is invoked in the following situations:
24869   *
24870   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
24871   *   committed value then `$render()` is called to update the input control.
24872   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
24873   *   the `$viewValue` are different from last time.
24874   *
24875   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
24876   * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
24877   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
24878   * invoked if you only change a property on the objects.
24879   */
24880  this.$render = noop;
24881
24882  /**
24883   * @ngdoc method
24884   * @name ngModel.NgModelController#$isEmpty
24885   *
24886   * @description
24887   * This is called when we need to determine if the value of an input is empty.
24888   *
24889   * For instance, the required directive does this to work out if the input has data or not.
24890   *
24891   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
24892   *
24893   * You can override this for input directives whose concept of being empty is different from the
24894   * default. The `checkboxInputType` directive does this because in its case a value of `false`
24895   * implies empty.
24896   *
24897   * @param {*} value The value of the input to check for emptiness.
24898   * @returns {boolean} True if `value` is "empty".
24899   */
24900  this.$isEmpty = function(value) {
24901    return isUndefined(value) || value === '' || value === null || value !== value;
24902  };
24903
24904  var currentValidationRunId = 0;
24905
24906  /**
24907   * @ngdoc method
24908   * @name ngModel.NgModelController#$setValidity
24909   *
24910   * @description
24911   * Change the validity state, and notify the form.
24912   *
24913   * This method can be called within $parsers/$formatters or a custom validation implementation.
24914   * However, in most cases it should be sufficient to use the `ngModel.$validators` and
24915   * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
24916   *
24917   * @param {string}
24917 validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
24918   *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
24919   *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
24920   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
24921   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
24922   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
24923   * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
24924   *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
24925   *                          Skipped is used by Angular when validators do not run because of parse errors and
24926   *                          when `$asyncValidators` do not run because any of the `$validators` failed.
24927   */
24928  addSetValidityMethod({
24929    ctrl: this,
24930    $element: $element,
24931    set: function(object, property) {
24932      object[property] = true;
24933    },
24934    unset: function(object, property) {
24935      delete object[property];
24936    },
24937    $animate: $animate
24938  });
24939
24940  /**
24941   * @ngdoc method
24942   * @name ngModel.NgModelController#$setPristine
24943   *
24944   * @description
24945   * Sets the control to its pristine state.
24946   *
24947   * This method can be called to remove the `ng-dirty` class and set the control to its pristine
24948   * state (`ng-pristine` class). A model is considered to be pristine when the control
24949   * has not been changed from when first compiled.
24950   */
24951  this.$setPristine = function() {
24952    ctrl.$dirty = false;
24953    ctrl.$pristine = true;
24954    $animate.removeClass($element, DIRTY_CLASS);
24955    $animate.addClass($element, PRISTINE_CLASS);
24956  };
24957
24958  /**
24959   * @ngdoc method
24960   * @name ngModel.NgModelController#$setDirty
24961   *
24962   * @description
24963   * Sets the control to its dirty state.
24964   *
24965   * This method can be called to remove the `ng-pristine` class and set the control to its dirty
24966   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
24967   * from when first compiled.
24968   */
24969  this.$setDirty = function() {
24970    ctrl.$dirty = true;
24971    ctrl.$pristine = false;
24972    $animate.removeClass($element, PRISTINE_CLASS);
24973    $animate.addClass($element, DIRTY_CLASS);
24974    ctrl.$$parentForm.$setDirty();
24975  };
24976
24977  /**
24978   * @ngdoc method
24979   * @name ngModel.NgModelController#$setUntouched
24980   *
24981   * @description
24982   * Sets the control to its untouched state.
24983   *
24984   * This method can be called to remove the `ng-touched` class and set the control to its
24985   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
24986   * by default, however this function can be used to restore that state if the model has
24987   * already been touched by the user.
24988   */
24989  this.$setUntouched = function() {
24990    ctrl.$touched = false;
24991    ctrl.$untouched = true;
24992    $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
24993  };
24994
24995  /**
24996   * @ngdoc method
24997   * @name ngModel.NgModelController#$setTouched
24998   *
24999   * @description
25000   * Sets the control to its touched state.
25001   *
25002   * This method can be called to remove the `ng-untouched` class and set the control to its
25003   * touched state (`ng-touched` class). A model is considered to be touched when the user has
25004   * first focused the control element and then shifted focus away from the control (blur event).
25005   */
25006  this.$setTouched = function() {
25007    ctrl.$touched = true;
25008    ctrl.$untouched = false;
25009    $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25010  };
25011
25012  /**
25013   * @ngdoc method
25014   * @name ngModel.NgModelController#$rollbackViewValue
25015   *
25016   * @description
25017   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25018   * which may be caused by a pending debounced event or because the input is waiting for a some
25019   * future event.
25020   *
25021   * If you have an input that uses `ng-model-options` to set up debounced events or events such
25022   * as blur you can have a situation where there is a period when the `$viewValue`
25023   * is out of synch with the ngModel's `$modelValue`.
25024   *
25025   * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
25026   * programmatically before these debounced/future events have resolved/occurred, because Angular's
25027   * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25028   *
25029   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25030   * input which may have such events pending. This is important in order to make sure that the
25031   * input field will be updated with the new model value and any pending operations are cancelled.
25032   *
25033   * <example name="ng-model-cancel-update" module="cancel-update-example">
25034   *   <file name="app.js">
25035   *     angular.module('cancel-update-example', [])
25036   *
25037   *     .controller('CancelUpdateController', ['$scope', function($scope) {
25038   *       $scope.resetWithCancel = function(e) {
25039   *         if (e.keyCode == 27) {
25040   *           $scope.myForm.myInput1.$rollbackViewValue();
25041   *           $scope.myValue = '';
25042   *         }
25043   *       };
25044   *       $scope.resetWithoutCancel = function(e) {
25045   *         if (e.keyCode == 27) {
25046   *           $scope.myValue = '';
25047   *         }
25048   *       };
25049   *     }]);
25050   *   </file>
25051   *   <file name="index.html">
25052   *     <div ng-controller="CancelUpdateController">
25053   *       <p>Try typing something in each input.  See that the model only updates when you
25054   *          blur off the input.
25055   *        </p>
25056   *        <p>Now see what happens if you start typing then press the Escape key</p>
25057   *
25058   *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25059   *         <p id="inputDescription1">With $rollbackViewValue()</p>
25060   *         <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
25061   *                ng-keydown="resetWithCancel($event)"><br/>
25062   *         myValue: "{{ myValue }}"
25063   *
25064   *         <p id="inputDescription2">Without $rollbackViewValue()</p>
25065   *         <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
25066   *                ng-keydown="resetWithoutCancel($event)"><br/>
25067   *         myValue: "{{ myValue }}"
25068   *       </form>
25069   *     </div>
25070   *   </file>
25071   * </example>
25072   */
25073  this.$rollbackViewValue = function() {
25074    $timeout.cancel(pendingDebounce);
25075    ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
25076    ctrl.$render();
25077  };
25078
25079  /**
25080   * @ngdoc method
25081   * @name ngModel.NgModelController#$validate
25082   *
25083   * @description
25084   * Runs each of the registered validators (first synchronous validators and then
25085   * asynchronous validators).
25086   * If the validity changes to invalid, the model will be set to `undefined`,
25087   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
25088   * If the validity changes to valid, it will set the model to the last available valid
25089   * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
25090   */
25091  this.$validate = function() {
25092    // ignore $validate before model is initialized
25093    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25094      return;
25095    }
25096
25097    var viewValue = ctrl.$$lastCommittedViewValue;
25098    // Note: we use the $$rawModelValue as $modelValue might have been
25099    // set to undefined during a view -> model update that found validation
25100    // errors. We can't parse the view here, since that could change
25101    // the model although neither viewValue nor the model on the scope changed
25102    var modelValue = ctrl.$$rawModelValue;
25103
25104    var prevValid = ctrl.$valid;
25105    var prevModelValue = ctrl.$modelValue;
25106
25107    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25108
25109    ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
25110      // If there was no change in validity, don't update the model
25111      // This prevents changing an invalid modelValue to undefined
25112      if (!allowInvalid && prevValid !== allValid) {
25113        // Note: Don't check ctrl.$valid here, as we could have
25114        // external validators (e.g. calculated on the server),
25115        // that just call $setValidity and need the model value
25116        // to calculate their validity.
25117        ctrl.$modelValue = allValid ? modelValue : undefined;
25118
25119        if (ctrl.$modelValue !== prevModelValue) {
25120          ctrl.$$writeModelToScope();
25121        }
25122      }
25123    });
25124
25125  };
25126
25127  this.$$runValidators = function(modelValue, viewValue, doneCallback) {
25128    currentValidationRunId++;
25129    var localValidationRunId = currentValidationRunId;
25130
25131    // check parser error
25132    if (!processParseErrors()) {
25133      validationDone(false);
25134      return;
25135    }
25136    if (!processSyncValidators()) {
25137      validationDone(false);
25138      return;
25139    }
25140    processAsyncValidators();
25141
25142    function processParseErrors() {
25143      var errorKey = ctrl.$$parserName || 'parse';
25144      if (isUndefined(parserValid)) {
25145        setValidity(errorKey, null);
25146      } else {
25147        if (!parserValid) {
25148          forEach(ctrl.$validators, function(v, name) {
25149            setValidity(name, null);
25150          });
25151          forEach(ctrl.$asyncValidators, function(v, name) {
25152            setValidity(name, null);
25153          });
25154        }
25155        // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
25156        setValidity(errorKey, parserValid);
25157        return parserValid;
25158      }
25159      return true;
25160    }
25161
25162    function processSyncValidators() {
25163      var syncValidatorsValid = true;
25164      forEach(ctrl.$validators, function(validator, name) {
25165        var result = validator(modelValue, viewValue);
25166        syncValidatorsValid = syncValidatorsValid && result;
25167        setValidity(name, result);
25168      });
25169      if (!syncValidatorsValid) {
25170        forEach(ctrl.$asyncValidators, function(v, name) {
25171          setValidity(name, null);
25172        });
25173        return false;
25174      }
25175      return true;
25176    }
25177
25178    function processAsyncValidators() {
25179      var validatorPromises = [];
25180      var allValid = true;
25181      forEach(ctrl.$asyncValidators, function(validator, name) {
25182        var promise = validator(modelValue, viewValue);
25183        if (!isPromiseLike(promise)) {
25184          throw ngModelMinErr("$asyncValidators",
25185            "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
25186        }
25187        setValidity(name, undefined);
25188        validatorPromises.push(promise.then(function() {
25189          setValidity(name, true);
25190        }, function(error) {
25191          allValid = false;
25192          setValidity(name, false);
25193        }));
25194      });
25195      if (!validatorPromises.length) {
25196        validationDone(true);
25197      } else {
25198        $q.all(validatorPromises).then(function() {
25199          validationDone(allValid);
25200        }, noop);
25201      }
25202    }
25203
25204    function setValidity(name, isValid) {
25205      if (localValidationRunId === currentValidationRunId) {
25206        ctrl.$setValidity(name, isValid);
25207      }
25208    }
25209
25210    function validationDone(allValid) {
25211      if (localValidationRunId === currentValidationRunId) {
25212
25213        doneCallback(allValid);
25214      }
25215    }
25216  };
25217
25218  /**
25219   * @ngdoc method
25220   * @name ngModel.NgModelController#$commitViewValue
25221   *
25222   * @description
25223   * Commit a pending update to the `$modelValue`.
25224   *
25225   * Updates may be pending by a debounced event or because the input is waiting for a some future
25226   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
25227   * usually handles calling this in response to input events.
25228   */
25229  this.$commitViewValue = function() {
25230    var viewValue = ctrl.$viewValue;
25231
25232    $timeout.cancel(pendingDebounce);
25233
25234    // If the view value has not changed then we should just exit, except in the case where there is
25235    // a native validator on the element. In this case the validation state may have changed even though
25236    // the viewValue has stayed empty.
25237    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
25238      return;
25239    }
25240    ctrl.$$lastCommittedViewValue = viewValue;
25241
25242    // change to dirty
25243    if (ctrl.$pristine) {
25244      this.$setDirty();
25245    }
25246    this.$$parseAndValidate();
25247  };
25248
25249  this.$$parseAndValidate = function() {
25250    var viewValue = ctrl.$$lastCommittedViewValue;
25251    var modelValue = viewValue;
25252    parserValid = isUndefined(modelValue) ? undefined : true;
25253
25254    if (parserValid) {
25255      for (var i = 0; i < ctrl.$parsers.length; i++) {
25256        modelValue = ctrl.$parsers[i](modelValue);
25257        if (isUndefined(modelValue)) {
25258          parserValid = false;
25259          break;
25260        }
25261      }
25262    }
25263    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25264      // ctrl.$modelValue has not been touched yet...
25265      ctrl.$modelValue = ngModelGet($scope);
25266    }
25267    var prevModelValue = ctrl.$modelValue;
25268    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25269    ctrl.$$rawModelValue = modelValue;
25270
25271    if (allowInvalid) {
25272      ctrl.$modelValue = modelValue;
25273      writeToModelIfNeeded();
25274    }
25275
25276    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
25277    // This can happen if e.g. $setViewValue is called from inside a parser
25278    ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
25279      if (!allowInvalid) {
25280        // Note: Don't check ctrl.$valid here, as we could have
25281        // external validators (e.g. calculated on the server),
25282        // that just call $setValidity and need the model value
25283        // to calculate their validity.
25284        ctrl.$modelValue = allValid ? modelValue : undefined;
25285        writeToModelIfNeeded();
25286      }
25287    });
25288
25289    function writeToModelIfNeeded() {
25290      if (ctrl.$modelValue !== prevModelValue) {
25291        ctrl.$$writeModelToScope();
25292      }
25293    }
25294  };
25295
25296  this.$$writeModelToScope = function() {
25297    ngModelSet($scope, ctrl.$modelValue);
25298    forEach(ctrl.$viewChangeListeners, function(listener) {
25299      try {
25300        listener();
25301      } catch (e) {
25302        $exceptionHandler(e);
25303      }
25304    });
25305  };
25306
25307  /**
25308   * @ngdoc method
25309   * @name ngModel.NgModelController#$setViewValue
25310   *
25311   * @description
25312   * Update the view value.
25313   *
25314   * This method should be called when a control wants to change the view value; typically,
25315   * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25316   * directive calls it when the value of the input changes and {@link ng.directive:select select}
25317   * calls it when an option is selected.
25318   *
25319   * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25320   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25321   * value sent directly for processing, finally to be applied to `$modelValue` and then the
25322   * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25323   * in the `$viewChangeListeners` list, are called.
25324   *
25325   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25326   * and the `default` trigger is not listed, all those actions will remain pending until one of the
25327   * `updateOn` events is triggered on the DOM element.
25328   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25329   * directive is used with a custom debounce for this particular event.
25330   * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25331   * is specified, once the timer runs out.
25332   *
25333   * When used with standard inputs, the view value will always be a string (which is in some cases
25334   * parsed into another type, such as a `Date` object for `input[date]`.)
25335   * However, custom controls might also pass objects to this method. In this case, we should make
25336   * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25337   * perform a deep watch of objects, it only looks for a change of identity. If you only change
25338   * the property of the object then ngModel will not realise that the object has changed and
25339   * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25340   * not change properties of the copy once it has been passed to `$setViewValue`.
25341   * Otherwise you may cause the model value on the scope to change incorrectly.
25342   *
25343   * <div class="alert alert-info">
25344   * In any case, the value passed to the method should always reflect the current value
25345   * of the control. For example, if you are calling `$setViewValue` for an input element,
25346   * you should pass the input DOM value. Otherwise, the control and the scope model become
25347   * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25348   * the control's DOM value in any way. If we want to change the control's DOM value
25349   * programmatically, we should update the `ngModel` scope expression. Its new value will be
25350   * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25351   * to update the DOM, and finally call `$validate` on it.
25352   * </div>
25353   *
25354   * @param {*} value value from the view.
25355   * @param {string} trigger Event that triggered the update.
25356   */
25357  this.$setViewValue = function(value, trigger) {
25358    ctrl.$viewValue = value;
25359    if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25360      ctrl.$$debounceViewValueCommit(trigger);
25361    }
25362  };
25363
25364  this.$$debounceViewValueCommit = function(trigger) {
25365    var debounceDelay = 0,
25366        options = ctrl.$options,
25367        debounce;
25368
25369    if (options && isDefined(options.debounce)) {
25370      debounce = options.debounce;
25371      if (isNumber(debounce)) {
25372        debounceDelay = debounce;
25373      } else if (isNumber(debounce[trigger])) {
25374        debounceDelay = debounce[trigger];
25375      } else if (isNumber(debounce['default'])) {
25376        debounceDelay = debounce['default'];
25377      }
25378    }
25379
25380    $timeout.cancel(pendingDebounce);
25381    if (debounceDelay) {
25382      pendingDebounce = $timeout(function() {
25383        ctrl.$commitViewValue();
25384      }, debounceDelay);
25385    } else if ($rootScope.$$phase) {
25386      ctrl.$commitViewValue();
25387    } else {
25388      $scope.$apply(function() {
25389        ctrl.$commitViewValue();
25390      });
25391    }
25392  };
25393
25394  // model -> value
25395  // Note: we cannot use a normal scope.$watch as we want to detect the following:
25396  // 1. scope value is 'a'
25397  // 2. user enters 'b'
25398  // 3. ng-change kicks in and reverts scope value to 'a'
25399  //    ->
25399 scope value did not change since the last digest as
25400  //       ng-change executes in apply phase
25401  // 4. view should be changed back to 'a'
25402  $scope.$watch(function ngModelWatch() {
25403    var modelValue = ngModelGet($scope);
25404
25405    // if scope model value and ngModel value are out of sync
25406    // TODO(perf): why not move this to the action fn?
25407    if (modelValue !== ctrl.$modelValue &&
25408       // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25409       (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25410    ) {
25411      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25412      parserValid = undefined;
25413
25414      var formatters = ctrl.$formatters,
25415          idx = formatters.length;
25416
25417      var viewValue = modelValue;
25418      while (idx--) {
25419        viewValue = formatters[idx](viewValue);
25420      }
25421      if (ctrl.$viewValue !== viewValue) {
25422        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25423        ctrl.$render();
25424
25425        ctrl.$$runValidators(modelValue, viewValue, noop);
25426      }
25427    }
25428
25429    return modelValue;
25430  });
25431}];
25432
25433
25434/**
25435 * @ngdoc directive
25436 * @name ngModel
25437 *
25438 * @element input
25439 * @priority 1
25440 *
25441 * @description
25442 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
25443 * property on the scope using {@link ngModel.NgModelController NgModelController},
25444 * which is created and exposed by this directive.
25445 *
25446 * `ngModel` is responsible for:
25447 *
25448 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
25449 *   require.
25450 * - Providing validation behavior (i.e. required, number, email, url).
25451 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
25452 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
25453 * - Registering the control with its parent {@link ng.directive:form form}.
25454 *
25455 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
25456 * current scope. If the property doesn't already exist on this scope, it will be created
25457 * implicitly and added to the scope.
25458 *
25459 * For best practices on using `ngModel`, see:
25460 *
25461 *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
25462 *
25463 * For basic examples, how to use `ngModel`, see:
25464 *
25465 *  - {@link ng.directive:input input}
25466 *    - {@link input[text] text}
25467 *    - {@link input[checkbox] checkbox}
25468 *    - {@link input[radio] radio}
25469 *    - {@link input[number] number}
25470 *    - {@link input[email] email}
25471 *    - {@link input[url] url}
25472 *    - {@link input[date] date}
25473 *    - {@link input[datetime-local] datetime-local}
25474 *    - {@link input[time] time}
25475 *    - {@link input[month] month}
25476 *    - {@link input[week] week}
25477 *  - {@link ng.directive:select select}
25478 *  - {@link ng.directive:textarea textarea}
25479 *
25480 * # CSS classes
25481 * The following CSS classes are added and removed on the associated input/select/textarea element
25482 * depending on the validity of the model.
25483 *
25484 *  - `ng-valid`: the model is valid
25485 *  - `ng-invalid`: the model is invalid
25486 *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
25487 *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
25488 *  - `ng-pristine`: the control hasn't been interacted with yet
25489 *  - `ng-dirty`: the control has been interacted with
25490 *  - `ng-touched`: the control has been blurred
25491 *  - `ng-untouched`: the control hasn't been blurred
25492 *  - `ng-pending`: any `$asyncValidators` are unfulfilled
25493 *
25494 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
25495 *
25496 * ## Animation Hooks
25497 *
25498 * Animations within models are triggered when any of the associated CSS classes are added and removed
25499 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
25500 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
25501 * The animations that are triggered within ngModel are similar to how they work in ngClass and
25502 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
25503 *
25504 * The following example shows a simple way to utilize CSS transitions to style an input element
25505 * that has been rendered as invalid after it has been validated:
25506 *
25507 * <pre>
25508 * //be sure to include ngAnimate as a module to hook into more
25509 * //advanced animations
25510 * .my-input {
25511 *   transition:0.5s linear all;
25512 *   background: white;
25513 * }
25514 * .my-input.ng-invalid {
25515 *   background: red;
25516 *   color:white;
25517 * }
25518 * </pre>
25519 *
25520 * @example
25521 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
25522     <file name="index.html">
25523       <script>
25524        angular.module('inputExample', [])
25525          .controller('ExampleController', ['$scope', function($scope) {
25526            $scope.val = '1';
25527          }]);
25528       </script>
25529       <style>
25530         .my-input {
25531           transition:all linear 0.5s;
25532           background: transparent;
25533         }
25534         .my-input.ng-invalid {
25535           color:white;
25536           background: red;
25537         }
25538       </style>
25539       <p id="inputDescription">
25540        Update input to see transitions when valid/invalid.
25541        Integer is a valid value.
25542       </p>
25543       <form name="testForm" ng-controller="ExampleController">
25544         <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
25545                aria-describedby="inputDescription" />
25546       </form>
25547     </file>
25548 * </example>
25549 *
25550 * ## Binding to a getter/setter
25551 *
25552 * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
25553 * function that returns a representation of the model when called with zero arguments, and sets
25554 * the internal state of a model when called with an argument. It's sometimes useful to use this
25555 * for models that have an internal representation that's different from what the model exposes
25556 * to the view.
25557 *
25558 * <div class="alert alert-success">
25559 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
25560 * frequently than other parts of your code.
25561 * </div>
25562 *
25563 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
25564 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
25565 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
25566 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
25567 *
25568 * The following example shows how to use `ngModel` with a getter/setter:
25569 *
25570 * @example
25571 * <example name="ngModel-getter-setter" module="getterSetterExample">
25572     <file name="index.html">
25573       <div ng-controller="ExampleController">
25574         <form name="userForm">
25575           <label>Name:
25576             <input type="text" name="userName"
25577                    ng-model="user.name"
25578                    ng-model-options="{ getterSetter: true }" />
25579           </label>
25580         </form>
25581         <pre>user.name = <span ng-bind="user.name()"></span></pre>
25582       </div>
25583     </file>
25584     <file name="app.js">
25585       angular.module('getterSetterExample', [])
25586         .controller('ExampleController', ['$scope', function($scope) {
25587           var _name = 'Brian';
25588           $scope.user = {
25589             name: function(newName) {
25590              // Note that newName can be undefined for two reasons:
25591              // 1. Because it is called as a getter and thus called with no arguments
25592              // 2. Because the property should actually be set to undefined. This happens e.g. if the
25593              //    input is invalid
25594              return arguments.length ? (_name = newName) : _name;
25595             }
25596           };
25597         }]);
25598     </file>
25599 * </example>
25600 */
25601var ngModelDirective = ['$rootScope', function($rootScope) {
25602  return {
25603    restrict: 'A',
25604    require: ['ngModel', '^?form', '^?ngModelOptions'],
25605    controller: NgModelController,
25606    // Prelink needs to run before any input directive
25607    // so that we can set the NgModelOptions in NgModelController
25608    // before anyone else uses it.
25609    priority: 1,
25610    compile: function ngModelCompile(element) {
25611      // Setup initial state of the control
25612      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
25613
25614      return {
25615        pre: function ngModelPreLink(scope, element, attr, ctrls) {
25616          var modelCtrl = ctrls[0],
25617              formCtrl = ctrls[1] || modelCtrl.$$parentForm;
25618
25619          modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
25620
25621          // notify others, especially parent forms
25622          formCtrl.$addControl(modelCtrl);
25623
25624          attr.$observe('name', function(newValue) {
25625            if (modelCtrl.$name !== newValue) {
25626              modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
25627            }
25628          });
25629
25630          scope.$on('$destroy', function() {
25631            modelCtrl.$$parentForm.$removeControl(modelCtrl);
25632          });
25633        },
25634        post: function ngModelPostLink(scope, element, attr, ctrls) {
25635          var modelCtrl = ctrls[0];
25636          if (modelCtrl.$options && modelCtrl.$options.updateOn) {
25637            element.on(modelCtrl.$options.updateOn, function(ev) {
25638              modelCtrl.$$debounceViewValueCommit(ev && ev.type);
25639            });
25640          }
25641
25642          element.on('blur', function(ev) {
25643            if (modelCtrl.$touched) return;
25644
25645            if ($rootScope.$$phase) {
25646              scope.$evalAsync(modelCtrl.$setTouched);
25647            } else {
25648              scope.$apply(modelCtrl.$setTouched);
25649            }
25650          });
25651        }
25652      };
25653    }
25654  };
25655}];
25656
25657var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25658
25659/**
25660 * @ngdoc directive
25661 * @name ngModelOptions
25662 *
25663 * @description
25664 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
25665 * events that will trigger a model update and/or a debouncing delay so that the actual update only
25666 * takes place when a timer expires; this timer will be reset after another change takes place.
25667 *
25668 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
25669 * be different from the value in the actual model. This means that if you update the model you
25670 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
25671 * order to make sure it is synchronized with the model and that any debounced action is canceled.
25672 *
25673 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
25674 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
25675 * important because `form` controllers are published to the related scope under the name in their
25676 * `name` attribute.
25677 *
25678 * Any pending changes will take place immediately when an enclosing form is submitted via the
25679 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
25680 * to have access to the updated model.
25681 *
25682 * `ngModelOptions` has an effect on the element it's declared on and its desce
25682ndants.
25683 *
25684 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
25685 *   - `updateOn`: string specifying which event should the input be bound to. You can set several
25686 *     events using an space delimited list. There is a special event called `default` that
25687 *     matches the default events belonging of the control.
25688 *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
25689 *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
25690 *     custom value for each event. For example:
25691 *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
25692 *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
25693 *     not validate correctly instead of the default behavior of setting the model to undefined.
25694 *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
25695       `ngModel` as getters/setters.
25696 *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
25697 *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
25698 *     continental US time zone abbreviations, but for general use, use a time zone offset, for
25699 *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
25700 *     If not specified, the timezone of the browser will be used.
25701 *
25702 * @example
25703
25704  The following example shows how to override immediate updates. Changes on the inputs within the
25705  form will update the model only when the control loses focus (blur event). If `escape` key is
25706  pressed while the input field is focused, the value is reset to the value in the current model.
25707
25708  <example name="ngModelOptions-directive-blur" module="optionsExample">
25709    <file name="index.html">
25710      <div ng-controller="ExampleController">
25711        <form name="userForm">
25712          <label>Name:
25713            <input type="text" name="userName"
25714                   ng-model="user.name"
25715                   ng-model-options="{ updateOn: 'blur' }"
25716                   ng-keyup="cancel($event)" />
25717          </label><br />
25718          <label>Other data:
25719            <input type="text" ng-model="user.data" />
25720          </label><br />
25721        </form>
25722        <pre>user.name = <span ng-bind="user.name"></span></pre>
25723      </div>
25724    </file>
25725    <file name="app.js">
25726      angular.module('optionsExample', [])
25727        .controller('ExampleController', ['$scope', function($scope) {
25728          $scope.user = { name: 'say', data: '' };
25729
25730          $scope.cancel = function(e) {
25731            if (e.keyCode == 27) {
25732              $scope.userForm.userName.$rollbackViewValue();
25733            }
25734          };
25735        }]);
25736    </file>
25737    <file name="protractor.js" type="protractor">
25738      var model = element(by.binding('user.name'));
25739      var input = element(by.model('user.name'));
25740      var other = element(by.model('user.data'));
25741
25742      it('should allow custom events', function() {
25743        input.sendKeys(' hello');
25744        input.click();
25745        expect(model.getText()).toEqual('say');
25746        other.click();
25747        expect(model.getText()).toEqual('say hello');
25748      });
25749
25750      it('should $rollbackViewValue when model changes', function() {
25751        input.sendKeys(' hello');
25752        expect(input.getAttribute('value')).toEqual('say hello');
25753        input.sendKeys(protractor.Key.ESCAPE);
25754        expect(input.getAttribute('value')).toEqual('say');
25755        other.click();
25756        expect(model.getText()).toEqual('say');
25757      });
25758    </file>
25759  </example>
25760
25761  This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
25762  If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
25763
25764  <example name="ngModelOptions-directive-debounce" module="optionsExample">
25765    <file name="index.html">
25766      <div ng-controller="ExampleController">
25767        <form name="userForm">
25768          <label>Name:
25769            <input type="text" name="userName"
25770                   ng-model="user.name"
25771                   ng-model-options="{ debounce: 1000 }" />
25772          </label>
25773          <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
25774          <br />
25775        </form>
25776        <pre>user.name = <span ng-bind="user.name"></span></pre>
25777      </div>
25778    </file>
25779    <file name="app.js">
25780      angular.module('optionsExample', [])
25781        .controller('ExampleController', ['$scope', function($scope) {
25782          $scope.user = { name: 'say' };
25783        }]);
25784    </file>
25785  </example>
25786
25787  This one shows how to bind to getter/setters:
25788
25789  <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
25790    <file name="index.html">
25791      <div ng-controller="ExampleController">
25792        <form name="userForm">
25793          <label>Name:
25794            <input type="text" name="userName"
25795                   ng-model="user.name"
25796                   ng-model-options="{ getterSetter: true }" />
25797          </label>
25798        </form>
25799        <pre>user.name = <span ng-bind="user.name()"></span></pre>
25800      </div>
25801    </file>
25802    <file name="app.js">
25803      angular.module('getterSetterExample', [])
25804        .controller('ExampleController', ['$scope', function($scope) {
25805          var _name = 'Brian';
25806          $scope.user = {
25807            name: function(newName) {
25808              // Note that newName can be undefined for two reasons:
25809              // 1. Because it is called as a getter and thus called with no arguments
25810              // 2. Because the property should actually be set to undefined. This happens e.g. if the
25811              //    input is invalid
25812              return arguments.length ? (_name = newName) : _name;
25813            }
25814          };
25815        }]);
25816    </file>
25817  </example>
25818 */
25819var ngModelOptionsDirective = function() {
25820  return {
25821    restrict: 'A',
25822    controller: ['$scope', '$attrs', function($scope, $attrs) {
25823      var that = this;
25824      this.$options = copy($scope.$eval($attrs.ngModelOptions));
25825      // Allow adding/overriding bound events
25826      if (isDefined(this.$options.updateOn)) {
25827        this.$options.updateOnDefault = false;
25828        // extract "default" pseudo-event from list of events that can trigger a model update
25829        this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
25830          that.$options.updateOnDefault = true;
25831          return ' ';
25832        }));
25833      } else {
25834        this.$options.updateOnDefault = true;
25835      }
25836    }]
25837  };
25838};
25839
25840
25841
25842// helper methods
25843function addSetValidityMethod(context) {
25844  var ctrl = context.ctrl,
25845      $element = context.$element,
25846      classCache = {},
25847      set = context.set,
25848      unset = context.unset,
25849      $animate = context.$animate;
25850
25851  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
25852
25853  ctrl.$setValidity = setValidity;
25854
25855  function setValidity(validationErrorKey, state, controller) {
25856    if (isUndefined(state)) {
25857      createAndSet('$pending', validationErrorKey, controller);
25858    } else {
25859      unsetAndCleanup('$pending', validationErrorKey, controller);
25860    }
25861    if (!isBoolean(state)) {
25862      unset(ctrl.$error, validationErrorKey, controller);
25863      unset(ctrl.$$success, validationErrorKey, controller);
25864    } else {
25865      if (state) {
25866        unset(ctrl.$error, validationErrorKey, controller);
25867        set(ctrl.$$success, validationErrorKey, controller);
25868      } else {
25869        set(ctrl.$error, validationErrorKey, controller);
25870        unset(ctrl.$$success, validationErrorKey, controller);
25871      }
25872    }
25873    if (ctrl.$pending) {
25874      cachedToggleClass(PENDING_CLASS, true);
25875      ctrl.$valid = ctrl.$invalid = undefined;
25876      toggleValidationCss('', null);
25877    } else {
25878      cachedToggleClass(PENDING_CLASS, false);
25879      ctrl.$valid = isObjectEmpty(ctrl.$error);
25880      ctrl.$invalid = !ctrl.$valid;
25881      toggleValidationCss('', ctrl.$valid);
25882    }
25883
25884    // re-read the state as the set/unset methods could have
25885    // combined state in ctrl.$error[validationError] (used for forms),
25886    // where setting/unsetting only increments/decrements the value,
25887    // and does not replace it.
25888    var combinedState;
25889    if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
25890      combinedState = undefined;
25891    } else if (ctrl.$error[validationErrorKey]) {
25892      combinedState = false;
25893    } else if (ctrl.$$success[validationErrorKey]) {
25894      combinedState = true;
25895    } else {
25896      combinedState = null;
25897    }
25898
25899    toggleValidationCss(validationErrorKey, combinedState);
25900    ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
25901  }
25902
25903  function createAndSet(name, value, controller) {
25904    if (!ctrl[name]) {
25905      ctrl[name] = {};
25906    }
25907    set(ctrl[name], value, controller);
25908  }
25909
25910  function unsetAndCleanup(name, value, controller) {
25911    if (ctrl[name]) {
25912      unset(ctrl[name], value, controller);
25913    }
25914    if (isObjectEmpty(ctrl[name])) {
25915      ctrl[name] = undefined;
25916    }
25917  }
25918
25919  function cachedToggleClass(className, switchValue) {
25920    if (switchValue && !classCache[className]) {
25921      $animate.addClass($element, className);
25922      classCache[className] = true;
25923    } else if (!switchValue && classCache[className]) {
25924      $animate.removeClass($element, className);
25925      classCache[className] = false;
25926    }
25927  }
25928
25929  function toggleValidationCss(validationErrorKey, isValid) {
25930    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
25931
25932    cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
25933    cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
25934  }
25935}
25936
25937function isObjectEmpty(obj) {
25938  if (obj) {
25939    for (var prop in obj) {
25940      if (obj.hasOwnProperty(prop)) {
25941        return false;
25942      }
25943    }
25944  }
25945  return true;
25946}
25947
25948/**
25949 * @ngdoc directive
25950 * @name ngNonBindable
25951 * @restrict AC
25952 * @priority 1000
25953 *
25954 * @description
25955 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
25956 * DOM element. This is useful if the element contains what appears to be Angular directives and
25957 * bindings but which should be ignored by Angular. This could be the case if you have a site that
25958 * displays snippets of code, for instance.
25959 *
25960 * @element ANY
25961 *
25962 * @example
25963 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
25964 * but the one wrapped in `ngNonBindable` is left alone.
25965 *
25966 * @example
25967    <example>
25968      <file name="index.html">
25969        <div>Normal: {{1 + 2}}</div>
25970        <div ng-non-bindable>Ignored: {{1 + 2}}</div>
25971      </file>
25972      <file name="protractor.js" type="protractor">
25973       it('should check ng-non-bindable', function() {
25974         expect(element(by.binding('1 + 2')).getText()).toContain('3');
25975         expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
25976       });
25977      </file>
25978    </example>
25979 */
25980var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
25981
25982/* global jqLiteRemove */
25983
25984var ngOptionsMinErr = minErr('ngOptions');
25985
25986/**
25987 * @ngdoc directive
25988 * @name ngOptions
25989 * @restrict A
25990 *
25991 * @description
25992 *
25993 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
25994 * elements for the `<select>` element using the array or object obtained by evaluating the
25995 * `ngOptions` comprehension expression.
25996 *
25997 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
25998 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
25999 * increasing speed by not creating a new scope for each repeated instance, as well as providing
26000 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26001 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26002 *  to a non-string value. This is because an option element can only be bound to string values at
26003 * present.
26004 *
26005 * When an item in the `<select>` menu is selected, the array element or object property
26006 * represented by the selected option will be bound to the model identified by the `ngModel`
26007 * directive.
26008 *
26009 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26010 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26011 * option. See example below for demonstration.
26012 *
26013 * ## Complex Models (objects or collections)
26014 *
26015 * **Note:** By default, `ngModel` watches the model by reference, not value. This is important when
26016 * binding any input directive to a model that is an object or a collection.
26017 *
26018 * Since this is a common situation for `ngOptions` the directive additionally watches the model using
26019 * `$watchCollection` when the select has the `multiple` attribute or when there is a `track by` clause in
26020 * the options expression. This allows ngOptions to trigger a re-rendering of the options even if the actual
26021 * object/collection has not changed identity but only a property on the object or an item in the collection
26022 * changes.
26023 *
26024 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26025 * if the model is an array). This means that changing a property deeper inside the object/collection that the
26026 * first level will not trigger a re-rendering.
26027 *
26028 *
26029 * ## `select` **`as`**
26030 *
26031 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26032 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26033 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26034 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26035 *
26036 *
26037 * ### `select` **`as`** and **`track by`**
26038 *
26039 * <div class="alert alert-warning">
26040 * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
26041 * </div>
26042 *
26043 * Consider the following example:
26044 *
26045 * ```html
26046 * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"></select>
26047 * ```
26048 *
26049 * ```js
26050 * $scope.values = [{
26051 *   id: 1,
26052 *   label: 'aLabel',
26053 *   subItem: { name: 'aSubItem' }
26054 * }, {
26055 *   id: 2,
26056 *   label: 'bLabel',
26057 *   subItem: { name: 'bSubItem' }
26058 * }];
26059 *
26060 * $scope.selected = { name: 'aSubItem' };
26061 * ```
26062 *
26063 * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
26064 * of the data source (to `item` in this example). To calculate whether an element is selected, we do the
26065 * following:
26066 *
26067 * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
26068 * 2. Apply **`track by`** to the already selected value in `ngModel`.
26069 *    In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
26070 *    value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
26071 *    a wrong object, the selected element can't be found, `<select>` is always reset to the "not
26072 *    selected" option.
26073 *
26074 *
26075 * @param {string} ngModel Assignable angular expression to data-bind to.
26076 * @param {string=} name Property name of the form under which the control is published.
26077 * @param {string=} required The control is considered valid only if value is entered.
26078 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26079 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26080 *    `required` when you want to data-bind to the `required` attribute.
26081 * @param {comprehension_expression=} ngOptions in one of the following forms:
26082 *
26083 *   * for array data sources:
26084 *     * `label` **`for`** `value` **`in`** `array`
26085 *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
26086 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
26087 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
26088 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26089 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26090 *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
26091 *        (for including a filter with `track by`)
26092 *   * for object data sources:
26093 *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26094 *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26095 *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
26096 *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
26097 *     * `select` **`as`** `label` **`group by`** `group`
26098 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26099 *     * `select` **`as`** `label` **`disable when`** `disable`
26100 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26101 *
26102 * Where:
26103 *
26104 *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
26105 *   * `value`: local variable which will refer to each item in the `array` or each property value
26106 *      of `object` during iteration.
26107 *   * `key`: local variable which will refer to a property name in `object` during iteration.
26108 *   * `label`: The result of this expression will be the label for `<option>` element. The
26109 *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
26110 *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
26111 *      element. If not specified, `select` expression will default to `value`.
26112 *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
26113 *      DOM element.
26114 *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
26115 *      element. Return `true` to disable.
26116 *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
26117 *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
26118 *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
26119 *      even when the options are recreated (e.g. reloaded from the server).
26120 *
26121 * @example
26122    <example module="selectExample">
26123      <file name="index.html">
26124        <script>
26125        angular.module('selectExample', [])
26126          .controller('ExampleController', ['$scope', function($scope) {
26127            $scope.colors = [
26128              {name:'black', shade:'dark'},
26129              {name:'white', shade:'light', notAnOption: true},
26130              {name:'red', shade:'dark'},
26131              {name:'blue', shade:'dark', notAnOption: true},
26132              {name:'yellow', shade:'light', notAnOption: false}
26133            ];
26134            $scope.myColor = $scope.colors[2]; // red
26135          }]);
26136        </script>
26137        <div ng-controller="ExampleController">
26138          <ul>
26139            <li ng-repeat="color in colors">
26140              <label>Name: <input ng-model="color.name"></label>
26141              <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
26142              <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
26143            </li>
26144            <li>
26145              <button ng-click="colors.push({})">add</button>
26146            </li>
26147          </ul>
26148          <hr/>
26149          <label>Color (null not allowed):
26150            <select ng-model="myColor" ng-options="color.name for color in colors"></select>
26151          </label><br/>
26152          <label>Color (null allowed):
26153          <span  class="nullable">
26154            <select ng-model="myColor" ng-options="color.name for color in colors">
26155              <option value="">-- choose color --</option>
26156            </select>
26157          </span></label><br/>
26158
26159          <label>Color grouped by shade:
26160            <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
26161            </select>
26162          </label><br/>
26163
26164          <label>Color grouped by shade, with some disabled:
26165            <select ng-model="myColor"
26166                  ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
26167            </select>
26168          </label><br/>
26169
26170
26171
26172          Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
26173          <br/>
26174          <hr/>
26175          Currently selected: {{ {selected_color:myColor} }}
26176          <div style="border:solid 1px black; height:20px"
26177               ng-style="{'background-color':myColor.name}">
26178          </div>
26179        </div>
26180      </file>
26181      <file name="protractor.js" type="protractor">
26182         it('should check ng-options', function() {
26183           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
26184           element.all(by.model('myColor')).first().click();
26185           element.all(by.css('select[ng-model="myColor"] option')).first().click();
26186           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
26187           element(by.css('.nullable select[ng-model="myColor"]')).click();
26188           element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
26189           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
26190         });
26191      </file>
26192    </example>
26193 */
26194
26195// jshint maxlen: false
26196//                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
26197var 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]+?))?$/;
26198                        // 1: value expression (valueFn)
26199                        // 2: label expression (displayFn)
26200                        // 3: group by expression (groupByFn)
26201                        // 4: disable when expression (disableWhenFn)
26202                        // 5: array item variable name
26203                        // 6: object item key variable name
26204                        // 7: object item value variable name
26205                        // 8: collection expression
26206                        // 9: track by expression
26207// jshint maxlen: 100
26208
26209
26210var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26211
26212  function parseOptionsExpression(optionsExp, selectElement, scope) {
26213
26214    var match = optionsExp.match(NG_OPTIONS_REGEXP);
26215    if (!(match)) {
26216      throw ngOptionsMinErr('iexp',
26217        "Expected expression in form of " +
26218        "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
26219        " but got '{0}'. Element: {1}",
26220        optionsExp, startingTag(selectElement));
26221    }
26222
26223    // Extract the parts from the ngOptions expression
26224
26225    // The variable name for the value of the item in the collection
26226    var valueName = match[5] || match[7];
26227    // The variable name for the key of the item in the collection
26228    var keyName = match[6];
26229
26230    // An expression that generates the viewValue for an option if there is a label expression
26231    var selectAs = / as /.test(match[0]) && match[1];
26232    // An expression that is used to track the id of each object in the options collection
26233    var trackBy = match[9];
26234    // An expression that generates the viewValue for an option if there is no label expression
26235    var valueFn = $parse(match[2] ? match[1] : valueName);
26236    var selectAsFn = selectAs && $parse(selectAs);
26237    var viewValueFn = selectAsFn || valueFn;
26238    var trackByFn = trackBy && $parse(trackBy);
26239
26240    // Get the value by which we are going to track the option
26241    // if we have a trackFn then use that (passing scope and locals)
26242    // otherwise just hash the given viewValue
26243    var getTrackByValueFn = trackBy ?
26244                              function(value, locals) { return trackByFn(scope, locals); } :
26245                              function getHashOfValue(value) { return hashKey(value); };
26246    var getTrackByValue = function(value, key) {
vendor: 5,877 bytes, lines 26247-26407
26247      return getTrackByValueFn(value, getLocals(value, key));
26248    };
26249
26250    var displayFn = $parse(match[2] || match[1]);
26251    var groupByFn = $parse(match[3] || '');
26252    var disableWhenFn = $parse(match[4] || '');
26253    var valuesFn = $parse(match[8]);
26254
26255    var locals = {};
26256    var getLocals = keyName ? function(value, key) {
26257      locals[keyName] = key;
26258      locals[valueName] = value;
26259      return locals;
26260    } : function(value) {
26261      locals[valueName] = value;
26262      return locals;
26263    };
26264
26265
26266    function Option(selectValue, viewValue, label, group, disabled) {
26267      this.selectValue = selectValue;
26268      this.viewValue = viewValue;
26269      this.label = label;
26270      this.group = group;
26271      this.disabled = disabled;
26272    }
26273
26274    function getOptionValuesKeys(optionValues) {
26275      var optionValuesKeys;
26276
26277      if (!keyName && isArrayLike(optionValues)) {
26278        optionValuesKeys = optionValues;
26279      } else {
26280        // if object, extract keys, in enumeration order, unsorted
26281        optionValuesKeys = [];
26282        for (var itemKey in optionValues) {
26283          if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26284            optionValuesKeys.push(itemKey);
26285          }
26286        }
26287      }
26288      return optionValuesKeys;
26289    }
26290
26291    return {
26292      trackBy: trackBy,
26293      getTrackByValue: getTrackByValue,
26294      getWatchables: $parse(valuesFn, function(optionValues) {
26295        // Create a collection of things that we would like to watch (watchedArray)
26296        // so that they can all be watched using a single $watchCollection
26297        // that only runs the handler once if anything changes
26298        var watchedArray = [];
26299        optionValues = optionValues || [];
26300
26301        var optionValuesKeys = getOptionValuesKeys(optionValues);
26302        var optionValuesLength = optionValuesKeys.length;
26303        for (var index = 0; index < optionValuesLength; index++) {
26304          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26305          var value = optionValues[key];
26306
26307          var locals = getLocals(optionValues[key], key);
26308          var selectValue = getTrackByValueFn(optionValues[key], locals);
26309          watchedArray.push(selectValue);
26310
26311          // Only need to watch the displayFn if there is a specific label expression
26312          if (match[2] || match[1]) {
26313            var label = displayFn(scope, locals);
26314            watchedArray.push(label);
26315          }
26316
26317          // Only need to watch the disableWhenFn if there is a specific disable expression
26318          if (match[4]) {
26319            var disableWhen = disableWhenFn(scope, locals);
26320            watchedArray.push(disableWhen);
26321          }
26322        }
26323        return watchedArray;
26324      }),
26325
26326      getOptions: function() {
26327
26328        var optionItems = [];
26329        var selectValueMap = {};
26330
26331        // The option values were already computed in the `getWatchables` fn,
26332        // which must have been called to trigger `getOptions`
26333        var optionValues = valuesFn(scope) || [];
26334        var optionValuesKeys = getOptionValuesKeys(optionValues);
26335        var optionValuesLength = optionValuesKeys.length;
26336
26337        for (var index = 0; index < optionValuesLength; index++) {
26338          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26339          var value = optionValues[key];
26340          var locals = getLocals(value, key);
26341          var viewValue = viewValueFn(scope, locals);
26342          var selectValue = getTrackByValueFn(viewValue, locals);
26343          var label = displayFn(scope, locals);
26344          var group = groupByFn(scope, locals);
26345          var disabled = disableWhenFn(scope, locals);
26346          var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26347
26348          optionItems.push(optionItem);
26349          selectValueMap[selectValue] = optionItem;
26350        }
26351
26352        return {
26353          items: optionItems,
26354          selectValueMap: selectValueMap,
26355          getOptionFromViewValue: function(value) {
26356            return selectValueMap[getTrackByValue(value)];
26357          },
26358          getViewValueFromOption: function(option) {
26359            // If the viewValue could be an object that may be mutated by the application,
26360            // we need to make a copy and not return the reference to the value on the option.
26361            return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26362          }
26363        };
26364      }
26365    };
26366  }
26367
26368
26369  // we can't just jqLite('<option>') since jqLite is not smart enough
26370  // to create it in <select> and IE barfs otherwise.
26371  var optionTemplate = document.createElement('option'),
26372      optGroupTemplate = document.createElement('optgroup');
26373
26374  return {
26375    restrict: 'A',
26376    terminal: true,
26377    require: ['select', '?ngModel'],
26378    link: function(scope, selectElement, attr, ctrls) {
26379
26380      // if ngModel is not defined, we don't need to do anything
26381      var ngModelCtrl = ctrls[1];
26382      if (!ngModelCtrl) return;
26383
26384      var selectCtrl = ctrls[0];
26385      var multiple = attr.multiple;
26386
26387      // The emptyOption allows the application developer to provide their own custom "empty"
26388      // option when the viewValue does not match any of the option values.
26389      var emptyOption;
26390      for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26391        if (children[i].value === '') {
26392          emptyOption = children.eq(i);
26393          break;
26394        }
26395      }
26396
26397      var providedEmptyOption = !!emptyOption;
26398
26399      var unknownOption = jqLite(optionTemplate.cloneNode(false));
26400      unknownOption.val('?');
26401
26402      var options;
26403      var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26404
26405
26406      var renderEmptyOption = function() {
26407        if (!providedEmptyOption) {
26408          selectElement.prepend(emptyOption);
26409        }
26410        selectElement.val('');
26411        emptyOption.prop('selected', true); // needed for IE
26412        emptyOption.attr('selected', true);
26413      };
26414
26415      var removeEmptyOption = function() {
26416        if (!providedEmptyOption) {
26417          emptyOption.remove();
26418        }
26419      };
26420
26421
26422      var renderUnknownOption = function() {
26423        selectElement.prepend(unknownOption);
26424        selectElement.val('?');
26425        unknownOption.prop('selected', true); // needed for IE
26426        unknownOption.attr('selected', true);
26427      };
26428
26429      var removeUnknownOption = function() {
26430        unknownOption.remove();
26431      };
26432
26433
26434      // Update the controller methods for multiple selectable options
26435      if (!multiple) {
26436
26437        selectCtrl.writeValue = function writeNgOptionsValue(value) {
26438          var option = options.getOptionFromViewValue(value);
26439
26440          if (option && !option.disabled) {
26441            if (selectElement[0].value !== option.selectValue) {
26442              removeUnknownOption();
26443              removeEmptyOption();
26444
26445              selectElement[0].value = option.selectValue;
26446              option.element.selected = true;
26447              option.element.setAttribute('selected', 'selected');
26448            }
26449          } else {
26450            if (value === null || providedEmptyOption) {
26451              removeUnknownOption();
26452              renderEmptyOption();
26453            } else {
26454              removeEmptyOption();
26455              renderUnknownOption();
26456            }
26457          }
26458        };
26459
26460        selectCtrl.readValue = function readNgOptionsValue() {
26461
26462          var selectedOption = options.selectValueMap[selectElement.val()];
26463
26464          if (selectedOption && !selectedOption.disabled) {
26465            removeEmptyOption();
26466            removeUnknownOption();
26467            return options.getViewValueFromOption(selectedOption);
26468          }
26469          return null;
26470        };
26471
26472        // If we are using `track by` then we must watch the tracked value on the model
26473        // since ngModel only watches for object identity change
26474        if (ngOptions.trackBy) {
26475          scope.$watch(
26476            function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
26477            function() { ngModelCtrl.$render(); }
26478          );
26479        }
26480
26481      } else {
26482
26483        ngModelCtrl.$isEmpty = function(value) {
26484          return !value || value.length === 0;
26485        };
26486
26487
26488        selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
26489          options.items.forEach(function(option) {
26490            option.element.selected = false;
26491          });
26492
26493          if (value) {
26494            value.forEach(function(item) {
26495              var option = options.getOptionFromViewValue(item);
26496              if (option && !option.disabled) option.element.selected = true;
26497            });
26498          }
26499        };
26500
26501
26502        selectCtrl.readValue = function readNgOptionsMultiple() {
26503          var selectedValues = selectElement.val() || [],
26504              selections = [];
26505
26506          forEach(selectedValues, function(value) {
26507            var option = options.selectValueMap[value];
26508            if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
26509          });
26510
26511          return selections;
26512        };
26513
26514        // If we are using `track by` then we must watch these tracked values on the model
26515        // since ngModel only watches for object identity change
26516        if (ngOptions.trackBy) {
26517
26518          scope.$watchCollection(function() {
26519            if (isArray(ngModelCtrl.$viewValue)) {
26520              return ngModelCtrl.$viewValue.map(function(value) {
26521                return ngOptions.getTrackByValue(value);
26522              });
26523            }
26524          }, function() {
26525            ngModelCtrl.$render();
26526          });
26527
26528        }
26529      }
26530
26531
26532      if (providedEmptyOption) {
26533
26534        // we need to remove it before calling selectElement.empty() because otherwise IE will
26535        // remove the label from the element. wtf?
26536        emptyOption.remove();
26537
26538        // compile the element since there might be bindings in it
26539        $compile(emptyOption)(scope);
26540
26541        // remove the class, which is added automatically because we recompile the element and it
26542        // becomes the compilation root
26543        emptyOption.removeClass('ng-scope');
26544      } else {
26545        emptyOption = jqLite(optionTemplate.cloneNode(false));
26546      }
26547
26548      // We need to do this here to ensure that the options object is defined
26549      // when we first hit it in writeNgOptionsValue
26550      updateOptions();
26551
26552      // We will re-render the option elements if the option values or labels change
26553      scope.$watchCollection(ngOptions.getWatchables, updateOptions);
26554
26555      // ------------------------------------------------------------------ //
26556
26557
26558      function updateOptionElement(option, element) {
26559        option.element = element;
26560        element.disabled = option.disabled;
26561        // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
26562        // selects in certain circumstances when multiple selects are next to each other and display
26563        // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
26564        // See https://github.com/angular/angular.js/issues/11314 for more info.
26565        // This is unfortunately untestable with unit / e2e tests
26566        if (option.label !== element.label) {
26567          element.label = option.label;
26568          element.textContent = option.label;
26569        }
26570        if (option.value !== element.value) element.value = option.selectValue;
26571      }
26572
26573      function addOrReuseElement(parent, current, type, templateElement) {
26574        var element;
26575        // Check whether we can reuse the next element
26576        if (current && lowercase(current.nodeName) === type) {
26577          // The next element is the right type so reuse it
26578          element = current;
26579        } else {
26580          // The next element is not the right type so create a new one
26581          element = templateElement.cloneNode(false);
26582          if (!current) {
26583            // There are no more elements so just append it to the select
26584            parent.appendChild(element);
26585          } else {
26586            // The next element is not a group so insert the new one
26587            parent.insertBefore(element, current);
26588          }
26589        }
26590        return element;
26591      }
26592
26593
26594      function removeExcessElements(current) {
26595        var next;
26596        while (current) {
26597          next = current.nextSibling;
26598          jqLiteRemove(current);
26599          current = next;
26600        }
26601      }
26602
26603
26604      function skipEmptyAndUnknownOptions(current) {
26605        var emptyOption_ = emptyOption && emptyOption[0];
26606        var unknownOption_ = unknownOption && unknownOption[0];
26607
26608        if (emptyOption_ || unknownOption_) {
26609          while (current &&
26610                (current === emptyOption_ ||
26611                current === unknownOption_ ||
26612                emptyOption_ && emptyOption_.nodeType === NODE_TYPE_COMMENT)) {
26613            // Empty options might have directives that transclude
26614            // and insert comments (e.g. ngIf)
26615            current = current.nextSibling;
26616          }
26617        }
26618        return current;
26619      }
26620
26621
26622      function updateOptions() {
26623
26624        var previousValue = options && selectCtrl.readValue();
26625
26626        options = ngOptions.getOptions();
26627
26628        var groupMap = {};
26629        var currentElement = selectElement[0].firstChild;
26630
26631        // Ensure that the empty option is always there if it was explicitly provided
26632        if (providedEmptyOption) {
26633          selectElement.prepend(emptyOption);
26634        }
26635
26636        currentElement = skipEmptyAndUnknownOptions(currentElement);
26637
26638        options.items.forEach(function updateOption(option) {
26639          var group;
26640          var groupElement;
26641          var optionElement;
26642
26643          if (option.group) {
26644
26645            // This option is to live in a group
26646            // See if we have already created this group
26647            group = groupMap[option.group];
26648
26649            if (!group) {
26650
26651              // We have not already created this group
26652              groupElement = addOrReuseElement(selectElement[0],
26653                                               currentElement,
26654                                               'optgroup',
26655                                               optGroupTemplate);
26656              // Move to the next element
26657              currentElement = groupElement.nextSibling;
26658
26659              // Update the label on the group element
26660              groupElement.label = option.group;
26661
26662              // Store it for use later
26663              group = groupMap[option.group] = {
26664                groupElement: groupElement,
26665                currentOptionElement: groupElement.firstChild
26666              };
26667
26668            }
26669
26670            // So now we have a group for this option we add the option to the group
26671            optionElement = addOrReuseElement(group.groupElement,
26672                                              group.currentOptionElement,
26673                                              'option',
26674                                              optionTemplate);
26675            updateOptionElement(option, optionElement);
26676            // Move to the next element
26677            group.currentOptionElement = optionElement.nextSibling;
26678
26679          } else {
26680
26681            // This option is not in a group
26682            optionElement = addOrReuseElement(selectElement[0],
26683                                              currentElement,
26684                                              'option',
26685                                              optionTemplate);
26686            updateOptionElement(option, optionElement);
26687            // Move to the next element
26688            currentElement = optionElement.nextSibling;
26689          }
26690        });
26691
26692
26693        // Now remove all excess options and group
26694        Object.keys(groupMap).forEach(function(key) {
26695          removeExcessElements(groupMap[key].currentOptionElement);
26696        });
26697        removeExcessElements(currentElement);
26698
26699        ngModelCtrl.$render();
26700
26701        // Check to see if the value has changed due to the update to the options
26702        if (!ngModelCtrl.$isEmpty(previousValue)) {
26703          var nextValue = selectCtrl.readValue();
26704          if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
26705            ngModelCtrl.$setViewValue(nextValue);
26706            ngModelCtrl.$render();
26707          }
26708        }
26709
26710      }
26711
26712    }
26713  };
26714}];
26715
26716/**
26717 * @ngdoc directive
26718 * @name ngPluralize
26719 * @restrict EA
26720 *
26721 * @description
26722 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
26723 * These rules are bundled with angular.js, but can be overridden
26724 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
26725 * by specifying the mappings between
26726 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26727 * and the strings to be displayed.
26728 *
26729 * # Plural categories and explicit number rules
26730 * There are two
26731 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26732 * in Angular's default en-US locale: "one" and "other".
26733 *
26734 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
26735 * any number that is not 1), an explicit number rule can only match one number. For example, the
26736 * explicit number rule for "3" matches the number 3. There are examples of plural categories
26737 * and explicit number rules throughout the rest of this documentation.
26738 *
26739 * # Configuring ngPluralize
26740 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
26741 * You can also provide an optional attribute, `offset`.
26742 *
26743 * The value of the `count` attribute can be either a string or an {@link guide/expression
26744 * Angular expression}; these are evaluated on the current scope for its bound value.
26745 *
26746 * The `when` attribute specifies the mappings between plural categories and the actual
26747 * string to be displayed. The value of the attribute should be a JSON object.
26748 *
26749 * The following example shows how to configure ngPluralize:
26750 *
26751 * ```html
26752 * <ng-pluralize count="personCount"
26753                 when="{'0': 'Nobody is viewing.',
26754 *                      'one': '1 person is viewing.',
26755 *                      'other': '{} people are viewing.'}">
26756 * </ng-pluralize>
26757 *```
26758 *
26759 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
26760 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
26761 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
26762 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
26763 * show "a dozen people are viewing".
26764 *
26765 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
26766 * into pluralized strings. In the previous example, Angular will replace `{}` with
26767 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
26768 * for <span ng-non-bindable>{{numberExpression}}</span>.
26769 *
26770 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
26771 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
26772 *
26773 * # Configuring ngPluralize with offset
26774 * The `offset` attribute allows further customization of pluralized text, which can result in
26775 * a better user experience. For example, instead of the message "4 people are viewing this document",
26776 * you might display "John, Kate and 2 others are viewing this document".
26777 * The offset attribute allows you to offset a number by any desired value.
26778 * Let's take a look at an example:
26779 *
26780 * ```html
26781 * <ng-pluralize count="personCount" offset=2
26782 *               when="{'0': 'Nobody is viewing.',
26783 *                      '1': '{{person1}} is viewing.',
26784 *                      '2': '{{person1}} and {{person2}} are viewing.',
26785 *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
26786 *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
26787 * </ng-pluralize>
26788 * ```
26789 *
26790 * Notice that we are still using two plural categories(one, other), but we added
26791 * three explicit number rules 0, 1 and 2.
26792 * When one person, perhaps John, views the document, "John is viewing" will be shown.
26793 * When three people view the document, no explicit number rule is found, so
26794 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
26795 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
26796 * is shown.
26797 *
26798 * Note that when you specify offsets, you must provide explicit number rules for
26799 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
26800 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
26801 * plural categories "one" and "other".
26802 *
26803 * @param {string|expression} count The variable to be bound to.
26804 * @param {string} when The mapping between plural category to its corresponding strings.
26805 * @param {number=} offset Offset to deduct from the total number.
26806 *
26807 * @example
26808    <example module="pluralizeExample">
26809      <file name="index.html">
26810        <script>
26811          angular.module('pluralizeExample', [])
26812            .controller('ExampleController', ['$scope', function($scope) {
26813              $scope.person1 = 'Igor';
26814              $scope.person2 = 'Misko';
26815              $scope.personCount = 1;
26816            }]);
26817        </script>
26818        <div ng-controller="ExampleController">
26819          <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
26820          <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
26821          <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
26822
26823          <!--- Example with simple pluralization rules for en locale --->
26824          Without Offset:
26825          <ng-pluralize count="personCount"
26826                        when="{'0': 'Nobody is viewing.',
26827                               'one': '1 person is viewing.',
26828                               'other': '{} people are viewing.'}">
26829          </ng-pluralize><br>
26830
26831          <!--- Example with offset --->
26832          With Offset(2):
26833          <ng-pluralize count="personCount" offset=2
26834                        when="{'0': 'Nobody is viewing.',
26835                               '1': '{{person1}} is viewing.',
26836                               '2': '{{person1}} and {{person2}} are viewing.',
26837                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
26838                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
26839          </ng-pluralize>
26840        </div>
26841      </file>
26842      <file name="protractor.js" type="protractor">
26843        it('should show correct pluralized string', function() {
26844          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
26845          var withOffset = element.all(by.css('ng-pluralize')).get(1);
26846          var countInput = element(by.model('personCount'));
26847
26848          expect(withoutOffset.getText()).toEqual('1 person is viewing.');
26849          expect(withOffset.getText()).toEqual('Igor is viewing.');
26850
26851          countInput.clear();
26852          countInput.sendKeys('0');
26853
26854          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
26855          expect(withOffset.getText()).toEqual('Nobody is viewing.');
26856
26857          countInput.clear();
26858          countInput.sendKeys('2');
26859
26860          expect(withoutOffset.getText()).toEqual('2 people are viewing.');
26861          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
26862
26863          countInput.clear();
26864          countInput.sendKeys('3');
26865
26866          expect(withoutOffset.getText()).toEqual('3 people are viewing.');
26867          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
26868
26869          countInput.clear();
26870          countInput.sendKeys('4');
26871
26872          expect(withoutOffset.getText()).toEqual('4 people are viewing.');
26873          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
26874        });
26875        it('should show data-bound names', function() {
26876          var withOffset = element.all(by.css('ng-pluralize')).get(1);
26877          var personCount = element(by.model('personCount'));
26878          var person1 = element(by.model('person1'));
26879          var person2 = element(by.model('person2'));
26880          personCount.clear();
26881          personCount.sendKeys('4');
26882          person1.clear();
26883          person1.sendKeys('Di');
26884          person2.clear();
26885          person2.sendKeys('Vojta');
26886          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
26887        });
26888      </file>
26889    </example>
26890 */
26891var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
26892  var BRACE = /{}/g,
26893      IS_WHEN = /^when(Minus)?(.+)$/;
26894
26895  return {
26896    link: function(scope, element, attr) {
26897      var numberExp = attr.count,
26898          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
26899          offset = attr.offset || 0,
26900          whens = scope.$eval(whenExp) || {},
26901          whensExpFns = {},
26902          startSymbol = $interpolate.startSymbol(),
26903          endSymbol = $interpolate.endSymbol(),
26904          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
26905          watchRemover = angular.noop,
26906          lastCount;
26907
vendor: 11,613 bytes, lines 26908-27161
26908      forEach(attr, function(expression, attributeName) {
26909        var tmpMatch = IS_WHEN.exec(attributeName);
26910        if (tmpMatch) {
26911          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
26912          whens[whenKey] = element.attr(attr.$attr[attributeName]);
26913        }
26914      });
26915      forEach(whens, function(expression, key) {
26916        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
26917
26918      });
26919
26920      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
26921        var count = parseFloat(newVal);
26922        var countIsNaN = isNaN(count);
26923
26924        if (!countIsNaN && !(count in whens)) {
26925          // If an explicit number rule such as 1, 2, 3... is defined, just use it.
26926          // Otherwise, check it against pluralization rules in $locale service.
26927          count = $locale.pluralCat(count - offset);
26928        }
26929
26930        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
26931        // In JS `NaN !== NaN`, so we have to exlicitly check.
26932        if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
26933          watchRemover();
26934          var whenExpFn = whensExpFns[count];
26935          if (isUndefined(whenExpFn)) {
26936            if (newVal != null) {
26937              $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
26938            }
26939            watchRemover = noop;
26940            updateElementText();
26941          } else {
26942            watchRemover = scope.$watch(whenExpFn, updateElementText);
26943          }
26944          lastCount = count;
26945        }
26946      });
26947
26948      function updateElementText(newText) {
26949        element.text(newText || '');
26950      }
26951    }
26952  };
26953}];
26954
26955/**
26956 * @ngdoc directive
26957 * @name ngRepeat
26958 * @multiElement
26959 *
26960 * @description
26961 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
26962 * instance gets its own scope, where the given loop variable is set to the current collection item,
26963 * and `$index` is set to the item index or key.
26964 *
26965 * Special properties are exposed on the local scope of each template instance, including:
26966 *
26967 * | Variable  | Type            | Details                                                                     |
26968 * |-----------|-----------------|-----------------------------------------------------------------------------|
26969 * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
26970 * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
26971 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
26972 * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
26973 * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
26974 * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
26975 *
26976 * <div class="alert alert-info">
26977 *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
26978 *   This may be useful when, for instance, nesting ngRepeats.
26979 * </div>
26980 *
26981 *
26982 * # Iterating over object properties
26983 *
26984 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
26985 * syntax:
26986 *
26987 * ```js
26988 * <div ng-repeat="(key, value) in myObj"> ... </div>
26989 * ```
26990 *
26991 * You need to be aware that the JavaScript specification does not define the order of keys
26992 * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
26993 * used to sort the keys alphabetically.)
26994 *
26995 * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
26996 * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
26997 * keys in the order in which they were defined, although there are exceptions when keys are deleted
26998 * and reinstated. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_issues
26999 *
27000 * If this is not desired, the recommended workaround is to convert your object into an array
27001 * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
27002 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27003 * or implement a `$watch` on the object yourself.
27004 *
27005 *
27006 * # Tracking and Duplicates
27007 *
27008 * When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM:
27009 *
27010 * * When an item is added, a new instance of the template is added to the DOM.
27011 * * When an item is removed, its template instance is removed from the DOM.
27012 * * When items are reordered, their respective templates are reordered in the DOM.
27013 *
27014 * By default, `ngRepeat` does not allow duplicate items in arrays. This is because when
27015 * there are duplicates, it is not possible to maintain a one-to-one mapping between collection
27016 * items and DOM elements.
27017 *
27018 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27019 * with your own using the `track by` expression.
27020 *
27021 * For example, you may track items by the index of each item in the collection, using the
27022 * special scope property `$index`:
27023 * ```html
27024 *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27025 *      {{n}}
27026 *    </div>
27027 * ```
27028 *
27029 * You may use arbitrary expressions in `track by`, including references to custom functions
27030 * on the scope:
27031 * ```html
27032 *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27033 *      {{n}}
27034 *    </div>
27035 * ```
27036 *
27037 * If you are working with objects that have an identifier property, you can track
27038 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27039 * will not have to rebuild the DOM elements for items it has already rendered, even if the
27040 * JavaScript objects in the collection have been substituted for new ones:
27041 * ```html
27042 *    <div ng-repeat="model in collection track by model.id">
27043 *      {{model.name}}
27044 *    </div>
27045 * ```
27046 *
27047 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
27048 * `$id` function, which tracks items by their identity:
27049 * ```html
27050 *    <div ng-repeat="obj in collection track by $id(obj)">
27051 *      {{obj.prop}}
27052 *    </div>
27053 * ```
27054 *
27055 * <div class="alert alert-warning">
27056 * **Note:** `track by` must always be the last expression:
27057 * </div>
27058 * ```
27059 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
27060 *     {{model.name}}
27061 * </div>
27062 * ```
27063 *
27064 * # Special repeat start and end points
27065 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
27066 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
27067 * 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)
27068 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
27069 *
27070 * The example below makes use of this feature:
27071 * ```html
27072 *   <header ng-repeat-start="item in items">
27073 *     Header {{ item }}
27074 *   </header>
27075 *   <div class="body">
27076 *     Body {{ item }}
27077 *   </div>
27078 *   <footer ng-repeat-end>
27079 *     Footer {{ item }}
27080 *   </footer>
27081 * ```
27082 *
27083 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
27084 * ```html
27085 *   <header>
27086 *     Header A
27087 *   </header>
27088 *   <div class="body">
27089 *     Body A
27090 *   </div>
27091 *   <footer>
27092 *     Footer A
27093 *   </footer>
27094 *   <header>
27095 *     Header B
27096 *   </header>
27097 *   <div class="body">
27098 *     Body B
27099 *   </div>
27100 *   <footer>
27101 *     Footer B
27102 *   </footer>
27103 * ```
27104 *
27105 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
27106 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
27107 *
27108 * @animations
27109 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
27110 *
27111 * **.leave** - when an item is removed from the list or when an item is filtered out
27112 *
27113 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
27114 *
27115 * @element ANY
27116 * @scope
27117 * @priority 1000
27118 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
27119 *   formats are currently supported:
27120 *
27121 *   * `variable in expression` – where variable is the user defined loop variable and `expression`
27122 *     is a scope expression giving the collection to enumerate.
27123 *
27124 *     For example: `album in artist.albums`.
27125 *
27126 *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
27127 *     and `expression` is the scope expression giving the collection to enumerate.
27128 *
27129 *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
27130 *
27131 *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
27132 *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
27133 *     is specified, ng-repeat associates elements by identity. It is an error to have
27134 *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
27135 *     mapped to the same DOM element, which is not possible.)
27136 *
27137 *     Note that the tracking expression must come last, after any filters, and the alias expression.
27138 *
27139 *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
27140 *     will be associated by item identity in the array.
27141 *
27142 *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
27143 *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
27144 *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
27145 *     element in the same way in the DOM.
27146 *
27147 *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
27148 *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
27149 *     property is same.
27150 *
27151 *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
27152 *     to items in conjunction with a tracking expression.
27153 *
27154 *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
27155 *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
27156 *     when a filter is active on the repeater, but the filtered result set is empty.
27157 *
27158 *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
27159 *     the items have been processed through the filter.
27160 *
27161 *     Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax s
27161o it can be used only at the end
27162 *     (and not as operator, inside an expression).
27163 *
27164 *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
27165 *
27166 * @example
27167 * This example initializes the scope to a list of names and
27168 * then uses `ngRepeat` to display every person:
27169  <example module="ngAnimate" deps="angular-animate.js" animations="true">
27170    <file name="index.html">
27171      <div ng-init="friends = [
27172        {name:'John', age:25, gender:'boy'},
27173        {name:'Jessie', age:30, gender:'girl'},
27174        {name:'Johanna', age:28, gender:'girl'},
27175        {name:'Joy', age:15, gender:'girl'},
27176        {name:'Mary', age:28, gender:'girl'},
27177        {name:'Peter', age:95, gender:'boy'},
27178        {name:'Sebastian', age:50, gender:'boy'},
27179        {name:'Erika', age:27, gender:'girl'},
27180        {name:'Patrick', age:40, gender:'boy'},
27181        {name:'Samantha', age:60, gender:'girl'}
27182      ]">
27183        I have {{friends.length}} friends. They are:
27184        <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
27185        <ul class="example-animate-container">
27186          <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
27187            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
27188          </li>
27189          <li class="animate-repeat" ng-if="results.length == 0">
27190            <strong>No results found...</strong>
27191          </li>
27192        </ul>
27193      </div>
27194    </file>
27195    <file name="animations.css">
27196      .example-animate-container {
27197        background:white;
27198        border:1px solid black;
27199        list-style:none;
27200        margin:0;
27201        padding:0 10px;
27202      }
27203
27204      .animate-repeat {
27205        line-height:40px;
27206        list-style:none;
27207        box-sizing:border-box;
27208      }
27209
27210      .animate-repeat.ng-move,
27211      .animate-repeat.ng-enter,
27212      .animate-repeat.ng-leave {
27213        transition:all linear 0.5s;
27214      }
27215
27216      .animate-repeat.ng-leave.ng-leave-active,
27217      .animate-repeat.ng-move,
27218      .animate-repeat.ng-enter {
27219        opacity:0;
27220        max-height:0;
27221      }
27222
27223      .animate-repeat.ng-leave,
27224      .animate-repeat.ng-move.ng-move-active,
27225      .animate-repeat.ng-enter.ng-enter-active {
27226        opacity:1;
27227        max-height:40px;
27228      }
27229    </file>
27230    <file name="protractor.js" type="protractor">
27231      var friends = element.all(by.repeater('friend in friends'));
27232
27233      it('should render initial data set', function() {
27234        expect(friends.count()).toBe(10);
27235        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
27236        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
27237        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
27238        expect(element(by.binding('friends.length')).getText())
27239            .toMatch("I have 10 friends. They are:");
27240      });
27241
27242       it('should update repeater when filter predicate changes', function() {
27243         expect(friends.count()).toBe(10);
27244
27245         element(by.model('q')).sendKeys('ma');
27246
27247         expect(friends.count()).toBe(2);
27248         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
27249         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
27250       });
27251      </file>
27252    </example>
27253 */
27254var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27255  var NG_REMOVED = '$$NG_REMOVED';
27256  var ngRepeatMinErr = minErr('ngRepeat');
27257
27258  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
27259    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
27260    scope[valueIdentifier] = value;
27261    if (keyIdentifier) scope[keyIdentifier] = key;
27262    scope.$index = index;
27263    scope.$first = (index === 0);
27264    scope.$last = (index === (arrayLength - 1));
27265    scope.$middle = !(scope.$first || scope.$last);
27266    // jshint bitwise: false
27267    scope.$odd = !(scope.$even = (index&1) === 0);
27268    // jshint bitwise: true
27269  };
27270
27271  var getBlockStart = function(block) {
27272    return block.clone[0];
27273  };
27274
27275  var getBlockEnd = function(block) {
27276    return block.clone[block.clone.length - 1];
27277  };
27278
27279
27280  return {
27281    restrict: 'A',
27282    multiElement: true,
27283    transclude: 'element',
27284    priority: 1000,
27285    terminal: true,
27286    $$tlb: true,
27287    compile: function ngRepeatCompile($element, $attr) {
27288      var expression = $attr.ngRepeat;
27289      var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
27290
27291      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*$/);
27292
27293      if (!match) {
27294        throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
27295            expression);
27296      }
27297
27298      var lhs = match[1];
27299      var rhs = match[2];
27300      var aliasAs = match[3];
27301      var trackByExp = match[4];
27302
27303      match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
27304
27305      if (!match) {
27306        throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
27307            lhs);
27308      }
27309      var valueIdentifier = match[3] || match[1];
27310      var keyIdentifier = match[2];
27311
27312      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
27313          /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
27314        throw ngRepeatMinErr('badident', "alias '{0}
vendor: 12,206 bytes, lines 27314-27586
27314' is invalid --- must be a valid JS identifier which is not a reserved name.",
27315          aliasAs);
27316      }
27317
27318      var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
27319      var hashFnLocals = {$id: hashKey};
27320
27321      if (trackByExp) {
27322        trackByExpGetter = $parse(trackByExp);
27323      } else {
27324        trackByIdArrayFn = function(key, value) {
27325          return hashKey(value);
27326        };
27327        trackByIdObjFn = function(key) {
27328          return key;
27329        };
27330      }
27331
27332      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
27333
27334        if (trackByExpGetter) {
27335          trackByIdExpFn = function(key, value, index) {
27336            // assign key, value, and $index to the locals so that they can be used in hash functions
27337            if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27338            hashFnLocals[valueIdentifier] = value;
27339            hashFnLocals.$index = index;
27340            return trackByExpGetter($scope, hashFnLocals);
27341          };
27342        }
27343
27344        // Store a list of elements from previous run. This is a hash where key is the item from the
27345        // iterator, and the value is objects with following properties.
27346        //   - scope: bound scope
27347        //   - element: previous element.
27348        //   - index: position
27349        //
27350        // We are using no-proto object so that we don't need to guard against inherited props via
27351        // hasOwnProperty.
27352        var lastBlockMap = createMap();
27353
27354        //watch props
27355        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27356          var index, length,
27357              previousNode = $element[0],     // node that cloned nodes should be inserted after
27358                                              // initialized to the comment node anchor
27359              nextNode,
27360              // Same as lastBlockMap but it has the current state. It will become the
27361              // lastBlockMap on the next iteration.
27362              nextBlockMap = createMap(),
27363              collectionLength,
27364              key, value, // key/value of iteration
27365              trackById,
27366              trackByIdFn,
27367              collectionKeys,
27368              block,       // last object information {scope, element, id}
27369              nextBlockOrder,
27370              elementsToRemove;
27371
27372          if (aliasAs) {
27373            $scope[aliasAs] = collection;
27374          }
27375
27376          if (isArrayLike(collection)) {
27377            collectionKeys = collection;
27378            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
27379          } else {
27380            trackByIdFn = trackByIdExpFn || trackByIdObjFn;
27381            // if object, extract keys, in enumeration order, unsorted
27382            collectionKeys = [];
27383            for (var itemKey in collection) {
27384              if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
27385                collectionKeys.push(itemKey);
27386              }
27387            }
27388          }
27389
27390          collectionLength = collectionKeys.length;
27391          nextBlockOrder = new Array(collectionLength);
27392
27393          // locate existing items
27394          for (index = 0; index < collectionLength; index++) {
27395            key = (collection === collectionKeys) ? index : collectionKeys[index];
27396            value = collection[key];
27397            trackById = trackByIdFn(key, value, index);
27398            if (lastBlockMap[trackById]) {
27399              // found previously seen block
27400              block = lastBlockMap[trackById];
27401              delete lastBlockMap[trackById];
27402              nextBlockMap[trackById] = block;
27403              nextBlockOrder[index] = block;
27404            } else if (nextBlockMap[trackById]) {
27405              // if collision detected. restore lastBlockMap and throw an error
27406              forEach(nextBlockOrder, function(block) {
27407                if (block && block.scope) lastBlockMap[block.id] = block;
27408              });
27409              throw ngRepeatMinErr('dupes',
27410                  "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
27411                  expression, trackById, value);
27412            } else {
27413              // new never before seen block
27414              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
27415              nextBlockMap[trackById] = true;
27416            }
27417          }
27418
27419          // remove leftover items
27420          for (var blockKey in lastBlockMap) {
27421            block = lastBlockMap[blockKey];
27422            elementsToRemove = getBlockNodes(block.clone);
27423            $animate.leave(elementsToRemove);
27424            if (elementsToRemove[0].parentNode) {
27425              // if the element was not removed yet because of pending animation, mark it as deleted
27426              // so that we can ignore it later
27427              for (index = 0, length = elementsToRemove.length; index < length; index++) {
27428                elementsToRemove[index][NG_REMOVED] = true;
27429              }
27430            }
27431            block.scope.$destroy();
27432          }
27433
27434          // we are not using forEach for perf reasons (trying to avoid #call)
27435          for (index = 0; index < collectionLength; index++) {
27436            key = (collection === collectionKeys) ? index : collectionKeys[index];
27437            value = collection[key];
27438            block = nextBlockOrder[index];
27439
27440            if (block.scope) {
27441              // if we have already seen this object, then we need to reuse the
27442              // associated scope/element
27443
27444              nextNode = previousNode;
27445
27446              // skip nodes that are already pending removal via leave animation
27447              do {
27448                nextNode = nextNode.nextSibling;
27449              } while (nextNode && nextNode[NG_REMOVED]);
27450
27451              if (getBlockStart(block) != nextNode) {
27452                // existing item which got moved
27453                $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
27454              }
27455              previousNode = getBlockEnd(block);
27456              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27457            } else {
27458              // new item which we don't know about
27459              $transclude(function ngRepeatTransclude(clone, scope) {
27460                block.scope = scope;
27461                // http://jsperf.com/clone-vs-createcomment
27462                var endNode = ngRepeatEndComment.cloneNode(false);
27463                clone[clone.length++] = endNode;
27464
27465                // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
27466                $animate.enter(clone, null, jqLite(previousNode));
27467                previousNode = endNode;
27468                // Note: We only need the first/last node of the cloned nodes.
27469                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27470                // by a directive with templateUrl when its template arrives.
27471                block.clone = clone;
27472                nextBlockMap[block.id] = block;
27473                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27474              });
27475            }
27476          }
27477          lastBlockMap = nextBlockMap;
27478        });
27479      };
27480    }
27481  };
27482}];
27483
27484var NG_HIDE_CLASS = 'ng-hide';
27485var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
27486/**
27487 * @ngdoc directive
27488 * @name ngShow
27489 * @multiElement
27490 *
27491 * @description
27492 * The `ngShow` directive shows or hides the given HTML element based on the expression
27493 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
27494 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27495 * in AngularJS and sets the display style to none (using an !important flag).
27496 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27497 *
27498 * ```html
27499 * <!-- when $scope.myValue is truthy (element is visible) -->
27500 * <div ng-show="myValue"></div>
27501 *
27502 * <!-- when $scope.myValue is falsy (element is hidden) -->
27503 * <div ng-show="myValue" class="ng-hide"></div>
27504 * ```
27505 *
27506 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
27507 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
27508 * from the element causing the element not to appear hidden.
27509 *
27510 * ## Why is !important used?
27511 *
27512 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27513 * can be easily overridden by heavier selectors. For example, something as simple
27514 * as changing the display style on a HTML list item would make hidden elements appear visible.
27515 * This also becomes a bigger issue when dealing with CSS frameworks.
27516 *
27517 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27518 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27519 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27520 *
27521 * ### Overriding `.ng-hide`
27522 *
27523 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27524 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27525 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
27526 * with extra animation classes that can be added.
27527 *
27528 * ```css
27529 * .ng-hide:not(.ng-hide-animate) {
27530 *   /&#42; this is just another form of hiding an element &#42;/
27531 *   display: block!important;
27532 *   position: absolute;
27533 *   top: -9999px;
27534 *   left: -9999px;
27535 * }
27536 * ```
27537 *
27538 * By default you don't need to override in CSS anything and the animations will work around the display style.
27539 *
27540 * ## A note about animations with `ngShow`
27541 *
27542 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27543 * is true and false. This system works like the animation system present with ngClass except that
27544 * you must also include the !important flag to override the display property
27545 * so that you can perform an animation when the element is hidden during the time of the animation.
27546 *
27547 * ```css
27548 * //
27549 * //a working example can be found at the bottom of this page
27550 * //
27551 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27552 *   /&#42; this is required as of 1.3x to properly
27553 *      apply all styling in a show/hide animation &#42;/
27554 *   transition: 0s linear all;
27555 * }
27556 *
27557 * .my-element.ng-hide-add-active,
27558 * .my-element.ng-hide-remove-active {
27559 *   /&#42; the transition is defined in the active class &#42;/
27560 *   transition: 1s linear all;
27561 * }
27562 *
27563 * .my-element.ng-hide-add { ... }
27564 * .my-element.ng-hide-add.ng-hide-add-active { ... }
27565 * .my-element.ng-hide-remove { ... }
27566 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27567 * ```
27568 *
27569 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27570 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27571 *
27572 * @animations
27573 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
27574 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
27575 *
27576 * @element ANY
27577 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
27578 *     then the element is shown or hidden respectively.
27579 *
27580 * @example
27581  <example module="ngAnimate" deps="angular-animate.js" animations="true">
27582    <file name="index.html">
27583      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
27584      <div>
27585        Show:
27586        <div class="check-element animate-show" ng-show="checked">
27587          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27588        </div>
27589      </div>
27590      <div>
27591        Hide:
27592        <div class="check-element animate-show" ng-hide="checked">
27593          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27594        </div>
27595      </div>
27596    </file>
27597    <file name="glyphicons.css">
27598      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27599    </file>
27600    <file name="animations.css">
27601      .animate-show {
27602        line-height: 20px;
27603        opacity: 1;
27604        padding: 10px;
27605        border: 1px solid black;
27606        background: white;
27607      }
27608
27609      .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
27610        transition: all linear 0.5s;
27611      }
27612
27613      .animate-show.ng-hide {
27614        line-height: 0;
27615        opacity: 0;
27616        padding: 0 10px;
27617      }
27618
27619      .check-element {
27620        padding: 10px;
27621        border: 1px solid black;
27622        background: white;
27623      }
27624    </file>
27625    <file name="protractor.js" type="protractor">
27626      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27627      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27628
27629      it('should check ng-show / ng-hide', function() {
27630        expect(thumbsUp.isDisplayed()).toBeFalsy();
27631        expect(thumbsDown.isDisplayed()).toBeTruthy();
27632
27633        element(by.model('checked')).click();
27634
27635        expect(thumbsUp.isDisplayed()).toBeTruthy();
27636        expect(thumbsDown.isDisplayed()).toBeFalsy();
27637      });
27638    </file>
27639  </example>
27640 */
27641var ngShowDirective = ['$animate', function($animate) {
27642  return {
27643    restrict: 'A',
27644    multiElement: true,
27645    link: function(scope, element, attr) {
27646      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
27647        // we're adding a temporary, animation-specific class for ng-hide since this way
27648        // we can control when the element is actually displayed on screen without having
27649        // to have a global/greedy CSS selector that breaks when other animations are run.
27650        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
27651        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
27652          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27653        });
27654      });
27655    }
27656  };
27657}];
27658
27659
27660/**
27661 * @ngdoc directive
27662 * @name ngHide
27663 * @multiElement
27664 *
27665 * @description
27666 * The `ngHide` directive shows or hides the given HTML element based on the expression
27667 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
27668 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27669 * in AngularJS and sets the display style to none (using an !important flag).
27670 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27671 *
27672 * ```html
27673 * <!-- when $scope.myValue is truthy (element is hidden) -->
27674 * <div ng-hide="myValue" class="ng-hide"></div>
27675 *
27676 * <!-- when $scope.myValue is falsy (element is visible) -->
27677 * <div ng-hide="myValue"></div>
27678 * ```
27679 *
27680 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
27681 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
27682 * from the element causing the element not to appear hidden.
27683 *
27684 * ## Why is !important used?
27685 *
27686 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27687 * can be easily overridden by heavier selectors. For example, something as simple
27688 * as changing the display style on a HTML list item would make hidden elements appear visible.
27689 * This also becomes a bigger issue when dealing with CSS frameworks.
27690 *
27691 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27692 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27693 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27694 *
27695 * ### Overriding `.ng-hide`
27696 *
27697 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27698 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27699 * class in CSS:
27700 *
27701 * ```css
27702 * .ng-hide {
27703 *   /&#42; this is just another form of hiding an element &#42;/
27704 *   display: block!important;
27705 *   position: absolute;
27706 *   top: -9999px;
27707 *   left: -9999px;
27708 * }
27709 * ```
27710 *
27711 * By default you don't need to override in CSS anything and the animations will work around the display style.
27712 *
27713 * ## A note about animations with `ngHide`
27714 *
27715 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27716 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
27717 * CSS class is added and removed for you instead of your own CSS class.
27718 *
27719 * ```css
27720 * //
27721 * //a working example can be found at the bottom of this page
27722 * //
27723 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27724 *   transition: 0.5s linear all;
27725 * }
27726 *
27727 * .my-element.ng-hide-add { ... }
27728 * .my-element.ng-hide-add.ng-hide-add-active { ... }
27729 * .my-element.ng-hide-remove { ... }
27730 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27731 * ```
27732 *
27733 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27734 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27735 *
27736 * @animations
27737 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
27738 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
27739 *
27740 * @element ANY
27741 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
27742 *     the element is shown or hidden respectively.
27743 *
27744 * @example
27745  <example module="ngAnimate" deps="angular-animate.js" animations="true">
27746    <file name="index.html">
27747      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
27748      <div>
27749        Show:
27750        <div class="check-element animate-hide" ng-show="checked">
27751          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27752        </div>
27753      </div>
27754      <div>
27755        Hide:
27756        <div class="check-element animate-hide" ng-hide="checked">
27757          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27758        </div>
27759      </div>
27760    </file>
27761    <file name="glyphicons.css">
27762      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27763    </file>
27764    <file name="animations.css">
27765      .animate-hide {
27766        transition: all linear 0.5s;
27767        line-height: 20px;
27768        opacity: 1;
27769        padding: 10px;
27770        border: 1px solid black;
27771        background: white;
27772      }
27773
27774      .animate-hide.ng-hide {
27775        line-height: 0;
27776        opacity: 0;
27777        padding: 0 10px;
27778      }
27779
27780      .check-element {
27781        padding: 10px;
27782        border: 1px solid black;
27783        background: white;
27784      }
27785    </file>
27786    <file name="protractor.js" type="protractor">
27787      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27788      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27789
27790      it('should check ng-show / ng-hide', function() {
27791        expect(thumbsUp.isDisplayed()).toBeFalsy();
27792        expect(thumbsDown.isDisplayed()).toBeTruthy();
27793
27794        element(by.model('checked')).click();
27795
27796        expect(thumbsUp.isDisplayed()).toBeTruthy();
27797        expect(thumbsDown.isDisplayed()).toBeFalsy();
27798      });
27799    </file>
27800  </example>
27801 */
27802var ngHideDirective = ['$animate', function($animate) {
27803  return {
27804    restrict: 'A',
27805    multiElement: true,
27806    link: function(scope, element, attr) {
27807      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
27808        // The comment inside of the ngShowDirective explains why we add and
27809        // remove a temporary class for the show/hide animation
27810        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
27811          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27812        });
27813      });
27814    }
27815  };
27816}];
27817
27818/**
27819 * @ngdoc directive
27820 * @name ngStyle
27821 * @restrict AC
27822 *
27823 * @description
27824 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
27825 *
27826 * @element ANY
27827 * @param {expression} ngStyle
27828 *
27829 * {@link guide/expression Expression} which evals to an
27830 * object whose keys are CSS style names and values are corresponding values for those CSS
27831 * keys.
27832 *
27833 * Since some CSS style names are not valid keys for an object, they must be quoted.
27834 * See the 'background-color' style in the example below.
27835 *
27836 * @example
27837   <example>
27838     <file name="index.html">
27839        <input type="button" value="set color" ng-click="myStyle={color:'red'}">
27840        <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
27841        <input type="button" value="clear" ng-click="myStyle={}">
27842        <br/>
27843        <span ng-style="myStyle">Sample Text</span>
27844        <pre>myStyle={{myStyle}}</pre>
27845     </file>
27846     <file name="style.css">
27847       span {
27848         color: black;
27849       }
27850     </file>
27851     <file name="protractor.js" type="protractor">
27852       var colorSpan = element(by.css('span'));
27853
27854       it('should check ng-style', function() {
27855         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
27856         element(by.css('input[value=\'set color\']')).click();
27857         expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
27858         element(by.css('input[value=clear]')).click();
27859         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
27860       });
27861     </file>
27862   </example>
27863 */
27864var ngStyleDirective = ngDirective(function(scope, element, attr) {
27865  scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
27866    if (oldStyles && (newStyles !== oldStyles)) {
27867      forEach(oldStyles, function(val, style) { element.css(style, '');});
27868    }
27869    if (newStyles) element.css(newStyles);
27870  }, true);
27871});
27872
27873/**
27874 * @ngdoc directive
27875 * @name ngSwitch
27876 * @restrict EA
27877 *
27878 * @description
27879 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
27880 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
27881 * as specified in the template.
27882 *
27883 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
27884 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
27885 * matches the value obtained from the evaluated expression. In other words, you define a container element
27886 * (where you place the directive), place an expression on the **`on="..."` attribute**
27887 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
27888 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
27889 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
27890 * attribute is displayed.
27891 *
27892 * <div class="alert alert-info">
27893 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
27894 * as literal string values to match against.
27895 * For example, **`ng-switch-when="someVal"`** will match against the string `"s
27895omeVal"` not against the
27896 * value of the expression `$scope.someVal`.
27897 * </div>
27898
27899 * @animations
27900 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
27901 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
27902 *
27903 * @usage
27904 *
27905 * ```
27906 * <ANY ng-switch="expression">
27907 *   <ANY ng-switch-when="matchValue1">...</ANY>
27908 *   <ANY ng-switch-when="matchValue2">...</ANY>
27909 *   <ANY ng-switch-default>...</ANY>
27910 * </ANY>
27911 * ```
27912 *
27913 *
27914 * @scope
27915 * @priority 1200
27916 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
27917 * On child elements add:
27918 *
27919 * * `ngSwitchWhen`: the case statement to match against. If match then this
27920 *   case will be displayed. If the same match appears multiple times, all the
27921 *   elements will be displayed.
27922 * * `ngSwitchDefault`: the default case when no other case match. If there
27923 *   are multiple default cases, all of them will be displayed when no other
27924 *   case match.
27925 *
27926 *
27927 * @example
27928  <example module="switchExample" deps="angular-animate.js" animations="true">
27929    <file name="index.html">
27930      <div ng-controller="ExampleController">
27931        <select ng-model="selection" ng-options="item for item in items">
27932        </select>
27933        <code>selection={{selection}}</code>
27934        <hr/>
27935        <div class="animate-switch-container"
27936          ng-switch on="selection">
27937            <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
27938            <div class="animate-switch" ng-switch-when="home">Home Span</div>
27939            <div class="animate-switch" ng-switch-default>default</div>
27940        </div>
27941      </div>
27942    </file>
27943    <file name="script.js">
27944      angular.module('switchExample', ['ngAnimate'])
27945        .controller('ExampleController', ['$scope', function($scope) {
27946          $scope.items = ['settings', 'home', 'other'];
27947          $scope.selection = $scope.items[0];
27948        }]);
27949    </file>
27950    <file name="animations.css">
27951      .animate-switch-container {
27952        position:relative;
27953        background:white;
27954        border:1px solid black;
27955        height:40px;
27956        overflow:hidden;
27957      }
27958
27959      .animate-switch {
27960        padding:10px;
27961      }
27962
27963      .animate-switch.ng-animate {
27964        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
27965
27966        position:absolute;
27967        top:0;
27968        left:0;
27969        right:0;
27970        bottom:0;
27971      }
27972
27973      .animate-switch.ng-leave.ng-leave-active,
27974      .animate-switch.ng-enter {
27975        top:-50px;
27976      }
27977      .animate-switch.ng-leave,
27978      .animate-switch.ng-enter.ng-enter-active {
27979        top:0;
27980      }
27981    </file>
27982    <file name="protractor.js" type="protractor">
27983      var switchElem = element(by.css('[ng-switch]'));
27984      var select = element(by.model('selection'));
27985
27986      it('should start in settings', function() {
27987        expect(switchElem.getText()).toMatch(/Settings Div/);
27988      });
27989      it('should change to home', function() {
27990        select.all(by.css('option')).get(1).click();
27991        expect(switchElem.getText()).toMatch(/Home Span/);
27992      });
27993      it('should select default', function() {
27994        select.all(by.css('option')).get(2).click();
27995        expect(switchElem.getText()).toMatch(/default/);
27996      });
27997    </file>
27998  </example>
27999 */
28000var ngSwitchDirective = ['$animate', function($animate) {
28001  return {
28002    require: 'ngSwitch',
28003
28004    // asks for $scope to fool the BC controller module
28005    controller: ['$scope', function ngSwitchController() {
28006     this.cases = {};
28007    }],
28008    link: function(scope, element, attr, ngSwitchController) {
28009      var watchExpr = attr.ngSwitch || attr.on,
28010          selectedTranscludes = [],
28011          selectedElements = [],
28012          previousLeaveAnimations = [],
28013          selectedScopes = [];
28014
28015      var spliceFactory = function(array, index) {
28016          return function() { array.splice(index, 1); };
28017      };
28018
28019      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28020        var i, ii;
28021        for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28022          $animate.cancel(previousLeaveAnimations[i]);
28023        }
28024        previousLeaveAnimations.length = 0;
28025
28026        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28027          var selected = getBlockNodes(selectedElements[i].clone);
28028          selectedScopes[i].$destroy();
28029          var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28030          promise.then(spliceFactory(previousLeaveAnimations, i));
28031        }
28032
28033        selectedElements.length = 0;
28034        selectedScopes.length = 0;
28035
28036        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
28037          forEach(selectedTranscludes, function(selectedTransclude) {
28038            selectedTransclude.transclude(function(caseElement, selectedScope) {
28039              selectedScopes.push(selectedScope);
28040              var anchor = selectedTransclude.element;
28041              caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
28042              var block = { clone: caseElement };
28043
28044              selectedElements.push(block);
28045              $animate.enter(caseElement, anchor.parent(), anchor);
28046            });
28047          });
28048        }
28049      });
28050    }
28051  };
28052}];
28053
28054var ngSwitchWhenDirective = ngDirective({
28055  transclude: 'element',
28056  priority: 1200,
28057  require: '^ngSwitch',
28058  multiElement: true,
28059  link: function(scope, element, attrs, ctrl, $transclude) {
28060    ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
28061    ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
28062  }
28063});
28064
28065var ngSwitchDefaultDirective = ngDirective({
28066  transclude: 'element',
28067  priority: 1200,
28068  require: '^ngSwitch',
28069  multiElement: true,
28070  link: function(scope, element, attr, ctrl, $transclude) {
28071    ctrl.cases['?'] = (ctrl.cases['?'] || []);
28072    ctrl.cases['?'].push({ transclude: $transclude, element: element });
28073   }
28074});
28075
28076/**
28077 * @ngdoc directive
28078 * @name ngTransclude
28079 * @restrict EAC
28080 *
28081 * @description
28082 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
28083 *
28084 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
28085 *
28086 * @element ANY
28087 *
28088 * @example
28089   <example module="transcludeExample">
28090     <file name="index.html">
28091       <script>
28092         angular.module('transcludeExample', [])
28093          .directive('pane', function(){
28094             return {
28095               restrict: 'E',
28096               transclude: true,
28097               scope: { title:'@' },
28098               template: '<div style="border: 1px solid black;">' +
28099                           '<div style="background-color: gray">{{title}}</div>' +
28100                           '<ng-transclude></ng-transclude>' +
28101                         '</div>'
28102             };
28103         })
28104         .controller('ExampleController', ['$scope', function($scope) {
28105           $scope.title = 'Lorem Ipsum';
28106           $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
28107         }]);
28108       </script>
28109       <div ng-controller="ExampleController">
28110         <input ng-model="title" aria-label="title"> <br/>
28111         <textarea ng-model="text" aria-label="text"></textarea> <br/>
28112         <pane title="{{title}}">{{text}}</pane>
28113       </div>
28114     </file>
28115     <file name="protractor.js" type="protractor">
28116        it('should have transcluded', function() {
28117          var titleElement = element(by.model('title'));
28118          titleElement.clear();
28119          titleElement.sendKeys('TITLE');
28120          var textElement = element(by.model('text'));
28121          textElement.clear();
28122          textElement.sendKeys('TEXT');
28123          expect(element(by.binding('title')).getText()).toEqual('TITLE');
28124          expect(element(by.binding('text')).getText()).toEqual('TEXT');
28125        });
28126     </file>
28127   </example>
28128 *
28129 */
28130var ngTranscludeDirective = ngDirective({
28131  restrict: 'EAC',
28132  link: function($scope, $element, $attrs, controller, $transclude) {
28133    if (!$transclude) {
28134      throw minErr('ngTransclude')('orphan',
28135       'Illegal use of ngTransclude directive in the template! ' +
28136       'No parent directive that requires a transclusion found. ' +
28137       'Element: {0}',
28138       startingTag($element));
28139    }
28140
28141    $transclude(function(clone) {
28142      $element.empty();
28143      $element.append(clone);
28144    });
28145  }
28146});
28147
28148/**
28149 * @ngdoc directive
28150 * @name script
28151 * @restrict E
28152 *
28153 * @description
28154 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
28155 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
28156 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
28157 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
28158 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
28159 *
28160 * @param {string} type Must be set to `'text/ng-template'`.
28161 * @param {string} id Cache name of the template.
28162 *
28163 * @example
28164  <example>
28165    <file name="index.html">
28166      <script type="text/ng-template" id="/tpl.html">
28167        Content of the template.
28168      </script>
28169
28170      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
28171      <div id="tpl-content" ng-include src="currentTpl"></div>
28172    </file>
28173    <file name="protractor.js" type="protractor">
28174      it('should load template defined inside script tag', function() {
28175        element(by.css('#tpl-link')).click();
28176        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
28177      });
28178    </file>
28179  </example>
28180 */
28181var scriptDirective = ['$templateCache', function($templateCache) {
28182  return {
28183    restrict: 'E',
28184    terminal: true,
28185    compile: function(element, attr) {
28186      if (attr.type == 'text/ng-template') {
28187        var templateUrl = attr.id,
28188            text = element[0].text;
28189
28190        $templateCache.put(templateUrl, text);
28191      }
28192    }
28193  };
28194}];
28195
28196var noopNgModelController = { $setViewValue: noop, $render: noop };
28197
28198/**
28199 * @ngdoc type
28200 * @name  select.SelectController
28201 * @description
28202 * The controller for the `<select>` directive. This provides support for reading
28203 * and writing the selected value(s) of the control and also coordinates dynamically
28204 * added `<option>` elements, perhaps by an `ngRepeat` directive.
28205 */
28206var SelectController =
28207        ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
28208
28209  var self = this,
28210      optionsMap = new HashMap();
28211
28212  // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
28213  self.ngModelCtrl = noopNgModelController;
28214
28215  // The "unknown" option is one that is prepended to the list if the viewValue
28216  // does not match any of the options. When it is rendered the value of the unknown
28217  // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
28218  //
28219  // We can't just jqLite('<option>') since jqLite is not smart enough
28220  // to create it in <select> and IE barfs otherwise.
28221  self.unknownOption = jqLite(document.createElement('option'));
28222  self.renderUnknownOption = function(val) {
28223    var unknownVal = '? ' + hashKey(val) + ' ?';
28224    self.unknownOption.val(unknownVal);
28225    $element.prepend(self.unknownOption);
28226    $element.val(unknownVal);
28227  };
28228
28229  $scope.$on('$destroy', function() {
28230    // disable unknown option so that we don't do work when the whole select is being destroyed
28231    self.renderUnknownOption = noop;
28232  });
28233
28234  self.removeUnknownOption = function() {
28235    if (self.unknownOption.parent()) self.unknownOption.remove();
28236  };
28237
28238
28239  // Read the value of the select control, the implementation of this changes depending
28240  // upon whether the select can have multiple values and whether ngOptions is at work.
28241  self.readValue = function readSingleValue() {
28242    self.removeUnknownOption();
28243    return $element.val();
28244  };
28245
28246
28247  // Write the value to the select control, the implementation of this changes depending
28248  // upon whether the select can have multiple values and whether ngOptions is at work.
28249  self.writeValue = function writeSingleValue(value) {
28250    if (self.hasOption(value)) {
28251      self.removeUnknownOption();
28252      $element.val(value);
28253      if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
28254    } else {
28255      if (value == null && self.emptyOption) {
28256        self.removeUnknownOption();
28257        $element.val('');
28258      } else {
28259        self.renderUnknownOption(value);
28260      }
28261    }
28262  };
28263
28264
28265  // Tell the select control that an option, with the given value, has been added
28266  self.addOption = function(value, element) {
28267    assertNotHasOwnProperty(value, '"option value"');
28268    if (value === '') {
28269      self.emptyOption = element;
28270    }
28271    var count = optionsMap.get(value) || 0;
28272    optionsMap.put(value, count + 1);
28273  };
28274
28275  // Tell the select control that an option, with the given value, has been removed
28276  self.removeOption = function(value) {
28277    var count = optionsMap.get(value);
28278    if (count) {
28279      if (count === 1) {
28280        optionsMap.remove(value);
28281        if (value === '') {
28282          self.emptyOption = undefined;
28283        }
28284      } else {
28285        optionsMap.put(value, count - 1);
28286      }
28287    }
28288  };
28289
28290  // Check whether the select control has an option matching the given value
28291  self.hasOption = function(value) {
28292    return !!optionsMap.get(value);
28293  };
28294}];
28295
28296/**
28297 * @ngdoc directive
28298 * @name select
28299 * @restrict E
28300 *
28301 * @description
28302 * HTML `SELECT` element with angular data-binding.
28303 *
28304 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28305 * between the scope and the `<select>` control (including setting default values).
28306 * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28307 * {@link ngOptions `ngOptions`} directives.
28308 *
28309 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28310 * to the model identified by the `ngModel` directive. With static or repeated options, this is
28311 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28312 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28313 *
28314 * <div class="alert alert-warning">
28315 * Note that the value of a `select` directive used without `ngOptions` is always a string.
28316 * When the model needs to be bound to a non-string value, you must either explictly convert it
28317 * using a directive (see example below) or use `ngOptions` to specify the set of options.
28318 * This is because an option element can only be bound to string values at present.
28319 * </div>
28320 *
28321 * If the viewValue of `ngModel` does not match any of the options, then the control
28322 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28323 *
28324 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28325 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28326 * option. See example below for demonstration.
28327 *
28328 * <div class="alert alert-info">
28329 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28330 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benef
28330its, such as
28331 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28332 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
28333 * a new scope for each repeated instance.
28334 * </div>
28335 *
28336 *
28337 * @param {string} ngModel Assignable angular expression to data-bind to.
28338 * @param {string=} name Property name of the form under which the control is published.
28339 * @param {string=} required Sets `required` validation error key if the value is not entered.
28340 * @param {string=} ngRequired Adds required attribute and required validation constraint to
28341 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
28342 * when you want to data-bind to the required attribute.
28343 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
28344 *    interaction with the select element.
28345 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
28346 * set on the model on selection. See {@link ngOptions `ngOptions`}.
28347 *
28348 * @example
28349 * ### Simple `select` elements with static options
28350 *
28351 * <example name="static-select" module="staticSelect">
28352 * <file name="index.html">
28353 * <div ng-controller="ExampleController">
28354 *   <form name="myForm">
28355 *     <label for="singleSelect"> Single select: </label><br>
28356 *     <select name="singleSelect" ng-model="data.singleSelect">
28357 *       <option value="option-1">Option 1</option>
28358 *       <option value="option-2">Option 2</option>
28359 *     </select><br>
28360 *
28361 *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
28362 *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
28363 *       <option value="">---Please select---</option> <!-- not selected / blank option -->
28364 *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
28365 *       <option value="option-2">Option 2</option>
28366 *     </select><br>
28367 *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
28368 *     <tt>singleSelect = {{data.singleSelect}}</tt>
28369 *
28370 *     <hr>
28371 *     <label for="multipleSelect"> Multiple select: </label><br>
28372 *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
28373 *       <option value="option-1">Option 1</option>
28374 *       <option value="option-2">Option 2</option>
28375 *       <option value="option-3">Option 3</option>
28376 *     </select><br>
28377 *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
28378 *   </form>
28379 * </div>
28380 * </file>
28381 * <file name="app.js">
28382 *  angular.module('staticSelect', [])
28383 *    .controller('ExampleController', ['$scope', function($scope) {
28384 *      $scope.data = {
28385 *       singleSelect: null,
28386 *       multipleSelect: [],
28387 *       option1: 'option-1',
28388 *      };
28389 *
28390 *      $scope.forceUnknownOption = function() {
28391 *        $scope.data.singleSelect = 'nonsense';
28392 *      };
28393 *   }]);
28394 * </file>
28395 *</example>
28396 *
28397 * ### Using `ngRepeat` to generate `select` options
28398 * <example name="ngrepeat-select" module="ngrepeatSelect">
28399 * <file name="index.html">
28400 * <div ng-controller="ExampleController">
28401 *   <form name="myForm">
28402 *     <label for="repeatSelect"> Repeat select: </label>
28403 *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
28404 *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
28405 *     </select>
28406 *   </form>
28407 *   <hr>
28408 *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
28409 * </div>
28410 * </file>
28411 * <file name="app.js">
28412 *  angular.module('ngrepeatSelect', [])
28413 *    .controller('ExampleController', ['$scope', function($scope) {
28414 *      $scope.data = {
28415 *       repeatSelect: null,
28416 *       availableOptions: [
28417 *         {id: '1', name: 'Option A'},
28418 *         {id: '2', name: 'Option B'},
28419 *         {id: '3', name: 'Option C'}
28420 *       ],
28421 *      };
28422 *   }]);
28423 * </file>
28424 *</example>
28425 *
28426 *
28427 * ### Using `select` with `ngOptions` and setting a default value
28428 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
28429 *
28430 * <example name="select-with-default-values" module="defaultValueSelect">
28431 * <file name="index.html">
28432 * <div ng-controller="ExampleController">
28433 *   <form name="myForm">
28434 *     <label for="mySelect">Make a choice:</label>
28435 *     <select name="mySelect" id="mySelect"
28436 *       ng-options="option.name for option in data.availableOptions track by option.id"
28437 *       ng-model="data.selectedOption"></select>
28438 *   </form>
28439 *   <hr>
28440 *   <tt>option = {{data.selectedOption}}</tt><br/>
28441 * </div>
28442 * </file>
28443 * <file name="app.js">
28444 *  angular.module('defaultValueSelect', [])
28445 *    .controller('ExampleController', ['$scope', function($scope) {
28446 *      $scope.data = {
28447 *       availableOptions: [
28448 *         {id: '1', name: 'Option A'},
28449 *         {id: '2', name: 'Option B'},
28450 *         {id: '3', name: 'Option C'}
28451 *       ],
28452 *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
28453 *       };
28454 *   }]);
28455 * </file>
28456 *</example>
28457 *
28458 *
28459 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
28460 *
28461 * <example name="select-with-non-string-options" module="nonStringSelect">
28462 *   <file name="index.html">
28463 *     <select ng-model="model.id" convert-to-number>
28464 *       <option value="0">Zero</option>
28465 *       <option value="1">One</option>
28466 *       <option value="2">Two</option>
28467 *     </select>
28468 *     {{ model }}
28469 *   </file>
28470 *   <file name="app.js">
28471 *     angular.module('nonStringSelect', [])
28472 *       .run(function($rootScope) {
28473 *         $rootScope.model = { id: 2 };
28474 *       })
28475 *       .directive('convertToNumber', function() {
28476 *         return {
28477 *           require: 'ngModel',
28478 *           link: function(scope, element, attrs, ngModel) {
28479 *             ngModel.$parsers.push(function(val) {
28480 *               return parseInt(val, 10);
28481 *             });
28482 *             ngModel.$formatters.push(function(val) {
28483 *               return '' + val;
28484 *             });
28485 *           }
28486 *         };
28487 *       });
28488 *   </file>
28489 *   <file name="protractor.js" type="protractor">
28490 *     it('should initialize to model', function() {
28491 *       var select = element(by.css('select'));
28492 *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
28493 *     });
28494 *   </file>
28495 * </example>
28496 *
28497 */
28498var selectDirective = function() {
28499
28500  return {
28501    restrict: 'E',
28502    require: ['select', '?ngModel'],
28503    controller: SelectController,
28504    link: function(scope, element, attr, ctrls) {
28505
28506      // if ngModel is not defined, we don't need to do anything
28507      var ngModelCtrl = ctrls[1];
28508      if (!ngModelCtrl) return;
28509
28510      var selectCtrl = ctrls[0];
28511
28512      selectCtrl.ngModelCtrl = ngModelCtrl;
28513
28514      // We delegate rendering to the `writeValue` method, which can be changed
28515      // if the select can have multiple selected values or if the options are being
28516      // generated by `ngOptions`
28517      ngModelCtrl.$render = function() {
28518        selectCtrl.writeValue(ngModelCtrl.$viewValue);
28519      };
28520
28521      // When the selected item(s) changes we delegate getting the value of the select control
28522      // to the `readValue` method, which can be changed if the select can have multiple
28523      // selected values or if the options are being generated by `ngOptions`
28524      element.on('change', function() {
28525        scope.$apply(function() {
28526          ngModelCtrl.$setViewValue(selectCtrl.readValue());
28527        });
28528      });
28529
28530      // If the select allows multiple values then we need to modify how we read and write
28531      // values from and to the control; also what it means for the value to be empty and
28532      // we have to add an extra watch since ngModel doesn't work well with arrays - it
28533      // doesn't trigger rendering if only an item in the array changes.
28534      if (attr.multiple) {
28535
28536        // Read value now needs to check each option to see if it is selected
28537        selectCtrl.readValue = function readMultipleValue() {
28538          var array = [];
28539          forEach(element.find('option'), function(option) {
28540            if (option.selected) {
28541              array.push(option.value);
28542            }
28543          });
28544          return array;
28545        };
28546
28547        // Write value now needs to set the selected property of each matching option
28548        selectCtrl.writeValue = function writeMultipleValue(value) {
28549          var items = new HashMap(value);
28550          forEach(element.find('option'), function(option) {
28551            option.selected = isDefined(items.get(option.value));
28552          });
28553        };
28554
28555        // we have to do it on each watch since ngModel watches reference, but
28556        // we need to work of an array, so we need to see if anything was inserted/removed
28557        var lastView, lastViewRef = NaN;
28558        scope.$watch(function selectMultipleWatch() {
28559          if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
28560            lastView = shallowCopy(ngModelCtrl.$viewValue);
28561            ngModelCtrl.$render();
28562          }
28563          lastViewRef = ngModelCtrl.$viewValue;
28564        });
28565
28566        // If we are a multiple select then value is now a collection
28567        // so the meaning of $isEmpty changes
28568        ngModelCtrl.$isEmpty = function(value) {
28569          return !value || value.length === 0;
28570        };
28571
28572      }
28573    }
28574  };
28575};
28576
28577
28578// The option directive is purely designed to communicate the existence (or lack of)
28579// of dynamically created (and destroyed) option elements to their containing select
28580// directive via its controller.
28581var optionDirective = ['$interpolate', function($interpolate) {
28582
28583  function chromeHack(optionElement) {
28584    // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28585    // Adding an <option selected="selected"> element to a <select required="required"> should
28586    // automatically select the new element
28587    if (optionElement[0].hasAttribute('selected')) {
28588      optionElement[0].selected = true;
28589    }
28590  }
28591
28592  return {
28593    restrict: 'E',
28594    priority: 100,
28595    compile: function(element, attr) {
28596
28597      if (isDefined(attr.value)) {
28598        // If the value attribute is defined, check if it contains an interpolation
28599        var valueInterpolated = $interpolate(attr.value, true);
28600      } else {
28601        // If the value attribute is not defined then we fall back to the
28602        // text content of the option element, which may be interpolated
28603        var interpolateFn = $interpolate(element.text(), true);
28604        if (!interpolateFn) {
28605          attr.$set('value', element.text());
28606        }
28607      }
28608
28609      return function(scope, element, attr) {
28610
28611        // This is an optimization over using ^^ since we don't want to have to search
28612        // all the way to the root of the DOM for every single option element
28613        var selectCtrlName = '$selectController',
28614            parent = element.parent(),
28615            selectCtrl = parent.data(selectCtrlName) ||
28616              parent.parent().data(selectCtrlName); // in case we are in optgroup
28617
28618        function addOption(optionValue) {
28619          selectCtrl.addOption(optionValue, element);
28620          selectCtrl.ngModelCtrl.$render();
28621          chromeHack(element);
28622        }
28623
28624        // Only update trigger option updates if this is an option within a `select`
28625        // that also has `ngModel` attached
28626        if (selectCtrl && selectCtrl.ngModelCtrl) {
28627
28628          if (valueInterpolated) {
28629            // The value attribute is interpolated
28630            var oldVal;
28631            attr.$observe('value', function valueAttributeObserveAction(newVal) {
28632              if (isDefined(oldVal)) {
28633                selectCtrl.removeOption(oldVal);
28634              }
28635              oldVal = newVal;
28636              addOption(newVal);
28637            });
28638          } else if (interpolateFn) {
28639            // The text content is interpolated
28640            scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
28641              attr.$set('value', newVal);
28642              if (oldVal !== newVal) {
28643                selectCtrl.removeOption(oldVal);
28644              }
28645              addOption(newVal);
28646            });
28647          } else {
28648            // The value attribute is static
28649            addOption(attr.value);
28650          }
28651
28652          element.on('$destroy', function() {
28653            selectCtrl.removeOption(attr.value);
28654            selectCtrl.ngModelCtrl.$render();
28655          });
28656        }
28657      };
28658    }
28659  };
28660}];
28661
28662var styleDirective = valueFn({
28663  restrict: 'E',
28664  terminal: false
28665});
28666
28667var requiredDirective = function() {
28668  return {
28669    restrict: 'A',
28670    require: '?ngModel',
28671    link: function(scope, elm, attr, ctrl) {
28672      if (!ctrl) return;
28673      attr.required = true; // force truthy in case we are on non input element
28674
28675      ctrl.$validators.required = function(modelValue, viewValue) {
28676        return !attr.required || !ctrl.$isEmpty(viewValue);
28677      };
28678
28679      attr.$observe('required', function() {
28680        ctrl.$validate();
28681      });
28682    }
28683  };
28684};
28685
28686
28687var patternDirective = function() {
28688  return {
28689    restrict: 'A',
28690    require: '?ngModel',
28691    link: function(scope, elm, attr, ctrl) {
28692      if (!ctrl) return;
28693
28694      var regexp, patternExp = attr.ngPattern || attr.pattern;
28695      attr.$observe('pattern', function(regex) {
28696        if (isString(regex) && regex.length > 0) {
28697          regex = new RegExp('^' + regex + '$');
28698        }
28699
28700        if (regex && !regex.test) {
28701          throw minErr('ngPattern')('noregexp',
28702            'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
28703            regex, startingTag(elm));
28704        }
28705
28706        regexp = regex || undefined;
28707        ctrl.$validate();
28708      });
28709
28710      ctrl.$validators.pattern = function(modelValue, viewValue) {
28711        // HTML5 pattern constraint validates the input value, so we validate the viewValue
28712        return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
28713      };
28714    }
28715  };
28716};
28717
28718
28719var maxlengthDirective = function() {
28720  return {
28721    restrict: 'A',
28722    require: '?ngModel',
28723    link: function(scope, elm, attr, ctrl) {
28724      if (!ctrl) return;
28725
28726      var maxlength = -1;
28727      attr.$observe('maxlength', function(value) {
28728        var intVal = toInt(value);
28729        maxlength = isNaN(intVal) ? -1 : intVal;
28730        ctrl.$validate();
28731      });
28732      ctrl.$validators.maxlength = function(modelValue, viewValue) {
28733        return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
28734      };
28735    }
28736  };
28737};
28738
28739var minlengthDirective = function() {
28740  return {
28741    restrict: 'A',
28742    require: '?ngModel',
28743    link: function(scope, elm, attr, ctrl) {
28744      if (!ctrl) return;
28745
28746      var minlength = 0;
28747      attr.$observe('minlength', function(value) {
28748        minlength = toInt(value) || 0;
28749        ctrl.$validate();
28750      });
28751      ctrl.$validators.minlength = function(modelValue, viewValue) {
28752        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
28753      };
28754    }
28755  };
28756};
28757
28758if (window.angular.bootstrap) {
28759  //AngularJS is already loaded, so we can return here...
28760  console.log('WARNING: Tried to load angular more than once.');
28761  return;
28762}
28763
28764//try to bind to jquery now so that one can write jqLite(document).ready()
28765//but we will rebind on bootstrap again.
28766bindJQuery();
28767
28768publishExternalAPI(angular);
28769
28770angular.module("ngLocale", [], ["$provide", function($provide) {
28771var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
28772function getDecimals(n) {
28773  n = n + '';
28774  var i = n.indexOf('.');
28775  return (i == -1) ? 0 : n.length - i - 1;
28776}
28777
28778function getVF(n, opt_precision) {
28779  var v = opt_precision;
28780
28781  if (undefined === v) {
28782    v = Math.min(getDecimals(n), 3);
28783  }
28784
28785  var base = Math.pow(10, v);
28786  var f = ((n * base) | 0) % base;
28787  return {v: v, f: f};
28788}
28789
28790$provide.value("$locale", {
28791  "DATETIME_FORMATS": {
28792    "AMPMS": [
28793      "AM",
28794      "PM"
28795    ],
28796    "DAY": [
28797      "Sunday",
28798      "Monday",
28799      "Tuesday",
28800      "Wednesday",
28801      "Thursday",
28802      "Friday",
28803      "Saturday"
28804    ],
28805    "ERANAMES": [
28806      "Before Christ",
28807      "Anno Domini"
28808    ],
28809    "ERAS": [
28810      "BC",
28811      "AD"
28812    ],
28813    "FIRSTDAYOFWEEK": 6,
28814    "MONTH": [
28815      "January",
28816      "February",
28817      "March",
28818      "April",
28819      "May",
28820      "June",
28821      "July",
28822      "August",
28823      "September",
28824      "October",
28825      "November",
28826      "December"
28827    ],
28828    "SHORTDAY": [
28829      "Sun",
28830      "Mon",
28831      "Tue",
28832      "Wed",
28833      "Thu",
28834      "Fri",
28835      "Sat"
28836    ],
28837    "SHORTMONTH": [
28838      "Jan",
28839      "Feb",
28840      "Mar",
28841      "Apr",
28842      "May",
28843      "Jun",
28844      "Jul",
28845      "Aug",
28846      "Sep",
28847      "Oct",
28848      "Nov",
28849      "Dec"
28850    ],
28851    "WEEKENDRANGE": [
28852      5,
28853      6
28854    ],
28855    "fullDate": "EEEE, MMMM d, y",
28856    "longDate": "MMMM d, y",
28857    "medium": "MMM d, y h:mm:ss a",
28858    "mediumDate": "MMM d, y",
28859    "mediumTime": "h:mm:ss a",
28860    "short": "M/d/yy h:mm a",
28861    "shortDate": "M/d/yy",
28862    "shortTime": "h:mm a"
28863  },
28864  "NUMBER_FORMATS": {
28865    "CURRENCY_SYM": "$",
28866    "DECIMAL_SEP": ".",
28867    "GROUP_SEP": ",",
28868    "PATTERNS": [
28869      {
28870        "gSize": 3,
28871        "lgSize": 3,
28872        "maxFrac": 3,
28873        "minFrac": 0,
28874        "minInt": 1,
28875        "negPre": "-",
28876        "negSuf": "",
28877        "posPre": "",
28878        "posSuf": ""
28879      },
28880      {
28881        "gSize": 3,
28882        "lgSize": 3,
28883        "maxFrac": 2,
28884        "minFrac": 2,
28885        "minInt": 1,
28886        "negPre": "-\u00a4",
28887        "negSuf": "",
28888        "posPre": "\u00a4",
28889        "posSuf": ""
28890      }
28891    ]
28892  },
28893  "id": "en-us",
28894  "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;}
28895});
28896}]);
28897
28898  jqLite(document).ready(function() {
28899    angularInit(document, bootstrap);
28900  });
28901
28902})(window, document);
28903
28904!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.