PageSourceSearch

https://app.nefiks.si/angular/core/1.4.1/angular.js

js nefiks.si collected 2026-10-01 13:05:00 UTC 1,038,340 bytes, 28,294 lines download raw bytes

1/**
2 * @license AngularJS v1.4.1
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.1/' +
61      (module ? module + '/' : '') + code;
62
63    for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
64      message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
65        encodeURIComponent(toDebugString(templateArgs[i]));
66    }
67
68    return new ErrorConstructor(message);
69  };
70}
71
72/* We need to tell jshint what variables are being exported */
73/* global angular: true,
74  msie: true,
75  jqLite: true,
76  jQuery: true,
77  slice: true,
78  splice: true,
79  push: true,
80  toString: true,
81  ngMinErr: true,
82  angularModule: true,
83  uid: true,
84  REGEX_STRING_REGEXP: true,
85  VALIDITY_STATE_PROPERTY: true,
86
87  lowercase: true,
88  uppercase: true,
89  manualLowercase: true,
90  manualUppercase: true,
91  nodeName_: true,
92  isArrayLike: true,
93  forEach: true,
94  forEachSorted: true,
95  reverseParams: true,
96  nextUid: true,
97  setHashKey: true,
98  extend: true,
99  toInt: true,
100  inherit: true,
101  merge: true,
102  noop: true,
103  identity: true,
104  valueFn: true,
105  isUndefined: true,
106  isDefined: true,
107  isObject: true,
108  isBlankObject: true,
109  isString: true,
110  isNumber: true,
111  isDate: true,
112  isArray: true,
113  isFunction: true,
114  isRegExp: true,
115  isWindow: true,
116  isScope: true,
117  isFile: true,
118  isFormData: true,
119  isBlob: true,
120  isBoolean: true,
121  isPromiseLike: true,
122  trim: true,
123  escapeForRegexp: true,
124  isElement: true,
125  makeMap: true,
126  includes: true,
127  arrayRemove: true,
128  copy: true,
129  shallowCopy: true,
130  equals: true,
131  csp: true,
132  jq: true,
133  concat: true,
134  sliceArgs: true,
135  bind: true,
136  toJsonReplacer: true,
137  toJson: true,
138  fromJson: true,
139  convertTimezoneToLocal: true,
140  timezoneToOffset: true,
141  startingTag: true,
142  tryDecodeURIComponent: true,
143  parseKeyValue: true,
144  toKeyValue: true,
145  encodeUriSegment: true,
146  encodeUriQuery: true,
147  angularInit: true,
148  bootstrap: true,
149  getTestability: true,
150  snake_case: true,
151  bindJQuery: true,
152  assertArg: true,
153  assertArgFn: true,
154  assertNotHasOwnProperty: true,
155  getter: true,
156  getBlockNodes: true,
157  hasOwnProperty: true,
158  createMap: true,
159
160  NODE_TYPE_ELEMENT: true,
161  NODE_TYPE_ATTRIBUTE: true,
162  NODE_TYPE_TEXT: true,
163  NODE_TYPE_COMMENT: true,
164  NODE_TYPE_DOCUMENT: true,
165  NODE_TYPE_DOCUMENT_FRAGMENT: true,
166*/
167
168////////////////////////////////////
169
170/**
171 * @ngdoc module
172 * @name ng
173 * @module ng
174 * @description
175 *
176 * # ng (core module)
177 * The ng module is loaded by default when an AngularJS application is started. The module itself
178 * contains the essential components for an AngularJS application to function. The table below
179 * lists a high level breakdown of each of the services/factories, filters, directives and testing
180 * components available within this core module.
181 *
182 * <div doc-module-components="ng"></div>
183 */
184
185var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
186
187// The name of a form control's ValidityState property.
188// This is used so that it's possible for internal tests to create mock ValidityStates.
189var VALIDITY_STATE_PROPERTY = 'validity';
190
191/**
192 * @ngdoc function
193 * @name angular.lowercase
194 * @module ng
195 * @kind function
196 *
197 * @description Converts the specified string to lowercase.
198 * @param {string} string String to be converted to lowercase.
199 * @returns {string} Lowercased string.
200 */
201var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
202var hasOwnProperty = Object.prototype.hasOwnProperty;
203
204/**
205 * @ngdoc function
206 * @name angular.uppercase
207 * @module ng
208 * @kind function
209 *
210 * @description Converts the specified string to uppercase.
211 * @param {string} string String to be converted to uppercase.
212 * @returns {string} Uppercased string.
213 */
214var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
215
216
217var manualLowercase = function(s) {
218  /* jshint bitwise: false */
219  return isString(s)
220      ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(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    }
vendor: 11,067 bytes, lines 339-804
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} 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 (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
425        baseExtend(dst[key], [src], true);
426      } else {
427        dst[key] = src;
428      }
429    }
430  }
431
432  setHashKey(dst, h);
433  return dst;
434}
435
436/**
437 * @ngdoc function
438 * @name angular.extend
439 * @module ng
440 * @kind function
441 *
442 * @description
443 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
444 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
445 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
446 *
447 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
448 * {@link angular.merge} for this.
449 *
450 * @param {Object} dst Destination object.
451 * @param {...Object} src Source object(s).
452 * @returns {Object} Reference to `dst`.
453 */
454function extend(dst) {
455  return baseExtend(dst, slice.call(arguments, 1), false);
456}
457
458
459/**
460* @ngdoc function
461* @name angular.merge
462* @module ng
463* @kind function
464*
465* @description
466* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
467* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
468* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
469*
470* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
471* objects, performing a deep copy.
472*
473* @param {Object} dst Destination object.
474* @param {...Object} src Source object(s).
475* @returns {Object} Reference to `dst`.
476*/
477function merge(dst) {
478  return baseExtend(dst, slice.call(arguments, 1), true);
479}
480
481
482
483function toInt(str) {
484  return parseInt(str, 10);
485}
486
487
488function inherit(parent, extra) {
489  return extend(Object.create(parent), extra);
490}
491
492/**
493 * @ngdoc function
494 * @name angular.noop
495 * @module ng
496 * @kind function
497 *
498 * @description
499 * A function that performs no operations. This function can be useful when writing code in the
500 * functional style.
501   ```js
502     function foo(callback) {
503       var result = calculateResult();
504       (callback || angular.noop)(result);
505     }
506   ```
507 */
508function noop() {}
509noop.$inject = [];
510
511
512/**
513 * @ngdoc function
514 * @name angular.identity
515 * @module ng
516 * @kind function
517 *
518 * @description
519 * A function that returns its first argument. This function is useful when writing code in the
520 * functional style.
521 *
522   ```js
523     function transformer(transformationFn, value) {
524       return (transformationFn || angular.identity)(value);
525     };
526   ```
527  * @param {*} value to be returned.
528  * @returns {*} the value passed in.
529 */
530function identity($) {return $;}
531identity.$inject = [];
532
533
534function valueFn(value) {return function() {return value;};}
535
536/**
537 * @ngdoc function
538 * @name angular.isUndefined
539 * @module ng
540 * @kind function
541 *
542 * @description
543 * Determines if a reference is undefined.
544 *
545 * @param {*} value Reference to check.
546 * @returns {boolean} True if `value` is undefined.
547 */
548function isUndefined(value) {return typeof value === 'undefined';}
549
550
551/**
552 * @ngdoc function
553 * @name angular.isDefined
554 * @module ng
555 * @kind function
556 *
557 * @description
558 * Determines if a reference is defined.
559 *
560 * @param {*} value Reference to check.
561 * @returns {boolean} True if `value` is defined.
562 */
563function isDefined(value) {return typeof value !== 'undefined';}
564
565
566/**
567 * @ngdoc function
568 * @name angular.isObject
569 * @module ng
570 * @kind function
571 *
572 * @description
573 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
574 * considered to be objects. Note that JavaScript arrays are objects.
575 *
576 * @param {*} value Reference to check.
577 * @returns {boolean} True if `value` is an `Object` but not `null`.
578 */
579function isObject(value) {
580  // http://jsperf.com/isobject4
581  return value !== null && typeof value === 'object';
582}
583
584
585/**
586 * Determine if a value is an object with a null prototype
587 *
588 * @returns {boolean} True if `value` is an `Object` with a null prototype
589 */
590function isBlankObject(value) {
591  return value !== null && typeof value === 'object' && !getPrototypeOf(value);
592}
593
594
595/**
596 * @ngdoc function
597 * @name angular.isString
598 * @module ng
599 * @kind function
600 *
601 * @description
602 * Determines if a reference is a `String`.
603 *
604 * @param {*} value Reference to check.
605 * @returns {boolean} True if `value` is a `String`.
606 */
607function isString(value) {return typeof value === 'string';}
608
609
610/**
611 * @ngdoc function
612 * @name angular.isNumber
613 * @module ng
614 * @kind function
615 *
616 * @description
617 * Determines if a reference is a `Number`.
618 *
619 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
620 *
621 * If you wish to exclude these then you can use the native
622 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
623 * method.
624 *
625 * @param {*} value Reference to check.
626 * @returns {boolean} True if `value` is a `Number`.
627 */
628function isNumber(value) {return typeof value === 'number';}
629
630
631/**
632 * @ngdoc function
633 * @name angular.isDate
634 * @module ng
635 * @kind function
636 *
637 * @description
638 * Determines if a value is a date.
639 *
640 * @param {*} value Reference to check.
641 * @returns {boolean} True if `value` is a `Date`.
642 */
643function isDate(value) {
644  return toString.call(value) === '[object Date]';
645}
646
647
648/**
649 * @ngdoc function
650 * @name angular.isArray
651 * @module ng
652 * @kind function
653 *
654 * @description
655 * Determines if a reference is an `Array`.
656 *
657 * @param {*} value Reference to check.
658 * @returns {boolean} True if `value` is an `Array`.
659 */
660var isArray = Array.isArray;
661
662/**
663 * @ngdoc function
664 * @name angular.isFunction
665 * @module ng
666 * @kind function
667 *
668 * @description
669 * Determines if a reference is a `Function`.
670 *
671 * @param {*} value Reference to check.
672 * @returns {boolean} True if `value` is a `Function`.
673 */
674function isFunction(value) {return typeof value === 'function';}
675
676
677/**
678 * Determines if a value is a regular expression object.
679 *
680 * @private
681 * @param {*} value Reference to check.
682 * @returns {boolean} True if `value` is a `RegExp`.
683 */
684function isRegExp(value) {
685  return toString.call(value) === '[object RegExp]';
686}
687
688
689/**
690 * Checks if `obj` is a window object.
691 *
692 * @private
693 * @param {*} obj Object to check
694 * @returns {boolean} True if `obj` is a window obj.
695 */
696function isWindow(obj) {
697  return obj && obj.window === obj;
698}
699
700
701function isScope(obj) {
702  return obj && obj.$evalAsync && obj.$watch;
703}
704
705
706function isFile(obj) {
707  return toString.call(obj) === '[object File]';
708}
709
710
711function isFormData(obj) {
712  return toString.call(obj) === '[object FormData]';
713}
714
715
716function isBlob(obj) {
717  return toString.call(obj) === '[object Blob]';
718}
719
720
721function isBoolean(value) {
722  return typeof value === 'boolean';
723}
724
725
726function isPromiseLike(obj) {
727  return obj && isFunction(obj.then);
728}
729
730
731var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/;
732function isTypedArray(value) {
733  return TYPED_ARRAY_REGEXP.test(toString.call(value));
734}
735
736
737var trim = function(value) {
738  return isString(value) ? value.trim() : value;
739};
740
741// Copied from:
742// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
743// Prereq: s is a string.
744var escapeForRegexp = function(s) {
745  return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
746           replace(/\x08/g, '\\x08');
747};
748
749
750/**
751 * @ngdoc function
752 * @name angular.isElement
753 * @module ng
754 * @kind function
755 *
756 * @description
757 * Determines if a reference is a DOM element (or wrapped jQuery element).
758 *
759 * @param {*} value Reference to check.
760 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
761 */
762function isElement(node) {
763  return !!(node &&
764    (node.nodeName  // we are a direct element
765    || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
766}
767
768/**
769 * @param str 'key1,key2,...'
770 * @returns {object} in the form of {key1:true, key2:true, ...}
771 */
772function makeMap(str) {
773  var obj = {}, items = str.split(","), i;
774  for (i = 0; i < items.length; i++) {
775    obj[items[i]] = true;
776  }
777  return obj;
778}
779
780
781function nodeName_(element) {
782  return lowercase(element.nodeName || (element[0] && element[0].nodeName));
783}
784
785function includes(array, obj) {
786  return Array.prototype.indexOf.call(array, obj) != -1;
787}
788
789function arrayRemove(array, value) {
790  var index = array.indexOf(value);
791  if (index >= 0) {
792    array.splice(index, 1);
793  }
794  return index;
795}
796
797/**
798 * @ngdoc function
799 * @name angular.copy
800 * @module ng
801 * @kind function
802 *
803 * @description
804 * Creates a deep copy of `source`, which should be an object or an array.
805 *
806 * * If no destination is supplied, a copy of the object or array is created.
807 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
808 *   are deleted and then all elements/properties from the source are copied to it.
809 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
810 * * If `source` is identical to 'destination' an exception will be thrown.
811 *
812 * @param {*} source The source that will be used to make a copy.
813 *                   Can be any type, including primitives, `null`, and `undefined`.
814 * @param {(Object|Array)=} destination Destination into which the source is copied. If
815 *     provided, must be of the same type as `source`.
816 * @returns {*} The copy or updated `destination`, if `destination` was specified.
817 *
818 * @example
819 <example module="copyExample">
820 <file name="index.html">
821 <div ng-controller="ExampleController">
822 <form novalidate class="simple-form">
823 Name: <input type="text" ng-model="user.name" /><br />
824 E-mail: <input type="email" ng-model="user.email" /><br />
825 Gender: <input type="radio" ng-model="user.gender" value="male" />male
826 <input type="radio" ng-model="user.gender" value="female" />female<br />
827 <button ng-click="reset()">RESET</button>
828 <button ng-click="update(user)">SAVE</button>
829 </form>
830 <pre>form = {{user | json}}</pre>
831 <pre>master = {{master | json}}</pre>
832 </div>
833
834 <script>
835  angular.module('copyExample', [])
836    .controller('ExampleController', ['$scope', function($scope) {
837      $scope.master= {};
838
839      $scope.update = function(user) {
840        // Example with 1 argument
841        $scope.master= angular.copy(user);
842      };
843
844      $scope.reset = function() {
845        // Example with 2 arguments
846        angular.copy($scope.master, $scope.user);
847      };
848
849      $scope.reset();
850    }]);
851 </script>
852 </file>
853 </example>
854 */
855function copy(source, destination, stackSource, stackDest) {
856  if (isWindow(source) || isScope(source)) {
857    throw ngMinErr('cpws',
858      "Can't copy! Making copies of Window or Scope instances is not supported.");
859  }
860  if (isTypedArray(destination)) {
861    throw ngMinErr('cpta',
862      "Can't copy! TypedArray destination cannot be mutated.");
863  }
864
865  if (!destination) {
866    destination = source;
867    if (isObject(source)) {
868      var index;
869      if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
870        return stackDest[index];
871      }
872
873      // TypedArray, Date and RegExp have specific copy functionality and must be
874      // pushed onto the stack before returning.
875      // Array and other objects create the base object and recurse to copy child
876      // objects. The array/object will be pushed onto the stack when recursed.
877      if (isArray(source)) {
878        return copy(source, [], stackSource, stackDest);
879      } else if (isTypedArray(source)) {
880        destination = new source.constructor(source);
881      } else if (isDate(source)) {
882        destination = new Date(source.getTime());
883      } else if (isRegExp(source)) {
884        destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
885        destination.lastIndex = source.lastIndex;
886      } else {
887        var emptyObject = Object.create(getPrototypeOf(source));
888        return copy(source, emptyObject, stackSource, stackDest);
889      }
890
891      if (stackDest) {
892        stackSource.push(source);
893        stackDest.push(destination);
894      }
895    }
896  } else {
897    if (source === destination) throw ngMinErr('cpi',
898      "Can't copy! Source and destination are identical.");
899
900    stackSource = stackSource || [];
901    stackDest = stackDest || [];
902
903    if (isObject(source)) {
904      stackSource.push(source);
905      stackDest.push(destination);
906    }
907
908    var result, key;
909    if (isArray(source)) {
910      destination.length = 0;
911      for (var i = 0; i < source.length; i++) {
912        destination.push(copy(source[i], null, stackSource, stackDest));
913      }
914    } else {
915      var h = destination.$$hashKey;
916      if (isArray(destination)) {
917        destination.length = 0;
918      } else {
919        forEach(destination, function(value, key) {
920          delete destination[key];
921        });
922      }
923      if (isBlankObject(source)) {
924        // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
925        for (key in source) {
926          destination[key] = copy(source[key], null, stackSource, stackDest);
927        }
928      } else if (source && typeof source.hasOwnProperty === 'function') {
929        // Slow path, which must rely on hasOwnProperty
930        for (key in source) {
931          if (source.hasOwnProperty(key)) {
932            destination[key] = copy(source[key], null, stackSource, stackDest);
933          }
934        }
935      } else {
936        // Slowest path --- hasOwnProperty can't be called as a method
937        for (key in source) {
938          if (hasOwnProperty.call(source, key)) {
939            destination[key] = copy(source[key], null, stackSource, stackDest);
940          }
941        }
942      }
943      setHashKey(destination,h);
944    }
945  }
946  return destination;
947}
948
949/**
950 * Creates a shallow copy of an object, an array or a primitive.
951 *
952 * Assumes that there are no proto properties for objects.
953 */
954function shallowCopy(src, dst) {
955  if (isArray(src)) {
956    dst = dst || [];
957
958    for (var i = 0, ii = src.length; i < ii; i++) {
959      dst[i] = src[i];
960    }
961  } else if (isObject(src)) {
962    dst = dst || {};
963
964    for (var key in src) {
965      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
966        dst[key] = src[key];
967      }
968    }
969  }
970
971  return dst || src;
972}
973
974
975/**
976 * @ngdoc function
977 * @name angular.equals
978 * @module ng
979 * @kind function
980 *
981 * @description
982 * Determines if two objects or two values are equivalent. Supports value types, regular
983 * expressions, arrays and objects.
984 *
985 * Two objects or values are considered equivalent if at least one of the following is true:
986 *
987 * * Both objects or values pass `===` comparison.
988 * * Both objects or values are of the same type and all of their properties are equal by
989 *   comparing them with `angular.equals`.
990 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
991 * * Both values represent the same regular expression (In JavaScript,
992 *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
993 *   representation matches).
994 *
995 * During a property comparison, properties of `function` type and properties with names
996 * that begin with `$` are ignored.
997 *
998 * Scope and DOMWindow objects are being compared only by identify (`===`).
999 *
1000 * @param {*} o1 Object or value to compare.
1001 * @param {*} o2 Object or value to compare.
1002 * @returns {boolean} True if arguments are equal.
1003 */
1004function equals(o1, o2) {
1005  if (o1 === o2) return true;
1006  if (o1 === null || o2 === null) return false;
1007  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1008  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1009  if (t1 == t2) {
1010    if (t1 == 'object') {
1011      if (isArray(o1)) {
1012        if (!isArray(o2)) return false;
1013        if ((length = o1.length) == o2.length) {
1014          for (key = 0; key < length; key++) {
1015            if (!equals(o1[key], o2[key])) return false;
1016          }
1017          return true;
1018        }
1019      } else if (isDate(o1)) {
1020        if (!isDate(o2)) return false;
1021        return equals(o1.getTime(), o2.getTime());
1022      } else if (isRegExp(o1)) {
1023        return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1024      } else {
1025        if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1026          isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1027        keySet = createMap();
1028        for (key in o1) {
1029          if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1030          if (!equals(o1[key], o2[key])) return false;
1031          keySet[key] = true;
1032        }
1033        for (key in o2) {
1034          if (!(key in keySet) &&
1035              key.charAt(0) !== '$' &&
1036              o2[key] !== undefined &&
1037              !isFunction(o2[key])) return false;
1038        }
1039        return true;
1040      }
1041    }
1042  }
1043  return false;
1044}
1045
1046var csp = function() {
1047  if (isDefined(csp.isActive_)) return csp.isActive_;
1048
1049  var active = !!(document.querySelector('[ng-csp]') ||
1050                  document.querySelector('[data-ng-csp]'));
1051
1052  if (!active) {
1053    try {
1054      /* jshint -W031, -W054 */
1055      new Function('');
1056      /* jshint +W031, +W054 */
1057    } catch (e) {
1058      active = true;
1059    }
1060  }
1061
1062  return (csp.isActive_ = active);
1063};
1064
1065/**
1066 * @ngdoc directive
1067 * @module ng
1068 * @name ngJq
1069 *
1070 * @element ANY
1071 * @param {string=} ngJq the name of the library available under `window`
1072 * to be used for angular.element
1073 * @description
1074 * Use this directive to force the angular.element library.  This should be
1075 * used to force either jqLite by leaving ng-jq blank or setting the name of
1076 * the jquery variable under window (eg. jQuery).
1077 *
1078 * Since angular looks for this directive when it is loaded (doesn't wait for the
1079 * DOMContentLoaded event), it must be placed on an element that comes before the script
1080 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1081 * others ignored.
1082 *
1083 * @example
1084 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1085 ```html
1086 <!doctype html>
1087 <html ng-app ng-jq>
1088 ...
1089 ...
1090 </html>
1091 ```
1092 * @example
1093 * This example shows how to use a jQuery based library of a different name.
1094 * The library name must be available at the top most 'window'.
1095 ```html
1096 <!doctype html>
1097 <html ng-app ng-jq="jQueryLib">
1098 ...
1099 ...
1100 </html>
1101 ```
1102 */
1103var jq = function() {
1104  if (isDefined(jq.name_)) return jq.name_;
1105  var el;
1106  var i, ii = ngAttrPrefixes.length, prefix, name;
1107  for (i = 0; i < ii; ++i) {
1108    prefix = ngAttrPrefixes[i];
1109    if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1110      name = el.getAttribute(prefix + 'jq');
1111      break;
1112    }
1113  }
1114
1115  return (jq.name_ = name);
1116};
1117
1118function concat(array1, array2, index) {
1119  return array1.concat(slice.call(array2, index));
1120}
1121
1122function sliceArgs(args, startIndex) {
1123  return slice.call(args, startIndex || 0);
1124}
1125
1126
1127/* jshint -W101 */
1128/**
1129 * @ngdoc function
1130 * @name angular.bind
1131 * @module ng
1132 * @kind function
1133 *
1134 * @description
1135 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1136 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1137 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1138 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1139 *
1140 * @param {Object} self Context which `fn` should be evaluated in.
1141 * @param {function()} fn Function to be bound.
1142 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1143 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1144 */
1145/* jshint +W101 */
1146function bind(self, fn) {
1147  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1148  if (isFunction(fn) && !(fn instanceof RegExp)) {
1149    return curryArgs.length
1150      ? function() {
1151          return arguments.length
1152            ? fn.apply(self, concat(curryArgs, arguments, 0))
1153            : fn.apply(self, curryArgs);
1154        }
1155      : function() {
1156          return arguments.length
1157            ? fn.apply(self, arguments)
1158            : fn.call(self);
1159        };
1160  } else {
1161    // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1162    return fn;
1163  }
1164}
1165
1166
1167function toJsonReplacer(key, value) {
1168  var val = value;
1169
1170  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1171    val = undefined;
1172  } else if (isWindow(value)) {
1173    val = '$WINDOW';
1174  } else if (value &&  document === value) {
1175    val = '$DOCUMENT';
1176  } else if (isScope(value)) {
1177    val = '$SCOPE';
1178  }
1179
1180  return val;
1181}
1182
1183
1184/**
1185 * @ngdoc function
1186 * @name angular.toJson
1187 * @module ng
1188 * @kind function
1189 *
1190 * @description
1191 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1192 * stripped since angular uses this notation internally.
1193 *
1194 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1195 * @param {boolean|number}
1195 [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1196 *    If set to an integer, the JSON output will contain that many spaces per indentation.
1197 * @returns {string|undefined} JSON-ified string representing `obj`.
1198 */
1199function toJson(obj, pretty) {
1200  if (typeof obj === 'undefined') return undefined;
1201  if (!isNumber(pretty)) {
1202    pretty = pretty ? 2 : null;
1203  }
1204  return JSON.stringify(obj, toJsonReplacer, pretty);
1205}
1206
1207
1208/**
1209 * @ngdoc function
1210 * @name angular.fromJson
1211 * @module ng
1212 * @kind function
1213 *
1214 * @description
1215 * Deserializes a JSON string.
1216 *
1217 * @param {string} json JSON string to deserialize.
1218 * @returns {Object|Array|string|number} Deserialized JSON string.
1219 */
1220function fromJson(json) {
1221  return isString(json)
1222      ? JSON.parse(json)
1223      : json;
1224}
1225
1226
1227function timezoneToOffset(timezone, fallback) {
1228  var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1229  return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1230}
1231
1232
1233function addDateMinutes(date, minutes) {
1234  date = new Date(date.getTime());
1235  date.setMinutes(date.getMinutes() + minutes);
1236  return date;
1237}
1238
1239
1240function convertTimezoneToLocal(date, timezone, reverse) {
1241  reverse = reverse ? -1 : 1;
1242  var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1243  return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1244}
1245
1246
1247/**
1248 * @returns {string} Returns the string representation of the element.
1249 */
1250function startingTag(element) {
1251  element = jqLite(element).clone();
1252  try {
1253    // turns out IE does not let you set .html() on elements which
1254    // are not allowed to have children. So we just ignore it.
1255    element.empty();
1256  } catch (e) {}
1257  var elemHtml = jqLite('<div>').append(element).html();
1258  try {
1259    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1260        elemHtml.
1261          match(/^(<[^>]+>)/)[1].
1262          replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1263  } catch (e) {
1264    return lowercase(elemHtml);
1265  }
1266
1267}
1268
1269
1270/////////////////////////////////////////////////
1271
1272/**
1273 * Tries to decode the URI component without throwing an exception.
1274 *
1275 * @private
1276 * @param str value potential URI component to check.
1277 * @returns {boolean} True if `value` can be decoded
1278 * with the decodeURIComponent function.
1279 */
1280function tryDecodeURIComponent(value) {
1281  try {
1282    return decodeURIComponent(value);
1283  } catch (e) {
1284    // Ignore any invalid uri component
1285  }
1286}
1287
1288
1289/**
1290 * Parses an escaped url query string into key-value pairs.
1291 * @returns {Object.<string,boolean|Array>}
1292 */
1293function parseKeyValue(/**string*/keyValue) {
1294  var obj = {}, key_value, key;
1295  forEach((keyValue || "").split('&'), function(keyValue) {
1296    if (keyValue) {
1297      key_value = keyValue.replace(/\+/g,'%20').split('=');
1298      key = tryDecodeURIComponent(key_value[0]);
1299      if (isDefined(key)) {
1300        var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1301        if (!hasOwnProperty.call(obj, key)) {
1302          obj[key] = val;
1303        } else if (isArray(obj[key])) {
1304          obj[key].push(val);
1305        } else {
1306          obj[key] = [obj[key],val];
1307        }
1308      }
1309    }
1310  });
1311  return obj;
1312}
1313
1314function toKeyValue(obj) {
1315  var parts = [];
1316  forEach(obj, function(value, key) {
1317    if (isArray(value)) {
1318      forEach(value, function(arrayValue) {
1319        parts.push(encodeUriQuery(key, true) +
1320                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1321      });
1322    } else {
1323    parts.push(encodeUriQuery(key, true) +
1324               (value === true ? '' : '=' + encodeUriQuery(value, true)));
1325    }
1326  });
1327  return parts.length ? parts.join('&') : '';
1328}
1329
1330
1331/**
1332 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1333 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1334 * segments:
1335 *    segment       = *pchar
1336 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1337 *    pct-encoded   = "%" HEXDIG HEXDIG
1338 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1339 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1340 *                     / "*" / "+" / "," / ";" / "="
1341 */
1342function encodeUriSegment(val) {
1343  return encodeUriQuery(val, true).
1344             replace(/%26/gi, '&').
1345             replace(/%3D/gi, '=').
1346             replace(/%2B/gi, '+');
1347}
1348
1349
1350/**
1351 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1352 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1353 * encoded per http://tools.ietf.org/html/rfc3986:
1354 *    query       = *( pchar / "/" / "?" )
1355 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1356 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1357 *    pct-encoded   = "%" HEXDIG HEXDIG
1358 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1359 *                     / "*" / "+" / "," / ";" / "="
1360 */
1361function encodeUriQuery(val, pctEncodeSpaces) {
1362  return encodeURIComponent(val).
1363             replace(/%40/gi, '@').
1364             replace(/%3A/gi, ':').
1365             replace(/%24/g, '$').
1366             replace(/%2C/gi, ',').
1367             replace(/%3B/gi, ';').
1368             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1369}
1370
1371var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1372
1373function getNgAttribute(element, ngAttr) {
1374  var attr, i, ii = ngAttrPrefixes.length;
1375  for (i = 0; i < ii; ++i) {
1376    attr = ngAttrPrefixes[i] + ngAttr;
1377    if (isString(attr = element.getAttribute(attr))) {
1378      return attr;
1379    }
1380  }
1381  return null;
1382}
1383
1384/**
1385 * @ngdoc directive
1386 * @name ngApp
1387 * @module ng
1388 *
1389 * @element ANY
1390 * @param {angular.Module} ngApp an optional application
1391 *   {@link angular.module module} name to load.
1392 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1393 *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1394 *   do not use explicit function annotation (and are thus unsuitable for minification), as de
1394scribed
1395 *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1396 *   tracking down the root of these bugs.
1397 *
1398 * @description
1399 *
1400 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1401 * designates the **root element** of the application and is typically placed near the root element
1402 * of the page - e.g. on the `<body>` or `<html>` tags.
1403 *
1404 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1405 * found in the document will be used to define the root element to auto-bootstrap as an
1406 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1407 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1408 *
1409 * You can specify an **AngularJS module** to be used as the root module for the application.  This
1410 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1411 * should contain the application code needed or have dependencies on other modules that will
1412 * contain the code. See {@link angular.module} for more information.
1413 *
1414 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1415 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1416 * would not be resolved to `3`.
1417 *
1418 * `ngApp` is the easiest, and most common way to bootstrap an application.
1419 *
1420 <example module="ngAppDemo">
1421   <file name="index.html">
1422   <div ng-controller="ngAppDemoController">
1423     I can add: {{a}} + {{b}} =  {{ a+b }}
1424   </div>
1425   </file>
1426   <file name="script.js">
1427   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1428     $scope.a = 1;
1429     $scope.b = 2;
1430   });
1431   </file>
1432 </example>
1433 *
1434 * Using `ngStrictDi`, you would see something like this:
1435 *
1436 <example ng-app-included="true">
1437   <file name="index.html">
1438   <div ng-app="ngAppStrictDemo" ng-strict-di>
1439       <div ng-controller="GoodController1">
1440           I can add: {{a}} + {{b}} =  {{ a+b }}
1441
1442           <p>This renders because the controller does not fail to
1443              instantiate, by using explicit annotation style (see
1444              script.js for details)
1445           </p>
1446       </div>
1447
1448       <div ng-controller="GoodController2">
1449           Name: <input ng-model="name"><br />
1450           Hello, {{name}}!
1451
1452           <p>This renders because the controller does not fail to
1453              instantiate, by using explicit annotation style
1454              (see script.js for details)
1455           </p>
1456       </div>
1457
1458       <div ng-controller="BadController">
1459           I can add: {{a}} + {{b}} =  {{ a+b }}
1460
1461           <p>The controller could not be instantiated, due to relying
1462              on automatic function annotations (which are disabled in
1463              strict mode). As such, the content of this section is not
1464              interpolated, and there should be an error in your web console.
1465           </p>
1466       </div>
1467   </div>
1468   </file>
1469   <file name="script.js">
1470   angular.module('ngAppStrictDemo', [])
1471     // BadController will fail to instantiate, due to relying on automatic function annotation,
1472     // rather than an explicit annotation
1473     .controller('BadController', function($scope) {
1474       $scope.a = 1;
1475       $scope.b = 2;
1476     })
1477     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1478     // due to using explicit annotations using the array style and $inject property, respectively.
1479     .controller('GoodController1', ['$scope', function($scope) {
1480       $scope.a = 1;
1481       $scope.b = 2;
1482     }])
1483     .controller('GoodController2', GoodController2);
1484     function GoodController2($scope) {
1485       $scope.name = "World";
1486     }
1487     GoodController2.$inject = ['$scope'];
1488   </file>
1489   <file name="style.css">
1490   div[ng-controller] {
1491       margin-bottom: 1em;
1492       -webkit-border-radius: 4px;
1493       border-radius: 4px;
1494       border: 1px solid;
1495       padding: .5em;
1496   }
1497   div[ng-controller^=Good] {
1498       border-color: #d6e9c6;
1499       background-color: #dff0d8;
1500       color: #3c763d;
1501   }
1502   div[ng-controller^=Bad] {
1503       border-color: #ebccd1;
1504       background-color: #f2dede;
1505       color: #a94442;
1506       margin-bottom: 0;
1507   }
1508   </file>
1509 </example>
1510 */
1511function angularInit(element, bootstrap) {
1512  var appElement,
1513      module,
1514      config = {};
1515
1516  // The element `element` has priority over any other element
1517  forEach(ngAttrPrefixes, function(prefix) {
1518    var name = prefix + 'app';
1519
1520    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1521      appElement = element;
1522      module = element.getAttribute(name);
1523    }
1524  });
1525  forEach(ngAttrPrefixes, function(prefix) {
1526    var name = prefix + 'app';
1527    var candidate;
1528
1529    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1530      appElement = candidate;
1531      module = candidate.getAttribute(name);
1532    }
1533  });
1534  if (appElement) {
1535    config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1536    bootstrap(appElement, module ? [module] : [], config);
1537  }
1538}
1539
1540/**
1541 * @ngdoc function
1542 * @name angular.bootstrap
1543 * @module ng
1544 * @description
1545 * Use this function to manually start up angular application.
1546 *
1547 * See: {@link guide/bootstrap Bootstrap}
1548 *
1549 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1550 * They must use {@link ng.directive:ngApp ngApp}.
1551 *
1552 * Angular will detect if it has been loaded into the browser more than once and only allow the
1553 * first loaded script to be bootstrapped and will report a warning to the browser console for
1554 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1555 * multiple instances of Angular try to work on the DOM.
1556 *
1557 * ```html
1558 * <!doctype html>
1559 * <html>
1560 * <body>
1561 * <div ng-controller="WelcomeController">
1562 *   {{greeting}}
1563 * </div>
1564 *
1565 * <script src="angular.js"></script>
1566 * <script>
1567 *   var app = angular.module('demo', [])
1568 *   .controller('WelcomeController', function($scope) {
1569 *       $scope.greeting = 'Welcome!';
1570 *   });
1571 *   angular.bootstrap(document, ['demo']);
1572 * </script>
1573 * </body>
1574 * </html>
1575 * ```
1576 *
1577 * @param {DOMElement} element DOM element which is the root of angular application.
1578 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1579 *     Each item in the array should be the name of a predefined module or a (DI annotated)
1580 *     function that will be invoked by the injector as a `config` block.
1581 *     See: {@link angular.module modules}
1582 * @param {Object=} config an object for defining configuration options for the application. The
1583 *     following keys are supported:
1584 *
1585 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1586 *   assist in finding bugs which break minified code. Defaults to `false`.
1587 *
1588 * @returns {auto.$injector} Returns the newly created injector for this app.
1589 */
1590function bootstrap(element, modules, config) {
1591  if (!isObject(config)) config = {};
1592  var defaultConfig = {
1593    strictDi: false
1594  };
1595  config = extend(defaultConfig, config);
1596  var doBootstrap = function() {
1597    element = jqLite(element);
1598
1599    if (element.injector()) {
1600      var tag = (element[0] === document) ? 'document' : startingTag(element);
1601      //Encode angle brackets to prevent input from being sanitized to empty string #8683
1602      throw ngMinErr(
1603          'btstrpd',
1604          "App Already Bootstrapped with this Element '{0}'",
1605          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1606    }
1607
1608    modules = modules || [];
1609    modules.unshift(['$provide', function($provide) {
1610      $provide.value('$rootElement', element);
1611    }]);
1612
1613    if (config.debugInfoEnabled) {
1614      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1615      modules.push(['$compileProvider', function($compileProvider) {
1616        $compileProvider.debugInfoEnabled(true);
1617      }]);
1618    }
1619
1620    modules.unshift('ng');
1621    var injector = createInjector(modules, config.strictDi);
1622    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1623       function bootstrapApply(scope, element, compile, injector) {
1624        scope.$apply(function() {
1625          element.data('$injector', injector);
1626          compile(element)(scope);
1627        });
1628      }]
1629    );
1630    return injector;
1631  };
1632
1633  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1634  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1635
1636  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1637    config.debugInfoEnabled = true;
1638    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1639  }
1640
1641  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1642    return doBootstrap();
1643  }
1644
1645  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1646  angular.resumeBootstrap = function(extraModules) {
1647    forEach(extraModules, function(module) {
1648      modules.push(module);
1649    });
1650    return doBootstrap();
1651  };
1652
1653  if (isFunction(angular.resumeDeferredBootstrap)) {
1654    angular.resumeDeferredBootstrap();
1655  }
1656}
1657
1658/**
1659 * @ngdoc function
1660 * @name angular.reloadWithDebugInfo
1661 * @module ng
1662 * @description
1663 * Use this function to reload the current application with debug information turned on.
1664 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1665 *
1666 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1667 */
1668function reloadWithDebugInfo() {
1669  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1670  window.location.reload();
1671}
1672
1673/**
1674 * @name angular.getTestability
1675 * @module ng
1676 * @description
1677 * Get the testability service for the instance of Angular on the given
1678 * element.
1679 * @param {DOMElement} element DOM element which is the root of angular application.
1680 */
1681function getTestability(rootElement) {
1682  var injector = angular.element(rootElement).injector();
1683  if (!injector) {
1684    throw ngMinErr('test',
1685      'no injector found for element argument to getTestability');
1686  }
1687  return injector.get('$$testability');
1688}
1689
1690var SNAKE_CASE_REGEXP = /[A-Z]/g;
1691function snake_case(name, separator) {
1692  separator = separator || '_';
1693  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1694    return (pos ? separator : '') + letter.toLowerCase();
1695  });
1696}
1697
1698var bindJQueryFired = false;
1699var skipDestroyOnNextJQueryCleanData;
1700function bindJQuery() {
1701  var originalCleanData;
1702
1703  if (bindJQueryFired) {
1704    return;
1705  }
1706
1707  // bind to jQuery if present;
1708  var jqName = jq();
1709  jQuery = window.jQuery; // use default jQuery.
1710  if (isDefined(jqName)) { // `ngJq` present
1711    jQuery = jqName === null ? undefined : window[jqName]; // if empty; use jqLite. if not empty, use jQuery specified by `ngJq`.
1712  }
1713
1714  // Use jQuery if it exists with proper functionality, otherwise default to us.
1715  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1716  // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1717  // versions. It will not work for sure with jQuery <1.7, though.
1718  if (jQuery && jQuery.fn.on) {
1719    jqLite = jQuery;
1720    extend(jQuery.fn, {
1721      scope: JQLitePrototype.scope,
1722      isolateScope: JQLitePrototype.isolateScope,
1723      controller: JQLitePrototype.controller,
1724      injector: JQLitePrototype.injector,
1725      inheritedData: JQLitePrototype.inheritedData
1726    });
1727
1728    // All nodes removed from the DOM via various jQuery APIs like .remove()
1729    // are passed through jQuery.cleanData. Monkey-patch this method to fire
1730    // the $destroy event on all removed nodes.
1731    originalCleanData = jQuery.cleanData;
1732    jQuery.cleanData = function(elems) {
1733      var events;
1734      if (!skipDestroyOnNextJQueryCleanData) {
1735        for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1736          events = jQuery._data(elem, "events");
1737          if (events && events.$destroy) {
1738            jQuery(elem).triggerHandler('$destroy');
1739          }
1740        }
1741      } else {
1742        skipDestroyOnNextJQueryCleanData = false;
1743      }
1744      originalCleanData(elems);
1745    };
1746  } else {
1747    jqLite = JQLite;
1748  }
1749
1750  angular.element = jqLite;
1751
1752  // Prevent double-proxying.
1753  bindJQueryFired = true;
1754}
1755
1756/**
1757 * throw error if the argument is falsy.
1758 */
1759function assertArg(arg, name, reason) {
1760  if (!arg) {
1761    throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1762  }
1763  return arg;
1764}
1765
1766function assertArgFn(arg, name, acceptArrayAnnotation) {
1767  if (acceptArrayAnnotation && isArray(arg)) {
1768      arg = arg[arg.length - 1];
1769  }
1770
1771  assertArg(isFunction(arg), name, 'not a function, got ' +
1772      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1773  return arg;
1774}
1775
1776/**
1777 * throw error if the name given is hasOwnProperty
1778 * @param  {String} name    the name to test
1779 * @param  {String} context the context in which the name is used, such as module or directive
1780 */
1781function assertNotHasOwnProperty(name, context) {
1782  if (name === 'hasOwnProperty') {
1783    throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1784  }
1785}
1786
1787/**
1788 * Return the value accessible from the object by path. Any undefined traversals are ignored
1789 * @param {Object} obj starting object
1790 * @param {String} path path to traverse
1791 * @param {boolean} [bindFnToScope=true]
1792 * @returns {Object} value as accessible by path
1793 */
1794//TODO(misko): this function needs to be removed
1795function getter(obj, path, bindFnToScope) {
1796  if (!path) return obj;
1797  var keys = path.split('.');
1798  var key;
1799  var lastInstance = obj;
1800  var len = keys.length;
1801
1802  for (var i = 0; i < len; i++) {
1803    key = keys[i];
1804    if (obj) {
1805      obj = (lastInstance = obj)[key];
1806    }
1807  }
1808  if (!bindFnToScope && isFunction(obj)) {
1809    return bind(lastInstance, obj);
1810  }
1811  return obj;
1812}
1813
1814/**
1815 * Return the DOM siblings between the first and last node in the given array.
1816 * @param {Array} array like object
1817 * @returns {jqLite} jqLite collection containing the nodes
1818 */
1819function getBlockNodes(nodes) {
1820  // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
1821  //             collection, otherwise update the original collection.
1822  var node = nodes[0];
1823  var endNode = nodes[nodes.length - 1];
1824  var blockNodes = [node];
1825
1826  do {
1827    node = node.nextSibling;
1828    if (!node) break;
1829    blockNodes.push(node);
1830  } while (node !== endNode);
1831
1832  return jqLite(blockNodes);
1833}
1834
1835
1836/**
1837 * Creates a new object without a prototype. This object is useful for lookup without having to
1838 * guard against prototypically inherited properties via hasOwnProperty.
1839 *
1840 * Related micro-benchmarks:
1841 * - http://jsperf.com/object-create2
1842 * - http://jsperf.com/proto-map-lookup/2
1843 * - http://jsperf.com/for-in-vs-object-keys2
1844 *
1845 * @returns {Object}
1846 */
1847function createMap() {
1848  return Object.create(null);
1849}
1850
1851var NODE_TYPE_ELEMENT = 1;
1852var NODE_TYPE_ATTRIBUTE = 2;
1853var NODE_TYPE_TEXT = 3;
1854var NODE_TYPE_COMMENT = 8;
1855var NODE_TYPE_DOCUMENT = 9;
1856var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1857
1858/**
1859 * @ngdoc type
1860 * @name angular.Module
1861 * @module ng
1862 * @description
1863 *
1864 * Interface for configuring angular {@link angular.module modules}.
1865 */
1866
1867function setupModuleLoader(window) {
1868
1869  var $injectorMinErr = minErr('$injector');
1870  var ngMinErr = minErr('ng');
1871
1872  function ensure(obj, name, factory) {
vendor: 4,138 bytes, lines 1873-1987
1873    return obj[name] || (obj[name] = factory());
1874  }
1875
1876  var angular = ensure(window, 'angular', Object);
1877
1878  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1879  angular.$$minErr = angular.$$minErr || minErr;
1880
1881  return ensure(angular, 'module', function() {
1882    /** @type {Object.<string, angular.Module>} */
1883    var modules = {};
1884
1885    /**
1886     * @ngdoc function
1887     * @name angular.module
1888     * @module ng
1889     * @description
1890     *
1891     * The `angular.module` is a global place for creating, registering and retrieving Angular
1892     * modules.
1893     * All modules (angular core or 3rd party) that should be available to an application must be
1894     * registered using this mechanism.
1895     *
1896     * When passed two or more arguments, a new module is created.  If passed only one argument, an
1897     * existing module (the name passed as the first argument to `module`) is retrieved.
1898     *
1899     *
1900     * # Module
1901     *
1902     * A module is a collection of services, directives, controllers, filters, and configuration information.
1903     * `angular.module` is used to configure the {@link auto.$injector $injector}.
1904     *
1905     * ```js
1906     * // Create a new module
1907     * var myModule = angular.module('myModule', []);
1908     *
1909     * // register a new service
1910     * myModule.value('appName', 'MyCoolApp');
1911     *
1912     * // configure existing services inside initialization blocks.
1913     * myModule.config(['$locationProvider', function($locationProvider) {
1914     *   // Configure existing providers
1915     *   $locationProvider.hashPrefix('!');
1916     * }]);
1917     * ```
1918     *
1919     * Then you can create an injector and load your modules like this:
1920     *
1921     * ```js
1922     * var injector = angular.injector(['ng', 'myModule'])
1923     * ```
1924     *
1925     * However it's more likely that you'll just use
1926     * {@link ng.directive:ngApp ngApp} or
1927     * {@link angular.bootstrap} to simplify this process for you.
1928     *
1929     * @param {!string} name The name of the module to create or retrieve.
1930     * @param {!Array.<string>=} requires If specified then new module is being created. If
1931     *        unspecified then the module is being retrieved for further configuration.
1932     * @param {Function=} configFn Optional configuration function for the module. Same as
1933     *        {@link angular.Module#config Module#config()}.
1934     * @returns {module} new module with the {@link angular.Module} api.
1935     */
1936    return function module(name, requires, configFn) {
1937      var assertNotHasOwnProperty = function(name, context) {
1938        if (name === 'hasOwnProperty') {
1939          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
1940        }
1941      };
1942
1943      assertNotHasOwnProperty(name, 'module');
1944      if (requires && modules.hasOwnProperty(name)) {
1945        modules[name] = null;
1946      }
1947      return ensure(modules, name, function() {
1948        if (!requires) {
1949          throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
1950             "the module name or forgot to load it. If registering a module ensure that you " +
1951             "specify the dependencies as the second argument.", name);
1952        }
1953
1954        /** @type {!Array.<Array.<*>>} */
1955        var invokeQueue = [];
1956
1957        /** @type {!Array.<Function>} */
1958        var configBlocks = [];
1959
1960        /** @type {!Array.<Function>} */
1961        var runBlocks = [];
1962
1963        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
1964
1965        /** @type {angular.Module} */
1966        var moduleInstance = {
1967          // Private state
1968          _invokeQueue: invokeQueue,
1969          _configBlocks: configBlocks,
1970          _runBlocks: runBlocks,
1971
1972          /**
1973           * @ngdoc property
1974           * @name angular.Module#requires
1975           * @module ng
1976           *
1977           * @description
1978           * Holds the list of modules which the injector will load before the current module is
1979           * loaded.
1980           */
1981          requires: requires,
1982
1983          /**
1984           * @ngdoc property
1985           * @name angular.Module#name
1986           * @module ng
1987           *
1988           * @description
1989           * Name of the module.
1990           */
1991          name: name,
1992
1993
1994          /**
1995           * @ngdoc method
1996           * @name angular.Module#provider
1997           * @module ng
1998           * @param {string} name service name
1999           * @param {Function} providerType Construction function for creating new instance of the
2000           *                                service.
2001           * @description
2002           * See {@link auto.$provide#provider $provide.provider()}.
2003           */
2004          provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2005
2006          /**
2007           * @ngdoc method
2008           * @name angular.Module#factory
2009           * @module ng
2010           * @param {string} name service name
2011           * @param {Function} providerFunction Function for creating new instance of the service.
2012           * @description
2013           * See {@link auto.$provide#factory $provide.factory()}.
2014           */
2015          factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2016
2017          /**
2018           * @ngdoc method
2019           * @name angular.Module#service
2020           * @module ng
2021           * @param {string} name service name
2022           * @param {Function} constructor A constructor function that will be instantiated.
2023           * @description
2024           * See {@link auto.$provide#service $provide.service()}.
2025           */
2026          service: invokeLaterAndSetModuleName('$provide', 'service'),
2027
2028          /**
2029           * @ngdoc method
2030           * @name angular.Module#value
2031           * @module ng
2032           * @param {string} name service name
2033           * @param {*} object Service instance object.
2034           * @description
2035           * See {@link auto.$provide#value $provide.value()}.
2036           */
2037          value: invokeLater('$provide', 'value'),
2038
2039          /**
2040           * @ngdoc method
2041           * @name angular.Module#constant
2042           * @module ng
2043           * @param {string} name constant name
2044           * @param {*} object Constant value.
2045           * @description
2046           * Because the constant are fixed, they get applied before other provide methods.
2047           * See {@link auto.$provide#constant $provide.constant()}.
2048           */
2049          constant: invokeLater('$provide', 'constant', 'unshift'),
2050
2051           /**
2052           * @ngdoc method
2053           * @name angular.Module#decorator
2054           * @module ng
2055           * @param {string} The name of the service to decorate.
2056           * @param {Function} This function will be invoked when the service needs to be
2057           *                                    instantiated and should return the decorated service instance.
2058           * @description
2059           * See {@link auto.$provide#decorator $provide.decorator()}.
2060           */
2061          decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2062
2063          /**
2064           * @ngdoc method
2065           * @name angular.Module#animation
2066           * @module ng
2067           * @param {string} name animation name
2068           * @param {Function} animationFactory Factory function for creating new instance of an
2069           *                                    animation.
2070           * @description
2071           *
2072           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2073           *
2074           *
2075           * Defines an animation hook that can be later used with
2076           * {@link $animate $animate} service and directives that use this service.
2077           *
2078           * ```js
2079           * module.animation('.animation-name', function($inject1, $inject2) {
2080           *   return {
2081           *     eventName : function(element, done) {
2082           *       //code to run the animation
2083           *       //once complete, then run done()
2084           *       return function cancellationFunction(element) {
2085           *         //code to cancel the animation
2086           *       }
2087           *     }
2088           *   }
2089           * })
2090           * ```
2091           *
2092           * See {@link ng.$animateProvider#register $animateProvider.register()} and
2093           * {@link ngAnimate ngAnimate module} for more information.
2094           */
2095          animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2096
2097          /**
2098           * @ngdoc method
2099           * @name angular.Module#filter
2100           * @module ng
2101           * @param {string} name Filter name - this must be a valid angular expression identifier
2102           * @param {Function} filterFactory Factory function for creating new instance of filter.
2103           * @description
2104           * See {@link ng.$filterProvider#register $filterProvider.register()}.
2105           *
2106           * <div class="alert alert-warning">
2107           * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2108           * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2109           * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2110           * (`myapp_subsection_filterx`).
2111           * </div>
2112           */
2113          filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2114
2115          /**
2116           * @ngdoc method
2117           * @name angular.Module#controller
2118           * @module ng
2119           * @param {string|Object} name Controller name, or an object map of controllers where the
2120           *    keys are the names and the values are the constructors.
2121           * @param {Function} constructor Controller constructor function.
2122           * @description
2123           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2124           */
2125          controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2126
2127          /**
2128           * @ngdoc method
2129           * @name angular.Module#directive
2130           * @module ng
2131           * @param {string|Object} name Directive name, or an object map of directives where the
2132           *    keys are the names and the values are the factories.
2133           * @param {Function} directiveFactory Factory function for creating new instance of
2134           * directives.
2135           * @description
2136           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2137           */
2138          directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2139
2140          /**
2141           * @ngdoc method
2142           * @name angular.Module#config
2143           * @module ng
2144           * @param {Function} configFn Execute this function on module load. Useful for service
2145           *    configuration.
2146           * @description
2147           * Use this method to register work which needs to be performed on module loading.
2148           * For more about how to configure services, see
2149           * {@link providers#provider-recipe Provider Recipe}.
2150           */
2151          config: config,
2152
2153          /**
2154           * @ngdoc method
2155           * @name angular.Module#run
2156           * @module ng
2157           * @param {Function} initializationFn Execute this function after injector creation.
2158           *    Useful for application initialization.
2159           * @description
2160           * Use this method to register work which should be performed when the injector is done
2161           * loading all modules.
2162           */
2163          run: function(block) {
2164            runBlocks.push(block);
2165            return this;
2166          }
2167        };
2168
2169        if (configFn) {
2170          config(configFn);
2171        }
2172
2173        return moduleInstance;
2174
2175        /**
2176         * @param {string} provider
2177         * @param {string} method
2178         * @param {String=} insertMethod
2179         * @returns {angular.Module}
2180         */
2181        function invokeLater(provider, method, insertMethod, queue) {
2182          if (!queue) queue = invokeQueue;
2183          return function() {
2184            queue[insertMethod || 'push']([provider, method, arguments]);
2185            return moduleInstance;
2186          };
2187        }
2188
2189        /**
2190         * @param {string} provider
2191         * @param {string} method
2192         * @returns {angular.Module}
2193         */
2194        function invokeLaterAndSetModuleName(provider, method) {
2195          return function(recipeName, factoryFunction) {
2196            if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2197            invokeQueue.push([provider, method, arguments]);
2198            return moduleInstance;
2199          };
2200        }
2201      });
2202    };
2203  });
2204
2205}
2206
2207/* global: toDebugString: true */
2208
2209function serializeObject(obj) {
2210  var seen = [];
2211
2212  return JSON.stringify(obj, function(key, val) {
2213    val = toJsonReplacer(key, val);
2214    if (isObject(val)) {
2215
2216      if (seen.indexOf(val) >= 0) return '<<already seen>>';
2217
2218      seen.push(val);
2219    }
2220    return val;
2221  });
2222}
2223
2224function toDebugString(obj) {
2225  if (typeof obj === 'function') {
2226    return obj.toString().replace(/ \{[\s\S]*$/, '');
2227  } else if (typeof obj === 'undefined') {
2228    return 'undefined';
2229  } else if (typeof obj !== 'string') {
2230    return serializeObject(obj);
2231  }
2232  return obj;
2233}
2234
2235/* global angularModule: true,
2236  version: true,
2237
2238  $LocaleProvider,
2239  $CompileProvider,
2240
2241  htmlAnchorDirective,
2242  inputDirective,
2243  inputDirective,
2244  formDirective,
2245  scriptDirective,
2246  selectDirective,
2247  styleDirective,
2248  optionDirective,
2249  ngBindDirective,
2250  ngBindHtmlDirective,
2251  ngBindTemplateDirective,
2252  ngClassDirective,
2253  ngClassEvenDirective,
2254  ngClassOddDirective,
2255  ngCspDirective,
2256  ngCloakDirective,
2257  ngControllerDirective,
2258  ngFormDirective,
2259  ngHideDirective,
2260  ngIfDirective,
2261  ngIncludeDirective,
2262  ngIncludeFillContentDirective,
2263  ngInitDirective,
2264  ngNonBindableDirective,
2265  ngPluralizeDirective,
2266  ngRepeatDirective,
2267  ngShowDirective,
2268  ngStyleDirective,
2269  ngSwitchDirective,
2270  ngSwitchWhenDirective,
2271  ngSwitchDefaultDirective,
2272  ngOptionsDirective,
2273  ngTranscludeDirective,
2274  ngModelDirective,
2275  ngListDirective,
2276  ngChangeDirective,
2277  patternDirective,
2278  patternDirective,
2279  requiredDirective,
2280  requiredDirective,
2281  minlengthDirective,
2282  minlengthDirective,
2283  maxlengthDirective,
2284  maxlengthDirective,
2285  ngValueDirective,
2286  ngModelOptionsDirective,
2287  ngAttributeAliasDirectives,
2288  ngEventDirectives,
2289
2290  $AnchorScrollProvider,
2291  $AnimateProvider,
2292  $$CoreAnimateQueueProvider,
2293  $$CoreAnimateRunnerProvider,
2294  $BrowserProvider,
2295  $CacheFactoryProvider,
2296  $ControllerProvider,
2297  $DocumentProvider,
2298  $ExceptionHandlerProvider,
2299  $FilterProvider,
2300  $InterpolateProvider,
2301  $IntervalProvider,
2302  $$HashMapProvider,
2303  $HttpProvider,
2304  $HttpParamSerializerProvider,
2305  $HttpParamSerializerJQLikeProvider,
2306  $HttpBackendProvider,
2307  $LocationProvider,
2308  $LogProvider,
2309  $ParseProvider,
2310  $RootScopeProvider,
2311  $QProvider,
2312  $$QProvider,
2313  $$SanitizeUriProvider,
2314  $SceProvider,
2315  $SceDelegateProvider,
2316  $SnifferProvider,
2317  $TemplateCacheProvider,
2318  $TemplateRequestProvider,
2319  $$TestabilityProvider,
2320  $TimeoutProvider,
2321  $$RAFProvider,
2322  $$AsyncCallbackProvider,
2323  $WindowProvider,
2324  $$jqLiteProvider,
2325  $$CookieReaderProvider
2326*/
2327
2328
2329/**
2330 * @ngdoc object
2331 * @name angular.version
2332 * @module ng
2333 * @description
2334 * An object that contains information about the current AngularJS version. This object has the
2335 * following properties:
2336 *
2337 * - `full` – `{string}` – Full version string, such as "0.9.18".
2338 * - `major` – `{number}` – Major version number, such as "0".
2339 * - `minor` – `{number}` – Minor version number, such as "9".
2340 * - `dot` – `{number}` – Dot version number, such as "18".
2341 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2342 */
2343var version = {
2344  full: '1.4.1',    // all of these placeholder strings will be replaced by grunt's
2345  major: 1,    // package task
2346  minor: 4,
2347  dot: 1,
2348  codeName: 'hyperionic-illumination'
2349};
2350
2351
2352function publishExternalAPI(angular) {
2353  extend(angular, {
2354    'bootstrap': bootstrap,
2355    'copy': copy,
2356    'extend': extend,
2357    'merge': merge,
2358    'equals': equals,
2359    'element': jqLite,
2360    'forEach': forEach,
2361    'injector': createInjector,
2362    'noop': noop,
2363    'bind': bind,
2364    'toJson': toJson,
2365    'fromJson': fromJson,
2366    'identity': identity,
2367    'isUndefined': isUndefined,
2368    'isDefined': isDefined,
2369    'isString': isString,
2370    'isFunction': isFunction,
2371    'isObject': isObject,
2372    'isNumber': isNumber,
2373    'isElement': isElement,
2374    'isArray': isArray,
2375    'version': version,
2376    'isDate': isDate,
2377    'lowercase': lowercase,
2378    'uppercase': uppercase,
2379    'callbacks': {counter: 0},
2380    'getTestability': getTestability,
2381    '$$minErr': minErr,
2382    '$$csp': csp,
2383    'reloadWithDebugInfo': reloadWithDebugInfo
2384  });
2385
2386  angularModule = setupModuleLoader(window);
2387  try {
2388    angularModule('ngLocale');
2389  } catch (e) {
2390    angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
2391  }
2392
2393  angularModule('ng', ['ngLocale'], ['$provide',
2394    function ngModule($provide) {
2395      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2396      $provide.provider({
2397        $$sanitizeUri: $$SanitizeUriProvider
2398      });
2399      $provide.provider('$compile', $CompileProvider).
2400        directive({
2401            a: htmlAnchorDirective,
2402            input: inputDirective,
2403            textarea: inputDirective,
2404            form: formDirective,
2405            script: scriptDirective,
2406            select: selectDirective,
2407            style: styleDirective,
2408            option: optionDirective,
2409            ngBind: ngBindDirective,
2410            ngBindHtml: ngBindHtmlDirective,
2411            ngBindTemplate: ngBindTemplateDirective,
2412            ngClass: ngClassDirective,
2413            ngClassEven: ngClassEvenDirective,
2414            ngClassOdd: ngClassOddDirective,
2415            ngCloak: ngCloakDirective,
2416            ngController: ngControllerDirective,
2417            ngForm: ngFormDirective,
2418            ngHide: ngHideDirective,
2419            ngIf: ngIfDirective,
2420            ngInclude: ngIncludeDirective,
2421            ngInit: ngInitDirective,
2422            ngNonBindable: ngNonBindableDirective,
2423            ngPluralize: ngPluralizeDirective,
2424            ngRepeat: ngRepeatDirective,
2425            ngShow: ngShowDirective,
2426            ngStyle: ngStyleDirective,
2427            ngSwitch: ngSwitchDirective,
2428            ngSwitchWhen: ngSwitchWhenDirective,
2429            ngSwitchDefault: ngSwitchDefaultDirective,
2430            ngOptions: ngOptionsDirective,
2431            ngTransclude: ngTranscludeDirective,
2432            ngModel: ngModelDirective,
2433            ngList: ngListDirective,
2434            ngChange: ngChangeDirective,
2435            pattern: patternDirective,
2436            ngPattern: patternDirective,
2437            required: requiredDirective,
2438            ngRequired: requiredDirective,
2439            minlength: minlengthDirective,
2440            ngMinlength: minlengthDirective,
2441            maxlength: maxlengthDirective,
2442            ngMaxlength: maxlengthDirective,
2443            ngValue: ngValueDirective,
2444            ngModelOptions: ngModelOptionsDirective
2445        }).
2446        directive({
2447          ngInclude: ngIncludeFillContentDirective
2448        }).
2449        directive(ngAttributeAliasDirectives).
2450        directive(ngEventDirectives);
2451      $provide.provider({
2452        $anchorScroll: $AnchorScrollProvider,
2453        $animate: $AnimateProvider,
2454        $$animateQueue: $$CoreAnimateQueueProvider,
2455        $$AnimateRunner: $$CoreAnimateRunnerProvider,
2456        $browser: $BrowserProvider,
2457        $cacheFactory: $CacheFactoryProvider,
2458        $controller: $ControllerProvider,
2459        $document: $DocumentProvider,
2460        $exceptionHandler: $ExceptionHandlerProvider,
2461        $filter: $FilterProvider,
2462        $interpolate: $InterpolateProvider,
2463        $interval: $IntervalProvider,
2464        $http: $HttpProvider,
2465        $httpParamSerializer: $HttpParamSerializerProvider,
2466        $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2467        $httpBackend: $HttpBackendProvider,
2468        $location: $LocationProvider,
2469        $log: $LogProvider,
2470        $parse: $ParseProvider,
2471        $rootScope: $RootScopeProvider,
2472        $q: $QProvider,
2473        $$q: $$QProvider,
2474        $sce: $SceProvider,
2475        $sceDelegate: $SceDelegateProvider,
2476        $sniffer: $SnifferProvider,
2477        $templateCache: $TemplateCacheProvider,
2478        $templateRequest: $TemplateRequestProvider,
2479        $$testability: $$TestabilityProvider,
2480        $timeout: $TimeoutProvider,
2481        $window: $WindowProvider,
2482        $$rAF: $$RAFProvider,
2483        $$asyncCallback: $$AsyncCallbackProvider,
2484        $$jqLite: $$jqLiteProvider,
2485        $$HashMap: $$HashMapProvider,
2486        $$cookieReader: $$CookieReaderProvider
2487      });
2488    }
2489  ]);
2490}
2491
2492/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2493 *     Any commits to this file should be reviewed with security in mind.  *
2494 *   Changes to this file can potentially create security vulnerabilities. *
2495 *          An approval from 2 Core members with history of modifying      *
2496 *                         this file is required.                          *
2497 *                                                                         *
2498 *  Does the change somehow allow for arbitrary javascript to be executed? *
2499 *    Or allows for someone to change the prototype of built-in objects?   *
2500 *     Or gives undesired access to variables likes document or window?    *
2501 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2502
2503/* global JQLitePrototype: true,
2504  addEventListenerFn: true,
2505  removeEventListenerFn: true,
2506  BOOLEAN_ATTR: true,
2507  ALIASED_ATTR: true,
2508*/
2509
2510//////////////////////////////////
2511//JQLite
2512//////////////////////////////////
2513
2514/**
2515 * @ngdoc function
2516 * @name angular.element
2517 * @module ng
2518 * @kind function
2519 *
2520 * @description
2521 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2522 *
2523 * If jQuery is available, `angular.element` is an alias for the
2524 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2525 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2526 *
2527 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2528 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2529 * commonly needed functionality with the goal of having a very small footprint.</div>
2530 *
2531 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
2532 *
2533 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2534 * jqLite; they are never raw DOM references.</div>
2535 *
2536 * ## Angular's jqLite
2537 * jqLite provides only the following jQuery methods:
2538 *
2539 * - [`addClass()`](http://api.jquery.com/addClass/)
2540 * - [`after()`](http://api.jquery.com/after/)
2541 * - [`append()`](http://api.jquery.com/append/)
2542 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2543 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2544 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2545 * - [`clone()`](http://api.jquery.com/clone/)
2546 * - [`contents()`](http://api.jquery.com/contents/)
2547 * - [`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'.
2548 * - [`data()`](http://api.jquery.com/data/)
2549 * - [`detach()`](http://api.jquery.com/detach/)
2550 * - [`empty()`](http://api.jquery.com/empty/)
2551 * - [`eq()`](http://api.jquery.com/eq/)
2552 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2553 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2554 * - [`html()`](http://api.jquery.com/html/)
2555 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2556 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2557 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
2558 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2559 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2560 * - [`prepend()`](http://api.jquery.com/prepend/)
2561 * - [`prop()`](http://api.jquery.com/prop/)
2562 * - [`ready()`](http://api.jquery.com/ready/)
2563 * - [`remove()`](http://api.jquery.com/remove/)
2564 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2565 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2566 * - [`removeData()`](http://api.jquery.com/removeData/)
2567 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2568 * - [`text()`](http://api.jquery.com/text/)
2569 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2570 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2571 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
2572 * - [`val()`](http://api.jquery.com/val/)
2573 * - [`wrap()`](http://api.jquery.com/wrap/)
2574 *
2575 * ## jQuery/jqLite Extras
2576 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2577 *
2578 * ### Events
2579 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction a
2579pis and fires this event
2580 *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2581 *    element before it is removed.
2582 *
2583 * ### Methods
2584 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2585 *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2586 *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2587 *   `'ngModel'`).
2588 * - `injector()` - retrieves the injector of the current element or its parent.
2589 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2590 *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2591 *   be enabled.
2592 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2593 *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2594 *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2595 *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2596 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2597 *   parent element is reached.
2598 *
2599 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2600 * @returns {Object} jQuery object.
2601 */
2602
2603JQLite.expando = 'ng339';
2604
2605var jqCache = JQLite.cache = {},
2606    jqId = 1,
2607    addEventListenerFn = function(element, type, fn) {
2608      element.addEventListener(type, fn, false);
2609    },
2610    removeEventListenerFn = function(element, type, fn) {
2611      element.removeEventListener(type, fn, false);
2612    };
2613
2614/*
2615 * !!! This is an undocumented "private" function !!!
2616 */
2617JQLite._data = function(node) {
2618  //jQuery always returns an object on cache miss
2619  return this.cache[node[this.expando]] || {};
2620};
2621
2622function jqNextId() { return ++jqId; }
2623
2624
2625var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2626var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2627var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2628var jqLiteMinErr = minErr('jqLite');
2629
2630/**
2631 * Converts snake_case to camelCase.
2632 * Also there is special case for Moz prefix starting with upper case letter.
2633 * @param name Name to normalize
2634 */
2635function camelCase(name) {
2636  return name.
2637    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2638      return offset ? letter.toUpperCase() : letter;
2639    }).
2640    replace(MOZ_HACK_REGEXP, 'Moz$1');
2641}
2642
2643var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
2644var HTML_REGEXP = /<|&#?\w+;/;
2645var TAG_NAME_REGEXP = /<([\w:]+)/;
2646var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
2647
2648var wrapMap = {
2649  'option': [1, '<select multiple="multiple">', '</select>'],
2650
2651  'thead': [1, '<table>', '</table>'],
2652  'col': [2, '<table><colgroup>', '</colgroup></table>'],
2653  'tr': [2, '<table><tbody>', '</tbody></table>'],
2654  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2655  '_default': [0, "", ""]
2656};
2657
2658wrapMap.optgroup = wrapMap.option;
2659wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2660wrapMap.th = wrapMap.td;
2661
2662
2663function jqLiteIsTextNode(html) {
2664  return !HTML_REGEXP.test(html);
2665}
2666
2667function jqLiteAcceptsData(node) {
2668  // The window object can accept data but has no nodeType
2669  // Otherwise we are only interested in elements (1) and documents (9)
2670  var nodeType = node.nodeType;
2671  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2672}
2673
2674function jqLiteHasData(node) {
2675  for (var key in jqCache[node.ng339]) {
2676    return true;
2677  }
2678  return false;
2679}
2680
2681function jqLiteBuildFragment(html, context) {
2682  var tmp, tag, wrap,
2683      fragment = context.createDocumentFragment(),
2684      nodes = [], i;
2685
2686  if (jqLiteIsTextNode(html)) {
2687    // Convert non-html into a text node
2688    nodes.push(context.createTextNode(html));
2689  } else {
2690    // Convert html into DOM nodes
2691    tmp = tmp || fragment.appendChild(context.createElement("div"));
2692    tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2693    wrap = wrapMap[tag] || wrapMap._default;
2694    tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2695
2696    // Descend through wrappers to the right content
2697    i = wrap[0];
2698    while (i--) {
2699      tmp = tmp.lastChild;
2700    }
2701
2702    nodes = concat(nodes, tmp.childNodes);
2703
2704    tmp = fragment.firstChild;
2705    tmp.textContent = "";
2706  }
2707
2708  // Remove wrapper from fragment
2709  fragment.textContent = "";
2710  fragment.innerHTML = ""; // Clear inner HTML
2711  forEach(nodes, function(node) {
2712    fragment.appendChild(node);
2713  });
2714
2715  return fragment;
2716}
2717
2718function jqLiteParseHTML(html, context) {
2719  context = context || document;
2720  var parsed;
2721
2722  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2723    return [context.createElement(parsed[1])];
2724  }
2725
2726  if ((parsed = jqLiteBuildFragment(html, context))) {
2727    return parsed.childNodes;
2728  }
2729
2730  return [];
2731}
2732
vendor: 1,065 bytes, lines 2733-2774
2733/////////////////////////////////////////////
2734function JQLite(element) {
2735  if (element instanceof JQLite) {
2736    return element;
2737  }
2738
2739  var argIsString;
2740
2741  if (isString(element)) {
2742    element = trim(element);
2743    argIsString = true;
2744  }
2745  if (!(this instanceof JQLite)) {
2746    if (argIsString && element.charAt(0) != '<') {
2747      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2748    }
2749    return new JQLite(element);
2750  }
2751
2752  if (argIsString) {
2753    jqLiteAddNodes(this, jqLiteParseHTML(element));
2754  } else {
2755    jqLiteAddNodes(this, element);
2756  }
2757}
2758
2759function jqLiteClone(element) {
2760  return element.cloneNode(true);
2761}
2762
2763function jqLiteDealoc(element, onlyDescendants) {
2764  if (!onlyDescendants) jqLiteRemoveData(element);
2765
2766  if (element.querySelectorAll) {
2767    var descendants = element.querySelectorAll('*');
2768    for (var i = 0, l = descendants.length; i < l; i++) {
2769      jqLiteRemoveData(descendants[i]);
2770    }
2771  }
2772}
2773
2774function jqLiteOff(element, type, fn, unsupported) {
2775  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2776
2777  var expandoStore = jqLiteExpandoStore(element);
2778  var events = expandoStore && expandoStore.events;
2779  var handle = expandoStore && expandoStore.handle;
2780
2781  if (!handle) return; //no listeners registered
2782
2783  if (!type) {
2784    for (type in events) {
2785      if (type !== '$destroy') {
2786        removeEventListenerFn(element, type, handle);
2787      }
2788      delete events[type];
2789    }
2790  } else {
2791    forEach(type.split(' '), function(type) {
2792      if (isDefined(fn)) {
2793        var listenerFns = events[type];
2794        arrayRemove(listenerFns || [], fn);
2795        if (listenerFns && listenerFns.length > 0) {
2796          return;
2797        }
2798      }
2799
2800      removeEventListenerFn(element, type, handle);
2801      delete events[type];
2802    });
2803  }
2804}
2805
2806function jqLiteRemoveData(element, name) {
2807  var expandoId = element.ng339;
2808  var expandoStore = expandoId && jqCache[expandoId];
2809
2810  if (expandoStore) {
2811    if (name) {
2812      delete expandoStore.data[name];
2813      return;
2814    }
2815
2816    if (expandoStore.handle) {
2817      if (expandoStore.events.$destroy) {
2818        expandoStore.handle({}, '$destroy');
2819      }
2820      jqLiteOff(element);
2821    }
2822    delete jqCache[expandoId];
2823    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2824  }
2825}
2826
2827
2828function jqLiteExpandoStore(element, createIfNecessary) {
2829  var expandoId = element.ng339,
2830      expandoStore = expandoId && jqCache[expandoId];
2831
2832  if (createIfNecessary && !expandoStore) {
2833    element.ng339 = expandoId = jqNextId();
2834    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2835  }
2836
2837  return expandoStore;
2838}
2839
2840
2841function jqLiteData(element, key, value) {
2842  if (jqLiteAcceptsData(element)) {
2843
2844    var isSimpleSetter = isDefined(value);
2845    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2846    var massGetter = !key;
2847    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2848    var data = expandoStore && expandoStore.data;
2849
2850    if (isSimpleSetter) { // data('key', value)
2851      data[key] = value;
2852    } else {
2853      if (massGetter) {  // data()
2854        return data;
2855      } else {
2856        if (isSimpleGetter) { // data('key')
2857          // don't force creation of expandoStore if it doesn't exist yet
2858          return data && data[key];
2859        } else { // mass-setter: data({key1: val1, key2: val2})
2860          extend(data, key);
2861        }
2862      }
2863    }
2864  }
2865}
2866
2867function jqLiteHasClass(element, selector) {
2868  if (!element.getAttribute) return false;
2869  return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2870      indexOf(" " + selector + " ") > -1);
2871}
2872
2873function jqLiteRemoveClass(element, cssClasses) {
2874  if (cssClasses && element.setAttribute) {
2875    forEach(cssClasses.split(' '), function(cssClass) {
2876      element.setAttribute('class', trim(
2877          (" " + (element.getAttribute('class') || '') + " ")
2878          .replace(/[\n\t]/g, " ")
2879          .replace(" " + trim(cssClass) + " ", " "))
2880      );
2881    });
2882  }
2883}
2884
2885function jqLiteAddClass(element, cssClasses) {
2886  if (cssClasses && element.setAttribute) {
2887    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2888                            .replace(/[\n\t]/g, " ");
2889
2890    forEach(cssClasses.split(' '), function(cssClass) {
2891      cssClass = trim(cssClass);
2892      if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2893        existingClasses += cssClass + ' ';
2894      }
2895    });
2896
2897    element.setAttribute('class', trim(existingClasses));
2898  }
2899}
2900
2901
2902function jqLiteAddNodes(root, elements) {
2903  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
2904
2905  if (elements) {
2906
2907    // if a Node (the most common case)
2908    if (elements.nodeType) {
2909      root[root.length++] = elements;
2910    } else {
2911      var length = elements.length;
2912
2913      // if an Array or NodeList and not a Window
2914      if (typeof length === 'number' && elements.window !== elements) {
2915        if (length) {
2916          for (var i = 0; i < length; i++) {
2917            root[root.length++] = elements[i];
2918          }
2919        }
2920      } else {
2921        root[root.length++] = elements;
2922      }
2923    }
2924  }
2925}
2926
2927
2928function jqLiteController(element, name) {
2929  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
2930}
2931
2932function jqLiteInheritedData(element, name, value) {
2933  // if element is the document object work with the html element instead
2934  // this makes $(document).scope() possible
2935  if (element.nodeType == NODE_TYPE_DOCUMENT) {
2936    element = element.documentElement;
2937  }
2938  var names = isArray(name) ? name : [name];
2939
2940  while (element) {
2941    for (var i = 0, ii = names.length; i < ii; i++) {
2942      if ((value = jqLite.data(element, names[i])) !== undefined) return value;
2943    }
2944
2945    // If dealing with a document fragment node with a host element, and no parent, use the host
2946    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
2947    // to lookup parent controllers.
2948    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
2949  }
2950}
2951
2952function jqLiteEmpty(element) {
2953  jqLiteDealoc(element, true);
2954  while (element.firstChild) {
2955    element.removeChild(element.firstChild);
2956  }
2957}
2958
2959function jqLiteRemove(element, keepData) {
2960  if (!keepData) jqLiteDealoc(element);
2961  var parent = element.parentNode;
2962  if (parent) parent.removeChild(element);
2963}
2964
2965
2966function jqLiteDocumentLoaded(action, win) {
2967  win = win || window;
2968  if (win.document.readyState === 'complete') {
2969    // Force the action to be run async for consistent behaviour
2970    // from the action's point of view
2971    // i.e. it will definitely not be in a $apply
2972    win.setTimeout(action);
2973  } else {
2974    // No need to unbind this handler as load is only ever called once
2975    jqLite(win).on('load', action);
2976  }
2977}
2978
2979//////////////////////////////////////////
2980// Functions which are declared directly.
2981//////////////////////////////////////////
2982var JQLitePrototype = JQLite.prototype = {
2983  ready: function(fn) {
2984    var fired = false;
2985
2986    function trigger() {
2987      if (fired) return;
2988      fired = true;
2989      fn();
2990    }
2991
2992    // check if document is already loaded
2993    if (document.readyState === 'complete') {
2994      setTimeout(trigger);
2995    } else {
2996      this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
2997      // we can not use jqLite since we are not done loading and jQuery could be loaded later.
2998      // jshint -W064
2999      JQLite(window).on('load', trigger); // fallback to window.onload for others
3000      // jshint +W064
3001    }
3002  },
3003  toString: function() {
3004    var value = [];
3005    forEach(this, function(e) { value.push('' + e);});
3006    return '[' + value.join(', ') + ']';
3007  },
3008
3009  eq: function(index) {
3010      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3011  },
3012
3013  length: 0,
3014  push: push,
3015  sort: [].sort,
3016  splice: [].splice
3017};
3018
3019//////////////////////////////////////////
3020// Functions iterating getter/setters.
3021// these functions return self on setter and
3022// value on get.
3023//////////////////////////////////////////
3024var BOOLEAN_ATTR = {};
3025forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3026  BOOLEAN_ATTR[lowercase(value)] = value;
3027});
3028var BOOLEAN_ELEMENTS = {};
3029forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3030  BOOLEAN_ELEMENTS[value] = true;
3031});
3032var ALIASED_ATTR = {
3033  'ngMinlength': 'minlength',
3034  'ngMaxlength': 'maxlength',
3035  'ngMin': 'min',
3036  'ngMax': 'max',
3037  'ngPattern': 'pattern'
3038};
3039
3040function getBooleanAttrName(element, name) {
3041  // check dom last since we will most likely fail on name
3042  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3043
3044  // booleanAttr is here twice to minimize DOM access
3045  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3046}
3047
3048function getAliasedAttrName(element, name) {
3049  var nodeName = element.nodeName;
3050  return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
3051}
3052
3053forEach({
3054  data: jqLiteData,
3055  removeData: jqLiteRemoveData,
3056  hasData: jqLiteHasData
3057}, function(fn, name) {
3058  JQLite[name] = fn;
3059});
3060
3061forEach({
3062  data: jqLiteData,
3063  inheritedData: jqLiteInheritedData,
3064
3065  scope: function(element) {
3066    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3067    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3068  },
3069
3070  isolateScope: function(element) {
3071    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3072    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3073  },
3074
3075  controller: jqLiteController,
3076
3077  injector: function(element) {
3078    return jqLiteInheritedData(element, '$injector');
3079  },
3080
3081  removeAttr: function(element, name) {
3082    element.removeAttribute(name);
3083  },
3084
3085  hasClass: jqLiteHasClass,
3086
3087  css: function(element, name, value) {
3088    name = camelCase(name);
3089
3090    if (isDefined(value)) {
3091      element.style[name] = value;
3092    } else {
3093      return element.style[name];
3094    }
3095  },
3096
3097  attr: function(element, name, value) {
3098    var nodeType = element.nodeType;
3099    if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3100      return;
3101    }
3102    var lowercasedName = lowercase(name);
3103    if (BOOLEAN_ATTR[lowercasedName]) {
3104      if (isDefined(value)) {
3105        if (!!value) {
3106          element[name] = true;
3107          element.setAttribute(name, lowercasedName);
3108        } else {
3109          element[name] = false;
3110          element.removeAttribute(lowercasedName);
3111        }
3112      } else {
3113        return (element[name] ||
3114                 (element.attributes.getNamedItem(name) || noop).specified)
3115               ? lowercasedName
3116               : undefined;
3117      }
3118    } else if (isDefined(value)) {
3119      element.setAttribute(name, value);
3120    } else if (element.getAttribute) {
3121      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3122      // some elements (e.g. Document) don't have get attribute, so return undefined
3123      var ret = element.getAttribute(name, 2);
3124      // normalize non-existing attributes to undefined (as jQuery)
3125      return ret === null ? undefined : ret;
3126    }
3127  },
3128
3129  prop: function(element, name, value) {
3130    if (isDefined(value)) {
3131      element[name] = value;
3132    } else {
3133      return element[name];
3134    }
3135  },
3136
3137  text: (function() {
3138    getText.$dv = '';
3139    return getText;
3140
3141    function getText(element, value) {
3142      if (isUndefined(value)) {
3143        var nodeType = element.nodeType;
3144        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3145      }
3146      element.textContent = value;
3147    }
3148  })(),
3149
3150  val: function(element, value) {
3151    if (isUndefined(value)) {
3152      if (element.multiple && nodeName_(element) === 'select') {
3153        var result = [];
vendor: 3,900 bytes, lines 3154-3283
3154        forEach(element.options, function(option) {
3155          if (option.selected) {
3156            result.push(option.value || option.text);
3157          }
3158        });
3159        return result.length === 0 ? null : result;
3160      }
3161      return element.value;
3162    }
3163    element.value = value;
3164  },
3165
3166  html: function(element, value) {
3167    if (isUndefined(value)) {
3168      return element.innerHTML;
3169    }
3170    jqLiteDealoc(element, true);
3171    element.innerHTML = value;
3172  },
3173
3174  empty: jqLiteEmpty
3175}, function(fn, name) {
3176  /**
3177   * Properties: writes return selection, reads return first value
3178   */
3179  JQLite.prototype[name] = function(arg1, arg2) {
3180    var i, key;
3181    var nodeCount = this.length;
3182
3183    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3184    // in a way that survives minification.
3185    // jqLiteEmpty takes no arguments but is a setter.
3186    if (fn !== jqLiteEmpty &&
3187        (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
3188      if (isObject(arg1)) {
3189
3190        // we are a write, but the object properties are the key/values
3191        for (i = 0; i < nodeCount; i++) {
3192          if (fn === jqLiteData) {
3193            // data() takes the whole object in jQuery
3194            fn(this[i], arg1);
3195          } else {
3196            for (key in arg1) {
3197              fn(this[i], key, arg1[key]);
3198            }
3199          }
3200        }
3201        // return self for chaining
3202        return this;
3203      } else {
3204        // we are a read, so read the first child.
3205        // TODO: do we still need this?
3206        var value = fn.$dv;
3207        // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3208        var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
3209        for (var j = 0; j < jj; j++) {
3210          var nodeValue = fn(this[j], arg1, arg2);
3211          value = value ? value + nodeValue : nodeValue;
3212        }
3213        return value;
3214      }
3215    } else {
3216      // we are a write, so apply to all children
3217      for (i = 0; i < nodeCount; i++) {
3218        fn(this[i], arg1, arg2);
3219      }
3220      // return self for chaining
3221      return this;
3222    }
3223  };
3224});
3225
3226function createEventHandler(element, events) {
3227  var eventHandler = function(event, type) {
3228    // jQuery specific api
3229    event.isDefaultPrevented = function() {
3230      return event.defaultPrevented;
3231    };
3232
3233    var eventFns = events[type || event.type];
3234    var eventFnsLength = eventFns ? eventFns.length : 0;
3235
3236    if (!eventFnsLength) return;
3237
3238    if (isUndefined(event.immediatePropagationStopped)) {
3239      var originalStopImmediatePropagation = event.stopImmediatePropagation;
3240      event.stopImmediatePropagation = function() {
3241        event.immediatePropagationStopped = true;
3242
3243        if (event.stopPropagation) {
3244          event.stopPropagation();
3245        }
3246
3247        if (originalStopImmediatePropagation) {
3248          originalStopImmediatePropagation.call(event);
3249        }
3250      };
3251    }
3252
3253    event.isImmediatePropagationStopped = function() {
3254      return event.immediatePropagationStopped === true;
3255    };
3256
3257    // Copy event handlers in case event handlers array is modified during execution.
3258    if ((eventFnsLength > 1)) {
3259      eventFns = shallowCopy(eventFns);
3260    }
3261
3262    for (var i = 0; i < eventFnsLength; i++) {
3263      if (!event.isImmediatePropagationStopped()) {
3264        eventFns[i].call(element, event);
3265      }
3266    }
3267  };
3268
3269  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3270  //       events on `element`
3271  eventHandler.elem = element;
3272  return eventHandler;
3273}
3274
3275//////////////////////////////////////////
3276// Functions iterating traversal.
3277// These functions chain results into a single
3278// selector.
3279//////////////////////////////////////////
3280forEach({
3281  removeData: jqLiteRemoveData,
3282
3283  on: function jqLiteOn(element, type, fn, unsupported) {
3284    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3285
3286    // Do not add event handlers to non-elements because they will not be cleaned up.
3287    if (!jqLiteAcceptsData(element)) {
3288      return;
3289    }
3290
3291    var expandoStore = jqLiteExpandoStore(element, true);
3292    var events = expandoStore.events;
3293    var handle = expandoStore.handle;
3294
3295    if (!handle) {
3296      handle = expandoStore.handle = createEventHandler(element, events);
3297    }
3298
3299    // http://jsperf.com/string-indexof-vs-split
3300    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3301    var i = types.length;
3302
3303    while (i--) {
3304      type = types[i];
3305      var eventFns = events[type];
3306
3307      if (!eventFns) {
3308        events[type] = [];
3309
3310        if (type === 'mouseenter' || type === 'mouseleave') {
3311          // Refer to jQuery's implementation of mouseenter & mouseleave
3312          // Read about mouseenter and mouseleave:
3313          // http://www.quirksmode.org/js/events_mouse.html#link8
3314
3315          jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
3316            var target = this, related = event.relatedTarget;
3317            // For mousenter/leave call the handler if related is outside the target.
3318            // NB: No relatedTarget if the mouse left/entered the browser window
3319            if (!related || (related !== target && !target.contains(related))) {
3320              handle(event, type);
3321            }
3322          });
3323
3324        } else {
3325          if (type !== '$destroy') {
3326            addEventListenerFn(element, type, handle);
3327          }
3328        }
3329        eventFns = events[type];
3330      }
3331      eventFns.push(fn);
3332    }
3333  },
3334
3335  off: jqLiteOff,
3336
3337  one: function(element, type, fn) {
3338    element = jqLite(element);
3339
3340    //add the listener twice so that when it is called
3341    //you can remove the original function and still be
3342    //able to call element.off(ev, fn) normally
3343    element.on(type, function onFn() {
3344      element.off(type, fn);
3345      element.off(type, onFn);
3346    });
3347    element.on(type, fn);
3348  },
3349
3350  replaceWith: function(element, replaceNode) {
3351    var index, parent = element.parentNode;
3352    jqLiteDealoc(element);
3353    forEach(new JQLite(replaceNode), function(node) {
3354      if (index) {
3355        parent.insertBefore(node, index.nextSibling);
3356      } else {
3357        parent.replaceChild(node, element);
3358      }
3359      index = node;
3360    });
3361  },
3362
3363  children: function(element) {
3364    var children = [];
3365    forEach(element.childNodes, function(element) {
3366      if (element.nodeType === NODE_TYPE_ELEMENT) {
3367        children.push(element);
3368      }
3369    });
3370    return children;
3371  },
3372
3373  contents: function(element) {
3374    return element.contentDocument || element.childNodes || [];
3375  },
3376
3377  append: function(element, node) {
3378    var nodeType = element.nodeType;
3379    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3380
3381    node = new JQLite(node);
3382
3383    for (var i = 0, ii = node.length; i < ii; i++) {
3384      var child = node[i];
3385      element.appendChild(child);
3386    }
3387  },
3388
3389  prepend: function(element, node) {
3390    if (element.nodeType === NODE_TYPE_ELEMENT) {
3391      var index = element.firstChild;
3392      forEach(new JQLite(node), function(child) {
3393        element.insertBefore(child, index);
3394      });
3395    }
3396  },
3397
3398  wrap: function(element, wrapNode) {
3399    wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3400    var parent = element.parentNode;
3401    if (parent) {
3402      parent.replaceChild(wrapNode, element);
3403    }
3404    wrapNode.appendChild(element);
3405  },
3406
3407  remove: jqLiteRemove,
3408
3409  detach: function(element) {
3410    jqLiteRemove(element, true);
3411  },
3412
3413  after: function(element, newElement) {
3414    var index = element, parent = element.parentNode;
3415    newElement = new JQLite(newElement);
3416
3417    for (var i = 0, ii = newElement.length; i < ii; i++) {
3418      var node = newElement[i];
3419      parent.insertBefore(node, index.nextSibling);
3420      index = node;
3421    }
3422  },
3423
3424  addClass: jqLiteAddClass,
3425  removeClass: jqLiteRemoveClass,
3426
3427  toggleClass: function(element, selector, condition) {
3428    if (selector) {
vendor: 5,441 bytes, lines 3429-3630
3429      forEach(selector.split(' '), function(className) {
3430        var classCondition = condition;
3431        if (isUndefined(classCondition)) {
3432          classCondition = !jqLiteHasClass(element, className);
3433        }
3434        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3435      });
3436    }
3437  },
3438
3439  parent: function(element) {
3440    var parent = element.parentNode;
3441    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3442  },
3443
3444  next: function(element) {
3445    return element.nextElementSibling;
3446  },
3447
3448  find: function(element, selector) {
3449    if (element.getElementsByTagName) {
3450      return element.getElementsByTagName(selector);
3451    } else {
3452      return [];
3453    }
3454  },
3455
3456  clone: jqLiteClone,
3457
3458  triggerHandler: function(element, event, extraParameters) {
3459
3460    var dummyEvent, eventFnsCopy, handlerArgs;
3461    var eventName = event.type || event;
3462    var expandoStore = jqLiteExpandoStore(element);
3463    var events = expandoStore && expandoStore.events;
3464    var eventFns = events && events[eventName];
3465
3466    if (eventFns) {
3467      // Create a dummy event to pass to the handlers
3468      dummyEvent = {
3469        preventDefault: function() { this.defaultPrevented = true; },
3470        isDefaultPrevented: function() { return this.defaultPrevented === true; },
3471        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3472        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3473        stopPropagation: noop,
3474        type: eventName,
3475        target: element
3476      };
3477
3478      // If a custom event was provided then extend our dummy event with it
3479      if (event.type) {
3480        dummyEvent = extend(dummyEvent, event);
3481      }
3482
3483      // Copy event handlers in case event handlers array is modified during execution.
3484      eventFnsCopy = shallowCopy(eventFns);
3485      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3486
3487      forEach(eventFnsCopy, function(fn) {
3488        if (!dummyEvent.isImmediatePropagationStopped()) {
3489          fn.apply(element, handlerArgs);
3490        }
3491      });
3492    }
3493  }
3494}, function(fn, name) {
3495  /**
3496   * chaining functions
3497   */
3498  JQLite.prototype[name] = function(arg1, arg2, arg3) {
3499    var value;
3500
3501    for (var i = 0, ii = this.length; i < ii; i++) {
3502      if (isUndefined(value)) {
3503        value = fn(this[i], arg1, arg2, arg3);
3504        if (isDefined(value)) {
3505          // any function which returns a value needs to be wrapped
3506          value = jqLite(value);
3507        }
3508      } else {
3509        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3510      }
3511    }
3512    return isDefined(value) ? value : this;
3513  };
3514
3515  // bind legacy bind/unbind to on/off
3516  JQLite.prototype.bind = JQLite.prototype.on;
3517  JQLite.prototype.unbind = JQLite.prototype.off;
3518});
3519
3520
3521// Provider for private $$jqLite service
3522function $$jqLiteProvider() {
3523  this.$get = function $$jqLite() {
3524    return extend(JQLite, {
3525      hasClass: function(node, classes) {
3526        if (node.attr) node = node[0];
3527        return jqLiteHasClass(node, classes);
3528      },
3529      addClass: function(node, classes) {
3530        if (node.attr) node = node[0];
3531        return jqLiteAddClass(node, classes);
3532      },
3533      removeClass: function(node, classes) {
3534        if (node.attr) node = node[0];
3535        return jqLiteRemoveClass(node, classes);
3536      }
3537    });
3538  };
3539}
3540
3541/**
3542 * Computes a hash of an 'obj'.
3543 * Hash of a:
3544 *  string is string
3545 *  number is number as string
3546 *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3547 *         that is also assigned to the $$hashKey property of the object.
3548 *
3549 * @param obj
3550 * @returns {string} hash string such that the same input will have the same hash string.
3551 *         The resulting string key is in 'type:hashKey' format.
3552 */
3553function hashKey(obj, nextUidFn) {
3554  var key = obj && obj.$$hashKey;
3555
3556  if (key) {
3557    if (typeof key === 'function') {
3558      key = obj.$$hashKey();
3559    }
3560    return key;
3561  }
3562
3563  var objType = typeof obj;
3564  if (objType == 'function' || (objType == 'object' && obj !== null)) {
3565    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3566  } else {
3567    key = objType + ':' + obj;
3568  }
3569
3570  return key;
3571}
3572
3573/**
3574 * HashMap which can use objects as keys
3575 */
3576function HashMap(array, isolatedUid) {
3577  if (isolatedUid) {
3578    var uid = 0;
3579    this.nextUid = function() {
3580      return ++uid;
3581    };
3582  }
3583  forEach(array, this.put, this);
3584}
3585HashMap.prototype = {
3586  /**
3587   * Store key value pair
3588   * @param key key to store can be any type
3589   * @param value value to store can be any type
3590   */
3591  put: function(key, value) {
3592    this[hashKey(key, this.nextUid)] = value;
3593  },
3594
3595  /**
3596   * @param key
3597   * @returns {Object} the value for the key
3598   */
3599  get: function(key) {
3600    return this[hashKey(key, this.nextUid)];
3601  },
3602
3603  /**
3604   * Remove the key/value pair
3605   * @param key
3606   */
3607  remove: function(key) {
3608    var value = this[key = hashKey(key, this.nextUid)];
3609    delete this[key];
3610    return value;
3611  }
3612};
3613
3614var $$HashMapProvider = [function() {
3615  this.$get = [function() {
3616    return HashMap;
3617  }];
3618}];
3619
3620/**
3621 * @ngdoc function
3622 * @module ng
3623 * @name angular.injector
3624 * @kind function
3625 *
3626 * @description
3627 * Creates an injector object that can be used for retrieving services as well as for
3628 * dependency injection (see {@link guide/di dependency injection}).
3629 *
3630 * @param {Array.<string|Function>}
3630 modules A list of module functions or their aliases. See
3631 *     {@link angular.module}. The `ng` module must be explicitly added.
3632 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3633 *     disallows argument name annotation inference.
3634 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3635 *
3636 * @example
3637 * Typical usage
3638 * ```js
3639 *   // create an injector
3640 *   var $injector = angular.injector(['ng']);
3641 *
3642 *   // use the injector to kick off your application
3643 *   // use the type inference to auto inject arguments, or use implicit injection
3644 *   $injector.invoke(function($rootScope, $compile, $document) {
3645 *     $compile($document)($rootScope);
3646 *     $rootScope.$digest();
3647 *   });
3648 * ```
3649 *
3650 * Sometimes you want to get access to the injector of a currently running Angular app
3651 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3652 * application has been bootstrapped. You can do this using the extra `injector()` added
3653 * to JQuery/jqLite elements. See {@link angular.element}.
3654 *
3655 * *This is fairly rare but could be the case if a third party library is injecting the
3656 * markup.*
3657 *
3658 * In the following example a new block of HTML containing a `ng-controller`
3659 * directive is added to the end of the document body by JQuery. We then compile and link
3660 * it into the current AngularJS scope.
3661 *
3662 * ```js
3663 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3664 * $(document.body).append($div);
3665 *
3666 * angular.element(document).injector().invoke(function($compile) {
3667 *   var scope = angular.element($div).scope();
3668 *   $compile($div)(scope);
3669 * });
3670 * ```
3671 */
3672
3673
3674/**
3675 * @ngdoc module
3676 * @name auto
3677 * @description
3678 *
3679 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3680 */
3681
3682var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
3683var FN_ARG_SPLIT = /,/;
3684var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3685var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3686var $injectorMinErr = minErr('$injector');
3687
3688function anonFn(fn) {
3689  // For anonymous functions, showing at the very least the function signature can help in
3690  // debugging.
3691  var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3692      args = fnText.match(FN_ARGS);
3693  if (args) {
3694    return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3695  }
3696  return 'fn';
3697}
3698
3699function annotate(fn, strictDi, name) {
3700  var $inject,
3701      fnText,
3702      argDecl,
3703      last;
3704
3705  if (typeof fn === 'function') {
3706    if (!($inject = fn.$inject)) {
3707      $inject = [];
3708      if (fn.length) {
3709        if (strictDi) {
3710          if (!isString(name) || !name) {
3711            name = fn.name || anonFn(fn);
3712          }
3713          throw $injectorMinErr('strictdi',
3714            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3715        }
3716        fnText = fn.toString().replace(STRIP_COMMENTS, '');
3717        argDecl = fnText.match(FN_ARGS);
3718        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3719          arg.replace(FN_ARG, function(all, underscore, name) {
3720            $inject.push(name);
3721          });
3722        });
3723      }
3724      fn.$inject = $inject;
3725    }
3726  } else if (isArray(fn)) {
3727    last = fn.length - 1;
3728    assertArgFn(fn[last], 'fn');
3729    $inject = fn.slice(0, last);
3730  } else {
3731    assertArgFn(fn, 'fn', true);
3732  }
3733  return $inject;
3734}
3735
3736///////////////////////////////////////
3737
3738/**
3739 * @ngdoc service
3740 * @name $injector
3741 *
3742 * @description
3743 *
3744 * `$injector` is used to retrieve object instances as defined by
3745 * {@link auto.$provide provider}, instantiate types, invoke methods,
3746 * and load modules.
3747 *
3748 * The following always holds true:
3749 *
3750 * ```js
3751 *   var $injector = angular.injector();
3752 *   expect($injector.get('$injector')).toBe($injector);
3753 *   expect($injector.invoke(function($injector) {
3754 *     return $injector;
3755 *   })).toBe($injector);
3756 * ```
3757 *
3758 * # Injection Function Annotation
3759 *
3760 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3761 * following are all valid ways of annotating function with injection arguments and are equivalent.
3762 *
3763 * ```js
3764 *   // inferred (only works if code not minified/obfuscated)
3765 *   $injector.invoke(function(serviceA){});
3766 *
3767 *   // annotated
3768 *   function explicit(serviceA) {};
3769 *   explicit.$inject = ['serviceA'];
3770 *   $injector.invoke(explicit);
3771 *
3772 *   // inline
3773 *   $injector.invoke(['serviceA', function(serviceA){}]);
3774 * ```
3775 *
3776 * ## Inference
3777 *
3778 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3779 * can then be parsed and the function arguments can be extracted. This method of discovering
3780 * annotations is disallowed when the injector is in strict mode.
3781 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3782 * argument names.
3783 *
3784 * ## `$inject` Annotation
3785 * By adding an `$inject` property onto a function the injection parameters can be specified.
3786 *
3787 * ## Inline
3788 * As an array of injection names, where the last item in the array is the function to call.
3789 */
3790
3791/**
3792 * @ngdoc method
3793 * @name $injector#get
3794 *
3795 * @description
3796 * Return an instance of the service.
3797 *
3798 * @param {string} name The name of the instance to retrieve.
3799 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3800 * @return {*} The instance.
3801 */
3802
3803/**
3804 * @ngdoc method
3805 * @name $injector#invoke
3806 *
3807 * @description
3808 * Invoke the method and supply the method arguments from the `$injector`.
3809 *
3810 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3811 *   injected according to the {@link guide/di $inject Annotation} rules.
3812 * @param {Object=} self The `this` for the invoked method.
3813 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3814 *                         object first, before the `$injector` is consulted.
3815 * @returns {*} the value returned by the invoked `fn` function.
3816 */
3817
3818/**
3819 * @ngdoc method
3820 * @name $injector#has
3821 *
3822 * @description
3823 * Allows the user to query if the particular service exists.
3824 *
3825 * @param {string} name Name of the service to query.
3826 * @returns {boolean} `true` if injector has given service.
3827 */
3828
3829/**
3830 * @ngdoc method
3831 * @name $injector#instantiate
3832 * @description
3833 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3834 * operator, and supplies all of the arguments to the constructor function as specified by the
3835 * constructor annotation.
3836 *
3837 * @param {Function} Type Annotated constructor function.
3838 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3839 * object first, before the `$injector` is consulted.
3840 * @returns {Object} new instance of `Type`.
3841 */
3842
3843/**
3844 * @ngdoc method
3845 * @name $injector#annotate
3846 *
3847 * @description
3848 * Returns an array of service names which the function is requesting for injection. This API is
3849 * used by the injector to determine which services need to be injected into the function when the
3850 * function is invoked. There are three ways in which the function can be annotated with the needed
3851 * dependencies.
3852 *
3853 * # Argument names
3854 *
3855 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3856 * by converting the function into a string using `toString()` method and extracting the argument
3857 * names.
3858 * ```js
3859 *   // Given
3860 *   function MyController($scope, $route) {
3861 *     // ...
3862 *   }
3863 *
3864 *   // Then
3865 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3866 * ```
3867 *
3868 * You can disallow this method by using strict injection mode.
3869 *
3870 * This method does not work with code minification / obfuscation. For this reason the following
3871 * annotation strategies are supported.
3872 *
3873 * # The `$inject` property
3874 *
3875 * If a function has an `$inject` property and its value is an array of strings, then the strings
3876 * represent names of services to be injected into the function.
3877 * ```js
3878 *   // Given
3879 *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
3880 *     // ...
3881 *   }
3882 *   // Define function dependencies
3883 *   MyController['$inject'] = ['$scope', '$route'];
3884 *
3885 *   // Then
3886 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3887 * ```
3888 *
3889 * # The array notation
3890 *
3891 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3892 * is very inconvenient. In these situations using the array notation to specify the dependencies in
3893 * a way that survives minification is a better choice:
3894 *
3895 * ```js
3896 *   // We wish to write this (not minification / obfuscation safe)
3897 *   injector.invoke(function($compile, $rootScope) {
3898 *     // ...
3899 *   });
3900 *
3901 *   // We are forced to write break inlining
3902 *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
3903 *     // ...
3904 *   };
3905 *   tmpFn.$inject = ['$compile', '$rootScope'];
3906 *   injector.invoke(tmpFn);
3907 *
3908 *   // To better support inline function the inline annotation is supported
3909 *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
3910 *     // ...
3911 *   }]);
3912 *
3913 *   // Therefore
3914 *   expect(injector.annotate(
3915 *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
3916 *    ).toEqual(['$compile', '$rootScope']);
3917 * ```
3918 *
3919 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
3920 * be retrieved as described above.
3921 *
3922 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
3923 *
3924 * @returns {Array.<string>} The names of the services which the function requires.
3925 */
3926
3927
3928
3929
3930/**
3931 * @ngdoc service
3932 * @name $provide
3933 *
3934 * @description
3935 *
3936 * The {@link auto.$provide $provide} service has a number of methods for registering components
3937 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
3938 * {@link angular.Module}.
3939 *
3940 * An Angular **service** is a singleton object created by a **service factory**.  These **service
3941 * factories** are functions which, in turn, are created by a **service provider**.
3942 * The **service providers** are constructor functions. When instantiated they must contain a
3943 * property called `$get`, which holds the **service factory** function.
3944 *
3945 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
3946 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
3947 * function to get the instance of the **service**.
3948 *
3949 * Often services have no configuration options and there is no need to add methods to the service
3950 * provider.  The provider will be no more than a constructor function with a `$get` property. For
3951 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
3952 * services without specifying a provider.
3953 *
3954 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
3955 *     {@link auto.$injector $injector}
3956 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
3957 *     providers and services.
3958 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
3959 *     services, not providers.
3960 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
3961 *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
3962 *     given factory function.
3963 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
3964 *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
3965 *      a new object using the given constructor function.
3966 *
3967 * See the individual methods for more information and examples.
3968 */
3969
3970/**
3971 * @ngdoc method
3972 * @name $provide#provider
3973 * @description
3974 *
3975 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
3976 * are constructor functions, whose instances are responsible for "providing" a factory for a
3977 * service.
3978 *
3979 * Service provider names start with the name of the service they provide followed by `Provider`.
3980 * For example, the {@link ng.$log $log} service has a provider called
3981 * {@link ng.$logProvider $logProvider}.
3982 *
3983 * Service provider objects can have additional methods which allow configuration of the provider
3984 * and its service. Importantly, you can configure what kind of service is created by the `$get`
3985 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
3986 * method {@link ng.$logProvider#debugEnabled debugEnabled}
3987 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
3988 * console or not.
3989 *
3990 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
3991                        'Provider'` key.
3992 * @param {(Object|function())} provider If the provider is:
3993 *
3994 *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
3995 *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
3996 *   - `Constructor`: a new instance of the provider will be created using
3997 *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
3998 *
3999 * @returns {Object} registered provider instance
4000
4001 * @example
4002 *
4003 * The following example shows how to create a simple event tracking service and register it using
4004 * {@link auto.$provide#provider $provide.provider()}.
4005 *
4006 * ```js
4007 *  // Define the eventTracker provider
4008 *  function EventTrackerProvider() {
4009 *    var trackingUrl = '/track';
4010 *
4011 *    // A provider method for configuring where the tracked events should been saved
4012 *    this.setTrackingUrl = function(url) {
4013 *      trackingUrl = url;
4014 *    };
4015 *
4016 *    // The service factory function
4017 *    this.$get = ['$http', function($http) {
4018 *      var trackedEvents = {};
4019 *      return {
4020 *        // Call this to track an event
4021 *        event: function(event) {
4022 *          var count = trackedEvents[event] || 0;
4023 *          count += 1;
4024 *          trackedEvents[event] = count;
4025 *          return count;
4026 *        },
4027 *        // Call this to save the tracked events to the trackingUrl
4028 *        save: function() {
4029 *          $http.post(trackingUrl, trackedEvents);
4030 *        }
4031 *      };
4032 *    }];
4033 *  }
4034 *
4035 *  describe('eventTracker', function() {
4036 *    var postSpy;
4037 *
4038 *    beforeEach(module(function($provide) {
4039 *      // Register the eventTracker provider
4040 *      $provide.provider('eventTracker', EventTrackerProvider);
4041 *    }));
4042 *
4043 *    beforeEach(module(function(eventTrackerProvider) {
4044 *      // Configure eventTracker provider
4045 *      eventTrackerProvider.setTrackingUrl('/custom-track');
4046 *    }));
4047 *
4048 *    it('tracks events', inject(function(eventTracker) {
4049 *      expect(eventTracker.event('login')).toEqual(1);
4050 *      expect(eventTracker.event('login')).toEqual(2);
4051 *    }));
4052 *
4053 *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4054 *      postSpy = spyOn($http, 'post');
4055 *      eventTracker.event('login');
4056 *      eventTracker.save();
4057 *      expect(postSpy).toHaveBeenCalled();
4058 *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4059 *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4060 *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4061 *    }));
4062 *  });
4063 * ```
4064 */
4065
4066/**
4067 * @ngdoc method
4068 * @name $provide#factory
4069 * @description
4070 *
4071 * Register a **service factory**, which will be called to return the service instance.
4072 * This is short for registering a service where its provider consists of only a `$get` property,
4073 * which is the given service factory function.
4074 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4075 * configure your service in a provider.
4076 *
4077 * @param {string} name The name of the instance.
4078 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4079 *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4080 * @returns {Object} registered provider instance
4081 *
4082 * @example
4083 * Here is an example of registering a service
4084 * ```js
4085 *   $provide.factory('ping', ['$http', function($http) {
4086 *     return function ping() {
4087 *       return $http.send('/ping');
4088 *     };
4089 *   }]);
4090 * ```
4091 * You would then inject and use this service like this:
4092 * ```js
4093 *   someModule.controller('Ctrl', ['ping', function(ping) {
4094 *     ping();
4095 *   }]);
4096 * ```
4097 */
4098
4099
4100/**
4101 * @ngdoc method
4102 * @name $provide#service
4103 * @description
4104 *
4105 * Register a **service constructor**, which will be invoked with `new` to create the service
4106 * instance.
4107 * This is short for registering a service where its provider's `$get` property is the service
4108 * constructor function that will be used to instantiate the service instance.
4109 *
4110 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4111 * as a type/class.
4112 *
4113 * @param {string} name The name of the instance.
4114 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4115 *     that will be instantiated.
4116 * @returns {Object} registered provider instance
4117 *
4118 * @example
4119 * Here is an example of registering a service using
4120 * {@link auto.$provide#service $provide.service(class)}.
4121 * ```js
4122 *   var Ping = function($http) {
4123 *     this.$http = $http;
4124 *   };
4125 *
4126 *   Ping.$inject = ['$http'];
4127 *
4128 *   Ping.prototype.send = function() {
4129 *     return this.$http.get('/ping');
4130 *   };
4131 *   $provide.service('ping', Ping);
4132 * ```
4133 * You would then inject and use this service like this:
4134 * ```js
4135 *   someModule.controller('Ctrl', ['ping', function(ping) {
4136 *     ping.send();
4137 *   }]);
4138 * ```
4139 */
4140
4141
4142/**
4143 * @ngdoc method
4144 * @name $provide#value
4145 * @description
4146 *
4147 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4148 * number, an array, an object or a function.  This is short for registering a service where its
4149 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4150 * service**.
4151 *
4152 * Value services are similar to constant services, except that they cannot be injected into a
4153 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4154 * an Angular
4155 * {@link auto.$provide#decorator decorator}.
4156 *
4157 * @param {string} name The name of the instance.
4158 * @param {*} value The value.
4159 * @returns {Object} registered provider instance
4160 *
4161 * @example
4162 * Here are some examples of creating value services.
4163 * ```js
4164 *   $provide.value('ADMIN_USER', 'admin');
4165 *
4166 *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4167 *
4168 *   $provide.value('halfOf', function(value) {
4169 *     return value / 2;
4170 *   });
4171 * ```
4172 */
4173
4174
4175/**
4176 * @ngdoc method
4177 * @name $provide#constant
4178 * @description
4179 *
4180 * Register a **constant service**, such as a string, a number, an array, an object or a function,
4181 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
4182 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4183 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4184 *
4185 * @param {string} name The name of the constant.
4186 * @param {*} value The constant value.
4187 * @returns {Object} registered instance
4188 *
4189 * @example
4190 * Here a some examples of creating constants:
4191 * ```js
4192 *   $provide.constant('SHARD_HEIGHT', 306);
4193 *
4194 *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4195 *
4196 *   $provide.constant('double', function(value) {
4197 *     return value * 2;
4198 *   });
4199 * ```
4200 */
4201
4202
4203/**
4204 * @ngdoc method
4205 * @name $provide#decorator
4206 * @description
4207 *
4208 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4209 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
4210 * service. The object returned by the decorator may be the original service, or a new service
4211 * object which replaces or wraps and delegates to the original service.
4212 *
4213 * @param {string} name The name of the service to decorate.
4214 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4215 *    instantiated and should return the decorated service instance. The function is called using
4216 *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4217 *    Local injection arguments:
4218 *
4219 *    * `$delegate` - The original service instance, which can be monkey patched, configured,
4220 *      decorated or delegated to.
4221 *
4222 * @example
4223 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4224 * calls to {@link ng.$log#error $log.warn()}.
4225 * ```js
4226 *   $provide.decorator('$log', ['$delegate', function($delegate) {
4227 *     $delegate.warn = $delegate.error;
4228 *     return $delegate;
4229 *   }]);
4230 * ```
4231 */
4232
4233
4234function createInjector(modulesToLoad, strictDi) {
4235  strictDi = (strictDi === true);
4236  var INSTANTIATING = {},
4237      providerSuffix = 'Provider',
4238      path = [],
4239      loadedModules = new HashMap([], true),
4240      providerCache = {
4241        $provide: {
4242            provider: supportObject(provider),
4243            factory: supportObject(factory),
4244            service: supportObject(service),
4245            value: supportObject(value),
4246            constant: supportObject(constant),
4247            decorator: decorator
4248          }
4249      },
4250      providerInjector = (providerCache.$injector =
4251          createInternalInjector(providerCache, function(serviceName, caller) {
4252            if (angular.isString(caller)) {
4253              path.push(caller);
4254            }
4255            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4256          })),
4257      instanceCache = {},
4258      instanceInjector = (instanceCache.$injector =
4259          createInternalInjector(instanceCache, function(serviceName, caller) {
4260            var provider = providerInjector.get(serviceName + providerSuffix, caller);
4261            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4262          }));
4263
4264
4265  forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4266
4267  return instanceInjector;
4268
4269  ////////////////////////////////////
4270  // $provider
4271  ////////////////////////////////////
4272
4273  function supportObject(delegate) {
4274    return function(key, value) {
4275      if (isObject(key)) {
4276        forEach(key, reverseParams(delegate));
4277      } else {
4278        return delegate(key, value);
4279      }
4280    };
4281  }
4282
4283  function provider(name, provider_) {
4284    assertNotHasOwnProperty(name, 'service');
4285    if (isFunction(provider_) || isArray(provider_)) {
4286      provider_ = providerInjector.instantiate(provider_);
4287    }
4288    if (!provider_.$get) {
4289      throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4290    }
4291    return providerCache[name + providerSuffix] = provider_;
4292  }
4293
4294  function enforceReturnValue(name, factory) {
4295    return function enforcedReturnValue() {
4296      var result = instanceInjector.invoke(factory, this);
4297      if (isUndefined(result)) {
4298        throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4299      }
4300      return result;
4301    };
4302  }
4303
4304  function factory(name, factoryFn, enforce) {
4305    return provider(name, {
4306      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4307    });
4308  }
4309
4310  function service(name, constructor) {
4311    return factory(name, ['$injector', function($injector) {
4312      return $injector.instantiate(constructor);
4313    }]);
4314  }
4315
4316  function value(name, val) { return factory(name, valueFn(val), false); }
4317
4318  function constant(name, value) {
4319    assertNotHasOwnProperty(name, 'constant');
4320    providerCache[name] = value;
4321    instanceCache[name] = value;
4322  }
4323
4324  function decorator(serviceName, decorFn) {
4325    var origProvider = providerInjector.get(serviceName + providerSuffix),
4326        orig$get = origProvider.$get;
4327
4328    origProvider.$get = function() {
4329      var origInstance = instanceInjector.invoke(orig$get, origProvider);
4330      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4331    };
4332  }
4333
4334  ////////////////////////////////////
4335  // Module Loading
4336  ////////////////////////////////////
4337  function loadModules(modulesToLoad) {
4338    var runBlocks = [], moduleFn;
vendor: 5,290 bytes, lines 4339-4496
4339    forEach(modulesToLoad, function(module) {
4340      if (loadedModules.get(module)) return;
4341      loadedModules.put(module, true);
4342
4343      function runInvokeQueue(queue) {
4344        var i, ii;
4345        for (i = 0, ii = queue.length; i < ii; i++) {
4346          var invokeArgs = queue[i],
4347              provider = providerInjector.get(invokeArgs[0]);
4348
4349          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4350        }
4351      }
4352
4353      try {
4354        if (isString(module)) {
4355          moduleFn = angularModule(module);
4356          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4357          runInvokeQueue(moduleFn._invokeQueue);
4358          runInvokeQueue(moduleFn._configBlocks);
4359        } else if (isFunction(module)) {
4360            runBlocks.push(providerInjector.invoke(module));
4361        } else if (isArray(module)) {
4362            runBlocks.push(providerInjector.invoke(module));
4363        } else {
4364          assertArgFn(module, 'module');
4365        }
4366      } catch (e) {
4367        if (isArray(module)) {
4368          module = module[module.length - 1];
4369        }
4370        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4371          // Safari & FF's stack traces don't contain error.message content
4372          // unlike those of Chrome and IE
4373          // So if stack doesn't contain message, we create a new string that contains both.
4374          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4375          /* jshint -W022 */
4376          e = e.message + '\n' + e.stack;
4377        }
4378        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4379                  module, e.stack || e.message || e);
4380      }
4381    });
4382    return runBlocks;
4383  }
4384
4385  ////////////////////////////////////
4386  // internal Injector
4387  ////////////////////////////////////
4388
4389  function createInternalInjector(cache, factory) {
4390
4391    function getService(serviceName, caller) {
4392      if (cache.hasOwnProperty(serviceName)) {
4393        if (cache[serviceName] === INSTANTIATING) {
4394          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4395                    serviceName + ' <- ' + path.join(' <- '));
4396        }
4397        return cache[serviceName];
4398      } else {
4399        try {
4400          path.unshift(serviceName);
4401          cache[serviceName] = INSTANTIATING;
4402          return cache[serviceName] = factory(serviceName, caller);
4403        } catch (err) {
4404          if (cache[serviceName] === INSTANTIATING) {
4405            delete cache[serviceName];
4406          }
4407          throw err;
4408        } finally {
4409          path.shift();
4410        }
4411      }
4412    }
4413
4414    function invoke(fn, self, locals, serviceName) {
4415      if (typeof locals === 'string') {
4416        serviceName = locals;
4417        locals = null;
4418      }
4419
4420      var args = [],
4421          $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4422          length, i,
4423          key;
4424
4425      for (i = 0, length = $inject.length; i < length; i++) {
4426        key = $inject[i];
4427        if (typeof key !== 'string') {
4428          throw $injectorMinErr('itkn',
4429                  'Incorrect injection token! Expected service name as string, got {0}', key);
4430        }
4431        args.push(
4432          locals && locals.hasOwnProperty(key)
4433          ? locals[key]
4434          : getService(key, serviceName)
4435        );
4436      }
4437      if (isArray(fn)) {
4438        fn = fn[length];
4439      }
4440
4441      // http://jsperf.com/angularjs-invoke-apply-vs-switch
4442      // #5388
4443      return fn.apply(self, args);
4444    }
4445
4446    function instantiate(Type, locals, serviceName) {
4447      // Check if Type is annotated and use just the given function at n-1 as parameter
4448      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4449      // Object creation: http://jsperf.com/create-constructor/2
4450      var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4451      var returnedValue = invoke(Type, instance, locals, serviceName);
4452
4453      return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4454    }
4455
4456    return {
4457      invoke: invoke,
4458      instantiate: instantiate,
4459      get: getService,
4460      annotate: createInjector.$$annotate,
4461      has: function(name) {
4462        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4463      }
4464    };
4465  }
4466}
4467
4468createInjector.$$annotate = annotate;
4469
4470/**
4471 * @ngdoc provider
4472 * @name $anchorScrollProvider
4473 *
4474 * @description
4475 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4476 * {@link ng.$location#hash $location.hash()} changes.
4477 */
4478function $AnchorScrollProvider() {
4479
4480  var autoScrollingEnabled = true;
4481
4482  /**
4483   * @ngdoc method
4484   * @name $anchorScrollProvider#disableAutoScrolling
4485   *
4486   * @description
4487   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4488   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4489   * Use this method to disable automatic scrolling.
4490   *
4491   * If automatic scrolling is disabled, one must explicitly call
4492   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4493   * current hash.
4494   */
4495  this.disableAutoScrolling = function() {
4496    autoScrollingEnabled = false;
4497  };
4498
4499  /**
4500   * @ngdoc service
4501   * @name $anchorScroll
4502   * @kind function
4503   * @requires $window
4504   * @requires $location
4505   * @requires $rootScope
4506   *
4507   * @description
4508   * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4509   * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4510   * in the
4511   * [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
4512   *
4513   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4514   * match any anchor whenever it changes. This can be disabled by calling
4515   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4516   *
4517   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4518   * vertical scroll-offset (either fixed or dynamic).
4519   *
4520   * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4521   *                       {@link ng.$location#hash $location.hash()} will be used.
4522   *
4523   * @property {(number|function|jqLite)} yOffset
4524   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4525   * positioned elements at the top of the page, such as navbars, headers etc.
4526   *
4527   * `yOffset` can be specified in various ways:
4528   * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4529   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4530   *   a number representing the offset (in pixels).<br /><br />
4531   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4532   *   the top of the page to the element's bottom will be used as offset.<br />
4533   *   **Note**: The element will be taken into account only as long as its `position` is set to
4534   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4535   *   their height and/or positioning according to the viewport's size.
4536   *
4537   * <br />
4538   * <div class="alert alert-warning">
4539   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4540   * not some child element.
4541   * </div>
4542   *
4543   * @example
4544     <example module="anchorScrollExample">
4545       <file name="index.html">
4546         <div id="scrollArea" ng-controller="ScrollController">
4547           <a ng-click="gotoBottom()">Go to bottom</a>
4548           <a id="bottom"></a> You're at the bottom!
4549         </div>
4550       </file>
4551       <file name="script.js">
4552         angular.module('anchorScrollExample', [])
4553           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4554             function ($scope, $location, $anchorScroll) {
4555               $scope.gotoBottom = function() {
4556                 // set the location.hash to the id of
4557                 // the element you wish to scroll to.
4558                 $location.hash('bottom');
4559
4560                 // call $anchorScroll()
4561                 $anchorScroll();
4562               };
4563             }]);
4564       </file>
4565       <file name="style.css">
4566         #scrollArea {
4567           height: 280px;
4568           overflow: auto;
4569         }
4570
4571         #bottom {
4572           display: block;
4573           margin-top: 2000px;
4574         }
4575       </file>
4576     </example>
4577   *
4578   * <hr />
4579   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4580   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4581   *
4582   * @example
4583     <example module="anchorScrollOffsetExample">
4584       <file name="index.html">
4585         <div class="fixed-header" ng-controller="headerCtrl">
4586           <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4587             Go to anchor {{x}}
4588           </a>
4589         </div>
4590         <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4591           Anchor {{x}} of 5
4592         </div>
4593       </file>
4594       <file name="script.js">
4595         angular.module('anchorScrollOffsetExample', [])
4596           .run(['$anchorScroll', function($anchorScroll) {
4597             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4598           }])
4599           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4600             function ($anchorScroll, $location, $scope) {
4601               $scope.gotoAnchor = function(x) {
4602                 var newHash = 'anchor' + x;
4603                 if ($location.hash() !== newHash) {
4604                   // set the $location.hash to `newHash` and
4605                   // $anchorScroll will automatically scroll to it
4606                   $location.hash('anchor' + x);
4607                 } else {
4608                   // call $anchorScroll() explicitly,
4609                   // since $location.hash hasn't changed
4610                   $anchorScroll();
4611                 }
4612               };
4613             }
4614           ]);
4615       </file>
4616       <file name="style.css">
4617         body {
4618           padding-top: 50px;
4619         }
4620
4621         .anchor {
4622           border: 2px dashed DarkOrchid;
4623           padding: 10px 10px 200px 10px;
4624         }
4625
4626         .fixed-header {
4627           background-color: rgba(0, 0, 0, 0.2);
4628           height: 50px;
4629           position: fixed;
4630           top: 0; left: 0; right: 0;
4631         }
4632
4633         .fixed-header > a {
4634           display: inline-block;
4635           margin: 5px 15px;
4636         }
4637       </file>
4638     </example>
4639   */
4640  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4641    var document = $window.document;
4642
4643    // Helper function to get first anchor from a NodeList
4644    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4645    //  and working in all supported browsers.)
4646    function getFirstAnchor(list) {
4647      var result = null;
4648      Array.prototype.some.call(list, function(element) {
4649        if (nodeName_(element) === 'a') {
4650          result = element;
4651          return true;
4652        }
4653      });
4654      return result;
4655    }
4656
4657    function getYOffset() {
4658
4659      var offset = scroll.yOffset;
4660
4661      if (isFunction(offset)) {
4662        offset = offset();
4663      } else if (isElement(offset)) {
4664        var elem = offset[0];
4665        var style = $window.getComputedStyle(elem);
4666        if (style.position !== 'fixed') {
4667          offset = 0;
4668        } else {
4669          offset = elem.getBoundingClientRect().bottom;
4670        }
4671      } else if (!isNumber(offset)) {
4672        offset = 0;
4673      }
4674
4675      return offset;
4676    }
4677
4678    function scrollTo(elem) {
4679      if (elem) {
4680        elem.scrollIntoView();
4681
4682        var offset = getYOffset();
4683
4684        if (offset) {
4685          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4686          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4687          // top of the viewport.
4688          //
4689          // IF the number of pixels from the top of `elem` to the end of the page's content is less
4690          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4691          // way down the page.
4692          //
4693          // This is often the case for elements near the bottom of the page.
4694          //
4695          // In such cases we do not need to scroll the whole `offset` up, just the difference between
4696          // the top of the element and the offset, which is enough to align the top of `elem` at the
4697          // desired position.
4698          var elemTop = elem.getBoundingClientRect().top;
4699          $window.scrollBy(0, elemTop - offset);
4700        }
4701      } else {
4702        $window.scrollTo(0, 0);
4703      }
4704    }
4705
4706    function scroll(hash) {
4707      hash = isString(hash) ? hash : $location.hash();
4708      var elm;
4709
4710      // empty hash, scroll to the top of the page
4711      if (!hash) scrollTo(null);
4712
4713      // element with given id
4714      else if ((elm = document.getElementById(hash))) scrollTo(elm);
4715
4716      // first anchor with given name :-D
4717      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4718
4719      // no element and hash == 'top', scroll to the top of the page
4720      else if (hash === 'top') scrollTo(null);
4721    }
4722
4723    // does not scroll when user clicks on anchor link that is currently on
4724    // (no url change, no $location.hash() change), browser native does scroll
4725    if (autoScrollingEnabled) {
4726      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4727        function autoScrollWatchAction(newVal, oldVal) {
4728          // skip the initial scroll if $location.hash is empty
4729          if (newVal === oldVal && newVal === '') return;
4730
4731          jqLiteDocumentLoaded(function() {
4732            $rootScope.$evalAsync(scroll);
4733          });
4734        });
4735    }
4736
4737    return scroll;
4738  }];
4739}
4740
4741var $animateMinErr = minErr('$animate');
4742var ELEMENT_NODE = 1;
4743var NG_ANIMATE_CLASSNAME = 'ng-animate';
4744
4745function mergeClasses(a,b) {
4746  if (!a && !b) return '';
4747  if (!a) return b;
4748  if (!b) return a;
4749  if (isArray(a)) a = a.join(' ');
4750  if (isArray(b)) b = b.join(' ');
4751  return a + ' ' + b;
4752}
4753
4754function extractElementNode(element) {
4755  for (var i = 0; i < element.length; i++) {
4756    var elm = element[i];
4757    if (elm.nodeType === ELEMENT_NODE) {
4758      return elm;
4759    }
4760  }
4761}
4762
4763function splitClasses(classes) {
4764  if (isString(classes)) {
4765    classes = classes.split(' ');
4766  }
4767
4768  // Use createMap() to prevent class assumptions involving property names in
4769  // Object.prototype
4770  var obj = createMap();
4771  forEach(classes, function(klass) {
4772    // sometimes the split leaves empty string values
4773    // incase extra spaces were applied to the options
4774    if (klass.length) {
4775      obj[klass] = true;
4776    }
4777  });
4778  return obj;
4779}
4780
4781// if any other type of options value besides an Object value is
4782// passed into the $animate.method() animation then this helper code
4783// will be run which will ignore it. While this patch is not the
4784// greatest solution to this, a lot of existing plugins depend on
4785// $animate to either call the callback (< 1.2) or return a promise
4786// that can be changed. This helper function ensures that the options
4787// are wiped clean incase a callback function is provided.
4788function prepareAnimateOptions(options) {
4789  return isObject(options)
4790      ? options
4791      : {};
4792}
4793
4794var $$CoreAnimateRunnerProvider = function() {
4795  this.$get = ['$q', '$$rAF', function($q, $$rAF) {
4796    function AnimateRunner() {}
4797    AnimateRunner.all = noop;
4798    AnimateRunner.chain = noop;
4799    AnimateRunner.prototype = {
4800      end: noop,
4801      cancel: noop,
4802      resume: noop,
4803      pause: noop,
4804      complete: noop,
4805      then: function(pass, fail) {
4806        return $q(function(resolve) {
4807          $$rAF(function() {
4808            resolve();
4809          });
4810        }).then(pass, fail);
4811      }
4812    };
4813    return AnimateRunner;
4814  }];
4815};
4816
4817// this is prefixed with Core since it conflicts with
4818// the animateQueueProvider defined in ngAnimate/animateQueue.js
4819var $$CoreAnimateQueueProvider = function() {
4820  var postDigestQueue = new HashMap();
4821  var postDigestElements = [];
4822
4823  this.$get = ['$$AnimateRunner', '$rootScope',
4824       function($$AnimateRunner,   $rootScope) {
4825    return {
4826      enabled: noop,
4827      on: noop,
4828      off: noop,
4829      pin: noop,
4830
4831      push: function(element, event, options, domOperation) {
4832        domOperation        && domOperation();
4833
4834        options = options || {};
4835        options.from        && element.css(options.from);
4836        options.to          && element.css(options.to);
4837
4838        if (options.addClass || options.removeClass) {
4839          addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
4840        }
4841
4842        return new $$AnimateRunner(); // jshint ignore:line
4843      }
4844    };
4845
4846    function addRemoveClassesPostDigest(element, add, remove) {
4847      var data = postDigestQueue.get(element);
4848      var classVal;
4849
4850      if (!data) {
4851        postDigestQueue.put(element, data = {});
4852        postDigestElements.push(element);
4853      }
4854
4855      if (add) {
4856        forEach(add.split(' '), function(className) {
4857          if (className) {
4858            data[className] = true;
4859          }
4860        });
4861      }
4862
4863      if (remove) {
4864        forEach(remove.split(' '), function(className) {
4865          if (className) {
4866            data[className] = false;
4867          }
4868        });
4869      }
4870
4871      if (postDigestElements.length > 1) return;
4872
4873      $rootScope.$$postDigest(function() {
4874        forEach(postDigestElements, function(element) {
4875          var data = postDigestQueue.get(element);
4876          if (data) {
4877            var existing = splitClasses(element.attr('class'));
4878            var toAdd = '';
4879            var toRemove = '';
4880            forEach(data, function(status, className) {
4881              var hasClass = !!existing[className];
4882              if (status !== hasClass) {
4883                if (status) {
4884                  toAdd += (toAdd.length ? ' ' : '') + className;
4885                } else {
4886                  toRemove += (toRemove.length ? ' ' : '') + className;
4887                }
4888              }
4889            });
4890
4891            forEach(element, function(elm) {
4892              toAdd    && jqLiteAddClass(elm, toAdd);
4893              toRemove && jqLiteRemoveClass(elm, toRemove);
4894            });
4895            postDigestQueue.remove(element);
4896          }
4897        });
4898
4899        postDigestElements.length = 0;
4900      });
4901    }
4902  }];
4903};
4904
4905/**
4906 * @ngdoc provider
4907 * @name $animateProvider
4908 *
4909 * @description
4910 * Default implementation of $animate that doesn't perform any animations, instead just
4911 * synchronously performs DOM updates and resolves the returned runner promise.
4912 *
4913 * In order to enable animations the `ngAnimate` module has to be loaded.
4914 *
4915 * To see the functional implementation check out `src/ngAnimate/animate.js`.
4916 */
4917var $AnimateProvider = ['$provide', function($provide) {
4918  var provider = this;
4919
4920  this.$$registeredAnimations = Object.create(null);
4921
4922   /**
4923   * @ngdoc method
4924   * @name $animateProvider#register
4925   *
4926   * @description
4927   * Registers a new injectable animation factory function. The factory function produces the
4928   * animation object which contains callback functions for each event that is expected to be
4929   * animated.
4930   *
4931   *   * `eventFn`: `function(element, ... , doneFunction, options)`
4932   *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
4933   *   on the type of animation additional arguments will be injected into the animation function. The
4934   *   list below explains the function signatures for the different animation methods:
4935   *
4936   *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
4937   *   - addClass: function(element, addedClasses, doneFunction, options)
4938   *   - removeClass: function(element, removedClasses, doneFunction, options)
4939   *   - enter, leave, move: function(element, doneFunction, options)
4940   *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
4941   *
4942   *   Make sure to trigger the `doneFunction` once the animation is fully complete.
4943   *
4944   * ```js
4945   *   return {
4946   *     //enter, leave, move signature
4947   *     eventFn : function(element, done, options) {
4948   *       //code to run the animation
4949   *       //once complete, then run done()
4950   *       return function endFunction(wasCancelled) {
4951   *         //code to cancel the animation
4952   *       }
4953   *     }
4954   *   }
4955   * ```
4956   *
4957   * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
4958   * @param {Function} factory The factory function that will be executed to return the animation
4959   *                           object.
4960   */
4961  this.register = function(name, factory) {
4962    if (name && name.charAt(0) !== '.') {
4963      throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
4964    }
4965
4966    var key = name + '-animation';
4967    provider.$$registeredAnimations[name.substr(1)] = key;
4968    $provide.factory(key, factory);
4969  };
4970
4971  /**
4972   * @ngdoc method
4973   * @name $animateProvider#classNameFilter
4974   *
4975   * @description
4976   * Sets and/or returns the CSS class regular expression that is checked when performing
4977   * an animation. Upon bootstrap the classNameFilter value is not set at all and will
4978   * therefore enable $animate to attempt to perform an animation on any element that is triggered.
4979   * When setting the `classNameFilter` value, animations will only be performed on elements
4980   * that successfully match the filter expression. This in turn can boost performance
4981   * for low-powered devices as well as applications containing a lot of structural operations.
4982   * @param {RegExp=} expression The className expression which will be checked against all animations
4983   * @return {RegExp} The current CSS className expression value. If null then there is no expression value
4984   */
4985  this.classNameFilter = function(expression) {
4986    if (arguments.length === 1) {
4987      this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
4988      if (this.$$classNameFilter) {
4989        var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
4990        if (reservedRegex.test(this.$$classNameFilter.toString())) {
4991          throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
4992
4993        }
4994      }
4995    }
4996    return this.$$classNameFilter;
4997  };
4998
4999  this.$get = ['$$animateQueue', function($$animateQueue) {
5000    function domInsert(element, parentElement, afterElement) {
5001      // if for some reason the previous element was removed
5002      // from the dom sometime before this code runs then let's
5003      // just stick to using the parent element as the anchor
5004      if (afterElement) {
5005        var afterNode = extractElementNode(afterElement);
5006        if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5007          afterElement = null;
5008        }
5009      }
5010      afterElement ? afterElement.after(element) : parentElement.prepend(element);
5011    }
5012
5013    /**
5014     * @ngdoc service
5015     * @name $animate
5016     * @description The $animate service exposes a series of DOM utility methods that provide support
5017     * for animation hooks. The default behavior is the application of DOM operations, however,
5018     * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5019     * to ensure that animation runs with the triggered DOM operation.
5020     *
5021     * By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't
5022     * included and only when it is active then the animation hooks that `$animate` triggers will be
5023     * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5024     * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5025     * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5026     *
5027     * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5028     *
5029     * To learn more about enabling animation support, click here to visit the
5030     * {@link ngAnimate ngAnimate module page}.
5031     */
5032    return {
5033      // we don't call it directly since non-existant arguments may
5034      // be interpreted as null within the sub enabled function
5035
5036      /**
5037       *
5038       * @ngdoc method
5039       * @name $animate#on
5040       * @kind function
5041       * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5042       *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5043       *    is fired with the following params:
5044       *
5045       * ```js
5046       * $animate.on('enter', container,
5047       *    function callback(element, phase) {
5048       *      // cool we detected an enter animation within the container
5049       *    }
5050       * );
5051       * ```
5052       *
5053       * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5054       * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5055       *     as well as among its children
5056       * @param {Function} callback the callback function that will be fired when the listener is triggered
5057       *
5058       * The arguments present in the callback function are:
5059       * * `element` - The captured DOM element that the animation was fired on.
5060       * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5061       */
5062      on: $$animateQueue.on,
5063
5064      /**
5065       *
5066       * @ngdoc method
5067       * @name $animate#off
5068       * @kind function
5069       * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5070       * can be used in three different ways depending on the arguments:
5071       *
5072       * ```js
5073       * // remove all the animation event listeners listening for `enter`
5074       * $animate.off('enter');
5075       *
5076       * // remove all the animation event listeners listening for `enter` on the given element and its children
5077       * $animate.off('enter', container);
5078       *
5079       * // remove the event listener function provided by `listenerFn` that is set
5080       * // to listen for `enter` on the given `element` as well as its children
5081       * $animate.off('enter', container, callback);
5082       * ```
5083       *
5084       * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5085       * @param {DOMElement=} container the container element the event listener was placed on
5086       * @param {Function=} callback the callback function that was registered as the listener
5087       */
5088      off: $$animateQueue.off,
5089
5090      /**
5091       * @ngdoc method
5092       * @name $animate#pin
5093       * @kind function
5094       * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5095       *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5096       *    element despite being outside the realm of the application or within another application. Say for example if the application
5097       *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5098       *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5099       *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5100       *
5101       *    Note that this feature is only active when the `ngAnimate` module is used.
5102       *
5103       * @param {DOMElement} element the external element that will be pinned
5104       * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5105       */
5106      pin: $$animateQueue.pin,
5107
5108      /**
5109       *
5110       * @ngdoc method
5111       * @name $animate#enabled
5112       * @kind function
5113       * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5114       * function can be called in four ways:
5115       *
5116       * ```js
5117       * // returns true or false
5118       * $animate.enabled();
5119       *
5120       * // changes the enabled state for all animations
5121       * $animate.enabled(false);
5122       * $animate.enabled(true);
5123       *
5124       * // returns true or false if animations are enabled for an element
5125       * $animate.enabled(element);
5126       *
5127       * // changes the enabled state for an element and its children
5128       * $animate.enabled(element, true);
5129       * $animate.enabled(element, false);
5130       * ```
5131       *
5132       * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5133       * @param {boolean=} enabled whether or not the animations will be enabled for the element
5134       *
5135       * @return {boolean} whether or not animations are enabled
5136       */
5137      enabled: $$animateQueue.enabled,
5138
5139      /**
5140       * @ngdoc method
5141       * @name $animate#cancel
5142       * @kind function
5143       * @description Cancels the provided animation.
5144       *
5145       * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5146       */
5147      cancel: function(runner) {
5148        runner.end && runner.end();
5149      },
5150
5151      /**
5152       *
5153       * @ngdoc method
5154       * @name $animate#enter
5155       * @kind function
5156       * @description Inserts the element into the DOM either after the `after` element (if provided) or
5157       *   as the first child within the `parent` element and then triggers an animation.
5158       *   A promise is returned that will be resolved during the next digest once the animation
5159       *   has completed.
5160       *
5161       * @param {DOMElement} element the element which will be inserted into the DOM
5162       * @param {DOMElement} parent the parent element which will append the element as
5163       *   a child (so long as the after element is not present)
5164       * @param {DOMElement=} after the sibling element after which the element will be appended
5165       * @param {object=} options an optional collection of options/styles that will be applied to the element
5166       *
5167       * @return {Promise} the animation callback promise
5168       */
5169      enter: function(element, parent, after, options) {
5170        parent = parent && jqLite(parent);
5171        after = after && jqLite(after);
5172        parent = parent || after.parent();
5173        domInsert(element, parent, after);
5174        return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5175      },
5176
5177      /**
5178       *
5179       * @ngdoc method
5180       * @name $animate#move
5181       * @kind function
5182       * @description Inserts (moves) the element into its new position in the DOM either after
5183       *   the `after` element (if provided) or as the first child within the `parent` element
5184       *   and then triggers an animation. A promise is returned that will be resolved
5185       *   during the next digest once the animation has completed.
5186       *
5187       * @param {DOMElement} element the element which will be moved into the new DOM position
5188       * @param {DOMElement} parent the parent element which will append the element as
5189       *   a child (so long as the after element is not present)
5190       * @param {DOMElement=} after the sibling element after which the element will be appended
5191       * @param {object=} options an optional collection of options/styles that will be applied to the element
5192       *
5193       * @return {Promise} the animation callback promise
5194       */
5195      move: function(element, parent, after, options) {
5196        parent = parent && jqLite(parent);
5197        after = after && jqLite(after);
5198        parent = parent || after.parent();
5199        domInsert(element, parent, after);
5200        return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5201      },
5202
5203      /**
5204       * @ngdoc method
5205       * @name $animate#leave
5206       * @kind function
5207       * @description Triggers an animation and then removes the element from the DOM.
5208       * When the function is called a promise is returned that will be resolved during the next
5209       * digest once the animation has completed.
5210       *
5211       * @param {DOMElement} element the element which will be removed from the DOM
5212       * @param {object=} options an optional collection of options/styles that will be applied to the element
5213       *
5214       * @return {Promise} the animation callback promise
5215       */
5216      leave: function(element, options) {
5217        return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5218          element.remove();
5219        });
5220      },
5221
5222      /**
5223       * @ngdoc method
5224       * @name $animate#addClass
5225       * @kind function
5226       *
5227       * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5228       *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5229       *   animation if element already contains the CSS class or if the class is removed at a later step.
5230       *   Note that class-based animations are treated differently compared to structural animations
5231       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5232       *   depending if CSS or JavaScript animations are used.
5233       *
5234       * @param {DOMElement} element the element which the CSS classes will be applied to
5235       * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5236       * @param {object=} options an optional collection of options/styles that will be applied to the element
5237       *
5238       * @return {Promise} the animation callback promise
5239       */
5240      addClass: function(element, className, options) {
5241        options = prepareAnimateOptions(options);
5242        options.addClass = mergeClasses(options.addclass, className);
5243        return $$animateQueue.push(element, 'addClass', options);
5244      },
5245
5246      /**
5247       * @ngdoc method
5248       * @name $animate#removeClass
5249       * @kind function
5250       *
5251       * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5252       *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5253       *   animation if element does not contain the CSS class or if the class is added at a later step.
5254       *   Note that class-based animations are treated differently compared to structural animations
5255       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5256       *   depending if CSS or JavaScript animations are used.
5257       *
5258       * @param {DOMElement} element the element which the CSS classes will be applied to
5259       * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5260       * @param {object=} options an optional collection of options/styles that will be applied to the element
5261       *
5262       * @return {Promise} the animation callback promise
5263       */
5264      removeClass: function(element, className, options) {
5265        options = prepareAnimateOptions(options);
5266        options.removeClass = mergeClasses(options.removeClass, className);
5267        return $$animateQueue.push(element, 'removeClass', options);
5268      },
5269
5270      /**
5271       * @ngdoc method
5272       * @name $animate#setClass
5273       * @kind function
5274       *
5275       * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5276       *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5277       *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5278       *    passed. Note that class-based animations are treated differently compared to structural animations
5279       *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5280       *    depending if CSS or JavaScript animations are used.
5281       *
5282       * @param {DOMElement} element the element which the CSS classes will be applied to
5283       * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5284       * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5285       * @param {object=} options an optional collection of options/styles that will be applied to the element
5286       *
5287       * @return {Promise} the animation callback promise
5288       */
5289      setClass: function(element, add, remove, options) {
5290        options = prepareAnimateOptions(options);
5291        options.addClass = mergeClasses(options.addClass, add);
5292        options.removeClass = mergeClasses(options.removeClass, remove);
5293        return $$animateQueue.push(element, 'setClass', options);
5294      },
5295
5296      /**
5297       * @ngdoc method
5298       * @name $animate#animate
5299       * @kind function
5300       *
5301       * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5302       * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
5303       * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
5304       * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
5305       * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
5306       *
5307       * @param {DOMElement} element the element which the CSS styles will be applied to
5308       * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5309       * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5310       * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5311       *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5312       *    (Note that if no animation is detected then this value will not be appplied to the element.)
5313       * @param {object=} options an optional collection of options/styles that will be applied to the element
5314       *
5315       * @return {Promise} the animation callback promise
5316       */
5317      animate: function(element, from, to, className, options) {
5318        options = prepareAnimateOptions(options);
5319        options.from = options.from ? extend(options.from, from) : from;
5320        options.to   = options.to   ? extend(options.to, to)     : to;
5321
5322        className = className || 'ng-inline-animate';
5323        options.tempClasses = mergeClasses(options.tempClasses, className);
5324        return $$animateQueue.push(element, 'animate', options);
5325      }
5326    };
5327  }];
5328}];
5329
5330function $$AsyncCallbackProvider() {
5331  this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
5332    return $$rAF.supported
5333      ? function(fn) { return $$rAF(fn); }
5334      : function(fn) {
5335        return $timeout(fn, 0, false);
5336      };
5337  }];
5338}
5339
5340/* global stripHash: true */
5341
5342/**
5343 * ! This is a private undocumented service !
5344 *
5345 * @name $browser
5346 * @requires $log
5347 * @description
5348 * This object has two goals:
5349 *
5350 * - hide all the global state in the browser caused by the window object
5351 * - abstract away all the browser specific features and inconsistencies
5352 *
5353 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5354 * service, which can be used for convenient testing of the application without the interaction with
5355 * the real browser apis.
5356 */
5357/**
5358 * @param {object} window The global window object.
5359 * @param {object} document jQuery wrapped document.
5360 * @param {object} $log window.console or an object with the same interface.
5361 * @param {object} $sniffer $sniffer service
5362 */
5363function Browser(window, document, $log, $sniffer) {
5364  var self = this,
5365      rawDocument = document[0],
5366      location = window.location,
5367      history = window.history,
5368      setTimeout = window.setTimeout,
5369      clearTimeout = window.clearTimeout,
5370      pendingDeferIds = {};
5371
5372  self.isMock = false;
5373
5374  var outstandingRequestCount = 0;
5375  var outstandingRequestCallbacks = [];
5376
5377  // TODO(vojta): remove this temporary api
5378  self.$$completeOutstandingRequest = completeOutstandingRequest;
5379  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5380
5381  /**
5382   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5383   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5384   */
5385  function completeOutstandingRequest(fn) {
5386    try {
5387      fn.apply(null, sliceArgs(arguments, 1));
5388    } finally {
5389      outstandingRequestCount--;
5390      if (outstandingRequestCount === 0) {
5391        while (outstandingRequestCallbacks.length) {
5392          try {
5393            outstandingRequestCallbacks.pop()();
5394          } catch (e) {
5395            $log.error(e);
5396          }
5397        }
5398      }
5399    }
5400  }
5401
5402  function getHash(url) {
5403    var index = url.indexOf('#');
5404    return index === -1 ? '' : url.substr(index + 1);
5405  }
5406
5407  /**
5408   * @private
5409   * Note: this method is used only by scenario runner
5410   * TODO(vojta): prefix this method with $$ ?
5411   * @param {function()} callback Function that will be called when no outstanding request
5412   */
5413  self.notifyWhenNoOutstandingRequests = function(callback) {
5414    if (outstandingRequestCount === 0) {
5415      callback();
5416    } else {
5417      outstandingRequestCallbacks.push(callback);
5418    }
5419  };
5420
5421  //////////////////////////////////////////////////////////////
5422  // URL API
5423  //////////////////////////////////////////////////////////////
5424
5425  var cachedState, lastHistoryState,
5426      lastBrowserUrl = location.href,
5427      baseElement = document.find('base'),
5428      reloadLocation = null;
5429
5430  cacheState();
5431  lastHistoryState = cachedState;
5432
5433  /**
5434   * @name $browser#url
5435   *
5436   * @description
5437   * GETTER:
5438   * Without any argument, this method just returns current value of location.href.
5439   *
5440   * SETTER:
5441   * With at least one argument, this method sets url to new value.
5442   * If html5 history api supported, pushState/replaceState is used, otherwise
5443   * location.href/location.replace is used.
5444   * Returns its own instance to allow chaining
5445   *
5446   * NOTE: this api is intended for use only by the $location service. Please use the
5447   * {@link ng.$location $location service} to change url.
5448   *
5449   * @param {string} url New url (when used as setter)
5450   * @param {boolean=} replace Should new url replace current history record?
5451   * @param {object=} state object to use with pushState/replaceState
5452   */
5453  self.url = function(url, replace, state) {
5454    // In modern browsers `history.state` is `null` by default; treating it separately
5455    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5456    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5457    if (isUndefined(state)) {
5458      state = null;
5459    }
5460
5461    // Android Browser BFCache causes location, history reference to become stale.
5462    if (location !== window.location) location = window.location;
5463    if (history !== window.history) history = window.history;
5464
5465    // setter
5466    if (url) {
5467      var sameState = lastHistoryState === state;
5468
5469      // Don't change anything if previous and current URLs and states match. This also prevents
5470      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5471      // See https://github.com/angular/angular.js/commit/ffb2701
5472      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5473        return self;
5474      }
5475      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5476      lastBrowserUrl = url;
5477      lastHistoryState = state;
5478      // Don't use history API if only the hash changed
5479      // due to a bug in IE10/IE11 which leads
5480      // to not firing a `hashchange` nor `popstate` event
5481      // in some cases (see #9143).
5482      if ($sniffer.history && (!sameBase || !sameState)) {
5483        history[replace ? 'replaceState' : 'pushState'](state, '', url);
5484        cacheState();
5485        // Do the assignment again so that those two variables are referentially identical.
5486        lastHistoryState = cachedState;
5487      } else {
5488        if (!sameBase || reloadLocation) {
5489          reloadLocation = url;
5490        }
5491        if (replace) {
5492          location.replace(url);
5493        } else if (!sameBase) {
5494          location.href = url;
5495        } else {
5496          location.hash = getHash(url);
5497        }
5498      }
5499      return self;
5500    // getter
5501    } else {
5502      // - reloadLocation is needed as browsers don't allow to read out
5503      //   the new location.href if a reload happened.
5504      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5505      return reloadLocation || location.href.replace(/%27/g,"'");
5506    }
5507  };
5508
5509  /**
5510   * @name $browser#state
5511   *
5512   * @description
5513   * This method is a getter.
5514   *
5515   * Return history.state or null if history.state is undefined.
5516   *
5517   * @returns {object} state
5518   */
5519  self.state = function() {
5520    return cachedState;
5521  };
5522
5523  var urlChangeListeners = [],
5524      urlChangeInit = false;
5525
5526  function cacheStateAndFireUrlChange() {
5527    cacheState();
5528    fireUrlChange();
5529  }
5530
5531  function getCurrentState() {
5532    try {
5533      return history.state;
5534    } catch (e) {
5535      // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5536    }
5537  }
5538
5539  // This variable should be used *only* inside the cacheState function.
5540  var lastCachedState = null;
5541  function cacheState() {
5542    // This should be the only place in $browser where `history.state` is read.
5543    cachedState = getCurrentState();
5544    cachedState = isUndefined(cachedState) ? null : cachedState;
5545
5546    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5547    if (equals(cachedState, lastCachedState)) {
5548      cachedState = lastCachedState;
5549    }
5550    lastCachedState = cachedState;
5551  }
5552
5553  function fireUrlChange() {
5554    if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5555      return;
5556    }
5557
5558    lastBrowserUrl = self.url();
5559    lastHistoryState = cachedState;
5560    forEach(urlChangeListeners, function(listener) {
5561      listener(self.url(), cachedState);
5562    });
5563  }
5564
5565  /**
5566   * @name $browser#onUrlChange
5567   *
5568   * @description
5569   * Register callback function that will be called, when url changes.
5570   *
5571   * It's only called when the url is changed from outside of angular:
5572   * - user types different url into address bar
5573   * - user clicks on history (forward/back) button
5574   * - user clicks on a link
5575   *
5576   * It's not called when url is changed by $browser.url() method
5577   *
5578   * The listener gets called with new url as parameter.
5579   *
5580   * NOTE: this api is intended for use only by the $location service. Please use the
5581   * {@link ng.$location $location service} to monitor url changes in angular apps.
5582   *
5583   * @param {function(string)} listener Listener function to be called when url changes.
5584   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5585   */
5586  self.onUrlChange = function(callback) {
5587    // TODO(vojta): refactor to use node's syntax for events
5588    if (!urlChangeInit) {
5589      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5590      // don't fire popstate when user change the address bar and don't fire hashchange when url
5591      // changed by push/replaceState
5592
5593      // html5 history api - popstate event
5594      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5595      // hashchange event
5596      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5597
5598      urlChangeInit = true;
5599    }
5600
5601    urlChangeListeners.push(callback);
5602    return callback;
5603  };
5604
5605  /**
5606   * @private
5607   * Remove popstate and hashchange handler from window.
5608   *
5609   * NOTE: this api is intended for use only by $rootScope.
5610   */
5611  self.$$applicationDestroyed = function() {
5612    jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5613  };
5614
5615  /**
5616   * Checks whether the url has changed outside of Angular.
5617   * Needs to be exported to be able to check for changes that have been done in sync,
5618   * as hashchange/popstate events fire in async.
5619   */
5620  self.$$checkUrlChange = fireUrlChange;
5621
5622  //////////////////////////////////////////////////////////////
5623  // Misc API
5624  //////////////////////////////////////////////////////////////
5625
5626  /**
5627   * @name $browser#baseHref
5628   *
5629   * @description
5630   * Returns current <base href>
5631   * (always relative - without domain)
5632   *
5633   * @returns {string} The current base href
5634   */
5635  self.baseHref = function() {
5636    var href = baseElement.attr('href');
5637    return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5638  };
5639
5640  /**
5641   * @name $browser#defer
5642   * @param {function()} fn A function, who's execution should be deferred.
5643   * @param {number=} [delay=0] of milliseconds to defer the function execution.
5644   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5645   *
5646   * @description
5647   * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5648   *
5649   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5650   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5651   * via `$browser.defer.flush()`.
5652   *
5653   */
5654  self.defer = function(fn, delay) {
5655    var timeoutId;
5656    outstandingRequestCount++;
5657    timeoutId = setTimeout(function() {
5658      delete pendingDeferIds[timeoutId];
5659      completeOutstandingRequest(fn);
5660    }, delay || 0);
5661    pendingDeferIds[timeoutId] = true;
5662    return timeoutId;
5663  };
5664
5665
5666  /**
5667   * @name $browser#defer.cancel
5668   *
5669   * @description
5670   * Cancels a deferred task identified with `deferId`.
5671   *
5672   * @param {*} deferId Token returned by the `$browser.defer` function.
5673   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5674   *                    canceled.
5675   */
5676  self.defer.cancel = function(deferId) {
5677    if (pendingDeferIds[deferId]) {
5678      delete pendingDeferIds[deferId];
5679      clearTimeout(deferId);
5680      completeOutstandingRequest(noop);
5681      return true;
5682    }
5683    return false;
5684  };
5685
5686}
5687
5688function $BrowserProvider() {
5689  this.$get = ['$window', '$log', '$sniffer', '$document',
5690      function($window, $log, $sniffer, $document) {
5691        return new Browser($window, $document, $log, $sniffer);
5692      }];
5693}
5694
5695/**
5696 * @ngdoc service
5697 * @name $cacheFactory
5698 *
5699 * @description
5700 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5701 * them.
5702 *
5703 * ```js
5704 *
5705 *  var cache = $cacheFactory('cacheId');
5706 *  expect($cacheFactory.get('cacheId')).toBe(cache);
5707 *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5708 *
5709 *  cache.put("key", "value");
5710 *  cache.put("another key", "another value");
5711 *
5712 *  // We've specified no options on creation
5713 *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5714 *
5715 * ```
5716 *
5717 *
5718 * @param {string} cacheId Name or id of the newly created cache.
5719 * @param {object=} options Options object that specifies the cache behavior. Properties:
5720 *
5721 *   - `{number=}` `capacity` — turns the cache into LRU cache.
5722 *
5723 * @returns {object} Newly created cache object with the following set of methods:
5724 *
5725 * - `{object}` `info()` — Returns id, size, and options of cache.
5726 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5727 *   it.
5728 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5729 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5730 * - `{void}` `removeAll()` — Removes all cached values.
5731 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5732 *
5733 * @example
5734   <example module="cacheExampleApp">
5735     <file name="index.html">
5736       <div ng-controller="CacheController">
5737         <input ng-model="newCacheKey" placeholder="Key">
5738         <input ng-model="newCacheValue" placeholder="Value">
5739         <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5740
5741         <p ng-if="keys.length">Cached Values</p>
5742         <div ng-repeat="key in keys">
5743           <span ng-bind="key"></span>
5744           <span>: </span>
5745           <b ng-bind="cache.get(key)"></b>
5746         </div>
5747
5748         <p>Cache Info</p>
5749         <div ng-repeat="(key, value) in cache.info()">
5750           <span ng-bind="key"></span>
5751           <span>: </span>
5752           <b ng-bind="value"></b>
5753         </div>
5754       </div>
5755     </file>
5756     <file name="script.js">
5757       angular.module('cacheExampleApp', []).
5758         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
5759           $scope.keys = [];
5760           $scope.cache = $cacheFactory('cacheId');
5761           $scope.put = function(key, value) {
5762             if ($scope.cache.get(key) === undefined) {
5763               $scope.keys.push(key);
5764             }
5765             $scope.cache.put(key, value === undefined ? null : value);
5766           };
5767         }]);
5768     </file>
5769     <file name="style.css">
5770       p {
5771         margin: 10px 0 3px;
5772       }
5773     </file>
5774   </example>
5775 */
5776function $CacheFactoryProvider() {
5777
5778  this.$get = function() {
5779    var caches = {};
5780
5781    function cacheFactory(cacheId, options) {
5782      if (cacheId in caches) {
5783        throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
5784      }
5785
5786      var size = 0,
5787          stats = extend({}, options, {id: cacheId}),
5788          data = {},
5789          capacity = (options && options.capacity) || Number.MAX_VALUE,
5790          lruHash = {},
5791          freshEnd = null,
5792          staleEnd = null;
5793
5794      /**
5795       * @ngdoc type
5796       * @name $cacheFactory.Cache
5797       *
5798       * @description
5799       * A cache object used to store and retrieve data, primarily used by
5800       * {@link $http $http} and the {@link ng.directive:script script} directive to cache
5801       * templates and other data.
5802       *
5803       * ```js
5804       *  angular.module('superCache')
5805       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
5806       *      return $cacheFactory('super-cache');
5807       *    }]);
5808       * ```
5809       *
5810       * Example test:
5811       *
5812       * ```js
5813       *  it('should behave like a cache', inject(function(superCache) {
5814       *    superCache.put('key', 'value');
5815       *    superCache.put('another key', 'another value');
5816       *
5817       *    expect(superCache.info()).toEqual({
5818       *      id: 'super-cache',
5819       *      size: 2
5820       *    });
5821       *
5822       *    superCache.remove('another key');
5823       *    expect(superCache.get('another key')).toBeUndefined();
5824       *
5825       *    superCache.removeAll();
5826       *    expect(superCache.info()).toEqual({
5827       *      id: 'super-cache',
5828       *      size: 0
5829       *    });
5830       *  }));
5831       * ```
5832       */
5833      return caches[cacheId] = {
5834
5835        /**
5836         * @ngdoc method
5837         * @name $cacheFactory.Cache#put
5838         * @kind function
5839         *
5840         * @description
5841         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
5842         * retrieved later, and incrementing the size of the cache if the key was not already
5843         * present in the cache. If behaving like an LRU cache, it will also remove stale
5844         * entries from the set.
5845         *
5846         * It will not insert undefined values into the cache.
5847         *
5848         * @param {string} key the key under which the cached data is stored.
5849         * @param {*} value the value to store alongside the key. If it is undefined, the key
5850         *    will not be stored.
5851         * @returns {*} the value stored.
5852         */
5853        put: function(key, value) {
5854          if (isUndefined(value)) return;
5855          if (capacity < Number.MAX_VALUE) {
5856            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
5857
5858            refresh(lruEntry);
5859          }
5860
5861          if (!(key in data)) size++;
5862          data[key] = value;
5863
5864          if (size > capacity) {
5865            this.remove(staleEnd.key);
5866          }
5867
5868          return value;
5869        },
5870
5871        /**
5872         * @ngdoc method
5873         * @name $cacheFactory.Cache#get
5874         * @kind function
5875         *
5876         * @description
5877         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
5878         *
5879         * @param {string} key the key of the data to be retrieved
5880         * @returns {*} the value stored.
5881         */
5882        get: function(key) {
5883          if (capacity < Number.MAX_VALUE) {
5884            var lruEntry = lruHash[key];
5885
5886            if (!lruEntry) return;
5887
5888            refresh(lruEntry);
5889          }
5890
5891          return data[key];
5892        },
5893
5894
5895        /**
5896         * @ngdoc method
5897         * @name $cacheFactory.Cache#remove
5898         * @kind function
5899         *
5900         * @description
5901         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
5902         *
5903         * @param {string} key the key of the entry to be removed
5904         */
5905        remove: function(key) {
5906          if (capacity < Number.MAX_VALUE) {
5907            var lruEntry = lruHash[key];
5908
5909            if (!lruEntry) return;
5910
5911            if (lruEntry == freshEnd) freshEnd = lruEntry.p;
5912            if (lruEntry == staleEnd) staleEnd = lruEntry.n;
5913            link(lruEntry.n,lruEntry.p);
5914
5915            delete lruHash[key];
5916          }
5917
5918          delete data[key];
5919          size--;
5920        },
5921
5922
5923        /**
5924         * @ngdoc method
5925         * @name $cacheFactory.Cache#removeAll
5926         * @kind function
5927         *
5928         * @description
5929         * Clears the cache object of any entries.
5930         */
5931        removeAll: function() {
5932          data = {};
5933          size = 0;
5934          lruHash = {};
5935          freshEnd = staleEnd = null;
5936        },
5937
5938
5939        /**
5940         * @ngdoc method
5941         * @name $cacheFactory.Cache#destroy
5942         * @kind function
5943         *
5944         * @description
5945         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
5946         * removing it from the {@link $cacheFactory $cacheFactory} set.
5947         */
5948        destroy: function() {
5949          data = null;
5950          stats = null;
5951          lruHash = null;
5952          delete caches[cacheId];
5953        },
5954
5955
5956        /**
5957         * @ngdoc method
5958         * @name $cacheFactory.Cache#info
5959         * @kind function
5960         *
5961         * @description
5962         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
5963         *
5964         * @returns {object} an object with the following properties:
5965         *   <ul>
5966         *     <li>**id**: the id of the cache instance</li>
5967         *     <li>**size**: the number of entries kept in the cache instance</li>
5968         *     <li>**...**: any additional properties from the options object when creating the
5969         *       cache.</li>
5970         *   </ul>
5971         */
5972        info: function() {
5973          return extend({}, stats, {size: size});
5974        }
5975      };
5976
5977
5978      /**
5979       * makes the `entry` the freshEnd of the LRU linked list
5980       */
5981      function refresh(entry) {
5982        if (entry != freshEnd) {
5983          if (!staleEnd) {
5984            staleEnd = entry;
5985          } else if (staleEnd == entry) {
5986            staleEnd = entry.n;
5987          }
5988
5989          link(entry.n, entry.p);
5990          link(entry, freshEnd);
5991          freshEnd = entry;
5992          freshEnd.n = null;
5993        }
5994      }
5995
5996
5997      /**
5998       * bidirectionally links two entries of the LRU linked list
5999       */
6000      function link(nextEntry, prevEntry) {
6001        if (nextEntry != prevEntry) {
6002          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6003          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6004        }
6005      }
6006    }
6007
6008
6009  /**
6010   * @ngdoc method
6011   * @name $cacheFactory#info
6012   *
6013   * @description
6014   * Get information about all the caches that have been created
6015   *
6016   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6017   */
6018    cacheFactory.info = function() {
6019      var info = {};
6020      forEach(caches, function(cache, cacheId) {
6021        info[cacheId] = cache.info();
6022      });
6023      return info;
6024    };
6025
6026
6027  /**
6028   * @ngdoc method
6029   * @name $cacheFactory#get
6030   *
6031   * @description
6032   * Get access to a cache object by the `cacheId` used when it was created.
6033   *
6034   * @param {string} cacheId Name or id of a cache to access.
6035   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6036   */
6037    cacheFactory.get = function(cacheId) {
6038      return caches[cacheId];
6039    };
6040
6041
6042    return cacheFactory;
6043  };
6044}
6045
6046/**
6047 * @ngdoc service
6048 * @name $templateCache
6049 *
6050 * @description
6051 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6052 * can load templates directly into the cache in a `script` tag, or by consuming the
6053 * `$templateCache` service directly.
6054 *
6055 * Adding via the `script` tag:
6056 *
6057 * ```html
6058 *   <script type="text/ng-template" id="templateId.html">
6059 *     <p>This is the content of the template</p>
6060 *   </script>
6061 * ```
6062 *
6063 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6064 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6065 * element with ng-app attribute), otherwise the template will be ignored.
6066 *
6067 * Adding via the `$templateCache` service:
6068 *
6069 * ```js
6070 * var myApp = angular.module('myApp', []);
6071 * myApp.run(function($templateCache) {
6072 *   $templateCache.put('templateId.html', 'This is the content of the template');
6073 * });
6074 * ```
6075 *
6076 * To retrieve the template later, simply use it in your HTML:
6077 * ```html
6078 * <div ng-include=" 'templateId.html' "></div>
6079 * ```
6080 *
6081 * or get it via Javascript:
6082 * ```js
6083 * $templateCache.get('templateId.html')
6084 * ```
6085 *
6086 * See {@link ng.$cacheFactory $cacheFactory}.
6087 *
6088 */
6089function $TemplateCacheProvider() {
6090  this.$get = ['$cacheFactory', function($cacheFactory) {
6091    return $cacheFactory('templates');
6092  }];
6093}
6094
6095/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6096 *     Any commits to this file should be reviewed with security in mind.  *
6097 *   Changes to this file can potentially create security vulnerabilities. *
6098 *          An approval from 2 Core members with history of modifying      *
6099 *                         this file is required.                          *
6100 *                                                                         *
6101 *  Does the change somehow allow for arbitrary javascript to be executed? *
6102 *    Or allows for someone to change the prototype of built-in objects?   *
6103 *     Or gives undesired access to variables likes document or window?    *
6104 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6105
6106/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6107 *
6108 * DOM-related variables:
6109 *
6110 * - "node" - DOM Node
6111 * - "element" - DOM Element or Node
6112 * - "$node" or "$element" - jqLite-wrapped node or element
6113 *
6114 *
6115 * Compiler related stuff:
6116 *
6117 * - "linkFn" - linking fn of a single directive
6118 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6119 * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
6120 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6121 */
6122
6123
6124/**
6125 * @ngdoc service
6126 * @name $compile
6127 * @kind function
6128 *
6129 * @description
6130 * Compiles an HTML string or DOM into a template and produces a template function, which
6131 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6132 *
6133 * The compilation is a process of walking the DOM tree and matching DOM elements to
6134 * {@link ng.$compileProvider#directive directives}.
6135 *
6136 * <div class="alert alert-warning">
6137 * **Note:** This document is an in-depth reference of all directive options.
6138 * For a gentle introduction to directives with examples of common use cases,
6139 * see the {@link guide/directive directive guide}.
6140 * </div>
6141 *
6142 * ## Comprehensive Directive API
6143 *
6144 * There are many different options for a directive.
6145 *
6146 * The difference resides in the return value of the factory function.
6147 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6148 * or just the `postLink` function (all other properties will have the default values).
6149 *
6150 * <div class="alert alert-success">
6151 * **Best Practice:** It's recommended to use the "directive definition object" form.
6152 * </div>
6153 *
6154 * Here's an example directive declared with a Directive Definition Object:
6155 *
6156 * ```js
6157 *   var myModule = angular.module(...);
6158 *
6159 *   myModule.directive('directiveName', function factory(injectables) {
6160 *     var directiveDefinitionObject = {
6161 *       priority: 0,
6162 *       template: '<div></div>
6162', // or // function(tElement, tAttrs) { ... },
6163 *       // or
6164 *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6165 *       transclude: false,
6166 *       restrict: 'A',
6167 *       templateNamespace: 'html',
6168 *       scope: false,
6169 *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6170 *       controllerAs: 'stringIdentifier',
6171 *       bindToController: false,
6172 *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6173 *       compile: function compile(tElement, tAttrs, transclude) {
6174 *         return {
6175 *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6176 *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
6177 *         }
6178 *         // or
6179 *         // return function postLink( ... ) { ... }
6180 *       },
6181 *       // or
6182 *       // link: {
6183 *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6184 *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
6185 *       // }
6186 *       // or
6187 *       // link: function postLink( ... ) { ... }
6188 *     };
6189 *     return directiveDefinitionObject;
6190 *   });
6191 * ```
6192 *
6193 * <div class="alert alert-warning">
6194 * **Note:** Any unspecified options will use the default value. You can see the default values below.
6195 * </div>
6196 *
6197 * Therefore the above can be simplified as:
6198 *
6199 * ```js
6200 *   var myModule = angular.module(...);
6201 *
6202 *   myModule.directive('directiveName', function factory(injectables) {
6203 *     var directiveDefinitionObject = {
6204 *       link: function postLink(scope, iElement, iAttrs) { ... }
6205 *     };
6206 *     return directiveDefinitionObject;
6207 *     // or
6208 *     // return function postLink(scope, iElement, iAttrs) { ... }
6209 *   });
6210 * ```
6211 *
6212 *
6213 *
6214 * ### Directive Definition Object
6215 *
6216 * The directive definition object provides instructions to the {@link ng.$compile
6217 * compiler}. The attributes are:
6218 *
6219 * #### `multiElement`
6220 * When this property is set to true, the HTML compiler will collect DOM nodes between
6221 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6222 * together as the directive elements. It is recommended that this feature be used on directives
6223 * which are not strictly behavioural (such as {@link ngClick}), and which
6224 * do not manipulate or replace child nodes (such as {@link ngInclude}).
6225 *
6226 * #### `priority`
6227 * When there are multiple directives defined on a single DOM element, sometimes it
6228 * is necessary to specify the order in which the directives are applied. The `priority` is used
6229 * to sort the directives before their `compile` functions get called. Priority is defined as a
6230 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6231 * are also run in priority order, but post-link functions are run in reverse order. The order
6232 * of directives with the same priority is undefined. The default priority is `0`.
6233 *
6234 * #### `terminal`
6235 * If set to true then the current `priority` will be the last set of directives
6236 * which will execute (any directives at the current priority will still execute
6237 * as the order of execution on same `priority` is undefined). Note that expressions
6238 * and other directives used in the directive's template will also be excluded from execution.
6239 *
6240 * #### `scope`
6241 * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
6242 * same element request a new scope, only one new scope is created. The new scope rule does not
6243 * apply for the root of the template since the root of the template always gets a new scope.
6244 *
6245 * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
6246 * normal scope in that it does not prototypically inherit from the parent scope. This is useful
6247 * when creating reusable components, which should not accidentally read or modify data in the
6248 * parent scope.
6249 *
6250 * The 'isolate' scope takes an object hash which defines a set of local scope properties
6251 * derived from the parent scope. These local properties are useful for aliasing values for
6252 * templates. Locals definition is a hash of local scope property to its source:
6253 *
6254 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6255 *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
6256 *   attribute name is assumed to be the same as the local name.
6257 *   Given `<widget my-attr="hello {{name}}">` and widget definition
6258 *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
6259 *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
6260 *   `localName` property on the widget scope. The `name` is read from the parent scope (not
6261 *   component scope).
6262 *
6263 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
6264 *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
6265 *   name is specified then the attribute name is assumed to be the same as the local name.
6266 *   Given `<widget my-attr="parentModel">` and widget definition of
6267 *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
6268 *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6269 *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
6270 *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
6271 *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
6272 *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
6273 *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
6274 *
6275 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
6276 *   If no `attr` name is specified then the attribute name is assumed to be the same as the
6277 *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
6278 *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
6279 *   a function wrapper for the `count = count + value` expression. Often it's desirable to
6280 *   pass data from the isolated scope via an expression to the parent scope, this can be
6281 *   done by passing a map of local variable names and values into the expression wrapper fn.
6282 *   For example, if the expression is `increment(amount)` then we can specify the amount value
6283 *   by calling the `localFn` as `localFn({amount: 22})`.
6284 *
6285 *
6286 * #### `bindToController`
6287 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
6288 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6289 * is instantiated, the initial values of the isolate scope bindings are already available.
6290 *
6291 * #### `controller`
6292 * Controller constructor function. The controller is instantiated before the
6293 * pre-linking phase and it is shared with other directives (see
6294 * `require` attribute). This allows the directives to communicate with each other and augment
6295 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6296 *
6297 * * `$scope` - Current scope associated with the element
6298 * * `$element` - Current element
6299 * * `$attrs` - Current attributes object for the element
6300 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6301 *   `function([scope], cloneLinkingFn, futureParentElement)`.
6302 *    * `scope`: optional argument to override the scope.
6303 *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6304 *    * `futureParentElement`:
6305 *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6306 *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6307 *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6308 *          and when the `cloneLinkinFn` is passed,
6309 *          as those elements need to created and cloned in a special way when they are defined outside their
6310 *          usual containers (e.g. like `<svg>`).
6311 *        * See also the `directive.templateNamespace` property.
6312 *
6313 *
6314 * #### `require`
6315 * Require another directive and inject its controller as the fourth argument to the linking function. The
6316 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6317 * injected argument will be an array in corresponding order. If no such directive can be
6318 * found, or if the directive does not have a controller, then an error is raised (unless no link function
6319 * is specified, in which case error checking is skipped). The name can be prefixed with:
6320 *
6321 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6322 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6323 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6324 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6325 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6326 *   `null` to the `link` fn if not found.
6327 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6328 *   `null` to the `link` fn if not found.
6329 *
6330 *
6331 * #### `controllerAs`
6332 * Identifier name for a reference to the controller in the directive's scope.
6333 * This allows the controller to be referenced from the directive template. The directive
6334 * needs to define a scope for this configuration to be used. Useful in the case when
6335 * directive is used as component.
6336 *
6337 *
6338 * #### `restrict`
6339 * String of subset of `EACM` which restricts the directive to a specific directive
6340 * declaration style. If omitted, the defaults (elements and attributes) are used.
6341 *
6342 * * `E` - Element name (default): `<my-directive></my-directive>`
6343 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6344 * * `C` - Class: `<div class="my-directive: exp;"></div>`
6345 * * `M` - Comment: `<!-- directive: my-directive exp -->`
6346 *
6347 *
6348 * #### `templateNamespace`
6349 * String representing the document type used by the markup in the template.
6350 * AngularJS needs this information as those elements need to be created and cloned
6351 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6352 *
6353 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6354 *   top-level elements such as `<svg>` or `<math>`.
6355 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6356 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6357 *
6358 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6359 *
6360 * #### `template`
6361 * HTML markup that may:
6362 * * Replace the contents of the directive's element (default).
6363 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6364 * * Wrap the contents of the directive's element (if `transclude` is true).
6365 *
6366 * Value may be:
6367 *
6368 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6369 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6370 *   function api below) and returns a string value.
6371 *
6372 *
6373 * #### `templateUrl`
6374 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6375 *
6376 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6377 * for later when the template has been resolved.  In the meantime it will continue to compile and link
6378 * sibling and parent elements as though this element had not contained any directives.
6379 *
6380 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6381 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6382 * case when only one deeply nested directive has `templateUrl`.
6383 *
6384 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6385 *
6386 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6387 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6388 * a string value representing the url.  In either case, the template URL is passed through {@link
6389 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6390 *
6391 *
6392 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6393 * specify what the template should replace. Defaults to `false`.
6394 *
6395 * * `true` - the template will replace the directive's element.
6396 * * `false` - the template will replace the contents of the directive's element.
6397 *
6398 * The replacement process migrates all of the attributes / classes from the old element to the new
6399 * one. See the {@link guide/directive#template-expanding-directive
6400 * Directives Guide} for an example.
6401 *
6402 * There are very few scenarios where element replacement is required for the application function,
6403 * the main one being reusable custom components that are used within SVG contexts
6404 * (because SVG doesn't work with custom elements in the DOM tree).
6405 *
6406 * #### `transclude`
6407 * Extract the contents of the element where the directive appears and make it available to the directive.
6408 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6409 * {@link $compile#transclusion Transclusion} section below.
6410 *
6411 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6412 * directive's element or the entire element:
6413 *
6414 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6415 * * `'element'` - transclude the whole of the directive's element including any directives on this
6416 *   element that defined at a lower priority than this directive. When used, the `template`
6417 *   property is ignored.
6418 *
6419 *
6420 * #### `compile`
6421 *
6422 * ```js
6423 *   function compile(tElement, tAttrs, transclude) { ... }
6424 * ```
6425 *
6426 * The compile function deals with transforming the template DOM. Since most directives do not do
6427 * template transformation, it is not used often. The compile function takes the following arguments:
6428 *
6429 *   * `tElement` - template element - The element where the directive has been declared. It is
6430 *     safe to do template transformation on the element and child elements only.
6431 *
6432 *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6433 *     between all directive compile functions.
6434 *
6435 *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6436 *
6437 * <div class="alert alert-warning">
6438 * **Note:** The template instance and the link instance may be different objects if the template has
6439 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6440 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6441 * should be done in a linking function rather than in a compile function.
6442 * </div>
6443
6444 * <div class="alert alert-warning">
6445 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6446 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6447 * stack overflow errors.
6448 *
6449 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6450 * a directive's template instead of relying on automatic template compilation via `template` or
6451 * `templateUrl` declaration or manual compilation inside the compile function.
6452 * </div>
6453 *
6454 * <div class="alert alert-danger">
6455 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6456 *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6457 *   to the link function instead.
6458 * </div>
6459
6460 * A compile function can have a return value which can be either a function or an object.
6461 *
6462 * * returning a (post-link) function - is equivalent to registering the linking function via the
6463 *   `link` property of the config object when the compile function is empty.
6464 *
6465 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6466 *   control when a linking function should be called during the linking phase. See info about
6467 *   pre-linking and post-linking functions below.
6468 *
6469 *
6470 * #### `link`
6471 * This property is used only if the `compile` property is not defined.
6472 *
6473 * ```js
6474 *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6475 * ```
6476 *
6477 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6478 * executed after the template has been cloned. This is where most of the directive logic will be
6479 * put.
6480 *
6481 *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6482 *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6483 *
6484 *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6485 *     manipulate the children of the element only in `postLink` function since the children have
6486 *     already been linked.
6487 *
6488 *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6489 *     between all directive linking functions.
6490 *
6491 *   * `controller` - the directive's required controller instance(s) - Instances are shared
6492 *     among all directives, which allows the directives to use the controllers as a communication
6493 *     channel. The exact value depends on the directive's `require` property:
6494 *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6495 *       * `string`: the controller instance
6496 *       * `array`: array of controller instances
6497 *
6498 *     If a required controller cannot be found, and it is optional, the instance is `null`,
6499 *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6500 *
6501 *     Note that you can also require the directive's own controller - it will be made available like
6502 *     like any other controller.
6503 *
6504 *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6505 *     This is the same as the `$transclude`
6506 *     parameter of directive controllers, see there for details.
6507 *     `function([scope], cloneLinkingFn, futureParentElement)`.
6508 *
6509 * #### Pre-linking function
6510 *
6511 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6512 * compiler linking function will fail to locate the correct elements for linking.
6513 *
6514 * #### Post-linking function
6515 *
6516 * Executed after the child elements are linked.
6517 *
6518 * Note that child elements that contain `templateUrl` directives will not have been compiled
6519 * and linked since they are waiting for their template to load asynchronously and their own
6520 * compilation and linking has been suspended until that occurs.
6521 *
6522 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6523 * for their async templates to be resolved.
6524 *
6525 *
6526 * ### Transclusion
6527 *
6528 * Transclusion is the process of extracting a collection of DOM element from one part of the DOM and
6529 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6530 * scope from where they were taken.
6531 *
6532 * Transclusion is used (often with {@link ngTransclude}) to insert the
6533 * original contents of a directive's element into a specified place in the template of the directive.
6534 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6535 * content has access to the properties on the scope from which it was taken, even if the directive
6536 * has isolated scope.
6537 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6538 *
6539 * This makes it possible for the widget to have private state for its template, while the transcluded
6540 * content has access to its originating scope.
6541 *
6542 * <div class="alert alert-warning">
6543 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6544 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6545 * Testing Transclusion Directives}.
6546 * </div>
6547 *
6548 * #### Transclusion Functions
6549 *
6550 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6551 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6552 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6553 *
6554 * <div class="alert alert-info">
6555 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6556 * ngTransclude will deal with it for us.
6557 * </div>
6558 *
6559 * If you want to manually control the insertion and removal of the transcluded content in your directive
6560 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6561 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6562 *
6563 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6564 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6565 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6566 *
6567 * <div class="alert alert-info">
6568 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6569 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6570 * </div>
6571 *
6572 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6573 * attach function**:
6574 *
6575 * ```js
6576 * var transcludedContent, transclusionScope;
6577 *
6578 * $transclude(function(clone, scope) {
6579 *   element.append(clone);
6580 *   transcludedContent = clone;
6581 *   transclusionScope = scope;
6582 * });
6583 * ```
6584 *
6585 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6586 * associated transclusion scope:
6587 *
6588 * ```js
6589 * transcludedContent.remove();
6590 * transclusionScope.$destroy();
6591 * ```
6592 *
6593 * <div class="alert alert-info">
6594 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6595 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
6596 * then you are also responsible for calling `$destroy` on the transclusion scope.
6597 * </div>
6598 *
6599 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6600 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6601 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6602 *
6603 *
6604 * #### Transclusion Scopes
6605 *
6606 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6607 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6608 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6609 * was taken.
6610 *
6611 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6612 * like this:
6613 *
6614 * ```html
6615 * <div ng-app>
6616 *   <div isolate>
6617 *     <div transclusion>
6618 *     </div>
6619 *   </div>
6620 * </div>
6621 * ```
6622 *
6623 * The `$parent` scope hierarchy will look like this:
6624 *
6625 * ```
6626 * - $rootScope
6627 *   - isolate
6628 *     - transclusion
6629 * ```
6630 *
6631 * but the scopes will inherit prototypically from different scopes to their `$parent`.
6632 *
6633 * ```
6634 * - $rootScope
6635 *   - transclusion
6636 * - isolate
6637 * ```
6638 *
6639 *
6640 * ### Attributes
6641 *
6642 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6643 * `link()` or `compile()` functions. It has a variety of uses.
6644 *
6645 * accessing *Normalized attribute names:*
6646 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6647 * the attributes object allows for normalized access to
6648 *   the attributes.
6649 *
6650 * * *Directive inter-communication:* All directives share the same instance of the attributes
6651 *   object which allows the directives to use the attributes object as inter directive
6652 *   communication.
6653 *
6654 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6655 *   allowing other directives to read the interpolated value.
6656 *
6657 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6658 *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6659 *   the only way to easily get the actual value because during the linking phase the interpolation
6660 *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
6661 *
6662 * ```js
6663 * function linkingFn(scope, elm, attrs, ctrl) {
6664 *   // get the attribute value
6665 *   console.log(attrs.ngModel);
6666 *
6667 *   // change the attribute
6668 *   attrs.$set('ngModel', 'new value');
6669 *
6670 *   // observe changes to interpolated attribute
6671 *   attrs.$observe('ngModel', function(value) {
6672 *     console.log('ngModel has changed value to ' + value);
6673 *   });
6674 * }
6675 * ```
6676 *
6677 * ## Example
6678 *
6679 * <div class="alert alert-warning">
6680 * **Note**: Typically directives are registered with `module.directive`. The example below is
6681 * to illustrate how `$compile` works.
6682 * </div>
6683 *
6684 <example module="compileExample">
6685   <file name="index.html">
6686    <script>
6687      angular.module('compileExample', [], function($compileProvider) {
6688        // configure new 'compile' directive by passing a directive
6689        // factory function. The factory function injects the '$compile'
6690        $compileProvider.directive('compile', function($compile) {
6691          // directive factory creates a link function
6692          return function(scope, element, attrs) {
6693            scope.$watch(
6694              function(scope) {
6695                 // watch the 'compile' expression for changes
6696                return scope.$eval(attrs.compile);
6697              },
6698              function(value) {
6699                // when the 'compile' expression changes
6700                // assign it into the current DOM
6701                element.html(value);
6702
6703                // compile the new DOM and link it to the current
6704                // scope.
6705                // NOTE: we only compile .childNodes so that
6706                // we don't get into infinite loop compiling ourselves
6707                $compile(element.contents())(scope);
6708              }
6709            );
6710          };
6711        });
6712      })
6713      .controller('GreeterController', ['$scope', function($scope) {
6714        $scope.name = 'Angular';
6715        $scope.html = 'Hello {{name}}';
6716      }]);
6717    </script>
6718    <div ng-controller="GreeterController">
6719      <input ng-model="name"> <br/>
6720      <textarea ng-model="html"></textarea> <br/>
6721      <div compile="html"></div>
6722    </div>
6723   </file>
6724   <file name="protractor.js" type="protractor">
6725     it('should auto compile', function() {
6726       var textarea = $('textarea');
6727       var output = $('div[compile]');
6728       // The initial state reads 'Hello Angular'.
6729       expect(output.getText()).toBe('Hello Angular');
6730       textarea.clear();
6731       textarea.sendKeys('{{name}}!');
6732       expect(output.getText()).toBe('Angular!');
6733     });
6734   </file>
6735 </example>
6736
6737 *
6738 *
6739 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
6740 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
6741 *
6742 * <div class="alert alert-danger">
6743 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
6744 *   e.g. will not use the right outer scope. Please pass the transclude function as a
6745 *   `parentBoundTranscludeFn` to the link function instead.
6746 * </div>
6747 *
6748 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
6749 *                 root element(s), not their children)
6750 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
6751 * (a DOM element/tree) to a scope. Where:
6752 *
6753 *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
6754 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
6755 *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
6756 *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
6757 *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
6758 *
6759 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
6760 *      * `scope` - is the current scope with which the linking function is working with.
6761 *
6762 *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
6763 *  keys may be used to control linking behavior:
6764 *
6765 *      * `parentBoundTranscludeFn` - the transclude function made available to
6766 *        directives; if given, it will be passed through to the link functions of
6767 *        directives found in `element` during compilation.
6768 *      * `transcludeControllers` - an object hash with keys that map controller names
6769 *        to controller instances; if given, it will make the controllers
6770 *        available to directives.
6771 *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
6772 *        the cloned elements; only needed for transcludes that are allowed to contain non html
6773 *        elements (e.g. SVG elements). See also the directive.controller property.
6774 *
6775 * Calling the linking function returns the element of the template. It is either the original
6776 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
6777 *
6778 * After linking the view is not updated until after a call to $digest which typically is done by
6779 * Angular automatically.
6780 *
6781 * If you need access to the bound view, there are two ways to do it:
6782 *
6783 * - If you are not asking the linking function to clone the template, create the DOM element(s)
6784 *   before you send them to the compiler and keep this reference around.
6785 *   ```js
6786 *     var element = $compile('<p>{{total}}</p>')(scope);
6787 *   ```
6788 *
6789 * - if on the other hand, you need the element to be cloned, the view reference from the original
6790 *   example would not point to the clone, but rather to the original template that was cloned. In
6791 *   this case, you can access the clone via the cloneAttachFn:
6792 *   ```js
6793 *     var templateElement = angular.element('<p>{{total}}</p>'),
6794 *         scope = ....;
6795 *
6796 *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
6797 *       //attach the clone to DOM document at the right place
6798 *     });
6799 *
6800 *     //now we have reference to the cloned DOM via `clonedElement`
6801 *   ```
6802 *
6803 *
6804 * For information on how the compiler works, see the
6805 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
6806 */
6807
6808var $compileMinErr = minErr('$compile');
6809
6810/**
6811 * @ngdoc provider
6812 * @name $compileProvider
6813 *
6814 * @description
6815 */
6816$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
6817function $CompileProvider($provide, $$sanitizeUriProvider) {
6818  var hasDirectives = {},
6819      Suffix = 'Directive',
6820      COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
6821      CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
6822      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
6823      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
6824
6825  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
6826  // The assumption is that future DOM event attribute names will begin with
6827  // 'on' and be composed of only English letters.
6828  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
6829
6830  function parseIsolateBindings(scope, directiveName, isController) {
6831    var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
6832
6833    var bindings = {};
6834
6835    forEach(scope, function(definition, scopeName) {
6836      var match = definition.match(LOCAL_REGEXP);
6837
6838      if (!match) {
6839        throw $compileMinErr('iscp',
6840            "Invalid {3} for directive '{0}'." +
6841            " Definition: {... {1}: '{2}' ...}",
6842            directiveName, scopeName, definition,
6843            (isController ? "controller bindings definition" :
6844            "isolate scope definition"));
6845      }
6846
6847      bindings[scopeName] = {
6848        mode: match[1][0],
6849        collection: match[2] === '*',
6850        optional: match[3] === '?',
6851        attrName: match[4] || scopeName
6852      };
6853    });
6854
6855    return bindings;
6856  }
6857
6858  function parseDirectiveBindings(directive, directiveName) {
6859    var bindings = {
6860      isolateScope: null,
6861      bindToController: null
6862    };
6863    if (isObject(directive.scope)) {
6864      if (directive.bindToController === true) {
6865        bindings.bindToController = parseIsolateBindings(directive.scope,
6866                                                         directiveName, true);
6867        bindings.isolateScope = {};
6868      } else {
6869        bindings.isolateScope = parseIsolateBindings(directive.scope,
6870                                                     directiveName, false);
6871      }
6872    }
6873    if (isObject(directive.bindToController)) {
6874      bindings.bindToController =
6875          parseIsolateBindings(directive.bindToController, directiveName, true);
6876    }
6877    if (isObject(bindings.bindToController)) {
6878      var controller = directive.controller;
6879      var controllerAs = directive.controllerAs;
6880      if (!controller) {
6881        // There is no controller, there may or may not be a controllerAs property
6882        throw $compileMinErr('noctrl',
6883              "Cannot bind to controller without directive '{0}'s controller.",
6884              directiveName);
6885      } else if (!identifierForController(controller, controllerAs)) {
6886        // There is a controller, but no identifier or controllerAs property
6887        throw $compileMinErr('noident',
6888              "Cannot bind to controller without identifier for directive '{0}'.",
6889              directiveName);
6890      }
6891    }
6892    return bindings;
6893  }
6894
6895  function assertValidDirectiveName(name) {
6896    var letter = name.charAt(0);
6897    if (!letter || letter !== lowercase(letter)) {
6898      throw $compileMinErr('baddir', "Directive name '{0}
vendor: 4,630 bytes, lines 6898-7010
6898' is invalid. The first character must be a lowercase letter", name);
6899    }
6900    if (name !== name.trim()) {
6901      throw $compileMinErr('baddir',
6902            "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
6903            name);
6904    }
6905  }
6906
6907  /**
6908   * @ngdoc method
6909   * @name $compileProvider#directive
6910   * @kind function
6911   *
6912   * @description
6913   * Register a new directive with the compiler.
6914   *
6915   * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
6916   *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
6917   *    names and the values are the factories.
6918   * @param {Function|Array} directiveFactory An injectable directive factory function. See
6919   *    {@link guide/directive} for more info.
6920   * @returns {ng.$compileProvider} Self for chaining.
6921   */
6922   this.directive = function registerDirective(name, directiveFactory) {
6923    assertNotHasOwnProperty(name, 'directive');
6924    if (isString(name)) {
6925      assertValidDirectiveName(name);
6926      assertArg(directiveFactory, 'directiveFactory');
6927      if (!hasDirectives.hasOwnProperty(name)) {
6928        hasDirectives[name] = [];
6929        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
6930          function($injector, $exceptionHandler) {
6931            var directives = [];
6932            forEach(hasDirectives[name], function(directiveFactory, index) {
6933              try {
6934                var directive = $injector.invoke(directiveFactory);
6935                if (isFunction(directive)) {
6936                  directive = { compile: valueFn(directive) };
6937                } else if (!directive.compile && directive.link) {
6938                  directive.compile = valueFn(directive.link);
6939                }
6940                directive.priority = directive.priority || 0;
6941                directive.index = index;
6942                directive.name = directive.name || name;
6943                directive.require = directive.require || (directive.controller && directive.name);
6944                directive.restrict = directive.restrict || 'EA';
6945                var bindings = directive.$$bindings =
6946                    parseDirectiveBindings(directive, directive.name);
6947                if (isObject(bindings.isolateScope)) {
6948                  directive.$$isolateBindings = bindings.isolateScope;
6949                }
6950                directive.$$moduleName = directiveFactory.$$moduleName;
6951                directives.push(directive);
6952              } catch (e) {
6953                $exceptionHandler(e);
6954              }
6955            });
6956            return directives;
6957          }]);
6958      }
6959      hasDirectives[name].push(directiveFactory);
6960    } else {
6961      forEach(name, reverseParams(registerDirective));
6962    }
6963    return this;
6964  };
6965
6966
6967  /**
6968   * @ngdoc method
6969   * @name $compileProvider#aHrefSanitizationWhitelist
6970   * @kind function
6971   *
6972   * @description
6973   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
6974   * urls during a[href] sanitization.
6975   *
6976   * The sanitization is a security measure aimed at preventing XSS attacks via html links.
6977   *
6978   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
6979   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
6980   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
6981   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
6982   *
6983   * @param {RegExp=} regexp New regexp to whitelist urls with.
6984   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
6985   *    chaining otherwise.
6986   */
6987  this.aHrefSanitizationWhitelist = function(regexp) {
6988    if (isDefined(regexp)) {
6989      $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
6990      return this;
6991    } else {
6992      return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
6993    }
6994  };
6995
6996
6997  /**
6998   * @ngdoc method
6999   * @name $compileProvider#imgSrcSanitizationWhitelist
7000   * @kind function
7001   *
7002   * @description
7003   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7004   * urls during img[src] sanitization.
7005   *
7006   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7007   *
7008   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7009   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7010   * regular expression. If a match is found, the original url is 
7010written into the dom. Otherwise,
7011   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7012   *
7013   * @param {RegExp=} regexp New regexp to whitelist urls with.
7014   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7015   *    chaining otherwise.
7016   */
7017  this.imgSrcSanitizationWhitelist = function(regexp) {
7018    if (isDefined(regexp)) {
7019      $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7020      return this;
7021    } else {
7022      return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7023    }
7024  };
7025
7026  /**
7027   * @ngdoc method
7028   * @name  $compileProvider#debugInfoEnabled
7029   *
7030   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7031   * current debugInfoEnabled state
7032   * @returns {*} current value if used as getter or itself (chaining) if used as setter
7033   *
7034   * @kind function
7035   *
7036   * @description
7037   * Call this method to enable/disable various debug runtime information in the compiler such as adding
7038   * binding information and a reference to the current scope on to DOM elements.
7039   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7040   * * `ng-binding` CSS class
7041   * * `$binding` data property containing an array of the binding expressions
7042   *
7043   * You may want to disable this in production for a significant performance boost. See
7044   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7045   *
7046   * The default value is true.
7047   */
7048  var debugInfoEnabled = true;
7049  this.debugInfoEnabled = function(enabled) {
7050    if (isDefined(enabled)) {
7051      debugInfoEnabled = enabled;
7052      return this;
7053    }
7054    return debugInfoEnabled;
7055  };
7056
7057  this.$get = [
7058            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7059            '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
7060    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
7061             $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
7062
7063    var Attributes = function(element, attributesToCopy) {
7064      if (attributesToCopy) {
7065        var keys = Object.keys(attributesToCopy);
7066        var i, l, key;
7067
7068        for (i = 0, l = keys.length; i < l; i++) {
7069          key = keys[i];
7070          this[key] = attributesToCopy[key];
7071        }
7072      } else {
7073        this.$attr = {};
7074      }
7075
7076      this.$$element = element;
7077    };
7078
7079    Attributes.prototype = {
7080      /**
7081       * @ngdoc method
7082       * @name $compile.directive.Attributes#$normalize
7083       * @kind function
7084       *
7085       * @description
7086       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7087       * `data-`) to its normalized, camelCase form.
7088       *
7089       * Also there is special case for Moz prefix starting with upper case letter.
7090       *
7091       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7092       *
7093       * @param {string} name Name to normalize
7094       */
7095      $normalize: directiveNormalize,
7096
7097
7098      /**
7099       * @ngdoc method
7100       * @name $compile.directive.Attributes#$addClass
7101       * @kind function
7102       *
7103       * @description
7104       * Adds the CSS class value specified by the classVal parameter to the element. If animations
7105       * are enabled then an animation will be triggered for the class addition.
7106       *
7107       * @param {string} classVal The className value that will be added to the element
7108       */
7109      $addClass: function(classVal) {
7110        if (classVal && classVal.length > 0) {
7111          $animate.addClass(this.$$element, classVal);
7112        }
7113      },
7114
7115      /**
7116       * @ngdoc method
7117       * @name $compile.directive.Attributes#$removeClass
7118       * @kind function
7119       *
7120       * @description
7121       * Removes the CSS class value specified by the classVal parameter from the element. If
7122       * animations are enabled then an animation will be triggered for the class removal.
7123       *
7124       * @param {string} classVal The className value that will be removed from the element
7125       */
7126      $removeClass: function(classVal) {
7127        if (classVal && classVal.length > 0) {
7128          $animate.removeClass(this.$$element, classVal);
7129        }
7130      },
7131
7132      /**
7133       * @ngdoc method
7134       * @name $compile.directive.Attributes#$updateClass
7135       * @kind function
7136       *
7137       * @description
7138       * Adds and removes the appropriate CSS class values to the element based on the difference
7139       * between the new and old CSS class values (specified as newClasses and oldClasses).
7140       *
7141       * @param {string} newClasses The current CSS className value
7142       * @param {string} oldClasses The former CSS className value
7143       */
7144      $updateClass: function(newClasses, oldClasses) {
7145        var toAdd = tokenDifference(newClasses, oldClasses);
7146        if (toAdd && toAdd.length) {
7147          $animate.addClass(this.$$element, toAdd);
7148        }
7149
7150        var toRemove = tokenDifference(oldClasses, newClasses);
7151        if (toRemove && toRemove.length) {
7152          $animate.removeClass(this.$$element, toRemove);
7153        }
7154      },
7155
7156      /**
7157       * Set a normalized attribute on the element in a way such that all directives
7158       * can share the attribute. This function properly handles boolean attributes.
7159       * @param {string} key Normalized key. (ie ngAttribute)
7160       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7161       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7162       *     Defaults to true.
7163       * @param {string=} attrName Optional none normalized name. Defaults to key.
7164       */
7165      $set: function(key, value, writeAttr, attrName) {
7166        // TODO: decide whether or not to throw an error if "class"
7167        //is set through this function since it may cause $updateClass to
7168        //become unstable.
7169
7170        var node = this.$$element[0],
7171            booleanKey = getBooleanAttrName(node, key),
7172            aliasedKey = getAliasedAttrName(node, key),
7173            observer = key,
7174            nodeName;
7175
7176        if (booleanKey) {
7177          this.$$element.prop(key, value);
7178          attrName = booleanKey;
7179        } else if (aliasedKey) {
7180          this[aliasedKey] = value;
7181          observer = aliasedKey;
7182        }
7183
7184        this[key] = value;
7185
7186        // translate normalized key to actual key
7187        if (attrName) {
7188          this.$attr[key] = attrName;
7189        } else {
7190          attrName = this.$attr[key];
7191          if (!attrName) {
7192            this.$attr[key] = attrName = snake_case(key, '-');
7193          }
7194        }
7195
7196        nodeName = nodeName_(this.$$element);
7197
7198        if ((nodeName === 'a' && key === 'href') ||
7199            (nodeName === 'img' && key === 'src')) {
7200          // sanitize a[href] and img[src] values
7201          this[key] = value = $$sanitizeUri(value, key === 'src');
7202        } else if (nodeName === 'img' && key === 'srcset') {
7203          // sanitize img[srcset] values
7204          var result = "";
7205
7206          // first check if there are spaces because it's not the same pattern
7207          var trimmedSrcset = trim(value);
7208          //                (   999x   ,|   999w   ,|   ,|,   )
7209          var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7210          var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7211
7212          // split srcset into tuple of uri and descriptor except for the last item
7213          var rawUris = trimmedSrcset.split(pattern);
7214
7215          // for each tuples
7216          var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7217          for (var i = 0; i < nbrUrisWith2parts; i++) {
7218            var innerIdx = i * 2;
7219            // sanitize the uri
7220            result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7221            // add the descriptor
7222            result += (" " + trim(rawUris[innerIdx + 1]));
7223          }
7224
7225          // split the last item into uri and descriptor
7226          var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7227
7228          // sanitize the last uri
7229          result += $$sanitizeUri(trim(lastTuple[0]), true);
7230
7231          // and add the last descriptor if any
7232          if (lastTuple.length === 2) {
7233            result += (" " + trim(lastTuple[1]));
7234          }
7235          this[key] = value = result;
7236        }
7237
7238        if (writeAttr !== false) {
7239          if (value === null || value === undefined) {
7240            this.$$element.removeAttr(attrName);
7241          } else {
7242            this.$$element.attr(attrName, value);
7243          }
7244        }
7245
7246        // fire observers
7247        var $$observers = this.$$observers;
7248        $$observers && forEach($$observers[observer], function(fn) {
7249          try {
7250            fn(value);
7251          } catch (e) {
7252            $exceptionHandler(e);
7253          }
7254        });
7255      },
7256
7257
7258      /**
7259       * @ngdoc method
7260       * @name $compile.directive.Attributes#$observe
7261       * @kind function
7262       *
7263       * @description
7264       * Observes an interpolated attribute.
7265       *
7266       * The observer function will be invoked once during the next `$digest` following
7267       * compilation. The observer is then invoked whenever the interpolated value
7268       * changes.
7269       *
7270       * @param {string} key Normalized key. (ie ngAttribute) .
7271       * @param {function(interpolatedValue)} fn Function that will be called whenever
7272                the interpolated value of the attribute changes.
7273       *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
7274       * @returns {function()} Returns a deregistration function for this observer.
7275       */
7276      $observe: function(key, fn) {
7277        var attrs = this,
7278            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7279            listeners = ($$observers[key] || ($$observers[key] = []));
7280
7281        listeners.push(fn);
7282        $rootScope.$evalAsync(function() {
7283          if (!listeners.$$inter && attrs.hasOwnProperty(key)) {
7284            // no one registered attribute interpolation function, so lets call it manually
7285            fn(attrs[key]);
7286          }
7287        });
7288
7289        return function() {
7290          arrayRemove(listeners, fn);
7291        };
7292      }
7293    };
7294
7295
7296    function safeAddClass($element, className) {
7297      try {
7298        $element.addClass(className);
7299      } catch (e) {
7300        // ignore, since it means that we are trying to set class on
7301        // SVG element, where class name is read-only.
7302      }
7303    }
7304
7305
7306    var startSymbol = $interpolate.startSymbol(),
7307        endSymbol = $interpolate.endSymbol(),
7308        denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
7309            ? identity
7310            : function denormalizeTemplate(template) {
7311              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7312        },
7313        NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7314
7315    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7316      var bindings = $element.data('$binding') || [];
7317
7318      if (isArray(binding)) {
7319        bindings = bindings.concat(binding);
7320      } else {
7321        bindings.push(binding);
7322      }
7323
7324      $element.data('$binding', bindings);
7325    } : noop;
7326
7327    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7328      safeAddClass($element, 'ng-binding');
7329    } : noop;
7330
7331    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7332      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7333      $element.data(dataName, scope);
7334    } : noop;
7335
7336    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7337      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7338    } : noop;
7339
7340    return compile;
7341
7342    //================================
7343
7344    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7345                        previousCompileContext) {
7346      if (!($compileNodes instanceof jqLite)) {
7347        // jquery always rewraps, whereas we need to preserve the original selector so that we can
7348        // modify it.
7349        $compileNodes = jqLite($compileNodes);
7350      }
7351      // We can not compile top level text elements since text nodes can be merged and we will
7352      // not be able to attach scope data to them, so we will wrap them in <span>
7353      forEach($compileNodes, function(node, index) {
7354        if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
7355          $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
7356        }
7357      });
7358      var compositeLinkFn =
7359              compileNodes($compileNodes, transcludeFn, $compileNodes,
7360                           maxPriority, ignoreDirective, previousCompileContext);
7361      compile.$$addScopeClass($compileNodes);
7362      var namespace = null;
7363      return function publicLinkFn(scope, cloneConnectFn, options) {
7364        assertArg(scope, 'scope');
7365
7366        options = options || {};
7367        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7368          transcludeControllers = options.transcludeControllers,
7369          futureParentElement = options.futureParentElement;
7370
7371        // When `parentBoundTranscludeFn` is passed, it is a
7372        // `controllersBoundTransclude` function (it was previously passed
7373        // as `transclude` to directive.link) so we must unwrap it to get
7374        // its `boundTranscludeFn`
7375        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7376          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7377        }
7378
7379        if (!namespace) {
7380          namespace = detectNamespaceForChildElements(futureParentElement);
7381        }
7382        var $linkNode;
7383        if (namespace !== 'html') {
7384          // When using a directive with replace:true and templateUrl the $compileNodes
7385          // (or a child element inside of them)
7386          // might change, so we need to recreate the namespace adapted compileNodes
7387          // for call to the link function.
7388          // Note: This will already clone the nodes...
7389          $linkNode = jqLite(
7390            wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7391          );
7392        } else if (cloneConnectFn) {
7393          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
vendor: 7,988 bytes, lines 7394-7588
7394          // and sometimes changes the structure of the DOM.
7395          $linkNode = JQLitePrototype.clone.call($compileNodes);
7396        } else {
7397          $linkNode = $compileNodes;
7398        }
7399
7400        if (transcludeControllers) {
7401          for (var controllerName in transcludeControllers) {
7402            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7403          }
7404        }
7405
7406        compile.$$addScopeInfo($linkNode, scope);
7407
7408        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7409        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7410        return $linkNode;
7411      };
7412    }
7413
7414    function detectNamespaceForChildElements(parentElement) {
7415      // TODO: Make this detect MathML as well...
7416      var node = parentElement && parentElement[0];
7417      if (!node) {
7418        return 'html';
7419      } else {
7420        return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7421      }
7422    }
7423
7424    /**
7425     * Compile function matches each node in nodeList against the directives. Once all directives
7426     * for a particular node are collected their compile functions are executed. The compile
7427     * functions return values - the linking functions - are combined into a composite linking
7428     * function, which is the a linking function for the node.
7429     *
7430     * @param {NodeList} nodeList an array of nodes or NodeList to compile
7431     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7432     *        scope argument is auto-generated to the new child of the transcluded parent scope.
7433     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7434     *        the rootElement must be set the jqLite collection of the compile root. This is
7435     *        needed so that the jqLite collection items can be replaced with widgets.
7436     * @param {number=} maxPriority Max directive priority.
7437     * @returns {Function} A composite linking function of all of the matched directives or null.
7438     */
7439    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7440                            previousCompileContext) {
7441      var linkFns = [],
7442          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7443
7444      for (var i = 0; i < nodeList.length; i++) {
7445        attrs = new Attributes();
7446
7447        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7448        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7449                                        ignoreDirective);
7450
7451        nodeLinkFn = (directives.length)
7452            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7453                                      null, [], [], previousCompileContext)
7454            : null;
7455
7456        if (nodeLinkFn && nodeLinkFn.scope) {
7457          compile.$$addScopeClass(attrs.$$element);
7458        }
7459
7460        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7461                      !(childNodes = nodeList[i].childNodes) ||
7462                      !childNodes.length)
7463            ? null
7464            : compileNodes(childNodes,
7465                 nodeLinkFn ? (
7466                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7467                     && nodeLinkFn.transclude) : transcludeFn);
7468
7469        if (nodeLinkFn || childLinkFn) {
7470          linkFns.push(i, nodeLinkFn, childLinkFn);
7471          linkFnFound = true;
7472          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7473        }
7474
7475        //use the previous context only for the first element in the virtual group
7476        previousCompileContext = null;
7477      }
7478
7479      // return a linking function if we have found anything, null otherwise
7480      return linkFnFound ? compositeLinkFn : null;
7481
7482      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7483        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7484        var stableNodeList;
7485
7486
7487        if (nodeLinkFnFound) {
7488          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7489          // offsets don't get screwed up
7490          var nodeListLength = nodeList.length;
7491          stableNodeList = new Array(nodeListLength);
7492
7493          // create a sparse array by only copying the elements which have a linkFn
7494          for (i = 0; i < linkFns.length; i+=3) {
7495            idx = linkFns[i];
7496            stableNodeList[idx] = nodeList[idx];
7497          }
7498        } else {
7499          stableNodeList = nodeList;
7500        }
7501
7502        for (i = 0, ii = linkFns.length; i < ii;) {
7503          node = stableNodeList[linkFns[i++]];
7504          nodeLinkFn = linkFns[i++];
7505          childLinkFn = linkFns[i++];
7506
7507          if (nodeLinkFn) {
7508            if (nodeLinkFn.scope) {
7509              childScope = scope.$new();
7510              compile.$$addScopeInfo(jqLite(node), childScope);
7511              var destroyBindings = nodeLinkFn.$$destroyBindings;
7512              if (destroyBindings) {
7513                nodeLinkFn.$$destroyBindings = null;
7514                childScope.$on('$destroyed', destroyBindings);
7515              }
7516            } else {
7517              childScope = scope;
7518            }
7519
7520            if (nodeLinkFn.transcludeOnThisElement) {
7521              childBoundTranscludeFn = createBoundTranscludeFn(
7522                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7523
7524            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7525              childBoundTranscludeFn = parentBoundTranscludeFn;
7526
7527            } else if (!parentBoundTranscludeFn && transcludeFn) {
7528              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7529
7530            } else {
7531              childBoundTranscludeFn = null;
7532            }
7533
7534            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn,
7535                       nodeLinkFn);
7536
7537          } else if (childLinkFn) {
7538            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7539          }
7540        }
7541      }
7542    }
7543
7544    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7545
7546      var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7547
7548        if (!transcludedScope) {
7549          transcludedScope = scope.$new(false, containingScope);
7550          transcludedScope.$$transcluded = true;
7551        }
7552
7553        return transcludeFn(transcludedScope, cloneFn, {
7554          parentBoundTranscludeFn: previousBoundTranscludeFn,
7555          transcludeControllers: controllers,
7556          futureParentElement: futureParentElement
7557        });
7558      };
7559
7560      return boundTranscludeFn;
7561    }
7562
7563    /**
7564     * Looks for directives on the given node and adds them to the directive collection which is
7565     * sorted.
7566     *
7567     * @param node Node to search.
7568     * @param directives An array to which the directives are added to. This array is sorted before
7569     *        the function returns.
7570     * @param attrs The shared attrs object which is used to populate the normalized attributes.
7571     * @param {number=} maxPriority Max directive priority.
7572     */
7573    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7574      var nodeType = node.nodeType,
7575          attrsMap = attrs.$attr,
7576          match,
7577          className;
7578
7579      switch (nodeType) {
7580        case NODE_TYPE_ELEMENT: /* Element */
7581          // use the node name: <directive>
7582          addDirective(directives,
7583              directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7584
7585          // iterate over the attributes
7586          for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7587                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7588            var attrStartName = false;
7589            var attrEndName = false;
7590
7591            attr = nAttrs[j];
7592            name = attr.name;
7593            value = trim(attr.value);
7594
7595            // support ngAttr attribute binding
7596            ngAttrName = directiveNormalize(name);
7597            if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7598              name = name.replace(PREFIX_REGEXP, '')
7599                .substr(8).replace(/_(.)/g, function(match, letter) {
7600                  return letter.toUpperCase();
7601                });
7602            }
7603
7604            var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
7605            if (directiveIsMultiElement(directiveNName)) {
7606              if (ngAttrName === directiveNName + 'Start') {
7607                attrStartName = name;
7608                attrEndName = name.substr(0, name.length - 5) + 'end';
7609                name = name.substr(0, name.length - 6);
7610              }
7611            }
7612
7613            nName = directiveNormalize(name.toLowerCase());
7614            attrsMap[nName] = name;
7615            if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7616                attrs[nName] = value;
7617                if (getBooleanAttrName(node, nName)) {
7618                  attrs[nName] = true; // presence means true
7619                }
7620            }
7621            addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7622            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7623                          attrEndName);
7624          }
7625
7626          // use class as directive
7627          className = node.className;
7628          if (isObject(className)) {
7629              // Maybe SVGAnimatedString
7630              className = className.animVal;
7631          }
7632          if (isString(className) && className !== '') {
7633            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7634              nName = directiveNormalize(match[2]);
7635              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7636                attrs[nName] = trim(match[3]);
7637              }
7638              className = className.substr(match.index + match[0].length);
7639            }
7640          }
7641          break;
7642        case NODE_TYPE_TEXT: /* Text Node */
7643          if (msie === 11) {
7644            // Workaround for #11781
7645            while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
7646              node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
7647              node.parentNode.removeChild(node.nextSibling);
7648            }
7649          }
7650          addTextInterpolateDirective(directives, node.nodeValue);
7651          break;
7652        case NODE_TYPE_COMMENT: /* Comment */
7653          try {
7654            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7655            if (match) {
7656              nName = directiveNormalize(match[1]);
7657              if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7658                attrs[nName] = trim(match[2]);
7659              }
7660            }
7661          } catch (e) {
7662            // turns out that under some circumstances IE9 throws errors when one attempts to read
7663            // comment's node value.
7664            // Just ignore it and continue. (Can't seem to reproduce in test case.)
7665          }
7666          break;
7667      }
7668
7669      directives.sort(byPriority);
7670      return directives;
7671    }
7672
7673    /**
7674     * Given a node with an directive-start it collects all of the siblings until it finds
7675     * directive-end.
7676     * @param node
7677     * @param attrStart
7678     * @param attrEnd
7679     * @returns {*}
7680     */
7681    function groupScan(node, attrStart, attrEnd) {
7682      var nodes = [];
7683      var depth = 0;
7684      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7685        do {
7686          if (!node) {
7687            throw $compileMinErr('uterdir',
7688                      "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7689                      attrStart, attrEnd);
7690          }
7691          if (node.nodeType == NODE_TYPE_ELEMENT) {
7692            if (node.hasAttribute(attrStart)) depth++;
7693            if (node.hasAttribute(attrEnd)) depth--;
7694          }
7695          nodes.push(node);
7696          node = node.nextSibling;
7697        } while (depth > 0);
7698      } else {
7699        nodes.push(node);
7700      }
7701
7702      return jqLite(nodes);
7703    }
7704
7705    /**
7706     * Wrapper for linking function which converts normal linking function into a grouped
7707     * linking function.
7708     * @param linkFn
7709     * @param attrStart
7710     * @param attrEnd
7711     * @returns {Function}
7712     */
7713    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7714      return function(scope, element, attrs, controllers, transcludeFn) {
7715        element = groupScan(element[0], attrStart, attrEnd);
7716        return linkFn(scope, element, attrs, controllers, transcludeFn);
7717      };
7718    }
7719
7720    /**
7721     * Once the directives have been collected, their compile functions are executed. This method
7722     * is responsible for inlining directive templates as well as terminating the application
7723     * of the directives if the terminal directive has been reached.
7724     *
7725     * @param {Array} directives Array of collected directives to execute their compile function.
7726     *        this needs to be pre-sorted by priority order.
7727     * @param {Node} compileNode The raw DOM node to apply the compile functions to
7728     * @param {Object} templateAttrs The shared attribute function
7729     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7730     *                                                  scope argument is auto-generated to the new
7731     *                                                  child of the transcluded parent scope.
7732     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
7733     *                              argument has the root jqLite array so that we can replace nodes
7734     *                              on it.
7735     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
7736     *                                           compiling the transclusion.
7737     * @param {Array.<Function>} preLinkFns
7738     * @param {Array.<Function>} postLinkFns
7739     * @param {Object} previousCompileContext Context used for previous compilation of the current
7740     *                                        node
7741     * @returns {Function} linkFn
7742     */
7743    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
7744                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
7745                                   previousCompileContext) {
7746      previousCompileContext = previousCompileContext || {};
7747
7748      var terminalPriority = -Number.MAX_VALUE,
7749          newScopeDirective,
7750          controllerDirectives = previousCompileContext.controllerDirectives,
7751          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
7752          templateDirective = previousCompileContext.templateDirective,
7753          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
7754          hasTranscludeDirective = false,
7755          hasTemplate = false,
7756          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
7757          $compileNode = templateAttrs.$$element = jqLite(compileNode),
7758          directive,
7759          directiveName,
7760          $template,
7761          replaceDirective = originalReplaceDirective,
7762          childTranscludeFn = transcludeFn,
7763          linkFn,
7764          directiveValue;
7765
7766      // executes all directives on the current element
7767      for (var i = 0, ii = directives.length; i < ii; i++) {
7768        directive = directives[i];
7769        var attrStart = directive.$$start;
7770        var attrEnd = directive.$$end;
7771
7772        // collect multiblock sections
7773        if (attrStart) {
7774          $compileNode = groupScan(compileNode, attrStart, attrEnd);
7775        }
7776        $template = undefined;
7777
7778        if (terminalPriority > directive.priority) {
7779          break; // prevent further processing of directives
7780        }
7781
7782        if (directiveValue = directive.scope) {
7783
7784          // skip the check for directives with async templates, we'll check the derived sync
7785          // directive when the template arrives
7786          if (!directive.templateUrl) {
7787            if (isObject(directiveValue)) {
7788              // This directive is trying to add an isolated scope.
7789              // Check that there is no scope of any kind already
7790              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
7791                                directive, $compileNode);
7792              newIsolateScopeDirective = directive;
7793            } else {
7794              // This directive is trying to add a child scope.
7795              // Check that there is no isolated scope already
7796              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
7797                                $compileNode);
7798            }
7799          }
7800
7801          newScopeDirective = newScopeDirective || directive;
7802        }
7803
7804        directiveName = directive.name;
7805
7806        if (!directive.templateUrl && directive.controller) {
7807          directiveValue = directive.controller;
7808          controllerDirectives = controllerDirectives || createMap();
7809          assertNoDuplicate("'" + directiveName + "' controller",
7810              controllerDirectives[directiveName], directive, $compileNode);
7811          controllerDirectives[directiveName] = directive;
7812        }
7813
7814        if (directiveValue = directive.transclude) {
7815          hasTranscludeDirective = true;
7816
7817          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
7818          // This option should only be used by directives that know how to safely handle element transclusion,
7819          // where the transcluded nodes are added or replaced after linking.
7820          if (!directive.$$tlb) {
7821            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
7822            nonTlbTranscludeDirective = directive;
7823          }
7824
7825          if (directiveValue == 'element') {
7826            hasElementTranscludeDirective = true;
7827            terminalPriority = directive.priority;
7828            $template = $compileNode;
7829            $compileNode = templateAttrs.$$element =
7830                jqLite(document.createComment(' ' + directiveName + ': ' +
7831                                              templateAttrs[directiveName] + ' '));
7832            compileNode = $compileNode[0];
7833            replaceWith(jqCollection, sliceArgs($template), compileNode);
7834
7835            childTranscludeFn = compile($template, transcludeFn, terminalPriority,
7836                                        replaceDirective && replaceDirective.name, {
7837                                          // Don't pass in:
7838                                          // - controllerDirectives - otherwise we'll create duplicates controllers
7839                                          // - newIsolateScopeDirective or templateDirective - comb
vendor: 4,670 bytes, lines 7839-7946
7839ining templates with
7840                                          //   element transclusion doesn't make sense.
7841                                          //
7842                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
7843                                          // on the same element more than once.
7844                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective
7845                                        });
7846          } else {
7847            $template = jqLite(jqLiteClone(compileNode)).contents();
7848            $compileNode.empty(); // clear contents
7849            childTranscludeFn = compile($template, transcludeFn);
7850          }
7851        }
7852
7853        if (directive.template) {
7854          hasTemplate = true;
7855          assertNoDuplicate('template', templateDirective, directive, $compileNode);
7856          templateDirective = directive;
7857
7858          directiveValue = (isFunction(directive.template))
7859              ? directive.template($compileNode, templateAttrs)
7860              : directive.template;
7861
7862          directiveValue = denormalizeTemplate(directiveValue);
7863
7864          if (directive.replace) {
7865            replaceDirective = directive;
7866            if (jqLiteIsTextNode(directiveValue)) {
7867              $template = [];
7868            } else {
7869              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
7870            }
7871            compileNode = $template[0];
7872
7873            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
7874              throw $compileMinErr('tplrt',
7875                  "Template for directive '{0}' must have exactly one root element. {1}",
7876                  directiveName, '');
7877            }
7878
7879            replaceWith(jqCollection, $compileNode, compileNode);
7880
7881            var newTemplateAttrs = {$attr: {}};
7882
7883            // combine directives from the original node and from the template:
7884            // - take the array of directives for this element
7885            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
7886            // - collect directives from the template and sort them by priority
7887            // - combine directives as: processed + template + unprocessed
7888            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
7889            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
7890
7891            if (newIsolateScopeDirective) {
7892              markDirectivesAsIsolate(templateDirectives);
7893            }
7894            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
7895            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
7896
7897            ii = directives.length;
7898          } else {
7899            $compileNode.html(directiveValue);
7900          }
7901        }
7902
7903        if (directive.templateUrl) {
7904          hasTemplate = true;
7905          assertNoDuplicate('template', templateDirective, directive, $compileNode);
7906          templateDirective = directive;
7907
7908          if (directive.replace) {
7909            replaceDirective = directive;
7910          }
7911
7912          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
7913              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
7914                controllerDirectives: controllerDirectives,
7915                newIsolateScopeDirective: newIsolateScopeDirective,
7916                templateDirective: templateDirective,
7917                nonTlbTranscludeDirective: nonTlbTranscludeDirective
7918              });
7919          ii = directives.length;
7920        } else if (directive.compile) {
7921          try {
7922            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
7923            if (isFunction(linkFn)) {
7924              addLinkFns(null, linkFn, attrStart, attrEnd);
7925            } else if (linkFn) {
7926              addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
7927            }
7928          } catch (e) {
7929            $exceptionHandler(e, startingTag($compileNode));
7930          }
7931        }
7932
7933        if (directive.terminal) {
7934          nodeLinkFn.terminal = true;
7935          terminalPriority = Math.max(terminalPriority, directive.priority);
7936        }
7937
7938      }
7939
7940      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
7941      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
7942      nodeLinkFn.templateOnThisElement = hasTemplate;
7943      nodeLinkFn.transclude = childTranscludeFn;
7944
7945      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
7946
7947      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
7948      return nodeLinkFn;
7949
7950      ////////////////////
7951
7952      function addLinkFns(pre, post, attrStart, attrEnd) {
7953        if (pre) {
7954          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
7955          pre.require = directive.require;
7956          pre.directiveName = directiveName;
7957          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7958            pre = cloneAndAnnotateFn(pre, {isolateScope: true});
7959          }
7960          preLinkFns.push(pre);
7961        }
7962        if (post) {
7963          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
7964          post.require = directive.require;
7965          post.directiveName = directiveName;
7966          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7967            post = cloneAndAnnotateFn(post, {isolateScope: true});
7968          }
7969          postLinkFns.push(post);
7970        }
7971      }
7972
7973
7974      function getControllers(directiveName, require, $element, elementControllers) {
7975        var value;
7976
7977        if (isString(require)) {
7978          var match = require.match(REQUIRE_PREFIX_REGEXP);
7979          var name = require.substring(match[0].length);
7980          var inheritType = match[1] || match[3];
7981          var optional = match[2] === '?';
7982
7983          //If only parents then start at the parent element
7984          if (inheritType === '^^') {
7985            $element = $element.parent();
7986          //Otherwise attempt getting the controller from elementControllers in case
7987          //the element is transcluded (and has no data) and to avoid .data if possible
7988          } else {
7989            value = elementControllers && elementControllers[name];
7990            value = value && value.instance;
7991          }
7992
7993          if (!value) {
7994            var dataName = '$' + name + 'Controller';
7995            value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
7996          }
7997
7998          if (!value && !optional) {
7999            throw $compileMinErr('ctreq',
8000                "Controller '{0}', required by directive '{1}', can't be found!",
8001                name, directiveName);
8002          }
8003        } else if (isArray(require)) {
8004          value = [];
8005          for (var i = 0, ii = require.length; i < ii; i++) {
8006            value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8007          }
8008        }
8009
8010        return value || null;
8011      }
8012
8013      function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8014        var elementControllers = createMap();
8015        for (var controllerKey in controllerDirectives) {
8016          var directive = controllerDirectives[controllerKey];
8017          var locals = {
8018            $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8019            $element: $element,
8020            $attrs: attrs,
8021            $transclude: transcludeFn
8022          };
8023
8024          var controller = directive.controller;
8025          if (controller == '@') {
8026            controller = attrs[directive.name];
8027          }
8028
8029          var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8030
8031          // For directives with element transclusion the element is a comment,
8032          // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
8033          // clean up (http://bugs.jquery.com/ticket/8335).
8034          // Instead, we save the controllers for the element in a local hash and attach to .data
8035          // later, once we have the actual element.
8036          elementControllers[directive.name] = controllerInstance;
8037          if (!hasElementTranscludeDirective) {
8038            $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8039          }
8040        }
8041        return elementControllers;
8042      }
8043
8044      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn,
8045                          thisLinkFn) {
8046        var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
8047            attrs;
8048
8049        if (compileNode === linkNode) {
8050          attrs = templateAttrs;
8051          $element = templateAttrs.$$element;
8052        } else {
8053          $element = jqLite(linkNode);
8054          attrs = new Attributes($element, templateAttrs);
8055        }
8056
8057        if (newIsolateScopeDirective) {
8058          isolateScope = scope.$new(true);
8059        }
8060
8061        if (boundTranscludeFn) {
8062          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8063          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8064          transcludeFn = controllersBoundTransclude;
8065          transcludeFn.$$boundTransclude = boundTranscludeFn;
8066        }
8067
8068        if (controllerDirectives) {
8069          elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8070        }
8071
8072        if (newIsolateScopeDirective) {
8073          // Initialize isolate scope bindings for new isolate scope directive.
8074          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8075              templateDirective === newIsolateScopeDirective.$$originalDirective)));
8076          compile.$$addScopeClass($element, true);
8077          isolateScope.$$isolateBindings =
8078              newIsolateScopeDirective.$$isolateBindings;
8079          initializeDirectiveBindings(scope, attrs, isolateScope,
8080                                      isolateScope.$$isolateBindings,
8081                                      newIsolateScopeDirective, isolateScope);
8082        }
8083        if (elementControllers) {
8084          // Initialize bindToController bindings for new/isolate scopes
8085          var scopeDirective = newIsolateScopeDirective || newScopeDirective;
8086          var bindings;
8087          var controllerForBindings;
8088          if (scopeDirective && elementControllers[scopeDirective.name]) {
8089            bindings = scopeDirective.$$bindings.bindToController;
8090            controller = elementControllers[scopeDirective.name];
8091
8092            if (controller && controller.identifier && bindings) {
8093              controllerForBindings = controller;
8094              thisLinkFn.$$destroyBindings =
8095                  initializeDirectiveBindings(scope, attrs, controller.instance,
8096                                              bindings, scopeDirective);
8097            }
8098          }
8099          for (i in elementControllers) {
8100            controller = elementControllers[i];
8101            var controllerResult = controller();
8102
8103            if (controllerResult !== controller.instance) {
8104              // If the controller constructor has a return value, overwrite the instance
8105              // from setupControllers and update the element data
8106              controller.instance = controllerResult;
8107              $element.data('$' + i + 'Controller', controllerResult);
8108              if (controller === controllerForBindings) {
8109                // Remove and re-install bindToController bindings
8110                thisLinkFn.$$destroyBindings();
8111                thisLinkFn.$$destroyBindings =
8112                  initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
8113              }
8114            }
8115          }
8116        }
8117
8118        // PRELINKING
8119        for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8120          linkFn = preLinkFns[i];
8121          invokeLinkFn(linkFn,
8122              linkFn.isolateScope ? isolateScope : scope,
8123              $element,
8124              attrs,
8125              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8126              transcludeFn
8127          );
8128        }
8129
8130        // RECURSION
8131        // We only pass the isolate scope, if the isolate directive has a template,
8132        // otherwise the child elements do not belong to the isolate directive.
8133        var scopeToChild = scope;
8134        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8135          scopeToChild = isolateScope;
8136        }
8137        childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8138
8139        // POSTLINKING
8140        for (i = postLinkFns.length - 1; i >= 0; i--) {
8141          linkFn = postLinkFns[i];
8142          invokeLinkFn(linkFn,
8143              linkFn.isolateScope ? isolateScope : scope,
8144              $element,
8145              attrs,
8146              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8147              transcludeFn
8148          );
8149        }
8150
8151        // This is the function that is injected as `$transclude`.
8152        // Note: all arguments are optional!
8153        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8154          var transcludeControllers;
8155
8156          // No scope passed in:
8157          if (!isScope(scope)) {
8158            futureParentElement = cloneAttachFn;
8159            cloneAttachFn = scope;
8160            scope = undefined;
8161          }
8162
8163          if (hasElementTranscludeDirective) {
8164            transcludeControllers = elementControllers;
8165          }
8166          if (!futureParentElement) {
8167            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8168          }
8169          return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8170        }
8171      }
8172    }
8173
8174    function markDirectivesAsIsolate(directives) {
8175      // mark all directives as needing isolate scope.
8176      for (var j = 0, jj = directives.length; j < jj; j++) {
8177        directives[j] = inherit(directives[j], {$$isolateScope: true});
8178      }
8179    }
8180
8181    /**
8182     * looks up the directive and decorates it with exception handling and proper parameters. We
8183     * call this the boundDirective.
8184     *
8185     * @param {string} name name of the directive to look up.
8186     * @param {string} location The directive must be found in specific format.
8187     *   String containing any of theses characters:
8188     *
8189     *   * `E`: element name
8190     *   * `A': attribute
8191     *   * `C`: class
8192     *   * `M`: comment
8193     * @returns {boolean} true if directive was added.
8194     */
8195    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8196                          endAttrName) {
8197      if (name === ignoreDirective) return null;
8198      var match = null;
8199      if (hasDirectives.hasOwnProperty(name)) {
8200        for (var directive, directives = $injector.get(name + Suffix),
8201            i = 0, ii = directives.length; i < ii; i++) {
8202          try {
8203            directive = directives[i];
8204            if ((maxPriority === undefined || maxPriority > directive.priority) &&
8205                 directive.restrict.indexOf(location) != -1) {
8206              if (startAttrName) {
8207                directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8208              }
8209              tDirectives.push(directive);
8210              match = directive;
8211            }
8212          } catch (e) { $exceptionHandler(e); }
8213        }
8214      }
8215      return match;
8216    }
8217
8218
8219    /**
8220     * looks up the directive and returns true if it is a multi-element directive,
8221     * and therefore requires DOM nodes between -start and -end markers to be grouped
8222     * together.
8223     *
8224     * @param {string} name name of the directive to look up.
8225     * @returns true if directive was registered as multi-element.
8226     */
8227    function directiveIsMultiElement(name) {
8228      if (hasDirectives.hasOwnProperty(name)) {
8229        for (var directive, directives = $injector.get(name + Suffix),
8230            i = 0, ii = directives.length; i < ii; i++) {
8231          directive = directives[i];
8232          if (directive.multiElement) {
8233            return true;
8234          }
8235        }
8236      }
8237      return false;
8238    }
8239
8240    /**
8241     * When the element is replaced with HTML template then the new attributes
8242     * on the template need to be merged with the existing attributes in the DOM.
8243     * The desired effect is to have both of the attributes present.
8244     *
8245     * @param {object} dst destination attributes (original DOM)
8246     * @param {object} src source attributes (from the directive template)
8247     */
8248    function mergeTemplateAttributes(dst, src) {
8249      var srcAttr = src.$attr,
8250          dstAttr = dst.$attr,
8251          $element = dst.$$element;
8252
8253      // reapply the old attributes to the new element
8254      forEach(dst, function(value, key) {
8255        if (key.charAt(0) != '$') {
8256          if (src[key] && src[key] !== value) {
8257            value += (key === 'style' ? ';' : ' ') + src[key];
8258          }
8259          dst.$set(key, value, true, srcAttr[key]);
8260        }
8261      });
8262
8263      // copy the new attributes on the old attrs object
8264      forEach(src, function(value, key) {
8265        if (key == 'class') {
8266          safeAddClass($element, value);
8267          dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8268        } else if (key == 'style') {
8269          $element.attr('style', $element.attr('style') + ';' + value);
8270          dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8271          // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8272          // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8273          // have an attribute like "has-own-property" or "data-has-own-property", etc.
8274        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8275          dst[key] = value;
8276          dstAttr[key] = srcAttr[key];
8277        }
8278      });
8279    }
8280
8281
8282    function compileTemplateUrl(directives, $compileNode, tAttrs,
8283        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8284      var linkQueue = [],
8285          afterTemplateNodeLinkFn,
8286          afterTemplateChildLinkFn,
8287          beforeTemplateCompileNode = $compileNode[0],
8288          origAsyncDirective = directives.shift(),
8289          derivedSyncDirective = inherit(origAsyncDirective, {
8290            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8291          }),
8292          templateUrl = (isFunction(origAsyncDirective.templateUrl))
8293              ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8294              : origAsyncDirective.templateUrl,
8295          templateNamespace = origAsyncDirective.templateNamespace;
8296
8297      $compileNode.empty();
8298
8299      $templateRequest($sce.getTrustedResourceUrl(templateUrl))
8300        .then(function(content) {
8301          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8302
8303          content = denormalizeTemplate(content);
8304
8305          if (origAsyncDirective.replace) {
8306            if (jqLiteIsTextNode(content)) {
8307              $template = [];
8308            } else {
8309              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8310            }
8311            compileNode = $template[0];
8312
8313            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8314              throw $compileMinErr('tplrt',
8315                  "Template for directive '{0}' must have exactly one root element. {1}",
8316                  origAsyncDirective.name, templateUrl);
8317            }
8318
8319            tempTemplateAttrs = {$attr: {}};
8320            replaceWith($rootElement, $compileNode, compileNode);
8321            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8322
8323            if (isObject(origAsyncDirective.scope)) {
8324              markDirectivesAsIsolate(templateDirectives);
8325            }
8326            directives = templateDirectives.concat(directives);
8327            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8328          } else {
8329            compileNode = beforeTemplateCompileNode;
8330            $compileNode.html(content);
8331          }
8332
8333          directives.unshift(derivedSyncDirective);
8334
8335          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8336              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8337              previousCompileContext);
vendor: 11,852 bytes, lines 8338-8615
8338          forEach($rootElement, function(node, i) {
8339            if (node == compileNode) {
8340              $rootElement[i] = $compileNode[0];
8341            }
8342          });
8343          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8344
8345          while (linkQueue.length) {
8346            var scope = linkQueue.shift(),
8347                beforeTemplateLinkNode = linkQueue.shift(),
8348                linkRootElement = linkQueue.shift(),
8349                boundTranscludeFn = linkQueue.shift(),
8350                linkNode = $compileNode[0];
8351
8352            if (scope.$$destroyed) continue;
8353
8354            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8355              var oldClasses = beforeTemplateLinkNode.className;
8356
8357              if (!(previousCompileContext.hasElementTranscludeDirective &&
8358                  origAsyncDirective.replace)) {
8359                // it was cloned therefore we have to clone as well.
8360                linkNode = jqLiteClone(compileNode);
8361              }
8362              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8363
8364              // Copy in CSS classes from original node
8365              safeAddClass(jqLite(linkNode), oldClasses);
8366            }
8367            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8368              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8369            } else {
8370              childBoundTranscludeFn = boundTranscludeFn;
8371            }
8372            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8373              childBoundTranscludeFn, afterTemplateNodeLinkFn);
8374          }
8375          linkQueue = null;
8376        });
8377
8378      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8379        var childBoundTranscludeFn = boundTranscludeFn;
8380        if (scope.$$destroyed) return;
8381        if (linkQueue) {
8382          linkQueue.push(scope,
8383                         node,
8384                         rootElement,
8385                         childBoundTranscludeFn);
8386        } else {
8387          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8388            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8389          }
8390          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn,
8391                                  afterTemplateNodeLinkFn);
8392        }
8393      };
8394    }
8395
8396
8397    /**
8398     * Sorting function for bound directives.
8399     */
8400    function byPriority(a, b) {
8401      var diff = b.priority - a.priority;
8402      if (diff !== 0) return diff;
8403      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8404      return a.index - b.index;
8405    }
8406
8407    function assertNoDuplicate(what, previousDirective, directive, element) {
8408
8409      function wrapModuleNameIfDefined(moduleName) {
8410        return moduleName ?
8411          (' (module: ' + moduleName + ')') :
8412          '';
8413      }
8414
8415      if (previousDirective) {
8416        throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8417            previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8418            directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8419      }
8420    }
8421
8422
8423    function addTextInterpolateDirective(directives, text) {
8424      var interpolateFn = $interpolate(text, true);
8425      if (interpolateFn) {
8426        directives.push({
8427          priority: 0,
8428          compile: function textInterpolateCompileFn(templateNode) {
8429            var templateNodeParent = templateNode.parent(),
8430                hasCompileParent = !!templateNodeParent.length;
8431
8432            // When transcluding a template that has bindings in the root
8433            // we don't have a parent and thus need to add the class during linking fn.
8434            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8435
8436            return function textInterpolateLinkFn(scope, node) {
8437              var parent = node.parent();
8438              if (!hasCompileParent) compile.$$addBindingClass(parent);
8439              compile.$$addBindingInfo(parent, interpolateFn.expressions);
8440              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8441                node[0].nodeValue = value;
8442              });
8443            };
8444          }
8445        });
8446      }
8447    }
8448
8449
8450    function wrapTemplate(type, template) {
8451      type = lowercase(type || 'html');
8452      switch (type) {
8453      case 'svg':
8454      case 'math':
8455        var wrapper = document.createElement('div');
8456        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8457        return wrapper.childNodes[0].childNodes;
8458      default:
8459        return template;
8460      }
8461    }
8462
8463
8464    function getTrustedContext(node, attrNormalizedName) {
8465      if (attrNormalizedName == "srcdoc") {
8466        return $sce.HTML;
8467      }
8468      var tag = nodeName_(node);
8469      // maction[xlink:href] can source SVG.  It's not limited to <maction>.
8470      if (attrNormalizedName == "xlinkHref" ||
8471          (tag == "form" && attrNormalizedName == "action") ||
8472          (tag != "img" && (attrNormalizedName == "src" ||
8473                            attrNormalizedName == "ngSrc"))) {
8474        return $sce.RESOURCE_URL;
8475      }
8476    }
8477
8478
8479    function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8480      var trustedContext = getTrustedContext(node, name);
8481      allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8482
8483      var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8484
8485      // no interpolation found -> ignore
8486      if (!interpolateFn) return;
8487
8488
8489      if (name === "multiple" && nodeName_(node) === "select") {
8490        throw $compileMinErr("selmulti",
8491            "Binding to the 'multiple' attribute is not supported. Element: {0}",
8492            startingTag(node));
8493      }
8494
8495      directives.push({
8496        priority: 100,
8497        compile: function() {
8498            return {
8499              pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8500                var $$observers = (attr.$$observers || (attr.$$observers = {}));
8501
8502                if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8503                  throw $compileMinErr('nodomevents',
8504                      "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
8505                          "ng- versions (such as ng-click instead of onclick) instead.");
8506                }
8507
8508                // If the attribute has changed since last $interpolate()ed
8509                var newValue = attr[name];
8510                if (newValue !== value) {
8511                  // we need to interpolate again since the attribute value has been updated
8512                  // (e.g. by another directive's compile function)
8513                  // ensure unset/empty values make interpolateFn falsy
8514                  interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8515                  value = newValue;
8516                }
8517
8518                // if attribute was updated so that there is no interpolation going on we don't want to
8519                // register any observers
8520                if (!interpolateFn) return;
8521
8522                // initialize attr object so that it's ready in case we need the value for isolate
8523                // scope initialization, otherwise the value would not be available from isolate
8524                // directive's linking fn during linking phase
8525                attr[name] = interpolateFn(scope);
8526
8527                ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8528                (attr.$$observers && attr.$$observers[name].$$scope || scope).
8529                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8530                    //special case for class attribute addition + removal
8531                    //so that class changes can tap into the animation
8532                    //hooks provided by the $animate service. Be sure to
8533                    //skip animations when the first digest occurs (when
8534                    //both the new and the old values are the same) since
8535                    //the CSS classes are the non-interpolated values
8536                    if (name === 'class' && newValue != oldValue) {
8537                      attr.$updateClass(newValue, oldValue);
8538                    } else {
8539                      attr.$set(name, newValue);
8540                    }
8541                  });
8542              }
8543            };
8544          }
8545      });
8546    }
8547
8548
8549    /**
8550     * This is a special jqLite.replaceWith, which can replace items which
8551     * have no parents, provided that the containing jqLite collection is provided.
8552     *
8553     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8554     *                               in the root of the tree.
8555     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8556     *                                  the shell, but replace its DOM node reference.
8557     * @param {Node} newNode The new DOM node.
8558     */
8559    function replaceWith($rootElement, elementsToRemove, newNode) {
8560      var firstElementToRemove = elementsToRemove[0],
8561          removeCount = elementsToRemove.length,
8562          parent = firstElementToRemove.parentNode,
8563          i, ii;
8564
8565      if ($rootElement) {
8566        for (i = 0, ii = $rootElement.length; i < ii; i++) {
8567          if ($rootElement[i] == firstElementToRemove) {
8568            $rootElement[i++] = newNode;
8569            for (var j = i, j2 = j + removeCount - 1,
8570                     jj = $rootElement.length;
8571                 j < jj; j++, j2++) {
8572              if (j2 < jj) {
8573                $rootElement[j] = $rootElement[j2];
8574              } else {
8575                delete $rootElement[j];
8576              }
8577            }
8578            $rootElement.length -= removeCount - 1;
8579
8580            // If the replaced element is also the jQuery .context then replace it
8581            // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8582            // http://api.jquery.com/context/
8583            if ($rootElement.context === firstElementToRemove) {
8584              $rootElement.context = newNode;
8585            }
8586            break;
8587          }
8588        }
8589      }
8590
8591      if (parent) {
8592        parent.replaceChild(newNode, firstElementToRemove);
8593      }
8594
8595      // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8596      var fragment = document.createDocumentFragment();
8597      fragment.appendChild(firstElementToRemove);
8598
8599      if (jqLite.hasData(firstElementToRemove)) {
8600        // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8601        // data here because there's no public interface in jQuery to do that and copying over
8602        // event listeners (which is the main use of private data) wouldn't work anyway.
8603        jqLite(newNode).data(jqLite(firstElementToRemove).data());
8604
8605        // Remove data of the replaced element. We cannot just call .remove()
8606        // on the element it since that would deallocate scope that is needed
8607        // for the new node. Instead, remove the data "manually".
8608        if (!jQuery) {
8609          delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8610        } else {
8611          // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8612          // the replaced element. The cleanData version monkey-patched by Angular would cause
8613          // the scope to be trashed and we do need the very same scope to work with the new
8614          // element. However, we cannot just cache the non-patched version and use it here as
8615          // that would break if another library patches the method after Angular does (one
8616          // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8617          // skipped this one time.
8618          skipDestroyOnNextJQueryCleanData = true;
8619          jQuery.cleanData([firstElementToRemove]);
8620        }
8621      }
8622
8623      for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8624        var element = elementsToRemove[k];
8625        jqLite(element).remove(); // must do this way to clean up expando
8626        fragment.appendChild(element);
8627        delete elementsToRemove[k];
8628      }
8629
8630      elementsToRemove[0] = newNode;
8631      elementsToRemove.length = 1;
8632    }
8633
8634
8635    function cloneAndAnnotateFn(fn, annotation) {
8636      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8637    }
8638
8639
8640    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8641      try {
8642        linkFn(scope, $element, attrs, controllers, transcludeFn);
8643      } catch (e) {
8644        $exceptionHandler(e, startingTag($element));
8645      }
8646    }
8647
8648
8649    // Set up $watches for isolate scope and controller bindings. This process
8650    // only occurs for isolate scopes and new scopes with controllerAs.
8651    function initializeDirectiveBindings(scope, attrs, destination, bindings,
8652                                         directive, newScope) {
8653      var onNewScopeDestroyed;
8654      forEach(bindings, function(definition, scopeName) {
8655        var attrName = definition.attrName,
8656        optional = definition.optional,
8657        mode = definition.mode, // @, =, or &
8658        lastValue,
8659        parentGet, parentSet, compare;
8660
8661        if (!hasOwnProperty.call(attrs, attrName)) {
8662          // In the case of user defined a binding with the same name as a method in Object.prototype but didn't set
8663          // the corresponding attribute. We need to make sure subsequent code won't access to the prototype function
8664          attrs[attrName] = undefined;
8665        }
8666
8667        switch (mode) {
8668
8669          case '@':
8670            if (!attrs[attrName] && !optional) {
8671              destination[scopeName] = undefined;
8672            }
8673
8674            attrs.$observe(attrName, function(value) {
8675              destination[scopeName] = value;
8676            });
8677            attrs.$$observers[attrName].$$scope = scope;
8678            if (attrs[attrName]) {
8679              // If the attribute has been provided then we trigger an interpolation to ensure
8680              // the value is there for use in the link fn
8681              destination[scopeName] = $interpolate(attrs[attrName])(scope);
8682            }
8683            break;
8684
8685          case '=':
8686            if (optional && !attrs[attrName]) {
8687              return;
8688            }
8689            parentGet = $parse(attrs[attrName]);
8690
8691            if (parentGet.literal) {
8692              compare = equals;
8693            } else {
8694              compare = function(a, b) { return a === b || (a !== a && b !== b); };
8695            }
8696            parentSet = parentGet.assign || function() {
8697              // reset the change, or we will throw this exception on every $digest
8698              lastValue = destination[scopeName] = parentGet(scope);
8699              throw $compileMinErr('nonassign',
8700                  "Expression '{0}' used with directive '{1}' is non-assignable!",
8701                  attrs[attrName], directive.name);
8702            };
8703            lastValue = destination[scopeName] = parentGet(scope);
8704            var parentValueWatch = function parentValueWatch(parentValue) {
8705              if (!compare(parentValue, destination[scopeName])) {
8706                // we are out of sync and need to copy
8707                if (!compare(parentValue, lastValue)) {
8708                  // parent changed and it has precedence
8709                  destination[scopeName] = parentValue;
8710                } else {
8711                  // if the parent can be assigned then do so
8712                  parentSet(scope, parentValue = destination[scopeName]);
8713                }
8714              }
8715              return lastValue = parentValue;
8716            };
8717            parentValueWatch.$stateful = true;
8718            var unwatch;
8719            if (definition.collection) {
8720              unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
8721            } else {
8722              unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
8723            }
8724            onNewScopeDestroyed = (onNewScopeDestroyed || []);
8725            onNewScopeDestroyed.push(unwatch);
8726            break;
8727
8728          case '&':
8729            parentGet = $parse(attrs[attrName]);
8730
8731            // Don't assign noop to destination if expression is not val
8731id
8732            if (parentGet === noop && optional) break;
8733
8734            destination[scopeName] = function(locals) {
8735              return parentGet(scope, locals);
8736            };
8737            break;
8738        }
8739      });
8740      var destroyBindings = onNewScopeDestroyed ? function destroyBindings() {
8741        for (var i = 0, ii = onNewScopeDestroyed.length; i < ii; ++i) {
8742          onNewScopeDestroyed[i]();
8743        }
8744      } : noop;
8745      if (newScope && destroyBindings !== noop) {
8746        newScope.$on('$destroy', destroyBindings);
8747        return noop;
8748      }
8749      return destroyBindings;
8750    }
8751  }];
8752}
8753
8754var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
8755/**
8756 * Converts all accepted directives format into proper directive name.
8757 * @param name Name to normalize
8758 */
8759function directiveNormalize(name) {
8760  return camelCase(name.replace(PREFIX_REGEXP, ''));
8761}
8762
8763/**
8764 * @ngdoc type
8765 * @name $compile.directive.Attributes
8766 *
8767 * @description
8768 * A shared object between directive compile / linking functions which contains normalized DOM
8769 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
8770 * needed since all of these are treated as equivalent in Angular:
8771 *
8772 * ```
8773 *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
8774 * ```
8775 */
8776
8777/**
8778 * @ngdoc property
8779 * @name $compile.directive.Attributes#$attr
8780 *
8781 * @description
8782 * A map of DOM element attribute names to the normalized name. This is
8783 * needed to do reverse lookup from normalized name back to actual name.
8784 */
8785
8786
8787/**
8788 * @ngdoc method
8789 * @name $compile.directive.Attributes#$set
8790 * @kind function
8791 *
8792 * @description
8793 * Set DOM element attribute value.
8794 *
8795 *
8796 * @param {string} name Normalized element attribute name of the property to modify. The name is
8797 *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
8798 *          property to the original name.
8799 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
8800 */
8801
8802
8803
8804/**
8805 * Closure compiler type information
8806 */
8807
8808function nodesetLinkingFn(
8809  /* angular.Scope */ scope,
8810  /* NodeList */ nodeList,
8811  /* Element */ rootElement,
8812  /* function(Function) */ boundTranscludeFn
8813) {}
8814
8815function directiveLinkingFn(
8816  /* nodesetLinkingFn */ nodesetLinkingFn,
8817  /* angular.Scope */ scope,
8818  /* Node */ node,
8819  /* Element */ rootElement,
8820  /* function(Function) */ boundTranscludeFn
8821) {}
8822
8823function tokenDifference(str1, str2) {
8824  var values = '',
8825      tokens1 = str1.split(/\s+/),
8826      tokens2 = str2.split(/\s+/);
8827
8828  outer:
8829  for (var i = 0; i < tokens1.length; i++) {
8830    var token = tokens1[i];
8831    for (var j = 0; j < tokens2.length; j++) {
8832      if (token == tokens2[j]) continue outer;
8833    }
8834    values += (values.length > 0 ? ' ' : '') + token;
8835  }
8836  return values;
8837}
8838
8839function removeComments(jqNodes) {
8840  jqNodes = jqLite(jqNodes);
8841  var i = jqNodes.length;
8842
8843  if (i <= 1) {
8844    return jqNodes;
8845  }
8846
8847  while (i--) {
8848    var node = jqNodes[i];
8849    if (node.nodeType === NODE_TYPE_COMMENT) {
8850      splice.call(jqNodes, i, 1);
8851    }
8852  }
8853  return jqNodes;
8854}
8855
8856var $controllerMinErr = minErr('$controller');
8857
8858
8859var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
8860function identifierForController(controller, ident) {
8861  if (ident && isString(ident)) return ident;
8862  if (isString(controller)) {
8863    var match = CNTRL_REG.exec(controller);
8864    if (match) return match[3];
8865  }
8866}
8867
8868
8869/**
8870 * @ngdoc provider
8871 * @name $controllerProvider
8872 * @description
8873 * The {@link ng.$controller $controller service} is used by Angular to create new
8874 * controllers.
8875 *
8876 * This provider allows controller registration via the
8877 * {@link ng.$controllerProvider#register register} method.
8878 */
8879function $ControllerProvider() {
8880  var controllers = {},
8881      globals = false;
8882
8883  /**
8884   * @ngdoc method
8885   * @name $controllerProvider#register
8886   * @param {string|Object} name Controller name, or an object map of controllers where the keys are
8887   *    the names and the values are the constructors.
8888   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
8889   *    annotations in the array notation).
8890   */
8891  this.register = function(name, constructor) {
8892    assertNotHasOwnProperty(name, 'controller');
8893    if (isObject(name)) {
8894      extend(controllers, name);
8895    } else {
8896      controllers[name] = constructor;
8897    }
8898  };
8899
8900  /**
8901   * @ngdoc method
8902   * @name $controllerProvider#allowGlobals
8903   * @description If called, allows `$controller` to find controller constructors on `window`
8904   */
8905  this.allowGlobals = function() {
8906    globals = true;
8907  };
8908
8909
8910  this.$get = ['$injector', '$window', function($injector, $window) {
8911
8912    /**
8913     * @ngdoc service
8914     * @name $controller
8915     * @requires $injector
8916     *
8917     * @param {Function|string} constructor If called with a function then it's considered to be the
8918     *    controller constructor function. Otherwise it's considered to be a string which is used
8919     *    to retrieve the controller constructor using the following steps:
8920     *
8921     *    * check if a controller with given name is registered via `$controllerProvider`
8922     *    * check if evaluating the string on the current scope returns a constructor
8923     *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
8924     *      `window` object (not recommended)
8925     *
8926     *    The string can use the `controller as property` syntax, where the controller instance is published
8927     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
8928     *    to work correctly.
8929     *
8930     * @param {Object} locals Injection locals for Controller.
8931     * @return {Object} Instance of given controller.
8932     *
vendor: 4,024 bytes, lines 8933-9037
8933     * @description
8934     * `$controller` service is responsible for instantiating controllers.
8935     *
8936     * It's just a simple call to {@link auto.$injector $injector}, but extracted into
8937     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
8938     */
8939    return function(expression, locals, later, ident) {
8940      // PRIVATE API:
8941      //   param `later` --- indicates that the controller's constructor is invoked at a later time.
8942      //                     If true, $controller will allocate the object with the correct
8943      //                     prototype chain, but will not invoke the controller until a returned
8944      //                     callback is invoked.
8945      //   param `ident` --- An optional label which overrides the label parsed from the controller
8946      //                     expression, if any.
8947      var instance, match, constructor, identifier;
8948      later = later === true;
8949      if (ident && isString(ident)) {
8950        identifier = ident;
8951      }
8952
8953      if (isString(expression)) {
8954        match = expression.match(CNTRL_REG);
8955        if (!match) {
8956          throw $controllerMinErr('ctrlfmt',
8957            "Badly formed controller string '{0}'. " +
8958            "Must match `__name__ as __id__` or `__name__`.", expression);
8959        }
8960        constructor = match[1],
8961        identifier = identifier || match[3];
8962        expression = controllers.hasOwnProperty(constructor)
8963            ? controllers[constructor]
8964            : getter(locals.$scope, constructor, true) ||
8965                (globals ? getter($window, constructor, true) : undefined);
8966
8967        assertArgFn(expression, constructor, true);
8968      }
8969
8970      if (later) {
8971        // Instantiate controller later:
8972        // This machinery is used to create an instance of the object before calling the
8973        // controller's constructor itself.
8974        //
8975        // This allows properties to be added to the controller before the constructor is
8976        // invoked. Primarily, this is used for isolate scope bindings in $compile.
8977        //
8978        // This feature is not intended for use by applications, and is thus not documented
8979        // publicly.
8980        // Object creation: http://jsperf.com/create-constructor/2
8981        var controllerPrototype = (isArray(expression) ?
8982          expression[expression.length - 1] : expression).prototype;
8983        instance = Object.create(controllerPrototype || null);
8984
8985        if (identifier) {
8986          addIdentifier(locals, identifier, instance, constructor || expression.name);
8987        }
8988
8989        var instantiate;
8990        return instantiate = extend(function() {
8991          var result = $injector.invoke(expression, instance, locals, constructor);
8992          if (result !== instance && (isObject(result) || isFunction(result))) {
8993            instance = result;
8994            if (identifier) {
8995              // If result changed, re-assign controllerAs value to scope.
8996              addIdentifier(locals, identifier, instance, constructor || expression.name);
8997            }
8998          }
8999          return instance;
9000        }, {
9001          instance: instance,
9002          identifier: identifier
9003        });
9004      }
9005
9006      instance = $injector.instantiate(expression, locals, constructor);
9007
9008      if (identifier) {
9009        addIdentifier(locals, identifier, instance, constructor || expression.name);
9010      }
9011
9012      return instance;
9013    };
9014
9015    function addIdentifier(locals, identifier, instance, name) {
9016      if (!(locals && isObject(locals.$scope))) {
9017        throw minErr('$controller')('noscp',
9018          "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9019          name, identifier);
9020      }
9021
9022      locals.$scope[identifier] = instance;
9023    }
9024  }];
9025}
9026
9027/**
9028 * @ngdoc service
9029 * @name $document
9030 * @requires $window
9031 *
9032 * @description
9033 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9034 *
9035 * @example
9036   <example module="documentExample">
9037     <file name="index.html">
9038       <div ng-controller="ExampleController">
9039         <p>$document title: <b ng-bind="title"></b></p>
9040         <p>window.document title: <b ng-bind="windowTitle"></b></p>
9041       </div>
9042     </file>
9043     <file name="script.js">
9044       angular.module('documentExample', [])
9045         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9046           $scope.title = $document[0].title;
9047           $scope.windowTitle = angular.element(window.document)[0].title;
9048         }]);
9049     </file>
9050   </example>
9051 */
9052function $DocumentProvider() {
9053  this.$get = ['$window', function(window) {
9054    return jqLite(window.document);
9055  }];
9056}
9057
9058/**
9059 * @ngdoc service
9060 * @name $exceptionHandler
9061 * @requires ng.$log
9062 *
9063 * @description
9064 * Any uncaught exception in angular expressions is delegated to this service.
9065 * The default implementation simply delegates to `$log.error` which logs it into
9066 * the browser console.
9067 *
9068 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9069 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9070 *
9071 * ## Example:
9072 *
9073 * ```js
9074 *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9075 *     return function(exception, cause) {
9076 *       exception.message += ' (caused by "' + cause + '")';
9077 *       throw exception;
9078 *     };
9079 *   });
9080 * ```
9081 *
9082 * This example will override the normal action of `$exceptionHandler`, to make angular
9083 * exceptions fail hard when they happen, instead of just logging to the console.
9084 *
9085 * <hr />
9086 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9087 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9088 * (unless executed during a digest).
9089 *
9090 * If you wish, you can manually delegate exceptions, e.g.
9091 * `try { ... } catch(e) { $exceptionHandler(e); }`
9092 *
9093 * @param {Error} exception Exception associated with the error.
9094 * @param {string=} cause optional information about the context in which
9095 *       the error was thrown.
9096 *
9097 */
9098function $ExceptionHandlerProvider() {
9099  this.$get = ['$log', function($log) {
9100    return function(exception, cause) {
9101      $log.error.apply($log, arguments);
9102    };
9103  }];
9104}
9105
9106var APPLICATION_JSON = 'application/json';
9107var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9108var JSON_START = /^\[|^\{(?!\{)/;
9109var JSON_ENDS = {
9110  '[': /]$/,
9111  '{': /}$/
9112};
9113var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9114
9115function serializeValue(v) {
9116  if (isObject(v)) {
9117    return isDate(v) ? v.toISOString() : toJson(v);
9118  }
9119  return v;
9120}
9121
9122
9123function $HttpParamSerializerProvider() {
9124  /**
9125   * @ngdoc service
9126   * @name $httpParamSerializer
9127   * @description
9128   *
9129   * Default {@link $http `$http`} params serializer that converts objects to strings
9130   * according to the following rules:
9131   *
9132   * * `{'foo': 'bar'}` results in `foo=bar`
9133   * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9134   * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9135   * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9136   *
9137   * Note that serializer will sort the request parameters alphabetically.
9138   * */
9139
9140  this.$get = function() {
9141    return function ngParamSerializer(params) {
9142      if (!params) return '';
9143      var parts = [];
9144      forEachSorted(params, function(value, key) {
9145        if (value === null || isUndefined(value)) return;
9146        if (isArray(value)) {
9147          forEach(value, function(v, k) {
9148            parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
9149          });
9150        } else {
9151          parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9152        }
9153      });
9154
9155      return parts.join('&');
9156    };
9157  };
9158}
9159
9160function $HttpParamSerializerJQLikeProvider() {
9161  /**
9162   * @ngdoc service
9163   * @name $httpParamSerializerJQLike
9164   * @description
9165   *
9166   * Alternative {@link $http `$http`} params serializer that follows
9167   * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9168   * The serializer will also sort the params alphabetically.
9169   *
9170   * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9171   *
9172   * ```js
9173   * $http({
9174   *   url: myUrl,
9175   *   method: 'GET',
9176   *   params: myParams,
9177   *   paramSerializer: '$httpParamSerializerJQLike'
9178   * });
9179   * ```
9180   *
9181   * It is also possible to set it as the default `paramSerializer` in the
9182   * {@link $httpProvider#defaults `$httpProvider`}.
9183   *
9184   * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9185   * form data for submission:
9186   *
9187   * ```js
9188   * .controller(function($http, $httpParamSerializerJQLike) {
9189   *   //...
9190   *
9191   *   $http({
9192   *     url: myUrl,
9193   *     method: 'POST',
9194   *     data: $httpParamSerializerJQLike(myData),
9195   *     headers: {
9196   *       'Content-Type': 'application/x-www-form-urlencoded'
9197   *     }
9198   *   });
9199   *
9200   * });
9201   * ```
9202   *
9203   * */
9204  this.$get = function() {
9205    return function jQueryLikeParamSerializer(params) {
9206      if (!params) return '';
9207      var parts = [];
9208      serialize(params, '', true);
9209      return parts.join('&');
9210
9211      function serialize(toSerialize, prefix, topLevel) {
9212        if (toSerialize === null || isUndefined(toSerialize)) return;
9213        if (isArray(toSerialize)) {
9214          forEach(toSerialize, function(value) {
9215            serialize(value, prefix + '[]');
9216          });
9217        } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9218          forEachSorted(toSerialize, function(value, key) {
9219            serialize(value, prefix +
9220                (topLevel ? '' : '[') +
9221                key +
9222                (topLevel ? '' : ']'));
9223          });
9224        } else {
9225          parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9226        }
9227      }
9228    };
9229  };
9230}
9231
9232function defaultHttpResponseTransform(data, headers) {
9233  if (isString(data)) {
9234    // Strip json vulnerability protection prefix and trim whitespace
9235    var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9236
9237    if (tempData) {
9238      var contentType = headers('Content-Type');
9239      if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9240        data = fromJson(tempData);
9241      }
9242    }
9243  }
9244
9245  return data;
9246}
9247
9248function isJsonLike(str) {
9249    var jsonStart = str.match(JSON_START);
9250    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9251}
9252
9253/**
9254 * Parse headers into key value object
9255 *
9256 * @param {string} headers Raw headers as a string
9257 * @returns {Object} Parsed headers as key value object
9258 */
9259function parseHeaders(headers) {
9260  var parsed = createMap(), i;
9261
9262  function fillInParsed(key, val) {
9263    if (key) {
9264      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9265    }
9266  }
9267
9268  if (isString(headers)) {
9269    forEach(headers.split('\n'), function(line) {
9270      i = line.indexOf(':');
vendor: 4,256 bytes, lines 9271-9408
9271      fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9272    });
9273  } else if (isObject(headers)) {
9274    forEach(headers, function(headerVal, headerKey) {
9275      fillInParsed(lowercase(headerKey), trim(headerVal));
9276    });
9277  }
9278
9279  return parsed;
9280}
9281
9282
9283/**
9284 * Returns a function that provides access to parsed headers.
9285 *
9286 * Headers are lazy parsed when first requested.
9287 * @see parseHeaders
9288 *
9289 * @param {(string|Object)} headers Headers to provide access to.
9290 * @returns {function(string=)} Returns a getter function which if called with:
9291 *
9292 *   - if called with single an argument returns a single header value or null
9293 *   - if called with no arguments returns an object containing all headers.
9294 */
9295function headersGetter(headers) {
9296  var headersObj;
9297
9298  return function(name) {
9299    if (!headersObj) headersObj =  parseHeaders(headers);
9300
9301    if (name) {
9302      var value = headersObj[lowercase(name)];
9303      if (value === void 0) {
9304        value = null;
9305      }
9306      return value;
9307    }
9308
9309    return headersObj;
9310  };
9311}
9312
9313
9314/**
9315 * Chain all given functions
9316 *
9317 * This function is used for both request and response transforming
9318 *
9319 * @param {*} data Data to transform.
9320 * @param {function(string=)} headers HTTP headers getter fn.
9321 * @param {number} status HTTP status code of the response.
9322 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9323 * @returns {*} Transformed data.
9324 */
9325function transformData(data, headers, status, fns) {
9326  if (isFunction(fns)) {
9327    return fns(data, headers, status);
9328  }
9329
9330  forEach(fns, function(fn) {
9331    data = fn(data, headers, status);
9332  });
9333
9334  return data;
9335}
9336
9337
9338function isSuccess(status) {
9339  return 200 <= status && status < 300;
9340}
9341
9342
9343/**
9344 * @ngdoc provider
9345 * @name $httpProvider
9346 * @description
9347 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9348 * */
9349function $HttpProvider() {
9350  /**
9351   * @ngdoc property
9352   * @name $httpProvider#defaults
9353   * @description
9354   *
9355   * Object containing default values for all {@link ng.$http $http} requests.
9356   *
9357   * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
9358   * that will provide the cache for all requests who set their `cache` property to `true`.
9359   * If you set the `default.cache = false` then only requests that specify their own custom
9360   * cache object will be cached. See {@link $http#caching $http Caching} for more information.
9361   *
9362   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9363   * Defaults value is `'XSRF-TOKEN'`.
9364   *
9365   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9366   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9367   *
9368   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9369   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9370   * setting default headers.
9371   *     - **`defaults.headers.common`**
9372   *     - **`defaults.headers.post`**
9373   *     - **`defaults.headers.put`**
9374   *     - **`defaults.headers.patch`**
9375   *
9376   *
9377   * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9378   *  used to the prepare string representation of request parameters (specified as an object).
9379   *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9380   *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9381   *
9382   **/
9383  var defaults = this.defaults = {
9384    // transform incoming response data
9385    transformResponse: [defaultHttpResponseTransform],
9386
9387    // transform outgoing request data
9388    transformRequest: [function(d) {
9389      return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9390    }],
9391
9392    // default headers
9393    headers: {
9394      common: {
9395        'Accept': 'application/json, text/plain, */*'
9396      },
9397      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9398      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9399      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9400    },
9401
9402    xsrfCookieName: 'XSRF-TOKEN',
9403    xsrfHeaderName: 'X-XSRF-TOKEN',
9404
9405    paramSerializer: '$httpParamSerializer'
9406  };
9407
9408  var useApplyAsync = false;
9409  /**
9410   * @ngdoc method
9411   * @name $httpProvider#useApplyAsync
9412   * @description
9413   *
9414   * Configure $http service to combine processing of multiple http responses received at around
9415   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9416   * significant performance improvement for bigger applications that make many HTTP requests
9417   * concurrently (common during application bootstrap).
9418   *
9419   * Defaults to false. If no value is specified, returns the current configured value.
9420   *
9421   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9422   *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9423   *    to load and share the same digest cycle.
9424   *
9425   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9426   *    otherwise, returns the current configured value.
9427   **/
9428  this.useApplyAsync = function(value) {
9429    if (isDefined(value)) {
9430      useApplyAsync = !!value;
9431      return this;
9432    }
9433    return useApplyAsync;
9434  };
9435
9436  /**
9437   * @ngdoc property
9438   * @name $httpProvider#interceptors
9439   * @description
9440   *
9441   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9442   * pre-processing of request or postprocessing of responses.
9443   *
9444   * These service factories are ordered by request, i.e. they are applied in the same order as the
9445   * array, on request, but reverse order, on response.
9446   *
9447   * {@link ng.$http#interceptors Interceptors detailed info}
9448   **/
9449  var interceptorFactories = this.interceptors = [];
9450
9451  this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9452      function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9453
9454    var defaultCache = $cacheFactory('$http');
9455
9456    /**
9457     * Make sure that default param serializer is exposed as a function
9458     */
9459    defaults.paramSerializer = isString(defaults.paramSerializer) ?
9460      $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9461
9462    /**
9463     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9464     * The reversal is needed so that we can build up the interception chain around the
9465     * server request.
9466     */
9467    var reversedInterceptors = [];
9468
9469    forEach(interceptorFactories, function(interceptorFactory) {
9470      reversedInterceptors.unshift(isString(interceptorFactory)
9471          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9472    });
9473
9474    /**
9475     * @ngdoc service
9476     * @kind function
9477     * @name $http
9478     * @requires ng.$httpBackend
9479     * @requires $cacheFactory
9480     * @requires $rootScope
9481     * @requires $q
9482     * @requires $injector
9483     *
9484     * @description
9485     * The `$http` service is a core Angular service that facilitates communication with the remote
9486     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9487     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9488     *
9489     * For unit testing applications that use `$http` service, see
9490     * {@link ngMock.$httpBackend $httpBackend mock}.
9491     *
9492     * For a higher level of abstraction, please check out the {@link ngResource.$resource
9493     * $resource} service.
9494     *
9495     * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
9496     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9497     * it is important to familiarize yourself with these APIs and the guarantees they provide.
9498     *
9499     *
9500     * ## General usage
9501     * The `$http` service is a function which takes a single argument — a configuration object —
9502     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}
9503     * with two $http specific methods: `success` and `error`.
9504     *
9505     * ```js
9506     *   // Simple GET request example :
9507     *   $http.get('/someUrl').
9508     *     success(function(data, status, headers, config) {
9509     *       // this callback will be called asynchronously
9510     *       // when the response is available
9511     *     }).
9512     *     error(function(data, status, headers, config) {
9513     *       // called asynchronously if an error occurs
9514     *       // or server returns response with an error status.
9515     *     });
9516     * ```
9517     *
9518     * ```js
9519     *   // Simple POST request example (passing data) :
9520     *   $http.post('/someUrl', {msg:'hello word!'}).
9521     *     success(function(data, status, headers, config) {
9522     *       // this callback will be called asynchronously
9523     *       // when the response is available
9524     *     }).
9525     *     error(function(data, status, headers, config) {
9526     *       // called asynchronously if an error occurs
9527     *       // or server returns response with an error status.
9528     *     });
9529     * ```
9530     *
9531     *
9532     * Since the returned value of calling the $http function is a `promise`, you can also use
9533     * the `then` method to register callbacks, and these callbacks will receive a single argument –
9534     * an object representing the response. See the API signature and type info below for more
9535     * details.
9536     *
9537     * A response status code between 200 and 299 is considered a success status and
9538     * will result in the success callback being called. Note that if the response is a redirect,
9539     * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9540     * called for such responses.
9541     *
9542     * ## Writing Unit Tests that use $http
9543     * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9544     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
9545     * request using trained responses.
9546     *
9547     * ```
9548     * $httpBackend.expectGET(...);
9549     * $http.get(...);
9550     * $httpBackend.flush();
9551     * ```
9552     *
9553     * ## Shortcut methods
9554     *
9555     * Shortcut methods are also available. All shortcut methods require passing in the URL, and
9556     * request data must be passed in for POST/PUT requests.
9557     *
9558     * ```js
9559     *   $http.get('/someUrl').success(successCallback);
9560     *   $http.post('/someUrl', data).success(successCallback);
9561     * ```
9562     *
9563     * Complete list of shortcut methods:
9564     *
9565     * - {@link ng.$http#get $http.get}
9566     * - {@link ng.$http#head $http.head}
9567     * - {@link ng.$http#post $http.post}
9568     * - {@link ng.$http#put $http.put}
9569     * - {@link ng.$http#delete $http.delete}
9570     * - {@link ng.$http#jsonp $http.jsonp}
9571     * - {@link ng.$http#patch $http.patch}
9572     *
9573     *
9574     * ## Setting HTTP Headers
9575     *
9576     * The $http service will automatically add certain HTTP headers to all requests. These defaults
9577     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
9578     * object, which currently contains this default configuration:
9579     *
9580     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
9581     *   - `Accept: application/json, text/plain, * / *`
9582     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
9583     *   - `Content-Type: application/json`
9584     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
9585     *   - `Content-Type: application/json`
9586     *
9587     * To add or overwrite these defaults, simply add or remove a property from these configuration
9588     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
9589     * with the lowercased HTTP method name as the key, e.g.
9590     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
9591     *
9592     * The defaults can also be set at runtime via the `$http.defaults` object in the same
9593     * fashion. For example:
9594     *
9595     * ```
9596     * module.run(function($http) {
9597     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
9598     * });
9599     * ```
9600     *
9601     * In addition, you can supply a `headers` property in the config object passed when
9602     * calling `$http(config)`, which overrides the defaults without changing them globally.
9603     *
9604     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
9605     * Use the `headers` property, setting the desired header to `undefined`. For example:
9606     *
9607     * ```js
9608     * var req = {
9609     *  method: 'POST',
9610     *  url: 'http://example.com',
9611     *  headers: {
9612     *    'Content-Type': undefined
9613     *  },
9614     *  data: { test: 'test' }
9615     * }
9616     *
9617     * $http(req).success(function(){...}).error(function(){...});
9618     * ```
9619     *
9620     * ## Transforming Requests and Responses
9621     *
9622     * Both requests and responses can be transformed using transformation functions: `transformRequest`
9623     * and `transformResponse`. These properties can be a single function that returns
9624     * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
9625     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
9626     *
9627     * ### Default Transformations
9628     *
9629     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
9630     * `defaults.transformResponse` properties. If a request does not provide its own transformations
9631     * then these will be applied.
9632     *
9633     * You can augment or replace the default transformations by modifying these properties by adding to or
9634     * replacing the array.
9635     *
9636     * Angular provides the following default transformations:
9637     *
9638     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
9639     *
9640     * - If the `data` property of the request configuration object contains an object, serialize it
9641     *   into JSON format.
9642     *
9643     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
9644     *
9645     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
9646     *  - If JSON response is detected, deserialize it using a JSON parser.
9647     *
9648     *
9649     * ### Overriding the Default Transformations Per Request
9650     *
9651     * If you wish override the request/response transformations only for a single request then provide
9652     * `transformRequest` and/or `transformResponse` properties on the configuration object passed
9653     * into `$http`.
9654     *
9655     * Note that if you provide these properties on the config object the default transformations will be
9656     * overwritten. If you wish to augment the default transformations then you must include them in your
9657     * local transformation array.
9658     *
9659     * The following code demonstrates adding a new response transformation to be run after the default response
9660     * transformations have been run.
9661     *
9662     * ```js
9663     * function appendTransform(defaults, transform) {
9664     *
9665     *   // We can't guarantee that the default transformation is an array
9666     *   defaults = angular.isArray(defaults) ? defaults : [defaults];
9667     *
9668     *   // Append the new transformation to the defaults
9669     *   return defaults.concat(transform);
9670     * }
9671     *
9672     * $http({
9673     *   url: '...',
9674     *   method: 'GET',
9675     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
9676     *     return doTransform(value);
9677     *   })
9678     * });
9679     * ```
9680     *
9681     *
9682     * ## Caching
9683     *
9684     * To enable caching, set the request configuration `cache` property to `true` (to use default
9685     * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
9686     * When the cache is enabled, `$http` stores the response from the server in the specified
9687     * cache. The next time the same request is made, the response is served from the cache without
9688     * sending a request to the server.
9689     *
9690     * Note that even if the response is served from cache, delivery of the data is asynchronous in
9691     * the same way that real requests are.
9692     *
9693     * If there are multiple GET requests for the same URL that should be cached using the same
9694     * cache, but the cache is not populated yet, only one request to the server will be made and
9695     * the remaining requests will be fulfilled using the response from the first request.
9696     *
9697     * You can change the default cache to a new object (built with
9698     * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
9699     * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
9700     * their `cache` property to `true` will now use this cache object.
9701     *
9702     * If you set the default cache to `false` then only requests that specify their own custom
9703     * cache object will be cached.
9704     *
9705     * ## Interceptors
9706     *
9707     * Before you start creating interceptors, be sure to understand the
9708     * {@link ng.$q $q and deferred/promise APIs}.
9709     *
9710     * For purposes of global error handling, authentication, or any kind of synchronous or
9711     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
9712     * able to intercept requests before they are handed to the server and
9713     * responses before they are handed over to the application code that
9714     * initiated these requests. The interceptors leverage the {@link ng.$q
9715     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
9716     *
9717     * The interceptors are service factories that are registered with the `$httpProvider` by
9718     * adding them to the `$httpProvider.interceptors` array. The factory is called and
9719     * injected with dependencies (if specified) and returns the interceptor.
9720     *
9721     * There are two kinds of interceptors (and two kinds of rejection interceptors):
9722     *
9723     *   * `request`: interceptors get called with a http `config` object. The function is free to
9724     *     modify the `config` object or create a new one. The function needs to return the `config`
9725     *     object directly, or a promise containing the `config` or a new `config` object.
9726     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
9727     *     resolved with a rejection.
9728     *   * `response`: interceptors get called with http `response` object. The function is free to
9729     *     modify the `response` object or create a new one. The function needs to return the `response`
9730     *     object directly, or as a promise containing the `response` or a new `response` object.
9731     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
9732     *     resolved with a rejection.
9733     *
9734     *
9735     * ```js
9736     *   // register the interceptor as a service
9737     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
9738     *     return {
9739     *       // optional method
9740     *       'request': function(config) {
9741     *         // do something on success
9742     *         return config;
9743     *       },
9744     *
9745     *       // optional method
9746     *      'requestError': function(rejection) {
9747     *         // do something on error
9748     *         if (canRecover(rejection)) {
9749     *           return responseOrNewPromise
9750     *         }
9751     *         return $q.reject(rejection);
9752     *       },
9753     *
9754     *
9755     *
9756     *       // optional method
9757     *       'response': function(response) {
9758     *         // do something on success
9759     *         return response;
9760     *       },
9761     *
9762     *       // optional method
9763     *      'responseError': function(rejection) {
9764     *         // do something on error
9765     *         if (canRecover(rejection)) {
9766     *           return responseOrNewPromise
9767     *         }
9768     *         return $q.reject(rejection);
9769     *       }
9770     *     };
9771     *   });
9772     *
9773     *   $httpProvider.interceptors.push('myHttpInterceptor');
9774     *
9775     *
9776     *   // alternatively, register the interceptor via an anonymous factory
9777     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
9778     *     return {
9779     *      'request': function(config) {
9780     *          // same as above
9781     *       },
9782     *
9783     *       'response': function(response) {
9784     *          // same as above
9785     *       }
9786     *     };
9787     *   });
9788     * ```
9789     *
9790     * ## Security Considerations
9791     *
9792     * When designing web applications, consider security threats from:
9793     *
9794     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9795     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
9796     *
9797     * Both server and the client must cooperate in order to eliminate these threats. Angular comes
9798     * pre-configured with strategies that address these issues, but for this to work backend server
9799     * cooperation is required.
9800     *
9801     * ### JSON Vulnerability Protection
9802     *
9803     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9804     * allows third party website to turn your JSON resource URL into
9805     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
9806     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
9807     * Angular will automatically strip the prefix before processing it as JSON.
9808     *
9809     * For example if your server needs to return:
9810     * ```js
9811     * ['one','two']
9812     * ```
9813     *
9814     * which is vulnerable to attack, your server can return:
9815     * ```js
9816     * )]}',
9817     * ['one','two']
9818     * ```
9819     *
9820     * Angular will strip the prefix, before processing the JSON.
9821     *
9822     *
9823     * ### Cross Site Request Forgery (XSRF) Protection
9824     *
9825     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
9826     * an unauthorized site can gain your user's private data. Angular provides a mechanism
9827     * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
9828     * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
9829     * JavaScript that runs on your domain could read the cookie, your server can be assured that
9830     * the XHR came from JavaScript running on your domain. The header will not be set for
9831     * cross-domain requests.
9832     *
9833     * To take advantage of this, your server needs to set a token in a JavaScript readable session
9834     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
9835     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
9836     * that only JavaScript running on your domain could have sent the request. The token must be
9837     * unique for each user and must be verifiable by the server (to prevent the JavaScript from
9838     * making up its own tokens). We recommend that the token is a digest of your site's
9839     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
9840     * for added security.
9841     *
9842     * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
9843     * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
9844     * or the per-request config object.
9845     *
9846     * In order to prevent collisions in environments where multiple Angular apps share the
9847     * same domain or subdomain, we recommend that each application uses unique cookie name.
9848     *
9849     *
9850     * @param {object} config Object describing the request to be made and how it should be
9851     *    processed. The object has following properties:
9852     *
9853     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
9854     *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
9855     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
9856     *      with the `paramSerializer` and appended as GET parameters.
9857     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
9858     *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
9859     *      HTTP headers to send to the server. If the return value of a function is null, the
9860     *      header will not be sent. Functions accept a config object as an argument.
9861     *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
9862     *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
9863     *    - **transformRequest** –
9864     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>
9864}` –
9865     *      transform function or an array of such functions. The transform function takes the http
9866     *      request body and headers and returns its transformed (typically serialized) version.
9867     *      See {@link ng.$http#overriding-the-default-transformations-per-request
9868     *      Overriding the Default Transformations}
9869     *    - **transformResponse** –
9870     *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
9871     *      transform function or an array of such functions. The transform function takes the http
9872     *      response body, headers and status and returns its transformed (typically deserialized) version.
9873     *      See {@link ng.$http#overriding-the-default-transformations-per-request
9874     *      Overriding the Default TransformationjqLiks}
9875     *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
9876     *      prepare the string representation of request parameters (specified as an object).
9877     *      If specified as string, it is interpreted as function registered with the
9878     *      {@link $injector $injector}, which means you can create your own serializer
9879     *      by registering it as a {@link auto.$provide#service service}.
9880     *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
9881     *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
9882     *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
9883     *      GET request, otherwise if a cache instance built with
9884     *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
9885     *      caching.
9886     *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
9887     *      that should abort the request when resolved.
9888     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
9889     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
9890     *      for more information.
9891     *    - **responseType** - `{string}` - see
9892     *      [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
9893     *
9894     * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
9895     *   standard `then` method and two http specific methods: `success` and `error`. The `then`
9896     *   method takes two arguments a success and an error callback which will be called with a
9897     *   response object. The `success` and `error` methods take a single argument - a function that
9898     *   will be called when the request succeeds or fails respectively. The arguments passed into
9899     *   these functions are destructured representation of the response object passed into the
9900     *   `then` method. The response object has these properties:
9901     *
9902     *   - **data** – `{string|Object}` – The response body transformed with the transform
9903     *     functions.
9904     *   - **status** – `{number}` – HTTP status code of the response.
9905     *   - **headers** – `{function([headerName])}` – Header getter function.
9906     *   - **config** – `{Object}` – The configuration object that was used to generate the request.
9907     *   - **statusText** – `{string}` – HTTP status text of the response.
9908     *
9909     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
9910     *   requests. This is primarily meant to be used for debugging purposes.
9911     *
9912     *
9913     * @example
9914<example module="httpExample">
9915<file name="index.html">
9916  <div ng-controller="FetchController">
9917    <select ng-model="method" aria-label="Request method">
9918      <option>GET</option>
9919      <option>JSONP</option>
9920    </select>
9921    <input type="text" ng-model="url" size="80" aria-label="URL" />
9922    <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
9923    <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
9924    <button id="samplejsonpbtn"
9925      ng-click="updateModel('JSONP',
9926                    'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
9927      Sample JSONP
9928    </button>
9929    <button id="invalidjsonpbtn"
9930      ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
9931        Invalid JSONP
9932      </button>
9933    <pre>http status code: {{status}}</pre>
9934    <pre>http response data: {{data}}</pre>
9935  </div>
9936</file>
9937<file name="script.js">
9938  angular.module('httpExample', [])
9939    .controller('FetchController', ['$scope', '$http', '$templateCache',
9940      function($scope, $http, $templateCache) {
9941        $scope.method = 'GET';
9942        $scope.url = 'http-hello.html';
9943
9944        $scope.fetch = function() {
9945          $scope.code = null;
9946          $scope.response = null;
9947
9948          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
9949            success(function(data, status) {
9950              $scope.status = status;
9951              $scope.data = data;
9952            }).
9953            error(function(data, status) {
9954              $scope.data = data || "Request failed";
9955              $scope.status = status;
9956          });
9957        };
9958
9959        $scope.updateModel = function(method, url) {
9960          $scope.method = method;
9961          $scope.url = url;
9962        };
9963      }]);
9964</file>
9965<file name="http-hello.html">
9966  Hello, $http!
9967</file>
9968<file name="protractor.js" type="protractor">
9969  var status = element(by.binding('status'));
9970  var data = element(by.binding('data'));
9971  var fetchBtn = element(by.id('fetchbtn'));
9972  var sampleGetBtn = element(by.id('samplegetbtn'));
9973  var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
9974  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
9975
9976  it('should make an xhr GET request', function() {
9977    sampleGetBtn.click();
9978    fetchBtn.click();
9979    expect(status.getText()).toMatch('200');
9980    expect(data.getText()).toMatch(/Hello, \$http!/);
9981  });
9982
9983// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
9984// it('should make a JSONP request to angularjs.org', function() {
9985//   sampleJsonpBtn.click();
9986//   fetchBtn.click();
9987//   expect(status.getText()).toMatch('200');
9988//   expect(data.getText()).toMatch(/Super Hero!/);
9989// });
9990
9991  it('should make JSONP request to invalid URL and invoke the error handler',
9992      function() {
9993    invalidJsonpBtn.click();
9994    fetchBtn.click();
9995    expect(status.getText()).toMatch('0');
9996    expect(data.getText()).toMatch('Request failed');
9997  });
9998</file>
9999</example>
10000     */
10001    function $http(requestConfig) {
10002
10003      if (!angular.isObject(requestConfig)) {
10004        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
10005      }
10006
10007      var config = extend({
10008        method: 'get',
10009        transformRequest: defaults.transformRequest,
10010        transformResponse: defaults.transformResponse,
10011        paramSerializer: defaults.paramSerializer
10012      }, requestConfig);
10013
10014      config.headers = mergeHeaders(requestConfig);
10015      config.method = uppercase(config.method);
10016      config.paramSerializer = isString(config.paramSerializer) ?
10017        $injector.get(config.paramSerializer) : config.paramSerializer;
10018
10019      var serverRequest = function(config) {
10020        var headers = config.headers;
10021        var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10022
10023        // strip content-type if data is undefined
10024        if (isUndefined(reqData)) {
10025          forEach(headers, function(value, header) {
10026            if (lowercase(header) === 'content-type') {
10027                delete headers[header];
10028            }
10029          });
10030        }
10031
10032        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10033          config.withCredentials = defaults.withCredentials;
10034        }
10035
10036        // send request
10037        return sendReq(config, reqData).then(transformResponse, transformResponse);
10038      };
10039
10040      var chain = [serverRequest, undefined];
10041      var promise = $q.when(config);
10042
10043      // apply interceptors
10044      forEach(reversedInterceptors, function(interceptor) {
10045        if (interceptor.request || interceptor.requestError) {
10046          chain.unshift(interceptor.request, interceptor.requestError);
10047        }
10048        if (interceptor.response || interceptor.responseError) {
10049          chain.push(interceptor.response, interceptor.responseError);
10050        }
10051      });
10052
10053      while (chain.length) {
10054        var thenFn = chain.shift();
10055        var rejectFn = chain.shift();
10056
10057        promise = promise.then(thenFn, rejectFn);
10058      }
10059
10060      promise.success = function(fn) {
10061        assertArgFn(fn, 'fn');
10062
10063        promise.then(function(response) {
10064          fn(response.data, response.status, response.headers, config);
10065        });
10066        return promise;
10067      };
10068
10069      promise.error = function(fn) {
10070        assertArgFn(fn, 'fn');
10071
10072        promise.then(null, function(response) {
10073          fn(response.data, response.status, response.headers, config);
10074        });
10075        return promise;
10076      };
10077
10078      return promise;
10079
10080      function transformResponse(response) {
10081        // make a copy since the response must be cacheable
10082        var resp = extend({}, response);
10083        if (!response.data) {
10084          resp.data = response.data;
10085        } else {
10086          resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
10087        }
10088        return (isSuccess(response.status))
10089          ? resp
10090          : $q.reject(resp);
10091      }
10092
10093      function executeHeaderFns(headers, config) {
10094        var headerContent, processedHeaders = {};
10095
10096        forEach(headers, function(headerFn, header) {
10097          if (isFunction(headerFn)) {
10098            headerContent = headerFn(config);
10099            if (headerContent != null) {
10100              processedHeaders[header] = headerContent;
10101            }
10102          } else {
10103            processedHeaders[header] = headerFn;
10104          }
10105        });
10106
10107        return processedHeaders;
10108      }
10109
10110      function mergeHeaders(config) {
10111        var defHeaders = defaults.headers,
10112            reqHeaders = extend({}, config.headers),
10113            defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10114
10115        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10116
vendor: 4,228 bytes, lines 10117-10256
10117        // using for-in instead of forEach to avoid unecessary iteration after header has been found
10118        defaultHeadersIteration:
10119        for (defHeaderName in defHeaders) {
10120          lowercaseDefHeaderName = lowercase(defHeaderName);
10121
10122          for (reqHeaderName in reqHeaders) {
10123            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10124              continue defaultHeadersIteration;
10125            }
10126          }
10127
10128          reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10129        }
10130
10131        // execute if header value is a function for merged headers
10132        return executeHeaderFns(reqHeaders, shallowCopy(config));
10133      }
10134    }
10135
10136    $http.pendingRequests = [];
10137
10138    /**
10139     * @ngdoc method
10140     * @name $http#get
10141     *
10142     * @description
10143     * Shortcut method to perform `GET` request.
10144     *
10145     * @param {string} url Relative or absolute URL specifying the destination of the request
10146     * @param {Object=} config Optional configuration object
10147     * @returns {HttpPromise} Future object
10148     */
10149
10150    /**
10151     * @ngdoc method
10152     * @name $http#delete
10153     *
10154     * @description
10155     * Shortcut method to perform `DELETE` request.
10156     *
10157     * @param {string} url Relative or absolute URL specifying the destination of the request
10158     * @param {Object=} config Optional configuration object
10159     * @returns {HttpPromise} Future object
10160     */
10161
10162    /**
10163     * @ngdoc method
10164     * @name $http#head
10165     *
10166     * @description
10167     * Shortcut method to perform `HEAD` request.
10168     *
10169     * @param {string} url Relative or absolute URL specifying the destination of the request
10170     * @param {Object=} config Optional configuration object
10171     * @returns {HttpPromise} Future object
10172     */
10173
10174    /**
10175     * @ngdoc method
10176     * @name $http#jsonp
10177     *
10178     * @description
10179     * Shortcut method to perform `JSONP` request.
10180     *
10181     * @param {string} url Relative or absolute URL specifying the destination of the request.
10182     *                     The name of the callback should be the string `JSON_CALLBACK`.
10183     * @param {Object=} config Optional configuration object
10184     * @returns {HttpPromise} Future object
10185     */
10186    createShortMethods('get', 'delete', 'head', 'jsonp');
10187
10188    /**
10189     * @ngdoc method
10190     * @name $http#post
10191     *
10192     * @description
10193     * Shortcut method to perform `POST` request.
10194     *
10195     * @param {string} url Relative or absolute URL specifying the destination of the request
10196     * @param {*} data Request content
10197     * @param {Object=} config Optional configuration object
10198     * @returns {HttpPromise} Future object
10199     */
10200
10201    /**
10202     * @ngdoc method
10203     * @name $http#put
10204     *
10205     * @description
10206     * Shortcut method to perform `PUT` request.
10207     *
10208     * @param {string} url Relative or absolute URL specifying the destination of the request
10209     * @param {*} data Request content
10210     * @param {Object=} config Optional configuration object
10211     * @returns {HttpPromise} Future object
10212     */
10213
10214     /**
10215      * @ngdoc method
10216      * @name $http#patch
10217      *
10218      * @description
10219      * Shortcut method to perform `PATCH` request.
10220      *
10221      * @param {string} url Relative or absolute URL specifying the destination of the request
10222      * @param {*} data Request content
10223      * @param {Object=} config Optional configuration object
10224      * @returns {HttpPromise} Future object
10225      */
10226    createShortMethodsWithData('post', 'put', 'patch');
10227
10228        /**
10229         * @ngdoc property
10230         * @name $http#defaults
10231         *
10232         * @description
10233         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10234         * default headers, withCredentials as well as request and response transformations.
10235         *
10236         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10237         */
10238    $http.defaults = defaults;
10239
10240
10241    return $http;
10242
10243
10244    function createShortMethods(names) {
10245      forEach(arguments, function(name) {
10246        $http[name] = function(url, config) {
10247          return $http(extend({}, config || {}, {
10248            method: name,
10249            url: url
10250          }));
10251        };
10252      });
10253    }
10254
10255
10256    function createShortMethodsWithData(name) {
10257      forEach(arguments, function(name) {
10258        $http[name] = function(url, data, config) {
10259          return $http(extend({}, config || {}, {
10260            method: name,
10261            url: url,
10262            data: data
10263          }));
10264        };
10265      });
10266    }
10267
10268
10269    /**
10270     * Makes the request.
10271     *
10272     * !!! ACCESSES CLOSURE VARS:
10273     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10274     */
10275    function sendReq(config, reqData) {
10276      var deferred = $q.defer(),
10277          promise = deferred.promise,
10278          cache,
10279          cachedResp,
10280          reqHeaders = config.headers,
10281          url = buildUrl(config.url, config.paramSerializer(config.params));
10282
10283      $http.pendingRequests.push(config);
10284      promise.then(removePendingReq, removePendingReq);
10285
10286
10287      if ((config.cache || defaults.cache) && config.cache !== false &&
10288          (config.method === 'GET' || config.method === 'JSONP')) {
10289        cache = isObject(config.cache) ? config.cache
10290              : isObject(defaults.cache) ? defaults.cache
10291              : defaultCache;
10292      }
10293
10294      if (cache) {
10295        cachedResp = cache.get(url);
10296        if (isDefined(cachedResp)) {
10297          if (isPromiseLike(cachedResp)) {
10298            // cached request has already been sent, but there is no response yet
10299            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10300          } else {
10301            // serving from cache
10302            if (isArray(cachedResp)) {
10303              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10304            } else {
10305              resolvePromise(cachedResp, 200, {}, 'OK');
10306            }
10307          }
10308        } else {
10309          // put the promise for the non-transformed response into cache as a placeholder
10310          cache.put(url, promise);
10311        }
10312      }
10313
10314
10315      // if we won't have the response in cache, set the xsrf headers and
10316      // send the request to the backend
10317      if (isUndefined(cachedResp)) {
10318        var xsrfValue = urlIsSameOrigin(config.url)
10319            ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10320            : undefined;
10321        if (xsrfValue) {
10322          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10323        }
10324
10325        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10326            config.withCredentials, config.responseType);
10327      }
10328
10329      return promise;
10330
10331
10332      /**
10333       * Callback registered to $httpBackend():
10334       *  - caches the response if desired
10335       *  - resolves the raw $http promise
10336       *  - calls $apply
10337       */
10338      function done(status, response, headersString, statusText) {
10339        if (cache) {
10340          if (isSuccess(status)) {
10341            cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10342          } else {
10343            // remove promise from the cache
10344            cache.remove(url);
10345          }
10346        }
10347
10348        function resolveHttpPromise() {
10349          resolvePromise(response, status, headersString, statusText);
10350        }
10351
10352        if (useApplyAsync) {
10353          $rootScope.$applyAsync(resolveHttpPromise);
10354        } else {
10355          resolveHttpPromise();
10356          if (!$rootScope.$$phase) $rootScope.$apply();
10357        }
10358      }
10359
10360
10361      /**
10362       * Resolves the raw $http promise.
10363       */
10364      function resolvePromise(response, status, headers, statusText) {
10365        // normalize internal statuses to 0
10366        status = Math.max(status, 0);
10367
10368        (isSuccess(status) ? deferred.resolve : deferred.reject)({
10369          data: response,
10370          status: status,
10371          headers: headersGetter(headers),
10372          config: config,
10373          statusText: statusText
10374        });
10375      }
10376
10377      function resolvePromiseWithResult(result) {
10378        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10379      }
10380
10381      function removePendingReq() {
10382        var idx = $http.pendingRequests.indexOf(config);
10383        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10384      }
10385    }
10386
10387
10388    function buildUrl(url, serializedParams) {
10389      if (serializedParams.length > 0) {
10390        url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10391      }
10392      return url;
10393    }
10394  }];
10395}
10396
10397function createXhr() {
10398    return new window.XMLHttpRequest();
10399}
10400
10401/**
10402 * @ngdoc service
10403 * @name $httpBackend
10404 * @requires $window
10405 * @requires $document
10406 *
10407 * @description
10408 * HTTP backend used by the {@link ng.$http service} that delegates to
10409 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10410 *
10411 * You should never need to use this service directly, instead use the higher-level abstractions:
10412 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10413 *
10414 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10415 * $httpBackend} which can be trained with responses.
10416 */
10417function $HttpBackendProvider() {
10418  this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
10419    return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
10420  }];
10421}
10422
10423function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10424  // TODO(vojta): fix the signature
10425  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10426    $browser.$$incOutstandingRequestCount();
10427    url = url || $browser.url();
10428
10429    if (lowercase(method) == 'jsonp') {
10430      var callbackId = '_' + (callbacks.counter++).toString(36);
10431      callbacks[callbackId] = function(data) {
10432        callbacks[callbackId].data = data;
10433        callbacks[callbackId].called = true;
10434      };
10435
10436      var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10437          callbackId, function(status, text) {
10438        completeRequest(callback, status, callbacks[callbackId].data, "", text);
10439        callbacks[callbackId] = noop;
10440      });
10441    } else {
10442
10443      var xhr = createXhr();
10444
10445      xhr.open(method, url, true);
10446      forEach(headers, function(value, key) {
10447        if (isDefined(value)) {
10448            xhr.setRequestHeader(key, value);
10449        }
10450      });
10451
10452      xhr.onload = function requestLoaded() {
10453        var statusText = xhr.statusText || '';
10454
10455        // responseText is the old-school way of retrieving response (supported by IE8 & 9)
10456        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10457        var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10458
10459        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10460        var status = xhr.status === 1223 ? 204 : xhr.status;
10461
10462        // fix status code when it is 0 (0 status is undocumented).
10463        // Occurs when accessing file resources or on Android 4.1 stock browser
10464        // while retrieving files from application cache.
10465        if (status === 0) {
10466          status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10467        }
10468
10469        completeRequest(callback,
10470            status,
10471            response,
10472            xhr.getAllResponseHeaders(),
10473            statusText);
10474      };
10475
10476      var requestError = function() {
10477        // The response is always empty
10478        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
10479        completeRequest(callback, -1, null, null, '');
10480      };
10481
10482      xhr.onerror = requestError;
10483      xhr.onabort = requestError;
10484
10485      if (withCredentials) {
10486        xhr.withCredentials = true;
10487      }
10488
10489      if (responseType) {
10490        try {
10491          xhr.responseType = responseType;
10492        } catch (e) {
10493          // WebKit added support for the json responseType value on 09/03/2013
10494          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
10495          // known to throw when setting the value "json" as the response type. Other older
10496          // browsers implementing the responseType
10497          //
10498          // The json response type can be ignored if not supported, because JSON payloads are
10499          // parsed on the client-side regardless.
10500          if (responseType !== 'json') {
10501            throw e;
10502          }
10503        }
10504      }
10505
10506      xhr.send(post);
10507    }
10508
10509    if (timeout > 0) {
10510      var timeoutId = $browserDefer(timeoutRequest, timeout);
10511    } else if (isPromiseLike(timeout)) {
10512      timeout.then(timeoutRequest);
10513    }
10514
10515
10516    function timeoutRequest() {
10517      jsonpDone && jsonpDone();
10518      xhr && xhr.abort();
10519    }
10520
10521    function completeRequest(callback, status, response, headersString, statusText) {
10522      // cancel timeout and subsequent timeout promise resolution
10523      if (timeoutId !== undefined) {
10524        $browserDefer.cancel(timeoutId);
10525      }
10526      jsonpDone = xhr = null;
10527
10528      callback(status, response, headersString, statusText);
10529      $browser.$$completeOutstandingRequest(noop);
10530    }
10531  };
10532
10533  function jsonpReq(url, callbackId, done) {
10534    // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
10535    // - fetches local scripts via XHR and evals them
10536    // - adds and immediately removes script elements from the document
10537    var script = rawDocument.createElement('script'), callback = null;
10538    script.type = "text/javascript";
10539    script.src = url;
10540    script.async = true;
10541
10542    callback = function(event) {
10543      removeEventListenerFn(script, "load", callback);
10544      removeEventListenerFn(script, "error", callback);
10545      rawDocument.body.removeChild(script);
10546      script = null;
10547      var status = -1;
10548      var text = "unknown";
10549
10550      if (event) {
10551        if (event.type === "load" && !callbacks[callbackId].called) {
10552          event = { type: "error" };
10553        }
10554        text = event.type;
10555        status = event.type === "error" ? 404 : 200;
10556      }
10557
10558      if (done) {
10559        done(status, text);
10560      }
10561    };
10562
10563    addEventListenerFn(script, "load", callback);
10564    addEventListenerFn(script, "error", callback);
10565    rawDocument.body.appendChild(script);
10566    return callback;
10567  }
10568}
10569
10570var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
10571$interpolateMinErr.throwNoconcat = function(text) {
10572  throw $interpolateMinErr('noconcat',
10573      "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10574      "interpolations that concatenate multiple expressions when a trusted value is " +
10575      "required.  See http://docs.angularjs.org/api/ng.$sce", text);
10576};
10577
10578$interpolateMinErr.interr = function(text, err) {
10579  return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
10580};
10581
10582/**
10583 * @ngdoc provider
10584 * @name $interpolateProvider
10585 *
10586 * @description
10587 *
10588 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
10589 *
10590 * @example
10591<example module="customInterpolationApp">
10592<file name="index.html">
10593<script>
10594  var customInterpolationApp = angular.module('customInterpolationApp', []);
10595
10596  customInterpolationApp.config(function($interpolateProvider) {
10597    $interpolateProvider.startSymbol('//');
10598    $interpolateProvider.endSymbol('//');
10599  });
10600
10601
10602  customInterpolationApp.controller('DemoController', function() {
10603      this.label = "This binding is brought you by // interpolation symbols.";
10604  });
10605</script>
10606<div ng-app="App" ng-controller="DemoController as demo">
10607    //demo.label//
10608</div>
10609</file>
10610<file name="protractor.js" type="protractor">
10611  it('should interpolate binding with custom symbols', function() {
10612    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
10613  });
10614</file>
10615</example>
10616 */
10617function $InterpolateProvider() {
10618  var startSymbol = '{{';
10619  var endSymbol = '}}';
10620
10621  /**
10622   * @ngdoc method
10623   * @name $interpolateProvider#startSymbol
10624   * @description
10625   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
10626   *
10627   * @param {string=} value new value to set the starting symbol to.
10628   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10629   */
10630  this.startSymbol = function(value) {
10631    if (value) {
10632      startSymbol = value;
10633      return this;
10634    } else {
10635      return startSymbol;
10636    }
10637  };
10638
10639  /**
10640   * @ngdoc method
10641   * @name $interpolateProvider#endSymbol
10642   * @description
10643   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10644   *
10645   * @param {string=} value new value to set the ending symbol to.
10646   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10647   */
10648  this.endSymbol = function(value) {
10649    if (value) {
10650      endSymbol = value;
10651      return this;
10652    } else {
10653      return endSymbol;
10654    }
10655  };
10656
10657
10658  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
10659    var startSymbolLength = startSymbol.length,
10660        endSymbolLength = endSymbol.length,
10661        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
10662        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
10663
10664    function escape(ch) {
10665      return '\\\\\\' + ch;
10666    }
10667
10668    function unescapeText(text) {
10669      return text.replace(escapedStartRegexp, startSymbol).
10670        replace(escapedEndRegexp, endSymbol);
10671    }
10672
10673    function stringify(value) {
10674      if (value == null) { // null || undefined
10675        return '';
10676      }
10677      switch (typeof value) {
10678        case 'string':
10679          break;
10680        case 'number':
10681          value = '' + value;
10682          break;
10683        default:
10684          value = toJson(value);
10685      }
10686
10687      return value;
10688    }
10689
10690    /**
10691     * @ngdoc service
10692     * @name $interpolate
10693     * @kind function
10694     *
10695     * @requires $parse
10696     * @requires $sce
10697     *
10698     * @description
10699     *
10700     * Compiles a string with markup into an interpolation function. This service is used by the
10701     * HTML {@link ng.$compile $compile} service for data binding. See
10702     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
10703     * interpolation markup.
10704     *
10705     *
10706     * ```js
10707     *   var $interpolate = ...; // injected
10708     *   var exp = $interpolate('Hello {{name | uppercase}}!');
10709     *   expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
10710     * ```
10711     *
10712     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
10713     * `true`, the interpolation function will return `undefined` unless all embedded expressions
10714     * evaluate to a value other than `undefined`.
10715     *
10716     * ```js
10717     *   var $interpolate = ...; // injected
10718     *   var context = {greeting: 'Hello', name: undefined };
10719     *
10720     *   // default "forgiving" mode
10721     *   var exp = $interpolate('{{greeting}} {{name}}!');
10722     *   expect(exp(context)).toEqual('Hello !');
10723     *
10724     *   // "allOrNothing" mode
10725     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
10726     *   expect(exp(context)).toBeUndefined();
10727     *   context.name = 'Angular';
10728     *   expect(exp(context)).toEqual('Hello Angular!');
10729     * ```
10730     *
10731     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
10732     *
10733     * ####Escaped Interpolation
10734     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
10735     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
10736     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
10737     * or binding.
10738     *
10739     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
10740     * degree, while also enabling code examples to work without relying on the
10741     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
10742     *
10743     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
10744     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
10745     * interpolation start/end markers with their escaped counterparts.**
10746     *
10747     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
10748     * output when the $interpolate service processes the text. So, for HTML elements interpolated
10749     * by {@link ng.$compile $compile}
10749, or otherwise interpolated with the `mustHaveExpression` parameter
10750     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
10751     * this is typically useful only when user-data is used in rendering a template from the server, or
10752     * when otherwise untrusted data is used by a directive.
10753     *
10754     * <example>
10755     *  <file name="index.html">
10756     *    <div ng-init="username='A user'">
10757     *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
10758     *        </p>
10759     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
10760     *        application, but fails to accomplish their task, because the server has correctly
10761     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
10762     *        characters.</p>
10763     *      <p>Instead, the result of the attempted script injection is visible, and can be removed
10764     *        from the database by an administrator.</p>
10765     *    </div>
10766     *  </file>
10767     * </example>
10768     *
10769     * @param {string} text The text with markup to interpolate.
10770     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
10771     *    embedded expression in order to return an interpolation function. Strings with no
10772     *    embedded expression will return null for the interpolation function.
10773     * @param {string=} trustedContext when provided, the returned function passes the interpolated
10774     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
10775     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
10776     *    provides Strict Contextual Escaping for details.
10777     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
10778     *    unless all embedded expressions evaluate to a value other than `undefined`.
10779     * @returns {function(context)} an interpolation function which is used to compute the
10780     *    interpolated string. The function has these parameters:
10781     *
10782     * - `context`: evaluation context for all expressions embedded in the interpolated text
10783     */
10784    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
10785      allOrNothing = !!allOrNothing;
10786      var startIndex,
10787          endIndex,
10788          index = 0,
10789          expressions = [],
10790          parseFns = [],
10791          textLength = text.length,
10792          exp,
10793          concat = [],
10794          expressionPositions = [];
10795
10796      while (index < textLength) {
10797        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
10798             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
10799          if (index !== startIndex) {
10800            concat.push(unescapeText(text.substring(index, startIndex)));
10801          }
10802          exp = text.substring(startIndex + startSymbolLength, endIndex);
10803          expressions.push(exp);
10804          parseFns.push($parse(exp, parseStringifyInterceptor));
10805          index = endIndex + endSymbolLength;
10806          expressionPositions.push(concat.length);
10807          concat.push('');
10808        } else {
10809          // we did not find an interpolation, so we have to add the remainder to the separators array
10810          if (index !== textLength) {
10811            concat.push(unescapeText(text.substring(index)));
10812          }
10813          break;
10814        }
10815      }
10816
10817      // Concatenating expressions makes it hard to reason about whether some combination of
10818      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
10819      // single expression be used for iframe[src], object[src], etc., we ensure that the value
10820      // that's used is assigned or constructed by some JS code somewhere that is more testable or
10821      // make it obvious that you bound the value to some user controlled value.  This helps reduce
10822      // the load when auditing for XSS issues.
10823      if (trustedContext && concat.length > 1) {
10824          $interpolateMinErr.throwNoconcat(text);
10825      }
10826
10827      if (!mustHaveExpression || expressions.length) {
10828        var compute = function(values) {
10829          for (var i = 0, ii = expressions.length; i < ii; i++) {
10830            if (allOrNothing && isUndefined(values[i])) return;
10831            concat[expressionPositions[i]] = values[i];
10832          }
10833          return concat.join('');
10834        };
10835
10836        var getValue = function(value) {
10837          return trustedContext ?
10838            $sce.getTrusted(trustedContext, value) :
10839            $sce.valueOf(value);
10840        };
10841
10842        return extend(function interpolationFn(context) {
10843            var i = 0;
10844            var ii = expressions.length;
10845            var values = new Array(ii);
10846
10847            try {
10848              for (; i < ii; i++) {
10849                values[i] = parseFns[i](context);
10850              }
10851
10852              return compute(values);
10853            } catch (err) {
10854              $exceptionHandler($interpolateMinErr.interr(text, err));
10855            }
10856
10857          }, {
10858          // all of these properties are undocumented for now
10859          exp: text, //just for compatibility with regular watchers created via $watch
10860          expressions: expressions,
10861          $$watchDelegate: function(scope, listener) {
10862            var lastValue;
10863            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
10864              var currValue = compute(values);
10865              if (isFunction(listener)) {
10866                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
10867              }
10868              lastValue = currValue;
10869            });
10870          }
10871        });
10872      }
10873
10874      function parseStringifyInterceptor(value) {
10875        try {
10876          value = getValue(value);
10877          return allOrNothing && !isDefined(value) ? value : stringify(value);
10878        } catch (err) {
10879          $exceptionHandler($interpolateMinErr.interr(text, err));
10880        }
10881      }
10882    }
10883
10884
10885    /**
10886     * @ngdoc method
10887     * @name $interpolate#startSymbol
10888     * @description
10889     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
10890     *
10891     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
10892     * the symbol.
10893     *
10894     * @returns {string} start symbol.
10895     */
10896    $interpolate.startSymbol = function() {
10897      return startSymbol;
10898    };
10899
10900
10901    /**
10902     * @ngdoc method
10903     * @name $interpolate#endSymbol
10904     * @description
10905     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10906     *
10907     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
10908     * the symbol.
10909     *
10910     * @returns {string} end symbol.
10911     */
10912    $interpolate.endSymbol = function() {
10913      return endSymbol;
10914    };
10915
10916    return $interpolate;
10917  }];
10918}
10919
10920function $IntervalProvider() {
10921  this.$get = ['$rootScope', '$window', '$q', '$$q',
10922       function($rootScope,   $window,   $q,   $$q) {
10923    var intervals = {};
10924
10925
10926     /**
10927      * @ngdoc service
10928      * @name $interval
10929      *
10930      * @description
10931      * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
10932      * milliseconds.
10933      *
10934      * The return value of registering an interval function is a promise. This promise will be
10935      * notified upon each tick of the interval, and will be resolved after `count` iterations, or
10936      * run indefinitely if `count` is not defined. The value of the notification will be the
10937      * number of iterations that have run.
10938      * To cancel an interval, call `$interval.cancel(promise)`.
10939      *
10940      * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
10941      * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
10942      * time.
10943      *
10944      * <div class="alert alert-warning">
10945      * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
10946      * with them.  In particular they are not automatically destroyed when a controller's scope or a
10947      * directive's element are destroyed.
10948      * You should take this into consideration and make sure to always cancel the interval at the
10949      * appropriate moment.  See the example below for more details on how and when to do this.
10950      * </div>
10951      *
10952      * @param {function()} fn A function that should be called repeatedly.
10953      * @param {number} delay Number of milliseconds between each function call.
10954      * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
10955      *   indefinitely.
10956      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
10957      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
10958      * @param {...*=} Pass additional parameters to the executed function.
10959      * @returns {promise} A promise which will be notified on each iteration.
10960      *
10961      * @example
10962      * <example module="intervalExample">
10963      * <file name="index.html">
10964      *   <script>
10965      *     angular.module('intervalExample', [])
10966      *       .controller('ExampleController', ['$scope', '$interval',
10967      *         function($scope, $interval) {
10968      *           $scope.format = 'M/d/yy h:mm:ss a';
10969      *           $scope.blood_1 = 100;
10970      *           $scope.blood_2 = 120;
10971      *
10972      *           var stop;
10973      *           $scope.fight = function() {
10974      *             // Don't start a new fight if we are already fighting
10975      *             if ( angular.isDefined(stop) ) return;
10976      *
10977      *             stop = $interval(function() {
10978      *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
10979      *                 $scope.blood_1 = $scope.blood_1 - 3;
10980      *                 $scope.blood_2 = $scope.blood_2 - 4;
10981      *               } else {
10982      *                 $scope.stopFight();
10983      *               }
10984      *             }, 100);
10985      *           };
10986      *
10987      *           $scope.stopFight = function() {
10988      *             if (angular.isDefined(stop)) {
10989      *               $interval.cancel(stop);
10990      *               stop = undefined;
10991      *             }
10992      *           };
10993      *
10994      *           $scope.resetFight = function() {
10995      *             $scope.blood_1 = 100;
10996      *             $scope.blood_2 = 120;
10997      *           };
10998      *
10999      *           $scope.$on('$destroy', function() {
11000      *             // Make sure that the interval is destroyed too
11001      *             $scope.stopFight();
11002      *           });
11003      *         }])
11004      *       // Register the 'myCurrentTime' directive factory method.
11005      *       // We inject $interval and dateFilter service since the factory method is DI.
11006      *       .directive('myCurrentTime', ['$interval', 'dateFilter',
11007      *         function($interval, dateFilter) {
11008      *           // return the directive link function. (compile function not needed)
11009      *           return function(scope, element, attrs) {
11010      *             var format,  // date format
11011      *                 stopTime; // so that we can cancel the time updates
11012      *
11013      *             // used to update the UI
11014      *             function updateTime() {
11015      *               element.text(dateFilter(new Date(), format));
11016      *             }
11017      *
11018      *             // watch the expression, and update the UI on change.
11019      *             scope.$watch(attrs.myCurrentTime, function(value) {
11020      *               format = value;
11021      *               updateTime();
11022      *             });
11023      *
11024      *             stopTime = $interval(updateTime, 1000);
11025      *
11026      *             // listen on DOM destroy (removal) event, and cancel the next UI update
11027      *             // to prevent updating time after the DOM element was removed.
11028      *             element.on('$destroy', function() {
11029      *               $interval.cancel(stopTime);
11030      *             });
11031      *           }
11032      *         }]);
11033      *   </script>
11034      *
11035      *   <div>
11036      *     <div ng-controller="ExampleController">
11037      *       <label>Date format: <input ng-model="format"></label> <hr/>
11038      *       Current time is: <span my-current-time="format"></span>
11039      *       <hr/>
11040      *       Blood 1 : <font color='red'>{{blood_1}}</font>
11041      *       Blood 2 : <font color='red'>{{blood_2}}</font>
11042      *       <button type="button" data-ng-click="fight()">Fight</button>
11043      *       <button type="button" data-ng-click="stopFight()">StopFight</button>
11044      *       <button type="button" data-ng-click="resetFight()">resetFight</button>
11045      *     </div>
11046      *   </div>
11047      *
11048      * </file>
11049      * </example>
11050      */
11051    function interval(fn, delay, count, invokeApply) {
11052      var hasParams = arguments.length > 4,
11053          args = hasParams ? sliceArgs(arguments, 4) : [],
11054          setInterval = $window.setInterval,
11055          clearInterval = $window.clearInterval,
11056          iteration = 0,
11057          skipApply = (isDefined(invokeApply) && !invokeApply),
11058          deferred = (skipApply ? $$q : $q).defer(),
11059          promise = deferred.promise;
11060
11061      count = isDefined(count) ? count : 0;
11062
11063      promise.then(null, null, (!hasParams) ? fn : function() {
11064        fn.apply(null, args);
11065      });
11066
11067      promise.$$intervalId = setInterval(function tick() {
11068        deferred.notify(iteration++);
11069
11070        if (count > 0 && iteration >= count) {
11071          deferred.resolve(iteration);
11072          clearInterval(promise.$$intervalId);
11073          delete intervals[promise.$$intervalId];
11074        }
11075
11076        if (!skipApply) $rootScope.$apply();
11077
11078      }, delay);
11079
11080      intervals[promise.$$intervalId] = deferred;
11081
11082      return promise;
11083    }
11084
11085
11086     /**
11087      * @ngdoc method
11088      * @name $interval#cancel
11089      *
11090      * @description
11091      * Cancels a task associated with the `promise`.
11092      *
11093      * @param {promise} promise returned by the `$interval` function.
11094      * @returns {boolean} Returns `true` if the task was successfully canceled.
11095      */
11096    interval.cancel = function(promise) {
11097      if (promise && promise.$$intervalId in intervals) {
11098        intervals[promise.$$intervalId].reject('canceled');
11099        $window.clearInterval(promise.$$intervalId);
11100        delete intervals[promise.$$intervalId];
11101        return true;
11102      }
11103      return false;
11104    };
11105
11106    return interval;
11107  }];
11108}
11109
11110/**
11111 * @ngdoc service
11112 * @name $locale
11113 *
11114 * @description
11115 * $locale service provides localization rules for various Angular components. As of right now the
11116 * only public api is:
11117 *
11118 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11119 */
11120function $LocaleProvider() {
11121  this.$get = function() {
11122    return {
11123      id: 'en-us',
11124
11125      NUMBER_FORMATS: {
11126        DECIMAL_SEP: '.',
11127        GROUP_SEP: ',',
11128        PATTERNS: [
11129          { // Decimal Pattern
11130            minInt: 1,
11131            minFrac: 0,
11132            maxFrac: 3,
11133            posPre: '',
11134            posSuf: '',
11135            negPre: '-',
11136            negSuf: '',
11137            gSize: 3,
11138            lgSize: 3
11139          },{ //Currency Pattern
11140            minInt: 1,
11141            minFrac: 2,
11142            maxFrac: 2,
11143            posPre: '\u00A4',
11144            posSuf: '',
11145            negPre: '(\u00A4',
11146            negSuf: ')',
11147            gSize: 3,
11148            lgSize: 3
11149          }
11150        ],
11151        CURRENCY_SYM: '$'
11152      },
11153
11154      DATETIME_FORMATS: {
11155        MONTH:
11156            'January,February,March,April,May,June,July,August,September,October,November,December'
11157            .split(','),
11158        SHORTMONTH:  'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
11159        DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
11160        SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
11161        AMPMS: ['AM','PM'],
11162        medium: 'MMM d, y h:mm:ss a',
11163        'short': 'M/d/yy h:mm a',
11164        fullDate: 'EEEE, MMMM d, y',
11165        longDate: 'MMMM d, y',
11166        mediumDate: 'MMM d, y',
11167        shortDate: 'M/d/yy',
11168        mediumTime: 'h:mm:ss a',
11169        shortTime: 'h:mm a',
11170        ERANAMES: [
11171          "Before Christ",
11172          "Anno Domini"
11173        ],
11174        ERAS: [
11175          "BC",
11176          "AD"
11177        ]
11178      },
11179
11180      pluralCat: function(num) {
11181        if (num === 1) {
11182          return 'one';
11183        }
11184        return 'other';
11185      }
11186    };
11187  };
11188}
11189
11190var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11191    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11192var $locationMinErr = minErr('$location');
11193
11194
11195/**
11196 * Encode path using encodeUriSegment, ignoring forward slashes
11197 *
11198 * @param {string} path Path to encode
11199 * @returns {string}
11200 */
11201function encodePath(path) {
11202  var segments = path.split('/'),
11203      i = segments.length;
11204
11205  while (i--) {
11206    segments[i] = encodeUriSegment(segments[i]);
11207  }
11208
11209  return segments.join('/');
11210}
11211
11212function parseAbsoluteUrl(absoluteUrl, locationObj) {
11213  var parsedUrl = urlResolve(absoluteUrl);
11214
11215  locationObj.$$protocol = parsedUrl.protocol;
11216  locationObj.$$host = parsedUrl.hostname;
11217  locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11218}
11219
11220
11221function parseAppUrl(relativeUrl, locationObj) {
11222  var prefixed = (relativeUrl.charAt(0) !== '/');
11223  if (prefixed) {
11224    relativeUrl = '/' + relativeUrl;
11225  }
11226  var match = urlResolve(relativeUrl);
11227  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11228      match.pathname.substring(1) : match.pathname);
11229  locationObj.$$search = parseKeyValue(match.search);
11230  locationObj.$$hash = decodeURIComponent(match.hash);
11231
11232  // make sure path starts with '/';
11233  if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11234    locationObj.$$path = '/' + locationObj.$$path;
11235  }
11236}
11237
11238
11239/**
11240 *
11241 * @param {string} begin
11242 * @param {string} whole
11243 * @returns {string} returns text from whole after begin or undefined if it does not begin with
11244 *                   expected string.
11245 */
11246function beginsWith(begin, whole) {
11247  if (whole.indexOf(begin) === 0) {
11248    return whole.substr(begin.length);
11249  }
11250}
11251
11252
11253function stripHash(url) {
11254  var index = url.indexOf('#');
11255  return index == -1 ? url : url.substr(0, index);
11256}
11257
11258function trimEmptyHash(url) {
11259  return url.replace(/(#.+)|#$/, '$1');
11260}
11261
11262
11263function stripFile(url) {
11264  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11265}
11266
11267/* return the server only (scheme://host:port) */
11268function serverBase(url) {
11269  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11270}
11271
11272
11273/**
11274 * LocationHtml5Url represents an url
11275 * This object is exposed as $location service when HTML5 mode is enabled and supported
11276 *
11277 * @constructor
11278 * @param {string} appBase application base URL
11279 * @param {string} basePrefix url path prefix
11280 */
11281function LocationHtml5Url(appBase, basePrefix) {
11282  this.$$html5 = true;
11283  basePrefix = basePrefix || '';
11284  var appBaseNoFile = stripFile(appBase);
11285  parseAbsoluteUrl(appBase, this);
11286
11287
11288  /**
11289   * Parse given html5 (regular) url string into properties
11290   * @param {string} url HTML5 url
11291   * @private
11292   */
11293  this.$$parse = function(url) {
11294    var pathUrl = beginsWith(appBaseNoFile, url);
11295    if (!isString(pathUrl)) {
11296      throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11297          appBaseNoFile);
11298    }
11299
11300    parseAppUrl(pathUrl, this);
11301
11302    if (!this.$$path) {
11303      this.$$path = '/';
11304    }
11305
11306    this.$$compose();
11307  };
11308
11309  /**
11310   * Compose url and update `absUrl` property
11311   * @private
11312   */
11313  this.$$compose = function() {
11314    var search = toKeyValue(this.$$search),
11315        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11316
11317    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11318    this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11319  };
11320
11321  this.$$parseLinkUrl = function(url, relHref) {
11322    if (relHref && relHref[0] === '#') {
11323      // special case for links to hash fragments:
11324      // keep the old url and only replace the hash fragment
11325      this.hash(relHref.slice(1));
11326      return true;
11327    }
11328    var appUrl, prevAppUrl;
11329    var rewrittenUrl;
11330
11331    if ((appUrl = beginsWith(appBase, url)) !== undefined) {
11332      prevAppUrl = appUrl;
11333      if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) {
11334        rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11335      } else {
11336        rewrittenUrl = appBase + prevAppUrl;
11337      }
11338    } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) {
11339      rewrittenUrl = appBaseNoFile + appUrl;
11340    } else if (appBaseNoFile == url + '/') {
11341      rewrittenUrl = appBaseNoFile;
11342    }
11343    if (rewrittenUrl) {
11344      this.$$parse(rewrittenUrl);
11345    }
11346    return !!rewrittenUrl;
11347  };
11348}
11349
11350
11351/**
11352 * LocationHashbangUrl represents url
11353 * This object is exposed as $location service when developer doesn't opt into html5 mode.
11354 * It also serves as the base class for html5 mode fallback on legacy browsers.
11355 *
11356 * @constructor
11357 * @param {string} appBase application base URL
11358 * @param {string} hashPrefix hashbang prefix
11359 */
11360function LocationHashbangUrl(appBase, hashPrefix) {
11361  var appBaseNoFile = stripFile(appBase);
11362
11363  parseAbsoluteUrl(appBase, this);
11364
11365
11366  /**
11367   * Parse given hashbang url into properties
11368   * @param {string} url Hashbang url
11369   * @private
11370   */
11371  this.$$parse = function(url) {
11372    var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11373    var withoutHashUrl;
11374
11375    if (withoutBaseUrl.charAt(0) === '#') {
11376
11377      // The rest of the url starts with a hash so we have
11378      // got either a hashbang path or a plain hash fragment
11379      withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11380      if (isUndefined(withoutHashUrl)) {
11381        // There was no hashbang prefix so we just have a hash fragment
11382        withoutHashUrl = withoutBaseUrl;
11383      }
11384
11385    } else {
11386      // There was no hashbang path nor hash fragment:
11387      // If we are in HTML5 mode we use what is left as the path;
11388      // Otherwise we ignore what is left
11389      withoutHashUrl = this.$$html5 ? withoutBaseUrl : '';
11390    }
11391
11392    parseAppUrl(withoutHashUrl, this);
11393
11394    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11395
11396    this.$$compose();
11397
11398    /*
11399     * In Windows, on an anchor node on documents loaded from
11400     * the filesystem, the browser will return a pathname
11401     * prefixed with the drive name ('/C:/path') when a
11402     * pathname without a drive is set:
11403     *  * a.setAttribute('href', '/foo')
11404     *   * a.pathname === '/C:/foo' //true
11405     *
11406     * Inside of Angular, we're always using pathnames that
11407     * do not include drive names for routing.
11408     */
11409    function removeWindowsDriveName(path, url, base) {
11410      /*
11411      Matches paths for file protocol on windows,
11412      such as /C:/foo/bar, and captures only /foo/bar.
11413      */
11414      var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11415
11416      var firstPathSegmentMatch;
11417
11418      //Get the relative path from the input URL.
11419      if (url.indexOf(base) === 0) {
11420        url = url.replace(base, '');
11421      }
11422
11423      // The input URL intentionally contains a first path segment that ends with a colon.
11424      if (windowsFilePathExp.exec(url)) {
11425        return path;
11426      }
11427
11428      firstPathSegmentMatch = windowsFilePathExp.exec(path);
11429      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11430    }
11431  };
11432
11433  /**
11434   * Compose hashbang url and update `absUrl` property
11435   * @private
11436   */
11437  this.$$compose = function() {
11438    var search = toKeyValue(this.$$search),
11439        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11440
11441    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11442    this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11443  };
11444
11445  this.$$parseLinkUrl = function(url, relHref) {
11446    if (stripHash(appBase) == stripHash(url)) {
11447      this.$$parse(url);
11448      return true;
11449    }
11450    return false;
11451  };
11452}
11453
11454
11455/**
11456 * LocationHashbangUrl represents url
11457 * This object is exposed as $location service when html5 history api is enabled but the browser
11458 * does not support it.
11459 *
11460 * @constructor
11461 * @param {string} appBase application base URL
11462 * @param {string} hashPrefix hashbang prefix
11463 */
11464function LocationHashbangInHtml5Url(appBase, hashPrefix) {
11465  this.$$html5 = true;
11466  LocationHashbangUrl.apply(this, arguments);
11467
11468  var appBaseNoFile = stripFile(appBase);
11469
11470  this.$$parseLinkUrl = function(url, relHref) {
11471    if (relHref && relHref[0] === '#') {
11472      // special case for links to hash fragments:
11473      // keep the old url and only replace the hash fragment
11474      this.hash(relHref.slice(1));
11475      return true;
11476    }
11477
11478    var rewrittenUrl;
11479    var appUrl;
11480
11481    if (appBase == stripHash(url)) {
11482      rewrittenUrl = url;
11483    } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11484      rewrittenUrl = appBase + hashPrefix + appUrl;
11485    } else if (appBaseNoFile === url + '/') {
11486      rewrittenUrl = appBaseNoFile;
11487    }
11488    if (rewrittenUrl) {
11489      this.$$parse(rewrittenUrl);
11490    }
11491    return !!rewrittenUrl;
11492  };
11493
11494  this.$$compose = function() {
11495    var search = toKeyValue(this.$$search),
11496        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11497
11498    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11499    // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#'
11500    this.$$absUrl = appBase + hashPrefix + this.$$url;
11501  };
11502
11503}
11504
11505
11506var locationPrototype = {
11507
11508  /**
11509   * Are we in html5 mode?
11510   * @private
11511   */
11512  $$html5: false,
11513
11514  /**
11515   * Has any change been replacing?
11516   * @private
11517   */
11518  $$replace: false,
11519
11520  /**
11521   * @ngdoc method
11522   * @name $location#absUrl
11523   *
11524   * @description
11525   * This method is getter only.
11526   *
11527   * Return full url representation with all segments encoded according to rules specified in
11528   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11529   *
11530   *
11531   * ```js
11532   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11533   * var absUrl = $location.absUrl();
11534   * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11535   * ```
11536   *
11537   * @return {string} full url
11538   */
11539  absUrl: locationGetter('$$absUrl'),
11540
11541  /**
11542   * @ngdoc method
11543   * @name $location#url
11544   *
11545   * @description
11546   * This method is getter / setter.
11547   *
11548   * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
11549   *
11550   * Change path, search and hash, when called with parameter and return `$location`.
11551   *
11552   *
11553   * ```js
11554   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11555   * var url = $location.url();
11556   * // => "/some/path?foo=bar&baz=xoxo"
11557   * ```
11558   *
11559   * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
11560   * @return {string} url
11561   */
11562  url: function(url) {
11563    if (isUndefined(url)) {
11564      return this.$$url;
11565    }
11566
11567    var match = PATH_MATCH.exec(url);
11568    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
11569    if (match[2] || match[1] || url === '') this.search(match[3] || '');
11570    this.hash(match[5] || '');
11571
11572    return this;
11573  },
11574
11575  /**
11576   * @ngdoc method
11577   * @name $location#protocol
11578   *
11579   * @description
11580   * This method is getter only.
11581   *
11582   * Return protocol of current url.
11583   *
11584   *
11585   * ```js
11586   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11587   * var protocol = $location.protocol();
11588   * // => "http"
11589   * ```
11590   *
11591   * @return {string} protocol of current url
11592   */
11593  protocol: locationGetter('$$protocol'),
11594
11595  /**
11596   * @ngdoc method
11597   * @name $location#host
11598   *
11599   * @description
11600   * This method is getter only.
11601   *
11602   * Return host of current url.
11603   *
11604   * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
11605   *
11606   *
11607   * ```js
11608   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11609   * var host = $location.host();
11610   * // => "example.com"
11611   *
11612   * // given url http://user:[email protected]:8080/#/some/path?foo=bar&baz=xoxo
11613   * host = $location.host();
11614   * // => "example.com"
11615   * host = location.host;
11616   * // => "example.com:8080"
11617   * ```
11618   *
11619   * @return {string} host of current url.
11620   */
11621  host: locationGetter('$$host'),
11622
11623  /**
11624   * @ngdoc method
11625   * @name $location#port
11626   *
11627   * @description
11628   * This method is getter only.
11629   *
11630   * Return port of current url.
11631   *
11632   *
11633   * ```js
11634   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11635   * var port = $location.port();
11636   * // => 80
11637   * ```
11638   *
11639   * @return {Number} port
11640   */
11641  port: locationGetter('$$port'),
11642
11643  /**
11644   * @ngdoc method
11645   * @name $location#path
11646   *
11647   * @description
11648   * This method is getter / setter.
11649   *
11650   * Return path of current url when called without any parameter.
11651   *
11652   * Change path when called with parameter and return `$location`.
11653   *
11654   * Note: Path should always begin with forward slash (/), this method will add the forward slash
11655   * if it is missing.
11656   *
11657   *
11658   * ```js
11659   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11660   * var path = $location.path();
11661   * // => "/some/path"
11662   * ```
11663   *
11664   * @param {(string|number)=} path New path
11665   * @return {string} path
11666   */
11667  path: locationGetterSetter('$$path', function(path) {
11668    path = path !== null ? path.toString() : '';
11669    return path.charAt(0) == '/' ? path : '/' + path;
11670  }),
11671
11672  /**
11673   * @ngdoc method
11674   * @name $location#search
11675   *
11676   * @description
11677   * This method is getter / setter.
11678   *
11679   * Return search part (as object) of current url when called without any parameter.
11680   *
11681   * Change search part when called with parameter and return `$location`.
11682   *
11683   *
11684   * ```js
11685   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11686   * var searchObject = $location.search();
11687   * // => {foo: 'bar', baz: 'xoxo'}
11688   *
11689   * // set foo to 'yipee'
11690   * $location.search('foo', 'yipee');
11691   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
11692   * ```
11693   *
11694   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
11695   * hash object.
11696   *
11697   * When called with a single argument the method acts as a setter, setting the `search` component
11698   * of `$location` to the specified value.
11699   *
11700   * If the argument is a hash object containing an array of values, these values will be encoded
11701   * as duplicate search parameters in the url.
11702   *
11703   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
11704   * will override only a single search property.
11705   *
11706   * If `paramValue` is an array, it will override the property of the `search` component of
11707   * `$location` specified via the first argument.
11708   *
11709   * If `paramValue` is `null`, the property specified via the first argument will be deleted.
11710   *
11711   * If `paramValue` is `true`, the property specified via the first argument will be added with 
vendor: 18,948 bytes, lines 11711-12300
11711no
11712   * value nor trailing equal sign.
11713   *
11714   * @return {Object} If called with no arguments returns the parsed `search` object. If called with
11715   * one or more arguments returns `$location` object itself.
11716   */
11717  search: function(search, paramValue) {
11718    switch (arguments.length) {
11719      case 0:
11720        return this.$$search;
11721      case 1:
11722        if (isString(search) || isNumber(search)) {
11723          search = search.toString();
11724          this.$$search = parseKeyValue(search);
11725        } else if (isObject(search)) {
11726          search = copy(search, {});
11727          // remove object undefined or null properties
11728          forEach(search, function(value, key) {
11729            if (value == null) delete search[key];
11730          });
11731
11732          this.$$search = search;
11733        } else {
11734          throw $locationMinErr('isrcharg',
11735              'The first argument of the `$location#search()` call must be a string or an object.');
11736        }
11737        break;
11738      default:
11739        if (isUndefined(paramValue) || paramValue === null) {
11740          delete this.$$search[search];
11741        } else {
11742          this.$$search[search] = paramValue;
11743        }
11744    }
11745
11746    this.$$compose();
11747    return this;
11748  },
11749
11750  /**
11751   * @ngdoc method
11752   * @name $location#hash
11753   *
11754   * @description
11755   * This method is getter / setter.
11756   *
11757   * Return hash fragment when called without any parameter.
11758   *
11759   * Change hash fragment when called with parameter and return `$location`.
11760   *
11761   *
11762   * ```js
11763   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
11764   * var hash = $location.hash();
11765   * // => "hashValue"
11766   * ```
11767   *
11768   * @param {(string|number)=} hash New hash fragment
11769   * @return {string} hash
11770   */
11771  hash: locationGetterSetter('$$hash', function(hash) {
11772    return hash !== null ? hash.toString() : '';
11773  }),
11774
11775  /**
11776   * @ngdoc method
11777   * @name $location#replace
11778   *
11779   * @description
11780   * If called, all changes to $location during current `$digest` will be replacing current history
11781   * record, instead of adding new one.
11782   */
11783  replace: function() {
11784    this.$$replace = true;
11785    return this;
11786  }
11787};
11788
11789forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
11790  Location.prototype = Object.create(locationPrototype);
11791
11792  /**
11793   * @ngdoc method
11794   * @name $location#state
11795   *
11796   * @description
11797   * This method is getter / setter.
11798   *
11799   * Return the history state object when called without any parameter.
11800   *
11801   * Change the history state object when called with one parameter and return `$location`.
11802   * The state object is later passed to `pushState` or `replaceState`.
11803   *
11804   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
11805   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
11806   * older browsers (like IE9 or Android < 4.0), don't use this method.
11807   *
11808   * @param {object=} state State object for pushState or replaceState
11809   * @return {object} state
11810   */
11811  Location.prototype.state = function(state) {
11812    if (!arguments.length) {
11813      return this.$$state;
11814    }
11815
11816    if (Location !== LocationHtml5Url || !this.$$html5) {
11817      throw $locationMinErr('nostate', 'History API state support is available only ' +
11818        'in HTML5 mode and only in browsers supporting HTML5 History API');
11819    }
11820    // The user might modify `stateObject` after invoking `$location.state(stateObject)`
11821    // but we're changing the $$state reference to $browser.state() during the $digest
11822    // so the modification window is narrow.
11823    this.$$state = isUndefined(state) ? null : state;
11824
11825    return this;
11826  };
11827});
11828
11829
11830function locationGetter(property) {
11831  return function() {
11832    return this[property];
11833  };
11834}
11835
11836
11837function locationGetterSetter(property, preprocess) {
11838  return function(value) {
11839    if (isUndefined(value)) {
11840      return this[property];
11841    }
11842
11843    this[property] = preprocess(value);
11844    this.$$compose();
11845
11846    return this;
11847  };
11848}
11849
11850
11851/**
11852 * @ngdoc service
11853 * @name $location
11854 *
11855 * @requires $rootElement
11856 *
11857 * @description
11858 * The $location service parses the URL in the browser address bar (based on the
11859 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
11860 * available to your application. Changes to the URL in the address bar are reflected into
11861 * $location service and changes to $location are reflected into the browser address bar.
11862 *
11863 * **The $location service:**
11864 *
11865 * - Exposes the current URL in the browser address bar, so you can
11866 *   - Watch and observe the URL.
11867 *   - Change the URL.
11868 * - Synchronizes the URL with the browser when the user
11869 *   - Changes the address bar.
11870 *   - Clicks the back or forward button (or clicks a History link).
11871 *   - Clicks on a link.
11872 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
11873 *
11874 * For more information see {@link guide/$location Developer Guide: Using $location}
11875 */
11876
11877/**
11878 * @ngdoc provider
11879 * @name $locationProvider
11880 * @description
11881 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
11882 */
11883function $LocationProvider() {
11884  var hashPrefix = '',
11885      html5Mode = {
11886        enabled: false,
11887        requireBase: true,
11888        rewriteLinks: true
11889      };
11890
11891  /**
11892   * @ngdoc method
11893   * @name $locationProvider#hashPrefix
11894   * @description
11895   * @param {string=} prefix Prefix for hash part (containing path and search)
11896   * @returns {*} current value if used as getter or itself (chaining) if used as setter
11897   */
11898  this.hashPrefix = function(prefix) {
11899    if (isDefined(prefix)) {
11900      hashPrefix = prefix;
11901      return this;
11902    } else {
11903      return hashPrefix;
11904    }
11905  };
11906
11907  /**
11908   * @ngdoc method
11909   * @name $locationProvider#html5Mode
11910   * @description
11911   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
11912   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
11913   *   properties:
11914   *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
11915   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
11916   *     support `pushState`.
11917   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
11918   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
11919   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
11920   *     See the {@link guide/$location $location guide for more information}
11921   *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
11922   *     enables/disables url rewriting for relative links.
11923   *
11924   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
11925   */
11926  this.html5Mode = function(mode) {
11927    if (isBoolean(mode)) {
11928      html5Mode.enabled = mode;
11929      return this;
11930    } else if (isObject(mode)) {
11931
11932      if (isBoolean(mode.enabled)) {
11933        html5Mode.enabled = mode.enabled;
11934      }
11935
11936      if (isBoolean(mode.requireBase)) {
11937        html5Mode.requireBase = mode.requireBase;
11938      }
11939
11940      if (isBoolean(mode.rewriteLinks)) {
11941        html5Mode.rewriteLinks = mode.rewriteLinks;
11942      }
11943
11944      return this;
11945    } else {
11946      return html5Mode;
11947    }
11948  };
11949
11950  /**
11951   * @ngdoc event
11952   * @name $location#$locationChangeStart
11953   * @eventType broadcast on root scope
11954   * @description
11955   * Broadcasted before a URL will change.
11956   *
11957   * This change can be prevented by calling
11958   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
11959   * details about event object. Upon successful change
11960   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
11961   *
11962   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11963   * the browser supports the HTML5 History API.
11964   *
11965   * @param {Object} angularEvent Synthetic event object.
11966   * @param {string} newUrl New URL
11967   * @param {string=} oldUrl URL that was before it was changed.
11968   * @param {string=} newState New history state object
11969   * @param {string=} oldState History state object that was before it was changed.
11970   */
11971
11972  /**
11973   * @ngdoc event
11974   * @name $location#$locationChangeSuccess
11975   * @eventType broadcast on root scope
11976   * @description
11977   * Broadcasted after a URL was changed.
11978   *
11979   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11980   * the browser supports the HTML5 History API.
11981   *
11982   * @param {Object} angularEvent Synthetic event object.
11983   * @param {string} newUrl New URL
11984   * @param {string=} oldUrl URL that was before it was changed.
11985   * @param {string=} newState New history state object
11986   * @param {string=} oldState History state object that was before it was changed.
11987   */
11988
11989  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
11990      function($rootScope, $browser, $sniffer, $rootElement, $window) {
11991    var $location,
11992        LocationMode,
11993        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
11994        initialUrl = $browser.url(),
11995        appBase;
11996
11997    if (html5Mode.enabled) {
11998      if (!baseHref && html5Mode.requireBase) {
11999        throw $locationMinErr('nobase',
12000          "$location in HTML5 mode requires a <base> tag to be present!");
12001      }
12002      appBase = serverBase(initialUrl) + (baseHref || '/');
12003      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12004    } else {
12005      appBase = stripHash(initialUrl);
12006      LocationMode = LocationHashbangUrl;
12007    }
12008    $location = new LocationMode(appBase, '#' + hashPrefix);
12009    $location.$$parseLinkUrl(initialUrl, initialUrl);
12010
12011    $location.$$state = $browser.state();
12012
12013    var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12014
12015    function setBrowserUrlWithFallback(url, replace, state) {
12016      var oldUrl = $location.url();
12017      var oldState = $location.$$state;
12018      try {
12019        $browser.url(url, replace, state);
12020
12021        // Make sure $location.state() returns referentially identical (not just deeply equal)
12022        // state object; this makes possible quick checking if the state changed in the digest
12023        // loop. Checking deep equality would be too expensive.
12024        $location.$$state = $browser.state();
12025      } catch (e) {
12026        // Restore old values if pushState fails
12027        $location.url(oldUrl);
12028        $location.$$state = oldState;
12029
12030        throw e;
12031      }
12032    }
12033
12034    $rootElement.on('click', function(event) {
12035      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12036      // currently we open nice url link and redirect then
12037
12038      if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12039
12040      var elm = jqLite(event.target);
12041
12042      // traverse the DOM up to find first A tag
12043      while (nodeName_(elm[0]) !== 'a') {
12044        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12045        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12046      }
12047
12048      var absHref = elm.prop('href');
12049      // get the actual href attribute - see
12050      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12051      var relHref = elm.attr('href') || elm.attr('xlink:href');
12052
12053      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12054        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12055        // an animation.
12056        absHref = urlResolve(absHref.animVal).href;
12057      }
12058
12059      // Ignore when url is started with javascript: or mailto:
12060      if (IGNORE_URI_REGEXP.test(absHref)) return;
12061
12062      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12063        if ($location.$$parseLinkUrl(absHref, relHref)) {
12064          // We do a preventDefault for all urls that are part of the angular application,
12065          // in html5mode and also without, so that we are able to abort navigation without
12066          // getting double entries in the location history.
12067          event.preventDefault();
12068          // update location manually
12069          if ($location.absUrl() != $browser.url()) {
12070            $rootScope.$apply();
12071            // hack to work around FF6 bug 684208 when scenario runner clicks on links
12072            $window.angular['ff-684208-preventDefault'] = true;
12073          }
12074        }
12075      }
12076    });
12077
12078
12079    // rewrite hashbang url <> html5 url
12080    if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12081      $browser.url($location.absUrl(), true);
12082    }
12083
12084    var initializing = true;
12085
12086    // update $location when $browser url changes
12087    $browser.onUrlChange(function(newUrl, newState) {
12088      $rootScope.$evalAsync(function() {
12089        var oldUrl = $location.absUrl();
12090        var oldState = $location.$$state;
12091        var defaultPrevented;
12092
12093        $location.$$parse(newUrl);
12094        $location.$$state = newState;
12095
12096        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12097            newState, oldState).defaultPrevented;
12098
12099        // if the location was changed by a `$locationChangeStart` handler then stop
12100        // processing this location change
12101        if ($location.absUrl() !== newUrl) return;
12102
12103        if (defaultPrevented) {
12104          $location.$$parse(oldUrl);
12105          $location.$$state = oldState;
12106          setBrowserUrlWithFallback(oldUrl, false, oldState);
12107        } else {
12108          initializing = false;
12109          afterLocationChange(oldUrl, oldState);
12110        }
12111      });
12112      if (!$rootScope.$$phase) $rootScope.$digest();
12113    });
12114
12115    // update browser
12116    $rootScope.$watch(function $locationWatch() {
12117      var oldUrl = trimEmptyHash($browser.url());
12118      var newUrl = trimEmptyHash($location.absUrl());
12119      var oldState = $browser.state();
12120      var currentReplace = $location.$$replace;
12121      var urlOrStateChanged = oldUrl !== newUrl ||
12122        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12123
12124      if (initializing || urlOrStateChanged) {
12125        initializing = false;
12126
12127        $rootScope.$evalAsync(function() {
12128          var newUrl = $location.absUrl();
12129          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12130              $location.$$state, oldState).defaultPrevented;
12131
12132          // if the location was changed by a `$locationChangeStart` handler then stop
12133          // processing this location change
12134          if ($location.absUrl() !== newUrl) return;
12135
12136          if (defaultPrevented) {
12137            $location.$$parse(oldUrl);
12138            $location.$$state = oldState;
12139          } else {
12140            if (urlOrStateChanged) {
12141              setBrowserUrlWithFallback(newUrl, currentReplace,
12142                                        oldState === $location.$$state ? null : $location.$$state);
12143            }
12144            afterLocationChange(oldUrl, oldState);
12145          }
12146        });
12147      }
12148
12149      $location.$$replace = false;
12150
12151      // we don't need to return anything because $evalAsync will make the digest loop dirty when
12152      // there is a change
12153    });
12154
12155    return $location;
12156
12157    function afterLocationChange(oldUrl, oldState) {
12158      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12159        $location.$$state, oldState);
12160    }
12161}];
12162}
12163
12164/**
12165 * @ngdoc service
12166 * @name $log
12167 * @requires $window
12168 *
12169 * @description
12170 * Simple service for logging. Default implementation safely writes the message
12171 * into the browser's console (if present).
12172 *
12173 * The main purpose of this service is to simplify debugging and troubleshooting.
12174 *
12175 * The default is to log `debug` messages. You can use
12176 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12177 *
12178 * @example
12179   <example module="logExample">
12180     <file name="script.js">
12181       angular.module('logExample', [])
12182         .controller('LogController', ['$scope', '$log', function($scope, $log) {
12183           $scope.$log = $log;
12184           $scope.message = 'Hello World!';
12185         }]);
12186     </file>
12187     <file name="index.html">
12188       <div ng-controller="LogController">
12189         <p>Reload this page with open console, enter text and hit the log button...</p>
12190         <label>Message:
12191         <input type="text" ng-model="message" /></label>
12192         <button ng-click="$log.log(message)">log</button>
12193         <button ng-click="$log.warn(message)">warn</button>
12194         <button ng-click="$log.info(message)">info</button>
12195         <button ng-click="$log.error(message)">error</button>
12196         <button ng-click="$log.debug(message)">debug</button>
12197       </div>
12198     </file>
12199   </example>
12200 */
12201
12202/**
12203 * @ngdoc provider
12204 * @name $logProvider
12205 * @description
12206 * Use the `$logProvider` to configure how the application logs messages
12207 */
12208function $LogProvider() {
12209  var debug = true,
12210      self = this;
12211
12212  /**
12213   * @ngdoc method
12214   * @name $logProvider#debugEnabled
12215   * @description
12216   * @param {boolean=} flag enable or disable debug level messages
12217   * @returns {*} current value if used as getter or itself (chaining) if used as setter
12218   */
12219  this.debugEnabled = function(flag) {
12220    if (isDefined(flag)) {
12221      debug = flag;
12222    return this;
12223    } else {
12224      return debug;
12225    }
12226  };
12227
12228  this.$get = ['$window', function($window) {
12229    return {
12230      /**
12231       * @ngdoc method
12232       * @name $log#log
12233       *
12234       * @description
12235       * Write a log message
12236       */
12237      log: consoleLog('log'),
12238
12239      /**
12240       * @ngdoc method
12241       * @name $log#info
12242       *
12243       * @description
12244       * Write an information message
12245       */
12246      info: consoleLog('info'),
12247
12248      /**
12249       * @ngdoc method
12250       * @name $log#warn
12251       *
12252       * @description
12253       * Write a warning message
12254       */
12255      warn: consoleLog('warn'),
12256
12257      /**
12258       * @ngdoc method
12259       * @name $log#error
12260       *
12261       * @description
12262       * Write an error message
12263       */
12264      error: consoleLog('error'),
12265
12266      /**
12267       * @ngdoc method
12268       * @name $log#debug
12269       *
12270       * @description
12271       * Write a debug message
12272       */
12273      debug: (function() {
12274        var fn = consoleLog('debug');
12275
12276        return function() {
12277          if (debug) {
12278            fn.apply(self, arguments);
12279          }
12280        };
12281      }())
12282    };
12283
12284    function formatError(arg) {
12285      if (arg instanceof Error) {
12286        if (arg.stack) {
12287          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12288              ? 'Error: ' + arg.message + '\n' + arg.stack
12289              : arg.stack;
12290        } else if (arg.sourceURL) {
12291          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12292        }
12293      }
12294      return arg;
12295    }
12296
12297    function consoleLog(type) {
12298      var console = $window.console || {},
12299          logFn = console[type] || console.log || noop,
12300          hasApply = false;
12301
12302      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12303      // The reason behind this is that console.log has type "object" in IE8...
12304      try {
12305        hasApply = !!logFn.apply;
12306      } catch (e) {}
12307
12308      if (hasApply) {
12309        return function() {
12310          var args = [];
12311          forEach(arguments, function(arg) {
12312            args.push(formatError(arg));
12313          });
12314          return logFn.apply(console, args);
12315        };
12316      }
12317
12318      // we are IE which either doesn't have window.console => this is noop and we do nothing,
12319      // or we are IE where console.log doesn't have apply so we log at least first 2 args
12320      return function(arg1, arg2) {
12321        logFn(arg1, arg2 == null ? '' : arg2);
12322      };
12323    }
12324  }];
12325}
12326
12327/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12328 *     Any commits to this file should be reviewed with security in mind.  *
12329 *   Changes to this file can potentially create security vulnerabilities. *
12330 *          An approval from 2 Core members with history of modifying      *
12331 *                         this file is required.                          *
12332 *                                                                         *
12333 *  Does the change somehow allow for arbitrary javascript to be executed? *
12334 *    Or allows for someone to change the prototype of built-in objects?   *
12335 *     Or gives undesired access to variables likes document or window?    *
12336 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12337
12338var $parseMinErr = minErr('$parse');
12339
12340// Sandboxing Angular Expressions
12341// ------------------------------
12342// Angular expressions are generally considered safe because these expressions only have direct
12343// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12344// obtaining a reference to native JS functions such as the Function constructor.
12345//
12346// As an example, consider the following Angular expression:
12347//
12348//   {}.toString.constructor('alert("evil JS code")')
12349//
12350// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12351// against the expression language, but not to prevent exploits that were enabled by exposing
12352// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12353// practice and therefore we are not even trying to protect against interaction with an object
12354// explicitly exposed in this way.
12355//
12356// In general, it is not possible to access a Window object from an angular expression unless a
12357// window or some DOM object that has a reference to window is published onto a Scope.
12358// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12359// native objects.
12360//
12361// See https://docs.angularjs.org/guide/security
12362
12363
12364function ensureSafeMemberName(name, fullExpression) {
12365  if (name === "__defineGetter__" || name === "__defineSetter__"
12366      || name === "__lookupGetter__" || name === "__lookupSetter__"
12367      || name === "__proto__") {
12368    throw $parseMinErr('isecfld',
12369        'Attempting to access a disallowed field in Angular expressions! '
12370        + 'Expression: {0}', fullExpression);
12371  }
12372  return name;
12373}
12374
12375function ensureSafeObject(obj, fullExpression) {
12376  // nifty check if obj is Function that is fast and works across iframes and other contexts
12377  if (obj) {
12378    if (obj.constructor === obj) {
12379      throw $parseMinErr('isecfn',
12380          'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12381          fullExpression);
12382    } else if (// isWindow(obj)
12383        obj.window === obj) {
12384      throw $parseMinErr('isecwindow',
12385          'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12386          fullExpression);
12387    } else if (// isElement(obj)
12388        obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12389      throw $parseMinErr('isecdom',
12390          'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12391          fullExpression);
12392    } else if (// block Object so that we can't get hold of dangerous Object.* methods
12393        obj === Object) {
12394      throw $parseMinErr('isecobj',
12395          'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12396          fullExpression);
12397    }
12398  }
12399  return obj;
12400}
12401
12402var CALL = Function.prototype.call;
12403var APPLY = Function.prototype.apply;
12404var BIND = Function.prototype.bind;
12405
12406function ensureSafeFunction(obj, fullExpression) {
12407  if (obj) {
12408    if (obj.constructor === obj) {
12409      throw $parseMinErr('isecfn',
12410        'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12411        fullExpression);
12412    } else if (obj === CALL || obj === APPLY || obj === BIND) {
12413      throw $parseMinErr('isecff',
12414        'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12415        fullExpression);
12416    }
12417  }
12418}
12419
12420var OPERATORS = createMap();
12421forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12422var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12423
12424
vendor: 47,196 bytes, lines 12425-13910
12425/////////////////////////////////////////
12426
12427
12428/**
12429 * @constructor
12430 */
12431var Lexer = function(options) {
12432  this.options = options;
12433};
12434
12435Lexer.prototype = {
12436  constructor: Lexer,
12437
12438  lex: function(text) {
12439    this.text = text;
12440    this.index = 0;
12441    this.tokens = [];
12442
12443    while (this.index < this.text.length) {
12444      var ch = this.text.charAt(this.index);
12445      if (ch === '"' || ch === "'") {
12446        this.readString(ch);
12447      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12448        this.readNumber();
12449      } else if (this.isIdent(ch)) {
12450        this.readIdent();
12451      } else if (this.is(ch, '(){}[].,;:?')) {
12452        this.tokens.push({index: this.index, text: ch});
12453        this.index++;
12454      } else if (this.isWhitespace(ch)) {
12455        this.index++;
12456      } else {
12457        var ch2 = ch + this.peek();
12458        var ch3 = ch2 + this.peek(2);
12459        var op1 = OPERATORS[ch];
12460        var op2 = OPERATORS[ch2];
12461        var op3 = OPERATORS[ch3];
12462        if (op1 || op2 || op3) {
12463          var token = op3 ? ch3 : (op2 ? ch2 : ch);
12464          this.tokens.push({index: this.index, text: token, operator: true});
12465          this.index += token.length;
12466        } else {
12467          this.throwError('Unexpected next character ', this.index, this.index + 1);
12468        }
12469      }
12470    }
12471    return this.tokens;
12472  },
12473
12474  is: function(ch, chars) {
12475    return chars.indexOf(ch) !== -1;
12476  },
12477
12478  peek: function(i) {
12479    var num = i || 1;
12480    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12481  },
12482
12483  isNumber: function(ch) {
12484    return ('0' <= ch && ch <= '9') && typeof ch === "string";
12485  },
12486
12487  isWhitespace: function(ch) {
12488    // IE treats non-breaking space as \u00A0
12489    return (ch === ' ' || ch === '\r' || ch === '\t' ||
12490            ch === '\n' || ch === '\v' || ch === '\u00A0');
12491  },
12492
12493  isIdent: function(ch) {
12494    return ('a' <= ch && ch <= 'z' ||
12495            'A' <= ch && ch <= 'Z' ||
12496            '_' === ch || ch === '$');
12497  },
12498
12499  isExpOperator: function(ch) {
12500    return (ch === '-' || ch === '+' || this.isNumber(ch));
12501  },
12502
12503  throwError: function(error, start, end) {
12504    end = end || this.index;
12505    var colStr = (isDefined(start)
12506            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
12507            : ' ' + end);
12508    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
12509        error, colStr, this.text);
12510  },
12511
12512  readNumber: function() {
12513    var number = '';
12514    var start = this.index;
12515    while (this.index < this.text.length) {
12516      var ch = lowercase(this.text.charAt(this.index));
12517      if (ch == '.' || this.isNumber(ch)) {
12518        number += ch;
12519      } else {
12520        var peekCh = this.peek();
12521        if (ch == 'e' && this.isExpOperator(peekCh)) {
12522          number += ch;
12523        } else if (this.isExpOperator(ch) &&
12524            peekCh && this.isNumber(peekCh) &&
12525            number.charAt(number.length - 1) == 'e') {
12526          number += ch;
12527        } else if (this.isExpOperator(ch) &&
12528            (!peekCh || !this.isNumber(peekCh)) &&
12529            number.charAt(number.length - 1) == 'e') {
12530          this.throwError('Invalid exponent');
12531        } else {
12532          break;
12533        }
12534      }
12535      this.index++;
12536    }
12537    this.tokens.push({
12538      index: start,
12539      text: number,
12540      constant: true,
12541      value: Number(number)
12542    });
12543  },
12544
12545  readIdent: function() {
12546    var start = this.index;
12547    while (this.index < this.text.length) {
12548      var ch = this.text.charAt(this.index);
12549      if (!(this.isIdent(ch) || this.isNumber(ch))) {
12550        break;
12551      }
12552      this.index++;
12553    }
12554    this.tokens.push({
12555      index: start,
12556      text: this.text.slice(start, this.index),
12557      identifier: true
12558    });
12559  },
12560
12561  readString: function(quote) {
12562    var start = this.index;
12563    this.index++;
12564    var string = '';
12565    var rawString = quote;
12566    var escape = false;
12567    while (this.index < this.text.length) {
12568      var ch = this.text.charAt(this.index);
12569      rawString += ch;
12570      if (escape) {
12571        if (ch === 'u') {
12572          var hex = this.text.substring(this.index + 1, this.index + 5);
12573          if (!hex.match(/[\da-f]{4}/i)) {
12574            this.throwError('Invalid unicode escape [\\u' + hex + ']');
12575          }
12576          this.index += 4;
12577          string += String.fromCharCode(parseInt(hex, 16));
12578        } else {
12579          var rep = ESCAPE[ch];
12580          string = string + (rep || ch);
12581        }
12582        escape = false;
12583      } else if (ch === '\\') {
12584        escape = true;
12585      } else if (ch === quote) {
12586        this.index++;
12587        this.tokens.push({
12588          index: start,
12589          text: rawString,
12590          constant: true,
12591          value: string
12592        });
12593        return;
12594      } else {
12595        string += ch;
12596      }
12597      this.index++;
12598    }
12599    this.throwError('Unterminated quote', start);
12600  }
12601};
12602
12603var AST = function(lexer, options) {
12604  this.lexer = lexer;
12605  this.options = options;
12606};
12607
12608AST.Program = 'Program';
12609AST.ExpressionStatement = 'ExpressionStatement';
12610AST.AssignmentExpression = 'AssignmentExpression';
12611AST.ConditionalExpression = 'ConditionalExpression';
12612AST.LogicalExpression = 'LogicalExpression';
12613AST.BinaryExpression = 'BinaryExpression';
12614AST.UnaryExpression = 'UnaryExpression';
12615AST.CallExpression = 'CallExpression';
12616AST.MemberExpression = 'MemberExpression';
12617AST.Identifier = 'Identifier';
12618AST.Literal = 'Literal';
12619AST.ArrayExpression = 'ArrayExpression';
12620AST.Property = 'Property';
12621AST.ObjectExpression = 'ObjectExpression';
12622AST.ThisExpression = 'ThisExpression';
12623
12624// Internal use only
12625AST.NGValueParameter = 'NGValueParameter';
12626
12627AST.prototype = {
12628  ast: function(text) {
12629    this.text = text;
12630    this.tokens = this.lexer.lex(text);
12631
12632    var value = this.program();
12633
12634    if (this.tokens.length !== 0) {
12635      this.throwError('is an unexpected token', this.tokens[0]);
12636    }
12637
12638    return value;
12639  },
12640
12641  program: function() {
12642    var body = [];
12643    while (true) {
12644      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12645        body.push(this.expressionStatement());
12646      if (!this.expect(';')) {
12647        return { type: AST.Program, body: body};
12648      }
12649    }
12650  },
12651
12652  expressionStatement: function() {
12653    return { type: AST.ExpressionStatement, expression: this.filterChain() };
12654  },
12655
12656  filterChain: function() {
12657    var left = this.expression();
12658    var token;
12659    while ((token = this.expect('|'))) {
12660      left = this.filter(left);
12661    }
12662    return left;
12663  },
12664
12665  expression: function() {
12666    return this.assignment();
12667  },
12668
12669  assignment: function() {
12670    var result = this.ternary();
12671    if (this.expect('=')) {
12672      result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
12673    }
12674    return result;
12675  },
12676
12677  ternary: function() {
12678    var test = this.logicalOR();
12679    var alternate;
12680    var consequent;
12681    if (this.expect('?')) {
12682      alternate = this.expression();
12683      if (this.consume(':')) {
12684        consequent = this.expression();
12685        return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
12686      }
12687    }
12688    return test;
12689  },
12690
12691  logicalOR: function() {
12692    var left = this.logicalAND();
12693    while (this.expect('||')) {
12694      left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
12695    }
12696    return left;
12697  },
12698
12699  logicalAND: function() {
12700    var left = this.equality();
12701    while (this.expect('&&')) {
12702      left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
12703    }
12704    return left;
12705  },
12706
12707  equality: function() {
12708    var left = this.relational();
12709    var token;
12710    while ((token = this.expect('==','!=','===','!=='))) {
12711      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
12712    }
12713    return left;
12714  },
12715
12716  relational: function() {
12717    var left = this.additive();
12718    var token;
12719    while ((token = this.expect('<', '>', '<=', '>='))) {
12720      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
12721    }
12722    return left;
12723  },
12724
12725  additive: function() {
12726    var left = this.multiplicative();
12727    var token;
12728    while ((token = this.expect('+','-'))) {
12729      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
12730    }
12731    return left;
12732  },
12733
12734  multiplicative: function() {
12735    var left = this.unary();
12736    var token;
12737    while ((token = this.expect('*','/','%'))) {
12738      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
12739    }
12740    return left;
12741  },
12742
12743  unary: function() {
12744    var token;
12745    if ((token = this.expect('+', '-', '!'))) {
12746      return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
12747    } else {
12748      return this.primary();
12749    }
12750  },
12751
12752  primary: function() {
12753    var primary;
12754    if (this.expect('(')) {
12755      primary = this.filterChain();
12756      this.consume(')');
12757    } else if (this.expect('[')) {
12758      primary = this.arrayDeclaration();
12759    } else if (this.expect('{')) {
12760      primary = this.object();
12761    } else if (this.constants.hasOwnProperty(this.peek().text)) {
12762      primary = copy(this.constants[this.consume().text]);
12763    } else if (this.peek().identifier) {
12764      primary = this.identifier();
12765    } else if (this.peek().constant) {
12766      primary = this.constant();
12767    } else {
12768      this.throwError('not a primary expression', this.peek());
12769    }
12770
12771    var next;
12772    while ((next = this.expect('(', '[', '.'))) {
12773      if (next.text === '(') {
12774        primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
12775        this.consume(')');
12776      } else if (next.text === '[') {
12777        primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
12778        this.consume(']');
12779      } else if (next.text === '.') {
12780        primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
12781      } else {
12782        this.throwError('IMPOSSIBLE');
12783      }
12784    }
12785    return primary;
12786  },
12787
12788  filter: function(baseExpression) {
12789    var args = [baseExpression];
12790    var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
12791
12792    while (this.expect(':')) {
12793      args.push(this.expression());
12794    }
12795
12796    return result;
12797  },
12798
12799  parseArguments: function() {
12800    var args = [];
12801    if (this.peekToken().text !== ')') {
12802      do {
12803        args.push(this.expression());
12804      } while (this.expect(','));
12805    }
12806    return args;
12807  },
12808
12809  identifier: function() {
12810    var token = this.consume();
12811    if (!token.identifier) {
12812      this.throwError('is not a valid identifier', token);
12813    }
12814    return { type: AST.Identifier, name: token.text };
12815  },
12816
12817  constant: function() {
12818    // TODO check that it is a constant
12819    return { type: AST.Literal, value: this.consume().value };
12820  },
12821
12822  arrayDeclaration: function() {
12823    var elements = [];
12824    if (this.peekToken().text !== ']') {
12825      do {
12826        if (this.peek(']')) {
12827          // Support trailing commas per ES5.1.
12828          break;
12829        }
12830        elements.push(this.expression());
12831      } while (this.expect(','));
12832    }
12833    this.consume(']');
12834
12835    return { type: AST.ArrayExpression, elements: elements };
12836  },
12837
12838  object: function() {
12839    var properties = [], property;
12840    if (this.peekToken().text !== '}') {
12841      do {
12842        if (this.peek('}')) {
12843          // Support trailing commas per ES5.1.
12844          break;
12845        }
12846        property = {type: AST.Property, kind: 'init'};
12847        if (this.peek().constant) {
12848          property.key = this.constant();
12849        } else if (this.peek().identifier) {
12850          property.key = this.identifier();
12851        } else {
12852          this.throwError("invalid key", this.peek());
12853        }
12854        this.consume(':');
12855        property.value = this.expression();
12856        properties.push(property);
12857      } while (this.expect(','));
12858    }
12859    this.consume('}');
12860
12861    return {type: AST.ObjectExpression, properties: properties };
12862  },
12863
12864  throwError: function(msg, token) {
12865    throw $parseMinErr('syntax',
12866        'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
12867          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
12868  },
12869
12870  consume: function(e1) {
12871    if (this.tokens.length === 0) {
12872      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
12873    }
12874
12875    var token = this.expect(e1);
12876    if (!token) {
12877      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
12878    }
12879    return token;
12880  },
12881
12882  peekToken: function() {
12883    if (this.tokens.length === 0) {
12884      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
12885    }
12886    return this.tokens[0];
12887  },
12888
12889  peek: function(e1, e2, e3, e4) {
12890    return this.peekAhead(0, e1, e2, e3, e4);
12891  },
12892
12893  peekAhead: function(i, e1, e2, e3, e4) {
12894    if (this.tokens.length > i) {
12895      var token = this.tokens[i];
12896      var t = token.text;
12897      if (t === e1 || t === e2 || t === e3 || t === e4 ||
12898          (!e1 && !e2 && !e3 && !e4)) {
12899        return token;
12900      }
12901    }
12902    return false;
12903  },
12904
12905  expect: function(e1, e2, e3, e4) {
12906    var token = this.peek(e1, e2, e3, e4);
12907    if (token) {
12908      this.tokens.shift();
12909      return token;
12910    }
12911    return false;
12912  },
12913
12914
12915  /* `undefined` is not a constant, it is an identifier,
12916   * but using it as an identifier is not supported
12917   */
12918  constants: {
12919    'true': { type: AST.Literal, value: true },
12920    'false': { type: AST.Literal, value: false },
12921    'null': { type: AST.Literal, value: null },
12922    'undefined': {type: AST.Literal, value: undefined },
12923    'this': {type: AST.ThisExpression }
12924  }
12925};
12926
12927function ifDefined(v, d) {
12928  return typeof v !== 'undefined' ? v : d;
12929}
12930
12931function plusFn(l, r) {
12932  if (typeof l === 'undefined') return r;
12933  if (typeof r === 'undefined') return l;
12934  return l + r;
12935}
12936
12937function isStateless($filter, filterName) {
12938  var fn = $filter(filterName);
12939  return !fn.$stateful;
12940}
12941
12942function findConstantAndWatchExpressions(ast, $filter) {
12943  var allConstants;
12944  var argsToWatch;
12945  switch (ast.type) {
12946  case AST.Program:
12947    allConstants = true;
12948    forEach(ast.body, function(expr) {
12949      findConstantAndWatchExpressions(expr.expression, $filter);
12950      allConstants = allConstants && expr.expression.constant;
12951    });
12952    ast.constant = allConstants;
12953    break;
12954  case AST.Literal:
12955    ast.constant = true;
12956    ast.toWatch = [];
12957    break;
12958  case AST.UnaryExpression:
12959    findConstantAndWatchExpressions(ast.argument, $filter);
12960    ast.constant = ast.argument.constant;
12961    ast.toWatch = ast.argument.toWatch;
12962    break;
12963  case AST.BinaryExpression:
12964    findConstantAndWatchExpressions(ast.left, $filter);
12965    findConstantAndWatchExpressions(ast.right, $filter);
12966    ast.constant = ast.left.constant && ast.right.constant;
12967    ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
12968    break;
12969  case AST.LogicalExpression:
12970    findConstantAndWatchExpressions(ast.left, $filter);
12971    findConstantAndWatchExpressions(ast.right, $filter);
12972    ast.constant = ast.left.constant && ast.right.constant;
12973    ast.toWatch = ast.constant ? [] : [ast];
12974    break;
12975  case AST.ConditionalExpression:
12976    findConstantAndWatchExpressions(ast.test, $filter);
12977    findConstantAndWatchExpressions(ast.alternate, $filter);
12978    findConstantAndWatchExpressions(ast.consequent, $filter);
12979    ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
12980    ast.toWatch = ast.constant ? [] : [ast];
12981    break;
12982  case AST.Identifier:
12983    ast.constant = false;
12984    ast.toWatch = [ast];
12985    break;
12986  case AST.MemberExpression:
12987    findConstantAndWatchExpressions(ast.object, $filter);
12988    if (ast.computed) {
12989      findConstantAndWatchExpressions(ast.property, $filter);
12990    }
12991    ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
12992    ast.toWatch = [ast];
12993    break;
12994  case AST.CallExpression:
12995    allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
12996    argsToWatch = [];
12997    forEach(ast.arguments, function(expr) {
12998      findConstantAndWatchExpressions(expr, $filter);
12999      allConstants = allConstants && expr.constant;
13000      if (!expr.constant) {
13001        argsToWatch.push.apply(argsToWatch, expr.toWatch);
13002      }
13003    });
13004    ast.constant = allConstants;
13005    ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13006    break;
13007  case AST.AssignmentExpression:
13008    findConstantAndWatchExpressions(ast.left, $filter);
13009    findConstantAndWatchExpressions(ast.right, $filter);
13010    ast.constant = ast.left.constant && ast.right.constant;
13011    ast.toWatch = [ast];
13012    break;
13013  case AST.ArrayExpression:
13014    allConstants = true;
13015    argsToWatch = [];
13016    forEach(ast.elements, function(expr) {
13017      findConstantAndWatchExpressions(expr, $filter);
13018      allConstants = allConstants && expr.constant;
13019      if (!expr.constant) {
13020        argsToWatch.push.apply(argsToWatch, expr.toWatch);
13021      }
13022    });
13023    ast.constant = allConstants;
13024    ast.toWatch = argsToWatch;
13025    break;
13026  case AST.ObjectExpression:
13027    allConstants = true;
13028    argsToWatch = [];
13029    forEach(ast.properties, function(property) {
13030      findConstantAndWatchExpressions(property.value, $filter);
13031      allConstants = allConstants && property.value.constant;
13032      if (!property.value.constant) {
13033        argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13034      }
13035    });
13036    ast.constant = allConstants;
13037    ast.toWatch = argsToWatch;
13038    break;
13039  case AST.ThisExpression:
13040    ast.constant = false;
13041    ast.toWatch = [];
13042    break;
13043  }
13044}
13045
13046function getInputs(body) {
13047  if (body.length != 1) return;
13048  var lastExpression = body[0].expression;
13049  var candidate = lastExpression.toWatch;
13050  if (candidate.length !== 1) return candidate;
13051  return candidate[0] !== lastExpression ? candidate : undefined;
13052}
13053
13054function isAssignable(ast) {
13055  return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13056}
13057
13058function assignableAST(ast) {
13059  if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13060    return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13061  }
13062}
13063
13064function isLiteral(ast) {
13065  return ast.body.length === 0 ||
13066      ast.body.length === 1 && (
13067      ast.body[0].expression.type === AST.Literal ||
13068      ast.body[0].expression.type === AST.ArrayExpression ||
13069      ast.body[0].expression.type === AST.ObjectExpression);
13070}
13071
13072function isConstant(ast) {
13073  return ast.constant;
13074}
13075
13076function ASTCompiler(astBuilder, $filter) {
13077  this.astBuilder = astBuilder;
13078  this.$filter = $filter;
13079}
13080
13081ASTCompiler.prototype = {
13082  compile: function(expression, expensiveChecks) {
13083    var self = this;
13084    var ast = this.astBuilder.ast(expression);
13085    this.state = {
13086      nextId: 0,
13087      filters: {},
13088      expensiveChecks: expensiveChecks,
13089      fn: {vars: [], body: [], own: {}},
13090      assign: {vars: [], body: [], own: {}},
13091      inputs: []
13092    };
13093    findConstantAndWatchExpressions(ast, self.$filter);
13094    var extra = '';
13095    var assignable;
13096    this.stage = 'assign';
13097    if ((assignable = assignableAST(ast))) {
13098      this.state.computing = 'assign';
13099      var result = this.nextId();
13100      this.recurse(assignable, result);
13101      extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13102    }
13103    var toWatch = getInputs(ast.body);
13104    self.stage = 'inputs';
13105    forEach(toWatch, function(watch, key) {
13106      var fnKey = 'fn' + key;
13107      self.state[fnKey] = {vars: [], body: [], own: {}};
13108      self.state.computing = fnKey;
13109      var intoId = self.nextId();
13110      self.recurse(watch, intoId);
13111      self.return_(intoId);
13112      self.state.inputs.push(fnKey);
13113      watch.watchId = key;
13114    });
13115    this.state.computing = 'fn';
13116    this.stage = 'main';
13117    this.recurse(ast);
13118    var fnString =
13119      // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13120      // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13121      '"' + this.USE + ' ' + this.STRICT + '";\n' +
13122      this.filterPrefix() +
13123      'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13124      extra +
13125      this.watchFns() +
13126      'return fn;';
13127
13128    /* jshint -W054 */
13129    var fn = (new Function('$filter',
13130        'ensureSafeMemberName',
13131        'ensureSafeObject',
13132        'ensureSafeFunction',
13133        'ifDefined',
13134        'plus',
13135        'text',
13136        fnString))(
13137          this.$filter,
13138          ensureSafeMemberName,
13139          ensureSafeObject,
13140          ensureSafeFunction,
13141          ifDefined,
13142          plusFn,
13143          expression);
13144    /* jshint +W054 */
13145    this.state = this.stage = undefined;
13146    fn.literal = isLiteral(ast);
13147    fn.constant = isConstant(ast);
13148    return fn;
13149  },
13150
13151  USE: 'use',
13152
13153  STRICT: 'strict',
13154
13155  watchFns: function() {
13156    var result = [];
13157    var fns = this.state.inputs;
13158    var self = this;
13159    forEach(fns, function(name) {
13160      result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13161    });
13162    if (fns.length) {
13163      result.push('fn.inputs=[' + fns.join(',') + '];');
13164    }
13165    return result.join('');
13166  },
13167
13168  generateFunction: function(name, params) {
13169    return 'function(' + params + '){' +
13170        this.varsPrefix(name) +
13171        this.body(name) +
13172        '};';
13173  },
13174
13175  filterPrefix: function() {
13176    var parts = [];
13177    var self = this;
13178    forEach(this.state.filters, function(id, filter) {
13179      parts.push(id + '=$filter(' + self.escape(filter) + ')');
13180    });
13181    if (parts.length) return 'var ' + parts.join(',') + ';';
13182    return '';
13183  },
13184
13185  varsPrefix: function(section) {
13186    return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13187  },
13188
13189  body: function(section) {
13190    return this.state[section].body.join('');
13191  },
13192
13193  recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13194    var left, right, self = this, args, expression;
13195    recursionFn = recursionFn || noop;
13196    if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13197      intoId = intoId || this.nextId();
13198      this.if_('i',
13199        this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13200        this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13201      );
13202      return;
13203    }
13204    switch (ast.type) {
13205    case AST.Program:
13206      forEach(ast.body, function(expression, pos) {
13207        self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13208        if (pos !== ast.body.length - 1) {
13209          self.current().body.push(right, ';');
13210        } else {
13211          self.return_(right);
13212        }
13213      });
13214      break;
13215    case AST.Literal:
13216      expression = this.escape(ast.value);
13217      this.assign(intoId, expression);
13218      recursionFn(expression);
13219      break;
13220    case AST.UnaryExpression:
13221      this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13222      expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13223      this.assign(intoId, expression);
13224      recursionFn(expression);
13225      break;
13226    case AST.BinaryExpression:
13227      this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13228      this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13229      if (ast.operator === '+') {
13230        expression = this.plus(left, right);
13231      } else if (ast.operator === '-') {
13232        expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13233      } else {
13234        expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13235      }
13236      this.assign(intoId, expression);
13237      recursionFn(expression);
13238      break;
13239    case AST.LogicalExpression:
13240      intoId = intoId || this.nextId();
13241      self.recurse(ast.left, intoId);
13242      self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13243      recursionFn(intoId);
13244      break;
13245    case AST.ConditionalExpression:
13246      intoId = intoId || this.nextId();
13247      self.recurse(ast.test, intoId);
13248      self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13249      recursionFn(intoId);
13250      break;
13251    case AST.Identifier:
13252      intoId = intoId || this.nextId();
13253      if (nameId) {
13254        nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13255        nameId.computed = false;
13256        nameId.name = ast.name;
13257      }
13258      ensureSafeMemberName(ast.name);
13259      self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13260        function() {
13261          self.if_(self.stage === 'inputs' || 's', function() {
13262            if (create && create !== 1) {
13263              self.if_(
13264                self.not(self.nonComputedMember('s', ast.name)),
13265                self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13266            }
13267            self.assign(intoId, self.nonComputedMember('s', ast.name));
13268          });
13269        }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13270        );
13271      if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13272        self.addEnsureSafeObject(intoId);
13273      }
13274      recursionFn(intoId);
13275      break;
13276    case AST.MemberExpression:
13277      left = nameId && (nameId.context = this.nextId()) || this.nextId();
13278      intoId = intoId || this.nextId();
13279      self.recurse(ast.object, left, undefined, function() {
13280        self.if_(self.notNull(left), function() {
13281          if (ast.computed) {
13282            right = self.nextId();
13283            self.recurse(ast.property, right);
13284            self.addEnsureSafeMemberName(right);
13285            if (create && create !== 1) {
13286              self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13287            }
13288            expression = self.ensureSafeObject(self.computedMember(left, right));
13289            self.assign(intoId, expression);
13290            if (nameId) {
13291              nameId.computed = true;
13292              nameId.name = right;
13293            }
13294          } else {
13295            ensureSafeMemberName(ast.property.name);
13296            if (create && create !== 1) {
13297              self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13298            }
13299            expression = self.nonComputedMember(left, ast.property.name);
13300            if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13301              expression = self.ensureSafeObject(expression);
13302            }
13303            self.assign(intoId, expression);
13304            if (nameId) {
13305              nameId.computed = false;
13306              nameId.name = ast.property.name;
13307            }
13308          }
13309        }, function() {
13310          self.assign(intoId, 'undefined');
13311        });
13312        recursionFn(intoId);
13313      }, !!create);
13314      break;
13315    case AST.CallExpression:
13316      intoId = intoId || this.nextId();
13317      if (ast.filter) {
13318        right = self.filter(ast.callee.name);
13319        args = [];
13320        forEach(ast.arguments, function(expr) {
13321          var argument = self.nextId();
13322          self.recurse(expr, argument);
13323          args.push(argument);
13324        });
13325        expression = right + '(' + args.join(',') + ')';
13326        self.assign(intoId, expression);
13327        recursionFn(intoId);
13328      } else {
13329        right = self.nextId();
13330        left = {};
13331        args = [];
13332        self.recurse(ast.callee, right, left, function() {
13333          self.if_(self.notNull(right), function() {
13334            self.addEnsureSafeFunction(right);
13335            forEach(ast.arguments, function(expr) {
13336              self.recurse(expr, self.nextId(), undefined, function(argument) {
13337                args.push(self.ensureSafeObject(argument));
13338              });
13339            });
13340            if (left.name) {
13341              if (!self.state.expensiveChecks) {
13342                self.addEnsureSafeObject(left.context);
13343              }
13344              expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13345            } else {
13346              expression = right + '(' + args.join(',') + ')';
13347            }
13348            expression = self.ensureSafeObject(expression);
13349            self.assign(intoId, expression);
13350          }, function() {
13351            self.assign(intoId, 'undefined');
13352          });
13353          recursionFn(intoId);
13354        });
13355      }
13356      break;
13357    case AST.AssignmentExpression:
13358      right = this.nextId();
13359      left = {};
13360      if (!isAssignable(ast.left)) {
13361        throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
13362      }
13363      this.recurse(ast.left, undefined, left, function() {
13364        self.if_(self.notNull(left.context), function() {
13365          self.recurse(ast.right, right);
13366          self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13367          expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13368          self.assign(intoId, expression);
13369          recursionFn(intoId || expression);
13370        });
13371      }, 1);
13372      break;
13373    case AST.ArrayExpression:
13374      args = [];
13375      forEach(ast.elements, function(expr) {
13376        self.recurse(expr, self.nextId(), undefined, function(argument) {
13377          args.push(argument);
13378        });
13379      });
13380      expression = '[' + args.join(',') + ']';
13381      this.assign(intoId, expression);
13382      recursionFn(expression);
13383      break;
13384    case AST.ObjectExpression:
13385      args = [];
13386      forEach(ast.properties, function(property) {
13387        self.recurse(property.value, self.nextId(), undefined, function(expr) {
13388          args.push(self.escape(
13389              property.key.type === AST.Identifier ? property.key.name :
13390                ('' + property.key.value)) +
13391              ':' + expr);
13392        });
13393      });
13394      expression = '{' + args.join(',') + '}';
13395      this.assign(intoId, expression);
13396      recursionFn(expression);
13397      break;
13398    case AST.ThisExpression:
13399      this.assign(intoId, 's');
13400      recursionFn('s');
13401      break;
13402    case AST.NGValueParameter:
13403      this.assign(intoId, 'v');
13404      recursionFn('v');
13405      break;
13406    }
13407  },
13408
13409  getHasOwnProperty: function(element, property) {
13410    var key = element + '.' + property;
13411    var own = this.current().own;
13412    if (!own.hasOwnProperty(key)) {
13413      own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13414    }
13415    return own[key];
13416  },
13417
13418  assign: function(id, value) {
13419    if (!id) return;
13420    this.current().body.push(id, '=', value, ';');
13421    return id;
13422  },
13423
13424  filter: function(filterName) {
13425    if (!this.state.filters.hasOwnProperty(filterName)) {
13426      this.state.filters[filterName] = this.nextId(true);
13427    }
13428    return this.state.filters[filterName];
13429  },
13430
13431  ifDefined: function(id, defaultValue) {
13432    return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13433  },
13434
13435  plus: function(left, right) {
13436    return 'plus(' + left + ',' + right + ')';
13437  },
13438
13439  return_: function(id) {
13440    this.current().body.push('return ', id, ';');
13441  },
13442
13443  if_: function(test, alternate, consequent) {
13444    if (test === true) {
13445      alternate();
13446    } else {
13447      var body = this.current().body;
13448      body.push('if(', test, '){');
13449      alternate();
13450      body.push('}');
13451      if (consequent) {
13452        body.push('else{');
13453        consequent();
13454        body.push('}');
13455      }
13456    }
13457  },
13458
13459  not: function(expression) {
13460    return '!(' + expression + ')';
13461  },
13462
13463  notNull: function(expression) {
13464    return expression + '!=null';
13465  },
13466
13467  nonComputedMember: function(left, right) {
13468    return left + '.' + right;
13469  },
13470
13471  computedMember: function(left, right) {
13472    return left + '[' + right + ']';
13473  },
13474
13475  member: function(left, right, computed) {
13476    if (computed) return this.computedMember(left, right);
13477    return this.nonComputedMember(left, right);
13478  },
13479
13480  addEnsureSafeObject: function(item) {
13481    this.current().body.push(this.ensureSafeObject(item), ';');
13482  },
13483
13484  addEnsureSafeMemberName: function(item) {
13485    this.current().body.push(this.ensureSafeMemberName(item), ';');
13486  },
13487
13488  addEnsureSafeFunction: function(item) {
13489    this.current().body.push(this.ensureSafeFunction(item), ';');
13490  },
13491
13492  ensureSafeObject: function(item) {
13493    return 'ensureSafeObject(' + item + ',text)';
13494  },
13495
13496  ensureSafeMemberName: function(item) {
13497    return 'ensureSafeMemberName(' + item + ',text)';
13498  },
13499
13500  ensureSafeFunction: function(item) {
13501    return 'ensureSafeFunction(' + item + ',text)';
13502  },
13503
13504  lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13505    var self = this;
13506    return function() {
13507      self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
13508    };
13509  },
13510
13511  lazyAssign: function(id, value) {
13512    var self = this;
13513    return function() {
13514      self.assign(id, value);
13515    };
13516  },
13517
13518  stringEscapeRegex: /[^ a-zA-Z0-9]/g,
13519
13520  stringEscapeFn: function(c) {
13521    return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
13522  },
13523
13524  escape: function(value) {
13525    if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
13526    if (isNumber(value)) return value.toString();
13527    if (value === true) return 'true';
13528    if (value === false) return 'false';
13529    if (value === null) return 'null';
13530    if (typeof value === 'undefined') return 'undefined';
13531
13532    throw $parseMinErr('esc', 'IMPOSSIBLE');
13533  },
13534
13535  nextId: function(skip, init) {
13536    var id = 'v' + (this.state.nextId++);
13537    if (!skip) {
13538      this.current().vars.push(id + (init ? '=' + init : ''));
13539    }
13540    return id;
13541  },
13542
13543  current: function() {
13544    return this.state[this.state.computing];
13545  }
13546};
13547
13548
13549function ASTInterpreter(astBuilder, $filter) {
13550  this.astBuilder = astBuilder;
13551  this.$filter = $filter;
13552}
13553
13554ASTInterpreter.prototype = {
13555  compile: function(expression, expensiveChecks) {
13556    var self = this;
13557    var ast = this.astBuilder.ast(expression);
13558    this.expression = expression;
13559    this.expensiveChecks = expensiveChecks;
13560    findConstantAndWatchExpressions(ast, self.$filter);
13561    var assignable;
13562    var assign;
13563    if ((assignable = assignableAST(ast))) {
13564      assign = this.recurse(assignable);
13565    }
13566    var toWatch = getInputs(ast.body);
13567    var inputs;
13568    if (toWatch) {
13569      inputs = [];
13570      forEach(toWatch, function(watch, key) {
13571        var input = self.recurse(watch);
13572        watch.input = input;
13573        inputs.push(input);
13574        watch.watchId = key;
13575      });
13576    }
13577    var expressions = [];
13578    forEach(ast.body, function(expression) {
13579      expressions.push(self.recurse(expression.expression));
13580    });
13581    var fn = ast.body.length === 0 ? function() {} :
13582             ast.body.length === 1 ? expressions[0] :
13583             function(scope, locals) {
13584               var lastValue;
13585               forEach(expressions, function(exp) {
13586                 lastValue = exp(scope, locals);
13587               });
13588               return lastValue;
13589             };
13590    if (assign) {
13591      fn.assign = function(scope, value, locals) {
13592        return assign(scope, locals, value);
13593      };
13594    }
13595    if (inputs) {
13596      fn.inputs = inputs;
13597    }
13598    fn.literal = isLiteral(ast);
13599    fn.constant = isConstant(ast);
13600    return fn;
13601  },
13602
13603  recurse: function(ast, context, create) {
13604    var left, right, self = this, args, expression;
13605    if (ast.input) {
13606      return this.inputs(ast.input, ast.watchId);
13607    }
13608    switch (ast.type) {
13609    case AST.Literal:
13610      return this.value(ast.value, context);
13611    case AST.UnaryExpression:
13612      right = this.recurse(ast.argument);
13613      return this['unary' + ast.operator](right, context);
13614    case AST.BinaryExpression:
13615      left = this.recurse(ast.left);
13616      right = this.recurse(ast.right);
13617      return this['binary' + ast.operator](left, right, context);
13618    case AST.LogicalExpression:
13619      left = this.recurse(ast.left);
13620      right = this.recurse(ast.right);
13621      return this['binary' + ast.operator](left, right, context);
13622    case AST.ConditionalExpression:
13623      return this['ternary?:'](
13624        this.recurse(ast.test),
13625        this.recurse(ast.alternate),
13626        this.recurse(ast.consequent),
13627        context
13628      );
13629    case AST.Identifier:
13630      ensureSafeMemberName(ast.name, self.expression);
13631      return self.identifier(ast.name,
13632                             self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
13633                             context, create, self.expression);
13634    case AST.MemberExpression:
13635      left = this.recurse(ast.object, false, !!create);
13636      if (!ast.computed) {
13637        ensureSafeMemberName(ast.property.name, self.expression);
13638        right = ast.property.name;
13639      }
13640      if (ast.computed) right = this.recurse(ast.property);
13641      return ast.computed ?
13642        this.computedMember(left, right, context, create, self.expression) :
13643        this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
13644    case AST.CallExpression:
13645      args = [];
13646      forEach(ast.arguments, function(expr) {
13647        args.push(self.recurse(expr));
13648      });
13649      if (ast.filter) right = this.$filter(ast.callee.name);
13650      if (!ast.filter) right = this.recurse(ast.callee, true);
13651      return ast.filter ?
13652        function(scope, locals, assign, inputs) {
13653          var values = [];
13654          for (var i = 0; i < args.length; ++i) {
13655            values.push(args[i](scope, locals, assign, inputs));
13656          }
13657          var value = right.apply(undefined, values, inputs);
13658          return context ? {context: undefined, name: undefined, value: value} : value;
13659        } :
13660        function(scope, locals, assign, inputs) {
13661          var rhs = right(scope, locals, assign, inputs);
13662          var value;
13663          if (rhs.value != null) {
13664            ensureSafeObject(rhs.context, self.expression);
13665            ensureSafeFunction(rhs.value, self.expression);
13666            var values = [];
13667            for (var i = 0; i < args.length; ++i) {
13668              values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
13669            }
13670            value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
13671          }
13672          return context ? {value: value} : value;
13673        };
13674    case AST.AssignmentExpression:
13675      left = this.recurse(ast.left, true, 1);
13676      right = this.recurse(ast.right);
13677      return function(scope, locals, assign, inputs) {
13678        var lhs = left(scope, locals, assign, inputs);
13679        var rhs = right(scope, locals, assign, inputs);
13680        ensureSafeObject(lhs.value, self.expression);
13681        lhs.context[lhs.name] = rhs;
13682        return context ? {value: rhs} : rhs;
13683      };
13684    case AST.ArrayExpression:
13685      args = [];
13686      forEach(ast.elements, function(expr) {
13687        args.push(self.recurse(expr));
13688      });
13689      return function(scope, locals, assign, inputs) {
13690        var value = [];
13691        for (var i = 0; i < args.length; ++i) {
13692          value.push(args[i](scope, locals, assign, inputs));
13693        }
13694        return context ? {value: value} : value;
13695      };
13696    case AST.ObjectExpression:
13697      args = [];
13698      forEach(ast.properties, function(property) {
13699        args.push({key: property.key.type === AST.Identifier ?
13700                        property.key.name :
13701                        ('' + property.key.value),
13702                   value: self.recurse(property.value)
13703        });
13704      });
13705      return function(scope, locals, assign, inputs) {
13706        var value = {};
13707        for (var i = 0; i < args.length; ++i) {
13708          value[args[i].key] = args[i].value(scope, locals, assign, inputs);
13709        }
13710        return context ? {value: value} : value;
13711      };
13712    case AST.ThisExpression:
13713      return function(scope) {
13714        return context ? {value: scope} : scope;
13715      };
13716    case AST.NGValueParameter:
13717      return function(scope, locals, assign, inputs) {
13718        return context ? {value: assign} : assign;
13719      };
13720    }
13721  },
13722
13723  'unary+': function(argument, context) {
13724    return function(scope, locals, assign, inputs) {
13725      var arg = argument(scope, locals, assign, inputs);
13726      if (isDefined(arg)) {
13727        arg = +arg;
13728      } else {
13729        arg = 0;
13730      }
13731      return context ? {value: arg} : arg;
13732    };
13733  },
13734  'unary-': function(argument, context) {
13735    return function(scope, locals, assign, inputs) {
13736      var arg = argument(scope, locals, assign, inputs);
13737      if (isDefined(arg)) {
13738        arg = -arg;
13739      } else {
13740        arg = 0;
13741      }
13742      return context ? {value: arg} : arg;
13743    };
13744  },
13745  'unary!': function(argument, context) {
13746    return function(scope, locals, assign, inputs) {
13747      var arg = !argument(scope, locals, assign, inputs);
13748      return context ? {value: arg} : arg;
13749    };
13750  },
13751  'binary+': function(left, right, context) {
13752    return function(scope, locals, assign, inputs) {
13753      var lhs = left(scope, locals, assign, inputs);
13754      var rhs = right(scope, locals, assign, inputs);
13755      var arg = plusFn(lhs, rhs);
13756      return context ? {value: arg} : arg;
13757    };
13758  },
13759  'binary-': function(left, right, context) {
13760    return function(scope, locals, assign, inputs) {
13761      var lhs = left(scope, locals, assign, inputs);
13762      var rhs = right(scope, locals, assign, inputs);
13763      var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
13764      return context ? {value: arg} : arg;
13765    };
13766  },
13767  'binary*': function(left, right, context) {
13768    return function(scope, locals, assign, inputs) {
13769      var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
13770      return context ? {value: arg} : arg;
13771    };
13772  },
13773  'binary/': function(left, right, context) {
13774    return function(scope, locals, assign, inputs) {
13775      var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
13776      return context ? {value: arg} : arg;
13777    };
13778  },
13779  'binary%': function(left, right, context) {
13780    return function(scope, locals, assign, inputs) {
13781      var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
13782      return context ? {value: arg} : arg;
13783    };
13784  },
13785  'binary===': function(left, right, context) {
13786    return function(scope, locals, assign, inputs) {
13787      var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
13788      return context ? {value: arg} : arg;
13789    };
13790  },
13791  'binary!==': function(left, right, context) {
13792    return function(scope, locals, assign, inputs) {
13793      var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
13794      return context ? {value: arg} : arg;
13795    };
13796  },
13797  'binary==': function(left, right, context) {
13798    return function(scope, locals, assign, inputs) {
13799      var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
13800      return context ? {value: arg} : arg;
13801    };
13802  },
13803  'binary!=': function(left, right, context) {
13804    return function(scope, locals, assign, inputs) {
13805      var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
13806      return context ? {value: arg} : arg;
13807    };
13808  },
13809  'binary<': function(left, right, context) {
13810    return function(scope, locals, assign, inputs) {
13811      var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
13812      return context ? {value: arg} : arg;
13813    };
13814  },
13815  'binary>': function(left, right, context) {
13816    return function(scope, locals, assign, inputs) {
13817      var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
13818      return context ? {value: arg} : arg;
13819    };
13820  },
13821  'binary<=': function(left, right, context) {
13822    return function(scope, locals, assign, inputs) {
13823      var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
13824      return context ? {value: arg} : arg;
13825    };
13826  },
13827  'binary>=': function(left, right, context) {
13828    return function(scope, locals, assign, inputs) {
13829      var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
13830      return context ? {value: arg} : arg;
13831    };
13832  },
13833  'binary&&': function(left, right, context) {
13834    return function(scope, locals, assign, inputs) {
13835      var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
13836      return context ? {value: arg} : arg;
13837    };
13838  },
13839  'binary||': function(left, right, context) {
13840    return function(scope, locals, assign, inputs) {
13841      var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
13842      return context ? {value: arg} : arg;
13843    };
13844  },
13845  'ternary?:': function(test, alternate, consequent, context) {
13846    return function(scope, locals, assign, inputs) {
13847      var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
13848      return context ? {value: arg} : arg;
13849    };
13850  },
13851  value: function(value, context) {
13852    return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
13853  },
13854  identifier: function(name, expensiveChecks, context, create, expression) {
13855    return function(scope, locals, assign, inputs) {
13856      var base = locals && (name in locals) ? locals : scope;
13857      if (create && create !== 1 && base && !(base[name])) {
13858        base[name] = {};
13859      }
13860      var value = base ? base[name] : undefined;
13861      if (expensiveChecks) {
13862        ensureSafeObject(value, expression);
13863      }
13864      if (context) {
13865        return {context: base, name: name, value: value};
13866      } else {
13867        return value;
13868      }
13869    };
13870  },
13871  computedMember: function(left, right, context, create, expression) {
13872    return function(scope, locals, assign, inputs) {
13873      var lhs = left(scope, locals, assign, inputs);
13874      var rhs;
13875      var value;
13876      if (lhs != null) {
13877        rhs = right(scope, locals, assign, inputs);
13878        ensureSafeMemberName(rhs, expression);
13879        if (create && create !== 1 && lhs && !(lhs[rhs])) {
13880          lhs[rhs] = {};
13881        }
13882        value = lhs[rhs];
13883        ensureSafeObject(value, expression);
13884      }
13885      if (context) {
13886        return {context: lhs, name: rhs, value: value};
13887      } else {
13888        return value;
13889      }
13890    };
13891  },
13892  nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
13893    return function(scope, locals, assign, inputs) {
13894      var lhs = left(scope, locals, assign, inputs);
13895      if (create && create !== 1 && lhs && !(lhs[right])) {
13896        lhs[right] = {};
13897      }
13898      var value = lhs != null ? lhs[right] : undefined;
13899      if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
13900        ensureSafeObject(value, expression);
13901      }
13902      if (context) {
13903        return {context: lhs, name: right, value: value};
13904      } else {
13905        return value;
13906      }
13907    };
13908  },
13909  inputs: function(input, watchId) {
13910    return function(scope, value, locals, inputs) {
13911      if (inputs) return inputs[watchId];
13912      return input(scope, value, locals);
13913    };
13914  }
13915};
13916
13917/**
13918 * @constructor
13919 */
13920var Parser = function(lexer, $filter, options) {
13921  this.lexer = lexer;
13922  this.$filter = $filter;
13923  this.options = options;
13924  this.ast = new AST(this.lexer);
13925  this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
13926                                   new ASTCompiler(this.ast, $filter);
13927};
13928
13929Parser.prototype = {
13930  constructor: Parser,
13931
13932  parse: function(text) {
13933    return this.astCompiler.compile(text, this.options.expensiveChecks);
13934  }
13935};
13936
13937//////////////////////////////////////////////////
13938// Parser helper functions
13939//////////////////////////////////////////////////
13940
13941function setter(obj, path, setValue, fullExp) {
13942  ensureSafeObject(obj, fullExp);
13943
13944  var element = path.split('.'), key;
13945  for (var i = 0; element.length > 1; i++) {
13946    key = ensureSafeMemberName(element.shift(), fullExp);
13947    var propertyObj = ensureSafeObject(obj[key], fullExp);
13948    if (!propertyObj) {
13949      propertyObj = {};
13950      obj[key] = propertyObj;
13951    }
13952    obj = propertyObj;
13953  }
13954  key = ensureSafeMemberName(element.shift(), fullExp);
13955  ensureSafeObject(obj[key], fullExp);
13956  obj[key] = setValue;
13957  return setValue;
13958}
13959
13960var getterFnCacheDefault = createMap();
13961var getterFnCacheExpensive = createMap();
13962
13963function isPossiblyDangerousMemberName(name) {
13964  return name == 'constructor';
13965}
13966
13967var objectValueOf = Object.prototype.valueOf;
13968
13969function getValueOf(value) {
13970  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
13971}
13972
13973///////////////////////////////////
13974
13975/**
13976 * @ngdoc service
13977 * @name $parse
13978 * @kind function
13979 *
13980 * @description
13981 *
13982 * Converts Angular {@link guide/expression expression} into a function.
13983 *
13984 * ```js
13985 *   var getter = $parse('user.name');
13986 *   var setter = getter.assign;
13987 *   var context = {user:{name:'angular'}};
13988 *   var locals = {user:{name:'local'}};
13989 *
13990 *   expect(getter(context)).toEqual('angular');
13991 *   setter(context, 'newValue');
13992 *   expect(context.user.name).toEqual('newValue');
13993 *   expect(getter(context, locals)).toEqual('local');
13994 * ```
13995 *
13996 *
13997 * @param {string} expression String expression to compile.
13998 * @returns {function(context, locals)} a function which represents the compiled expression:
13999 *
14000 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
14001 *      are evaluated against (typically a scope object).
14002 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
14003 *      `context`.
14004 *
14005 *    The returned function also has the following properties:
14006 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14007 *        literal.
14008 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14009 *        constant literals.
14010 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14011 *        set to a function to change its value on the given context.
14012 *
14013 */
14014
14015
14016/**
14017 * @ngdoc provider
14018 * @name $parseProvider
14019 *
14020 * @description
14021 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14022 *  service.
14023 */
14024function $ParseProvider() {
14025  var cacheDefault = createMap();
14026  var cacheExpensive = createMap();
14027
14028  this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
14029    var $parseOptions = {
14030          csp: $sniffer.csp,
14031          expensiveChecks: false
14032        },
14033        $parseOptionsExpensive = {
14034          csp: $sniffer.csp,
14035          expensiveChecks: true
14036        };
14037
14038    return function $parse(exp, interceptorFn, expensiveChecks) {
14039      var parsedExpression, oneTime, cacheKey;
14040
14041      switch (typeof exp) {
14042        case 'string':
14043          exp = exp.trim();
14044          cacheKey = exp;
14045
14046          var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14047          parsedExpression = cache[cacheKey];
14048
14049          if (!parsedExpression) {
14050            if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14051              oneTime = true;
14052              exp = exp.substring(2);
14053            }
14054            var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14055            var lexer = new Lexer(parseOptions);
14056            var parser = new Parser(lexer, $filter, parseOptions);
14057            parsedExpression = parser.parse(exp);
14058            if (parsedExpression.constant) {
14059              parsedExpression.$$watchDelegate = constantWatchDelegate;
14060            } else if (oneTime) {
14061              parsedExpression.$$watchDelegate = parsedExpression.literal ?
14062                  oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14063            } else if (parsedExpression.inputs) {
14064              parsedExpression.$$watchDelegate = inputsWatchDelegate;
14065            }
14066            cache[cacheKey] = parsedExpression;
14067          }
14068          return addInterceptor(parsedExpression, interceptorFn);
14069
14070        case 'function':
14071          return addInterceptor(exp, interceptorFn);
14072
14073        default:
14074          return noop;
14075      }
14076    };
14077
14078    function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14079
14080      if (newValue == null || oldValueOfValue == null) { // null/undefined
14081        return newValue === oldValueOfValue;
14082      }
14083
14084      if (typeof newValue === 'object') {
14085
14086        // attempt to convert the value to a primitive type
14087        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14088        //             be cheaply dirty-checked
14089        newValue = getValueOf(newValue);
14090
14091        if (typeof newValue === 'object') {
14092          // objects/arrays are not supported - deep-watching them would be too expensive
14093          return false;
14094        }
14095
14096        // fall-through to the primitive equality check
14097      }
14098
14099      //Primitive or NaN
14100      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14101    }
14102
14103    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14104      var inputExpressions = parsedExpression.inputs;
14105      var lastResult;
14106
14107      if (inputExpressions.length === 1) {
14108        var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14109        inputExpressions = inputExpressions[0];
14110        return scope.$watch(function expressionInputWatch(scope) {
14111          var newInputValue = inputExpressions(scope);
14112          if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14113            lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14114            oldInputValueOf = newInputValue && getValueOf(newInputValue);
14115          }
14116          return lastResult;
14117        }, listener, objectEquality, prettyPrintExpression);
14118      }
14119
14120      var oldInputValueOfValues = [];
14121      var oldInputValues = [];
14122      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14123        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14124        oldInputValues[i] = null;
14125      }
14126
14127      return scope.$watch(function expressionInputsWatch(scope) {
14128        var changed = false;
14129
14130        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14131          var newInputValue = inputExpressions[i](scope);
14132          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14133            oldInputValues[i] = newInputValue;
14134            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14135          }
14136        }
14137
14138        if (changed) {
14139          lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14140        }
14141
14142        return lastResult;
14143      }, listener, objectEquality, prettyPrintExpression);
14144    }
14145
14146    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14147      var unwatch, lastValue;
14148      return unwatch = scope.$watch(function oneTimeWatch(scope) {
14149        return parsedExpression(scope);
14150      }, function oneTimeListener(value, old, scope) {
14151        lastValue = value;
14152        if (isFunction(listener)) {
14153          listener.apply(this, arguments);
14154        }
14155        if (isDefined(value)) {
14156          scope.$$postDigest(function() {
14157            if (isDefined(lastValue)) {
14158              unwatch();
14159            }
14160          });
14161        }
14162      }, objectEquality);
14163    }
14164
14165    function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14166      var unwatch, lastValue;
14167      return unwatch = scope.$watch(function oneTimeWatch(scope) {
14168        return parsedExpression(scope);
14169      }, function oneTimeListener(value, old, scope) {
14170        lastValue = value;
14171        if (isFunction(listener)) {
14172          listener.call(this, value, old, scope);
14173        }
14174        if (isAllDefined(value)) {
14175          scope.$$postDigest(function() {
14176            if (isAllDefined(lastValue)) unwatch();
14177          });
14178        }
14179      }, objectEquality);
14180
14181      function isAllDefined(value) {
14182        var allDefined = true;
14183        forEach(value, function(val) {
14184          if (!isDefined(val)) allDefined = false;
14185        });
14186        return allDefined;
14187      }
14188    }
14189
14190    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14191      var unwatch;
14192      return unwatch = scope.$watch(function constantWatch(scope) {
14193        return parsedExpression(scope);
14194      }, function constantListener(value, old, scope) {
14195        if (isFunction(listener)) {
14196          listener.apply(this, arguments);
14197        }
14198        unwatch();
14199      }, objectEquality);
14200    }
14201
14202    function addInterceptor(parsedExpression, interceptorFn) {
14203      if (!interceptorFn) return parsedExpression;
14204      var watchDelegate = parsedExpression.$$watchDelegate;
14205
14206      var regularWatch =
14207          watchDelegate !== oneTimeLiteralWatchDelegate &&
14208          watchDelegate !== oneTimeWatchDelegate;
14209
14210      var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14211        var value = parsedExpression(scope, locals, assign, inputs);
14212        return interceptorFn(value, scope, locals);
14213      } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14214        var value = parsedExpression(scope, locals, assign, inputs);
14215        var result = interceptorFn(value, scope, locals);
14216        // we only return the interceptor's result if the
14217        // initial value is defined (for bind-once)
14218        return isDefined(value) ? result : value;
14219      };
14220
14221      // Propagate $$watchDelegates other then inputsWatchDelegate
14222      if (parsedExpression.$$watchDelegate &&
14223          parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14224        fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14225      } else if (!interceptorFn.$stateful) {
14226        // If there is an interceptor, but no watchDelegate then treat the interceptor like
14227        // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14228        fn.$$watchDelegate = inputsWatchDelegate;
14229        fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14230      }
14231
14232      return fn;
14233    }
14234  }];
14235}
14236
14237/**
14238 * @ngdoc service
14239 * @name $q
14240 * @requires $rootScope
14241 *
14242 * @description
14243 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14244 * when they are done processing.
14245 *
14246 * This is an implementation of promises/deferred objects inspired by
14247 * [Kris Kowal's Q](https://github.com/kriskowal/q).
14248 *
14249 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14250 * implementations, and the other which resembles ES6 promises to some degree.
14251 *
14252 * # $q constructor
14253 *
14254 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14255 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14256 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14257 *
14258 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14259 * available yet.
14260 *
14261 * It can be used like so:
14262 *
14263 * ```js
14264 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14265 *   // are available in the current lexical scope (they could have been injected or passed in).
14266 *
14267 *   function asyncGreet(name) {
14268 *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
14269 *     return $q(function(resolve, reject) {
14270 *       setTimeout(function() {
14271 *         if (okToGreet(name)) {
14272 *           resolve('Hello, ' + name + '!');
14273 *         } else {
14274 *           reject('Greeting ' + name + ' is not allowed.');
14275 *         }
14276 *       }, 1000);
14277 *     });
14278 *   }
14279 *
14280 *   var promise = asyncGreet('Robin Hood');
14281 *   promise.then(function(greeting) {
14282 *     alert('Success: ' + greeting);
14283 *   }, function(reason) {
14284 *     alert('Failed: ' + reason);
14285 *   });
14286 * ```
14287 *
14288 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14289 *
14290 * However, the more traditional CommonJS-style usage is still available, and documented below.
14291 *
14292 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14293 * interface for interacting with an object that represents the result of an action that is
14294 * performed asynchronously, and may or may not be finished at any given point in time.
14295 *
14296 * From the perspective of dealing with error handling, deferred and promise APIs are to
14297 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14298 *
14299 * ```js
14300 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14301 *   // are available in the current lexical scope (they could have been injected or passed in).
14302 *
14303 *   function asyncGreet(name) {
14304 *     var deferred = $q.defer();
14305 *
14306 *     setTimeout(function() {
14307 *       deferred.notify('About to greet ' + name + '.');
14308 *
14309 *       if (okToGreet(name)) {
14310 *         deferred.resolve('Hello, ' + name + '!');
14311 *       } else {
14312 *         deferred.reject('Greeting ' + name + ' is not allowed.');
14313 *       }
14314 *     }, 1000);
14315 *
14316 *     return deferred.promise;
14317 *   }
14318 *
14319 *   var promise = asyncGreet('Robin Hood');
14320 *   promise.then(function(greeting) {
14321 *     alert('Success: ' + greeting);
14322 *   }, function(reason) {
14323 *     alert('Failed: ' + reason);
14324 *   }, function(update) {
14325 *     alert('Got notification: ' + update);
14326 *   });
14327 * ```
14328 *
14329 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14330 * comes in the way of guarantees that promise and deferred APIs make, see
14331 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14332 *
14333 * Additionally the promise api allows for composition that is very hard to do with the
14334 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14335 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14336 * section on serial or parallel joining of promises.
14337 *
14338 * # The Deferred API
14339 *
14340 * A new instance of deferred is constructed by calling `$q.defer()`.
14341 *
14342 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14343 * that can be used for signaling the successful or unsuccessful completion, as well as the status
14344 * of the task.
14345 *
14346 * **Methods**
14347 *
14348 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14349 *   constructed via `$q.reject`, the promise will be rejected instead.
14350 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14351 *   resolving it with a rejection constructed via `$q.reject`.
14352 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14353 *   multiple times before the promise is either resolved or rejected.
14354 *
14355 * **Properties**
14356 *
14357 * - promise – `{Promise}` – promise object associated with this deferred.
14358 *
14359 *
14360 * # The Promise API
14361 *
14362 * A new promise instance is created when a deferred instance is created and can be retrieved by
14363 * calling `deferred.promise`.
14364 *
14365 * The purpose of the promise object is to allow for interested parties to get access to the result
14366 * of the deferred task when it completes.
14367 *
14368 * **Methods**
14369 *
14370 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14371 *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14372 *   as soon as the result is available. The callbacks are called with a single argument: the result
14373 *   or rejection reason. Additionally, the notify callback may be called zero or more times to
14374 *   provide a progress indication, before the promise is resolved or rejected.
14375 *
14376 *   This method *returns a new promise* which is resolved or rejected via the return value of the
14377 *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14378 *   with the value which is resolved in that promise using
14379 *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14380 *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14381 *   resolved or rejected from the notifyCallback method.
14382 *
14383 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14384 *
14385 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14386 *   but to do so without modifying the final value. This is useful to release resources or do some
14387 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14388 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14389 *   more information.
14390 *
14391 * # Chaining promises
14392 *
14393 * Because calling the `then` method of a promise returns a new derived promise, it is easily
14394 * possible to create a chain of promises:
14395 *
14396 * ```js
14397 *   promiseB = promiseA.then(function(result) {
14398 *     return result + 1;
14399 *   });
14400 *
14401 *   // promiseB will be resolved immediately after promiseA is resolved and its value
14402 *   // will be the result of promiseA incremented by 1
14403 * ```
14404 *
14405 * It is possible to create chains of any length and since a promise can be resolved with another
14406 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14407 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14408 * $http's response interceptors.
14409 *
14410 *
14411 * # Differences between Kris Kowal's Q and $q
14412 *
14413 *  There are two main differences:
14414 *
14415 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14416 *   mechanism in angular, which means faster propagation of resolution or rejection into your
14417 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
14418 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14419 *   all the important functionality needed for common async tasks.
14420 *
14421 *  # Testing
14422 *
14423 *  ```js
14424 *    it('should simulate promise', inject(function($q, $rootScope) {
14425 *      var deferred = $q.defer();
14426 *      var promise = deferred.promise;
14427 *      var resolvedValue;
14428 *
14429 *      promise.then(function(value) { resolvedValue = value; });
14430 *      expect(resolvedValue).toBeUndefined();
14431 *
14432 *      // Simulate resolving of promise
14433 *      deferred.resolve(123);
14434 *      // Note that the 'then' function does not get called synchronously.
14435 *      // This is because we want the promise API to always be async, whether or not
14436 *      // it got called synchronously or asynchronously.
14437 *      expect(resolvedValue).toBeUndefined();
14438 *
14439 *      // Propagate promise resolution to 'then' functions using $apply().
14440 *      $rootScope.$apply();
14441 *      expect(resolvedValue).toEqual(123);
14442 *    }));
14443 *  ```
14444 *
14445 * @param {function(function, function)} resolver Function which is responsible for resolving or
14446 *   rejecting the newly created promise. The first parameter is a function which resolves the
14447 *   promise, the second parameter is a function which rejects the promise.
14448 *
14449 * @returns {Promise} The newly created promise.
14450 */
14451function $QProvider() {
14452
14453  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14454    return qFactory(function(callback) {
14455      $rootScope.$evalAsync(callback);
14456    }, $exceptionHandler);
14457  }];
14458}
14459
14460function $$QProvider() {
14461  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
14462    return qFactory(function(callback) {
14463      $browser.defer(callback);
14464    }, $exceptionHandler);
14465  }];
14466}
14467
14468/**
14469 * Constructs a promise manager.
14470 *
14471 * @param {function(function)} nextTick Function for executing functions in the next turn.
14472 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
14473 *     debugging purposes.
14474 * @returns {object} Promise manager.
14475 */
14476function qFactory(nextTick, exceptionHandler) {
14477  var $qMinErr = minErr('$q', TypeError);
14478  function callOnce(self, resolveFn, rejectFn) {
14479    var called = false;
14480    function wrap(fn) {
14481      return function(value) {
14482        if (called) return;
14483        called = true;
14484        fn.call(self, value);
14485      };
14486    }
14487
14488    return [wrap(resolveFn), wrap(rejectFn)];
14489  }
14490
14491  /**
14492   * @ngdoc method
14493   * @name ng.$q#defer
14494   * @kind function
14495   *
14496   * @description
14497   * Creates a `Deferred` object which represents a task which will finish in the future.
14498   *
14499   * @returns {Deferred} Returns a new instance of deferred.
14500   */
14501  var defer = function() {
14502    return new Deferred();
14503  };
14504
14505  function Promise() {
14506    this.$$state = { status: 0 };
14507  }
14508
14509  Promise.prototype = {
14510    then: function(onFulfilled, onRejected, progressBack) {
14511      var result = new Deferred();
14512
14513      this.$$state.pending = this.$$state.pending || [];
14514      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
14515      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
14516
14517      return result.promise;
14518    },
14519
14520    "catch": function(callback) {
14521      return this.then(null, callback);
14522    },
14523
14524    "finally": function(callback, progressBack) {
14525      return this.then(function(value) {
14526        return handleCallback(value, true, callback);
14527      }, function(error) {
14528        return handleCallback(error, false, callback);
14529      }, progressBack);
14530    }
14531  };
14532
14533  //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
14534  function simpleBind(context, fn) {
14535    return function(value) {
14536      fn.call(context, value);
14537    };
14538  }
14539
14540  function processQueue(state) {
14541    var fn, deferred, pending;
14542
14543    pending = state.pending;
14544    state.processScheduled = false;
14545    state.pending = undefined;
14546    for (var i = 0, ii = pending.length; i < ii; ++i) {
14547      deferred = pending[i][0];
14548      fn = pending[i][state.status];
14549      try {
14550        if (isFunction(fn)) {
14551          deferred.resolve(fn(state.value));
14552        } else if (state.status === 1) {
14553          deferred.resolve(state.value);
14554        } else {
14555          deferred.reject(state.value);
14556        }
14557      } catch (e) {
14558        deferred.reject(e);
14559        exceptionHandler(e);
14560      }
14561    }
14562  }
14563
14564  function scheduleProcessQueue(state) {
14565    if (state.processScheduled || !state.pending) return;
14566    state.processScheduled = true;
14567    nextTick(function() { processQueue(state); });
14568  }
14569
14570  function Deferred() {
14571    this.promise = new Promise();
14572    //Necessary to support unbound execution :/
14573    this.resolve = simpleBind(this, this.resolve);
14574    this.reject = simpleBind(this, this.reject);
14575    this.notify = simpleBind(this, this.notify);
14576  }
14577
14578  Deferred.prototype = {
14579    resolve: function(val) {
14580      if (this.promise.$$state.status) return;
14581      if (val === this.promise) {
14582        this.$$reject($qMinErr(
14583          'qcycle',
14584          "Expected promise to be resolved with value other than 
14584itself '{0}'",
14585          val));
14586      } else {
14587        this.$$resolve(val);
14588      }
14589
14590    },
14591
14592    $$resolve: function(val) {
14593      var then, fns;
14594
14595      fns = callOnce(this, this.$$resolve, this.$$reject);
14596      try {
14597        if ((isObject(val) || isFunction(val))) then = val && val.then;
14598        if (isFunction(then)) {
14599          this.promise.$$state.status = -1;
14600          then.call(val, fns[0], fns[1], this.notify);
14601        } else {
14602          this.promise.$$state.value = val;
14603          this.promise.$$state.status = 1;
14604          scheduleProcessQueue(this.promise.$$state);
14605        }
14606      } catch (e) {
14607        fns[1](e);
14608        exceptionHandler(e);
14609      }
14610    },
14611
14612    reject: function(reason) {
14613      if (this.promise.$$state.status) return;
14614      this.$$reject(reason);
14615    },
14616
14617    $$reject: function(reason) {
14618      this.promise.$$state.value = reason;
14619      this.promise.$$state.status = 2;
14620      scheduleProcessQueue(this.promise.$$state);
14621    },
14622
14623    notify: function(progress) {
14624      var callbacks = this.promise.$$state.pending;
14625
14626      if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
14627        nextTick(function() {
14628          var callback, result;
14629          for (var i = 0, ii = callbacks.length; i < ii; i++) {
14630            result = callbacks[i][0];
14631            callback = callbacks[i][3];
14632            try {
14633              result.notify(isFunction(callback) ? callback(progress) : progress);
14634            } catch (e) {
14635              exceptionHandler(e);
14636            }
14637          }
14638        });
14639      }
14640    }
14641  };
14642
14643  /**
14644   * @ngdoc method
14645   * @name $q#reject
14646   * @kind function
14647   *
14648   * @description
14649   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
14650   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
14651   * a promise chain, you don't need to worry about it.
14652   *
14653   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
14654   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
14655   * a promise error callback and you want to forward the error to the promise derived from the
14656   * current promise, you have to "rethrow" the error by returning a rejection constructed via
14657   * `reject`.
14658   *
14659   * ```js
14660   *   promiseB = promiseA.then(function(result) {
14661   *     // success: do something and resolve promiseB
14662   *     //          with the old or a new result
14663   *     return result;
14664   *   }, function(reason) {
14665   *     // error: handle the error if possible and
14666   *     //        resolve promiseB with newPromiseOrValue,
14667   *     //        otherwise forward the rejection to promiseB
14668   *     if (canHandle(reason)) {
14669   *      // handle the error and recover
14670   *      return newPromiseOrValue;
14671   *     }
14672   *     return $q.reject(reason);
14673   *   });
14674   * ```
14675   *
14676   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
14677   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
14678   */
14679  var reject = function(reason) {
14680    var result = new Deferred();
14681    result.reject(reason);
14682    return result.promise;
14683  };
14684
14685  var makePromise = function makePromise(value, resolved) {
14686    var result = new Deferred();
14687    if (resolved) {
14688      result.resolve(value);
14689    } else {
14690      result.reject(value);
14691    }
14692    return result.promise;
14693  };
14694
14695  var handleCallback = function handleCallback(value, isResolved, callback) {
14696    var callbackOutput = null;
14697    try {
14698      if (isFunction(callback)) callbackOutput = callback();
14699    } catch (e) {
14700      return makePromise(e, false);
14701    }
14702    if (isPromiseLike(callbackOutput)) {
14703      return callbackOutput.then(function() {
14704        return makePromise(value, isResolved);
14705      }, function(error) {
14706        return makePromise(error, false);
14707      });
14708    } else {
14709      return makePromise(value, isResolved);
14710    }
14711  };
14712
14713  /**
14714   * @ngdoc method
14715   * @name $q#when
14716   * @kind function
14717   *
14718   * @description
14719   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
14720   * This is useful when you are dealing with an object that might or might not be a promise, or if
14721   * the promise comes from a source that can't be trusted.
14722   *
14723   * @param {*} value Value or a promise
14724   * @returns {Promise} Returns a promise of the passed value or promise
14725   */
14726
14727
14728  var when = function(value, callback, errback, progressBack) {
14729    var result = new Deferred();
14730    result.resolve(value);
14731    return result.promise.then(callback, errback, progressBack);
14732  };
14733
14734  /**
14735   * @ngdoc method
14736   * @name $q#resolve
14737   * @kind function
14738   *
14739   * @description
14740   * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
14741   *
14742   * @param {*} value Value or a promise
14743   * @returns {Promise} Returns a promise of the passed value or promise
14744   */
14745  var resolve = when;
14746
14747  /**
14748   * @ngdoc method
14749   * @name $q#all
14750   * @kind function
14751   *
14752   * @description
14753   * Combines multiple promises into a single promise that is resolved when all of the input
14754   * promises are resolved.
14755   *
14756   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
14757   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
14758   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
14759   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
14760   *   with the same rejection value.
14761   */
14762
14763  function all(promises) {
14764    var deferred = new Deferred(),
14765        counter = 0,
14766        results = isArray(promises) ? [] : {};
14767
14768    forEach(promises, function(promise, key) {
14769      counter++;
14770      when(promise).then(function(value) {
14771        if (results.hasOwnProperty(key)) return;
14772        results[key] = value;
14773        if (!(--counter)) deferred.resolve(results);
14774      }, function(reason) {
14775        if (results.hasOwnProperty(key)) return;
14776        deferred.reject(reason);
14777      });
14778    });
14779
14780    if (counter === 0) {
14781      deferred.resolve(results);
14782    }
14783
14784    return deferred.promise;
14785  }
14786
14787  var $Q = function Q(resolver) {
14788    if (!isFunction(resolver)) {
14789      throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
14790    }
14791
14792    if (!(this instanceof Q)) {
14793      // More useful when $Q is the Promise itself.
14794      return new Q(resolver);
14795    }
14796
14797    var deferred = new Deferred();
14798
14799    function resolveFn(value) {
14800      deferred.resolve(value);
14801    }
14802
14803    function rejectFn(reason) {
14804      deferred.reject(reason);
14805    }
14806
14807    resolver(resolveFn, rejectFn);
14808
14809    return deferred.promise;
14810  };
14811
14812  $Q.defer = defer;
14813  $Q.reject = reject;
14814  $Q.when = when;
14815  $Q.resolve = resolve;
14816  $Q.all = all;
14817
14818  return $Q;
14819}
14820
14821function $$RAFProvider() { //rAF
14822  this.$get = ['$window', '$timeout', function($window, $timeout) {
14823    var requestAnimationFrame = $window.requestAnimationFrame ||
14824                                $window.webkitRequestAnimationFrame;
14825
14826    var cancelAnimationFrame = $window.cancelAnimationFrame ||
14827                               $window.webkitCancelAnimationFrame ||
14828                               $window.webkitCancelRequestAnimationFrame;
14829
14830    var rafSupported = !!requestAnimationFrame;
14831    var rafFn = rafSupported
14832      ? function(fn) {
14833          var id = requestAnimationFrame(fn);
14834          return function() {
14835            cancelAnimationFrame(id);
14836          };
14837        }
14838      : function(fn) {
14839          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
14840          return function() {
14841            $timeout.cancel(timer);
14842          };
14843        };
14844
14845    queueFn.supported = rafSupported;
14846
14847    var cancelLastRAF;
14848    var taskCount = 0;
14849    var taskQueue = [];
14850    return queueFn;
14851
14852    function flush() {
14853      for (var i = 0; i < taskQueue.length; i++) {
14854        var task = taskQueue[i];
14855        if (task) {
14856          taskQueue[i] = null;
14857          task();
14858        }
14859      }
14860      taskCount = taskQueue.length = 0;
14861    }
14862
14863    function queueFn(asyncFn) {
14864      var index = taskQueue.length;
14865
14866      taskCount++;
14867      taskQueue.push(asyncFn);
14868
14869      if (index === 0) {
14870        cancelLastRAF = rafFn(flush);
14871      }
14872
14873      return function cancelQueueFn() {
14874        if (index >= 0) {
14875          taskQueue[index] = null;
14876          index = null;
14877
14878          if (--taskCount === 0 && cancelLastRAF) {
14879            cancelLastRAF();
14880            cancelLastRAF = null;
14881            taskQueue.length = 0;
14882          }
14883        }
14884      };
14885    }
14886  }];
14887}
14888
14889/**
14890 * DESIGN NOTES
14891 *
14892 * The design decisions behind the scope are heavily favored for speed and memory consumption.
14893 *
14894 * The typical use of scope is to watch the expressions, which most of the time return the same
14895 * value as last time so we optimize the operation.
14896 *
14897 * Closures construction is expensive in terms of speed as well as memory:
14898 *   - No closures, instead use prototypical inheritance for API
14899 *   - Internal state needs to be stored on scope directly, which means that private state is
14900 *     exposed as $$____ properties
14901 *
14902 * Loop operations are optimized by using while(count--) { ... }
14903 *   - this means that in order to keep the same order of execution as addition we have to add
14904 *     items to the array at the beginning (unshift) instead of at the end (push)
14905 *
14906 * Child scopes are created and removed often
14907 *   - Using an array would be slow since inserts in middle are expensive so we use linked list
14908 *
14909 * There are few watches then a lot of observers. This is why you don't want the observer to be
14910 * implemented in the same way as watch. Watch requires return of initialization function which
14911 * are expensive to construct.
14912 */
14913
14914
14915/**
14916 * @ngdoc provider
14917 * @name $rootScopeProvider
14918 * @description
14919 *
14920 * Provider for the $rootScope service.
14921 */
14922
14923/**
14924 * @ngdoc method
14925 * @name $rootScopeProvider#digestTtl
14926 * @description
14927 *
14928 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
14929 * assuming that the model is unstable.
14930 *
14931 * The current default is 10 iterations.
14932 *
14933 * In complex applications it's possible that the dependencies between `$watch`s will result in
14934 * several digest iterations. However if an application needs more than the default 10 digest
14935 * iterations for its model to stabilize then you should investigate what is causing the model to
14936 * continuously change during the digest.
14937 *
14938 * Increasing the TTL could have performance implications, so you should not change it without
14939 * proper justification.
14940 *
14941 * @param {number} limit The number of digest iterations.
14942 */
14943
14944
14945/**
14946 * @ngdoc service
14947 * @name $rootScope
14948 * @description
14949 *
14950 * Every application has a single root {@link ng.$rootScope.Scope scope}.
14951 * All other scopes are descendant scopes of the root scope. Scopes provide separation
14952 * between the model and the view, via a mechanism for watching the model for changes.
14953 * They also provide an event emission/broadcast and subscription facility. See the
14954 * {@link guide/scope developer guide on scopes}.
14955 */
14956function $RootScopeProvider() {
14957  var TTL = 10;
14958  var $rootScopeMinErr = minErr('$rootScope');
14959  var lastDirtyWatch = null;
14960  var applyAsyncId = null;
14961
14962  this.digestTtl = function(value) {
14963    if (arguments.length) {
14964      TTL = value;
14965    }
14966    return TTL;
14967  };
14968
14969  function createChildScopeClass(parent) {
14970    function ChildScope() {
vendor: 4,153 bytes, lines 14971-15087
14971      this.$$watchers = this.$$nextSibling =
14972          this.$$childHead = this.$$childTail = null;
14973      this.$$listeners = {};
14974      this.$$listenerCount = {};
14975      this.$$watchersCount = 0;
14976      this.$id = nextUid();
14977      this.$$ChildScope = null;
14978    }
14979    ChildScope.prototype = parent;
14980    return ChildScope;
14981  }
14982
14983  this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
14984      function($injector, $exceptionHandler, $parse, $browser) {
14985
14986    function destroyChildScope($event) {
14987        $event.currentScope.$$destroyed = true;
14988    }
14989
14990    /**
14991     * @ngdoc type
14992     * @name $rootScope.Scope
14993     *
14994     * @description
14995     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
14996     * {@link auto.$injector $injector}. Child scopes are created using the
14997     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
14998     * compiled HTML template is executed.)
14999     *
15000     * Here is a simple scope snippet to show how you can interact with the scope.
15001     * ```html
15002     * <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
15003     * ```
15004     *
15005     * # Inheritance
15006     * A scope can inherit from a parent scope, as in this example:
15007     * ```js
15008         var parent = $rootScope;
15009         var child = parent.$new();
15010
15011         parent.salutation = "Hello";
15012         expect(child.salutation).toEqual('Hello');
15013
15014         child.salutation = "Welcome";
15015         expect(child.salutation).toEqual('Welcome');
15016         expect(parent.salutation).toEqual('Hello');
15017     * ```
15018     *
15019     * When interacting with `Scope` in tests, additional helper methods are available on the
15020     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15021     * details.
15022     *
15023     *
15024     * @param {Object.<string, function()>=} providers Map of service factory which need to be
15025     *                                       provided for the current scope. Defaults to {@link ng}.
15026     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15027     *                              append/override services provided by `providers`. This is handy
15028     *                              when unit-testing and having the need to override a default
15029     *                              service.
15030     * @returns {Object} Newly created scope.
15031     *
15032     */
15033    function Scope() {
15034      this.$id = nextUid();
15035      this.$$phase = this.$parent = this.$$watchers =
15036                     this.$$nextSibling = this.$$prevSibling =
15037                     this.$$childHead = this.$$childTail = null;
15038      this.$root = this;
15039      this.$$destroyed = false;
15040      this.$$listeners = {};
15041      this.$$listenerCount = {};
15042      this.$$watchersCount = 0;
15043      this.$$isolateBindings = null;
15044    }
15045
15046    /**
15047     * @ngdoc property
15048     * @name $rootScope.Scope#$id
15049     *
15050     * @description
15051     * Unique scope ID (monotonically increasing) useful for debugging.
15052     */
15053
15054     /**
15055      * @ngdoc property
15056      * @name $rootScope.Scope#$parent
15057      *
15058      * @description
15059      * Reference to the parent scope.
15060      */
15061
15062      /**
15063       * @ngdoc property
15064       * @name $rootScope.Scope#$root
15065       *
15066       * @description
15067       * Reference to the root scope.
15068       */
15069
15070    Scope.prototype = {
15071      constructor: Scope,
15072      /**
15073       * @ngdoc method
15074       * @name $rootScope.Scope#$new
15075       * @kind function
15076       *
15077       * @description
15078       * Creates a new child {@link ng.$rootScope.Scope scope}.
15079       *
15080       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15081       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15082       *
15083       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15084       * desired for the scope and its child scopes to be permanently detached from the parent and
15085       * thus stop participating in model change detection and listener notification by invoking.
15086       *
15087       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15088       *         parent scope. The scope is isolated, as it can not see parent scope properties.
15089       *         When creating widgets, it is useful for the widget to not accidentally read parent
15090       *         state.
15091       *
15092       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15093       *                              of the newly created scope. Defaults to `this` scope if not provided.
15094       *                              This is used when creating a transclude scope to correctly place it
15095       *                              in the scope hierarchy while maintaining the correct prototypical
15096       *                              inheritance.
15097       *
15098       * @returns {Object} The newly created child scope.
15099       *
15100       */
15101      $new: function(isolate, parent) {
15102        var child;
15103
15104        parent = parent || this;
15105
15106        if (isolate) {
15107          child = new Scope();
15108          child.$root = this.$root;
15109        } else {
15110          // Only create a child scope class if somebody asks for one,
15111          // but cache it to allow the VM to optimize lookups.
15112          if (!this.$$ChildScope) {
15113            this.$$ChildScope = createChildScopeClass(this);
15114          }
15115          child = new this.$$ChildScope();
15116        }
15117        child.$parent = parent;
15118        child.$$prevSibling = parent.$$childTail;
15119        if (parent.$$childHead) {
15120          parent.$$childTail.$$nextSibling = child;
15121          parent.$$childTail = child;
15122        } else {
15123          parent.$$childHead = parent.$$childTail = child;
15124        }
15125
15126        // When the new scope is not isolated or we inherit from `this`, and
15127        // the parent scope is destroyed, the property `$$destroyed` is inherited
15128        // prototypically. In all other cases, this property needs to be set
15129        // when the parent scope is destroyed.
15130        // The listener needs to be added after the parent is set
15131        if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15132
15133        return child;
15134      },
15135
15136      /**
15137       * @ngdoc method
15138       * @name $rootScope.Scope#$watch
15139       * @kind function
15140       *
15141       * @description
15142       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15143       *
15144       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15145       *   $digest()} and should return the value that will be watched. (Since
15146       *   {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
15147       *   `watchExpression` can execute multiple times per
15148       *   {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
15149       * - The `listener` is called only when the value from the current `watchExpression` and the
15150       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
15151       *   see below). Inequality is determined according to reference inequality,
15152       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15153       *    via the `!==` Javascript operator, unless `objectEquality == true`
15154       *   (see next point)
15155       * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15156       *   according to the {@link angular.equals} function. To save the value of the object for
15157       *   later comparison, the {@link angular.copy} function is used. This therefore means that
15158       *   watching complex objects will have adverse memory and performance implications.
15159       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15160       *   This is achieved by rerunning the watchers until no changes are detected. The rerun
15161       *   iteration limit is 10 to prevent an infinite loop deadlock.
15162       *
15163       *
15164       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15165       * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
15166       * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
15167       * change is detected, be prepared for multiple calls to your listener.)
15168       *
15169       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15170       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15171       * watcher. In rare cases, this is undesirable because the listener is called when the result
15172       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15173       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15174       * listener was called due to initialization.
15175       *
15176       *
15177       *
15178       * # Example
15179       * ```js
15180           // let's assume that scope was dependency injected as the $rootScope
15181           var scope = $rootScope;
15182           scope.name = 'misko';
15183           scope.counter = 0;
15184
15185           expect(scope.counter).toEqual(0);
15186           scope.$watch('name', function(newValue, oldValue) {
15187             scope.counter = scope.counter + 1;
15188           });
15189           expect(scope.counter).toEqual(0);
15190
15191           scope.$digest();
15192           // the listener is always called during the first $digest loop after it was registered
15193           expect(scope.counter).toEqual(1);
15194
15195           scope.$digest();
15196           // but now it will not be called unless the value changes
15197           expect(scope.counter).toEqual(1);
15198
15199           scope.name = 'adam';
15200           scope.$digest();
15201           expect(scope.counter).toEqual(2);
15202
15203
15204
15205           // Using a function as a watchExpression
15206           var food;
15207           scope.foodCounter = 0;
15208           expect(scope.foodCounter).toEqual(0);
15209           scope.$watch(
15210             // This function returns the value being watched. It is called for each turn of the $digest loop
15211             function() { return food; },
15212             // This is the change listener, called when the value returned from the above function changes
15213             function(newValue, oldValue) {
15214               if ( newValue !== oldValue ) {
15215                 // Only increment the counter if the value changed
15216                 scope.foodCounter = scope.foodCounter + 1;
15217               }
15218             }
15219           );
15220           // No digest has been run so the counter will be zero
15221           expect(scope.foodCounter).toEqual(0);
15222
15223           // Run the digest but since food has not changed count will still be zero
15224           scope.$digest();
15225           expect(scope.foodCounter).toEqual(0);
15226
15227           // Update food and run digest.  Now the counter will increment
15228           food = 'cheeseburger';
15229           scope.$digest();
15230           expect(scope.foodCounter).toEqual(1);
15231
15232       * ```
15233       *
15234       *
15235       *
15236       * @param {(function()|string)} watchExpression Expression that is evaluated on each
15237       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15238       *    a call to the `listener`.
15239       *
15240       *    - `string`: Evaluated as {@link guide/expression expression}
15241       *    - `function(scope)`: called with current `scope` as a parameter.
15242       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15243       *    of `watchExpression` changes.
15244       *
15245       *    - `newVal` contains the current value of the `watchExpression`
15246       *    - `oldVal` contains the previous value of the `watchExpression`
15247       *    - `scope` refers to the current scope
15248       * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15249       *     comparing for reference equality.
15250       * @returns {function()} Returns a deregistration function for this listener.
15251       */
15252      $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15253        var get = $parse(watchExp);
15254
15255        if (get.$$watchDelegate) {
15256          return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15257        }
15258        var scope = this,
15259            array = scope.$$watchers,
15260            watcher = {
15261              fn: listener,
15262              last: initWatchVal,
15263              get: get,
15264              exp: prettyPrintExpression || watchExp,
15265              eq: !!objectEquality
15266            };
15267
15268        lastDirtyWatch = null;
15269
15270        if (!isFunction(listener)) {
15271          watcher.fn = noop;
15272        }
15273
15274        if (!array) {
15275          array = scope.$$watchers = [];
15276        }
15277        // we use unshift since we use a while loop in $digest for speed.
15278        // the while loop reads in reverse order.
15279        array.unshift(watcher);
15280        incrementWatchersCount(this, 1);
15281
15282        return function deregisterWatch() {
15283          if (arrayRemove(array, watcher) >= 0) {
15284            incrementWatchersCount(scope, -1);
15285          }
15286          lastDirtyWatch = null;
15287        };
15288      },
15289
15290      /**
15291       * @ngdoc method
15292       * @name $rootScope.Scope#$watchGroup
15293       * @kind function
15294       *
15295       * @description
15296       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15297       * If any one expression in the collection changes the `listener` is executed.
15298       *
15299       * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15300       *   call to $digest() to see if any items changes.
15301       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15302       *
15303       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15304       * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15305       *
15306       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15307       *    expression in `watchExpressions` changes
15308       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15309       *    those of `watchExpression`
15310       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15311       *    those of `watchExpression`
15312       *    The `scope` refers to the current scope.
15313       * @returns {function()} Returns a de-registration function for all listeners.
15314       */
15315      $watchGroup: function(watchExpressions, listener) {
15316        var oldValues = new Array(watchExpressions.length);
15317        var newValues = new Array(watchExpressions.length);
15318        var deregisterFns = [];
15319        var self = this;
15320        var changeReactionScheduled = false;
vendor: 10,180 bytes, lines 15321-15583
15321        var firstRun = true;
15322
15323        if (!watchExpressions.length) {
15324          // No expressions means we call the listener ASAP
15325          var shouldCall = true;
15326          self.$evalAsync(function() {
15327            if (shouldCall) listener(newValues, newValues, self);
15328          });
15329          return function deregisterWatchGroup() {
15330            shouldCall = false;
15331          };
15332        }
15333
15334        if (watchExpressions.length === 1) {
15335          // Special case size of one
15336          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15337            newValues[0] = value;
15338            oldValues[0] = oldValue;
15339            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15340          });
15341        }
15342
15343        forEach(watchExpressions, function(expr, i) {
15344          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15345            newValues[i] = value;
15346            oldValues[i] = oldValue;
15347            if (!changeReactionScheduled) {
15348              changeReactionScheduled = true;
15349              self.$evalAsync(watchGroupAction);
15350            }
15351          });
15352          deregisterFns.push(unwatchFn);
15353        });
15354
15355        function watchGroupAction() {
15356          changeReactionScheduled = false;
15357
15358          if (firstRun) {
15359            firstRun = false;
15360            listener(newValues, newValues, self);
15361          } else {
15362            listener(newValues, oldValues, self);
15363          }
15364        }
15365
15366        return function deregisterWatchGroup() {
15367          while (deregisterFns.length) {
15368            deregisterFns.shift()();
15369          }
15370        };
15371      },
15372
15373
15374      /**
15375       * @ngdoc method
15376       * @name $rootScope.Scope#$watchCollection
15377       * @kind function
15378       *
15379       * @description
15380       * Shallow watches the properties of an object and fires whenever any of the properties change
15381       * (for arrays, this implies watching the array items; for object maps, this implies watching
15382       * the properties). If a change is detected, the `listener` callback is fired.
15383       *
15384       * - The `obj` collection is observed via standard $watch operation and is examined on every
15385       *   call to $digest() to see if any items have been added, removed, or moved.
15386       * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15387       *   adding, removing, and moving items belonging to an object or array.
15388       *
15389       *
15390       * # Example
15391       * ```js
15392          $scope.names = ['igor', 'matias', 'misko', 'james'];
15393          $scope.dataCount = 4;
15394
15395          $scope.$watchCollection('names', function(newNames, oldNames) {
15396            $scope.dataCount = newNames.length;
15397          });
15398
15399          expect($scope.dataCount).toEqual(4);
15400          $scope.$digest();
15401
15402          //still at 4 ... no changes
15403          expect($scope.dataCount).toEqual(4);
15404
15405          $scope.names.pop();
15406          $scope.$digest();
15407
15408          //now there's been a change
15409          expect($scope.dataCount).toEqual(3);
15410       * ```
15411       *
15412       *
15413       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15414       *    expression value should evaluate to an object or an array which is observed on each
15415       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15416       *    collection will trigger a call to the `listener`.
15417       *
15418       * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15419       *    when a change is detected.
15420       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
15421       *    - The `oldCollection` object is a copy of the former collection data.
15422       *      Due to performance considerations, the`oldCollection` value is computed only if the
15423       *      `listener` function declares two or more arguments.
15424       *    - The `scope` argument refers to the current scope.
15425       *
15426       * @returns {function()} Returns a de-registration function for this listener. When the
15427       *    de-registration function is executed, the internal watch operation is terminated.
15428       */
15429      $watchCollection: function(obj, listener) {
15430        $watchCollectionInterceptor.$stateful = true;
15431
15432        var self = this;
15433        // the current value, updated on each dirty-check run
15434        var newValue;
15435        // a shallow copy of the newValue from the last dirty-check run,
15436        // updated to match newValue during dirty-check run
15437        var oldValue;
15438        // a shallow copy of the newValue from when the last change happened
15439        var veryOldValue;
15440        // only track veryOldValue if the listener is asking for it
15441        var trackVeryOldValue = (listener.length > 1);
15442        var changeDetected = 0;
15443        var changeDetector = $parse(obj, $watchCollectionInterceptor);
15444        var internalArray = [];
15445        var internalObject = {};
15446        var initRun = true;
15447        var oldLength = 0;
15448
15449        function $watchCollectionInterceptor(_value) {
15450          newValue = _value;
15451          var newLength, key, bothNaN, newItem, oldItem;
15452
15453          // If the new value is undefined, then return undefined as the watch may be a one-time watch
15454          if (isUndefined(newValue)) return;
15455
15456          if (!isObject(newValue)) { // if primitive
15457            if (oldValue !== newValue) {
15458              oldValue = newValue;
15459              changeDetected++;
15460            }
15461          } else if (isArrayLike(newValue)) {
15462            if (oldValue !== internalArray) {
15463              // we are transitioning from something which was not an array into array.
15464              oldValue = internalArray;
15465              oldLength = oldValue.length = 0;
15466              changeDetected++;
15467            }
15468
15469            newLength = newValue.length;
15470
15471            if (oldLength !== newLength) {
15472              // if lengths do not match we need to trigger change notification
15473              changeDetected++;
15474              oldValue.length = oldLength = newLength;
15475            }
15476            // copy the items to oldValue and look for changes.
15477            for (var i = 0; i < newLength; i++) {
15478              oldItem = oldValue[i];
15479              newItem = newValue[i];
15480
15481              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15482              if (!bothNaN && (oldItem !== newItem)) {
15483                changeDetected++;
15484                oldValue[i] = newItem;
15485              }
15486            }
15487          } else {
15488            if (oldValue !== internalObject) {
15489              // we are transitioning from something which was not an object into object.
15490              oldValue = internalObject = {};
15491              oldLength = 0;
15492              changeDetected++;
15493            }
15494            // copy the items to oldValue and look for changes.
15495            newLength = 0;
15496            for (key in newValue) {
15497              if (newValue.hasOwnProperty(key)) {
15498                newLength++;
15499                newItem = newValue[key];
15500                oldItem = oldValue[key];
15501
15502                if (key in oldValue) {
15503                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15504                  if (!bothNaN && (oldItem !== newItem)) {
15505                    changeDetected++;
15506                    oldValue[key] = newItem;
15507                  }
15508                } else {
15509                  oldLength++;
15510                  oldValue[key] = newItem;
15511                  changeDetected++;
15512                }
15513              }
15514            }
15515            if (oldLength > newLength) {
15516              // we used to have more keys, need to find them and destroy them.
15517              changeDetected++;
15518              for (key in oldValue) {
15519                if (!newValue.hasOwnProperty(key)) {
15520                  oldLength--;
15521                  delete oldValue[key];
15522                }
15523              }
15524            }
15525          }
15526          return changeDetected;
15527        }
15528
15529        function $watchCollectionAction() {
15530          if (initRun) {
15531            initRun = false;
15532            listener(newValue, newValue, self);
15533          } else {
15534            listener(newValue, veryOldValue, self);
15535          }
15536
15537          // make a copy for the next time a collection is changed
15538          if (trackVeryOldValue) {
15539            if (!isObject(newValue)) {
15540              //primitive
15541              veryOldValue = newValue;
15542            } else if (isArrayLike(newValue)) {
15543              veryOldValue = new Array(newValue.length);
15544              for (var i = 0; i < newValue.length; i++) {
15545                veryOldValue[i] = newValue[i];
15546              }
15547            } else { // if object
15548              veryOldValue = {};
15549              for (var key in newValue) {
15550                if (hasOwnProperty.call(newValue, key)) {
15551                  veryOldValue[key] = newValue[key];
15552                }
15553              }
15554            }
15555          }
15556        }
15557
15558        return this.$watch(changeDetector, $watchCollectionAction);
15559      },
15560
15561      /**
15562       * @ngdoc method
15563       * @name $rootScope.Scope#$digest
15564       * @kind function
15565       *
15566       * @description
15567       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
15568       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
15569       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
15570       * until no more listeners are firing. This means that it is possible to get into an infinite
15571       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
15572       * iterations exceeds 10.
15573       *
15574       * Usually, you don't call `$digest()` directly in
15575       * {@link ng.directive:ngController controllers} or in
15576       * {@link ng.$compileProvider#directive directives}.
15577       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
15578       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
15579       *
15580       * If you want to be notified whenever `$digest()` is called,
15581       * you can register a `watchExpression` function with
15582       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
15583       *
15584       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
15585       *
15586       * # Example
15587       * ```js
15588           var scope = ...;
15589           scope.name = 'misko';
15590           scope.counter = 0;
15591
15592           expect(scope.counter).toEqual(0);
15593           scope.$watch('name', function(newValue, oldValue) {
15594             scope.counter = scope.counter + 1;
15595           });
15596           expect(scope.counter).toEqual(0);
15597
15598           scope.$digest();
15599           // the listener is always called during the first $digest loop after it was registered
15600           expect(scope.counter).toEqual(1);
15601
15602           scope.$digest();
15603           // but now it will not be called unless the value changes
15604           expect(scope.counter).toEqual(1);
15605
15606           scope.name = 'adam';
15607           scope.$digest();
15608           expect(scope.counter).toEqual(2);
15609       * ```
15610       *
15611       */
15612      $digest: function() {
15613        var watch, value, last,
15614            watchers,
15615            length,
15616            dirty, ttl = TTL,
15617            next, current, target = this,
15618            watchLog = [],
15619            logIdx, logMsg, asyncTask;
15620
15621        beginPhase('$digest');
15622        // Check for changes to browser url that happened in sync before the call to $digest
15623        $browser.$$checkUrlChange();
15624
15625        if (this === $rootScope && applyAsyncId !== null) {
15626          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
15627          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
15628          $browser.defer.cancel(applyAsyncId);
15629          flushApplyAsync();
15630        }
15631
15632        lastDirtyWatch = null;
15633
15634        do { // "while dirty" loop
15635          dirty = false;
15636          current = target;
15637
15638          while (asyncQueue.length) {
15639            try {
15640              asyncTask = asyncQueue.shift();
15641              asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
15642            } catch (e) {
15643              $exceptionHandler(e);
15644            }
15645            lastDirtyWatch = null;
15646          }
15647
15648          traverseScopesLoop:
15649          do { // "traverse the scopes" loop
15650            if ((watchers = current.$$watchers)) {
15651              // process our watches
15652              length = watchers.length;
15653              while (length--) {
15654                try {
15655                  watch = watchers[length];
15656                  // Most common watches are on primitives, in which case we can short
15657                  // circuit it with === operator, only when === fails do we use .equals
15658                  if (watch) {
15659                    if ((value = watch.get(current)) !== (last = watch.last) &&
15660                        !(watch.eq
15661                            ? equals(value, last)
15662                            : (typeof value === 'number' && typeof last === 'number'
15663                               && isNaN(value) && isNaN(last)))) {
15664                      dirty = true;
15665                      lastDirtyWatch = watch;
15666                      watch.last = watch.eq ? copy(value, null) : value;
15667                      watch.fn(value, ((last === initWatchVal) ? value : last), current);
15668                      if (ttl < 5) {
15669                        logIdx = 4 - ttl;
15670                        if (!watchLog[logIdx]) watchLog[logIdx] = [];
15671                        watchLog[logIdx].push({
15672                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
15673                          newVal: value,
15674                          oldVal: last
15675                        });
15676                      }
15677                    } else if (watch === lastDirtyWatch) {
15678                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
15679                      // have already been tested.
15680                      dirty = false;
15681                      break traverseScopesLoop;
15682                    }
15683                  }
15684                } catch (e) {
15685                  $exceptionHandler(e);
15686                }
15687              }
15688            }
15689
15690            // Insanity Warning: scope depth-first traversal
15691            // yes, this code is a bit crazy, but it works and we have tests to prove it!
15692            // this piece should be kept in sync with the traversal in $broadcast
15693            if (!(next = ((current.$$watchersCount && current.$$childHead) ||
15694                (current !== target && current.$$nextSibling)))) {
15695              while (current !== target && !(next = current.$$nextSibling)) {
15696                current = current.$parent;
15697              }
15698            }
15699          } while ((current = next));
15700
15701          // `break traverseScopesLoop;` takes us to here
15702
15703          if ((dirty || asyncQueue.length) && !(ttl--)) {
15704            clearPhase();
15705            throw $rootScopeMinErr('infdig',
15706                '{0} $digest() iterations reached. Aborting!\n' +
15707                'Watchers fired in the last 5 iterations: {1}',
15708                TTL, watchLog);
15709          }
15710
15711        }
vendor: 5,032 bytes, lines 15711-15837
15711 while (dirty || asyncQueue.length);
15712
15713        clearPhase();
15714
15715        while (postDigestQueue.length) {
15716          try {
15717            postDigestQueue.shift()();
15718          } catch (e) {
15719            $exceptionHandler(e);
15720          }
15721        }
15722      },
15723
15724
15725      /**
15726       * @ngdoc event
15727       * @name $rootScope.Scope#$destroy
15728       * @eventType broadcast on scope being destroyed
15729       *
15730       * @description
15731       * Broadcasted when a scope and its children are being destroyed.
15732       *
15733       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
15734       * clean up DOM bindings before an element is removed from the DOM.
15735       */
15736
15737      /**
15738       * @ngdoc method
15739       * @name $rootScope.Scope#$destroy
15740       * @kind function
15741       *
15742       * @description
15743       * Removes the current scope (and all of its children) from the parent scope. Removal implies
15744       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
15745       * propagate to the current scope and its children. Removal also implies that the current
15746       * scope is eligible for garbage collection.
15747       *
15748       * The `$destroy()` is usually used by directives such as
15749       * {@link ng.directive:ngRepeat ngRepeat} for managing the
15750       * unrolling of the loop.
15751       *
15752       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
15753       * Application code can register a `$destroy` event handler that will give it a chance to
15754       * perform any necessary cleanup.
15755       *
15756       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
15757       * clean up DOM bindings before an element is removed from the DOM.
15758       */
15759      $destroy: function() {
15760        // We can't destroy a scope that has been already destroyed.
15761        if (this.$$destroyed) return;
15762        var parent = this.$parent;
15763
15764        this.$broadcast('$destroy');
15765        this.$$destroyed = true;
15766
15767        if (this === $rootScope) {
15768          //Remove handlers attached to window when $rootScope is removed
15769          $browser.$$applicationDestroyed();
15770        }
15771
15772        incrementWatchersCount(this, -this.$$watchersCount);
15773        for (var eventName in this.$$listenerCount) {
15774          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
15775        }
15776
15777        // sever all the references to parent scopes (after this cleanup, the current scope should
15778        // not be retained by any of our references and should be eligible for garbage collection)
15779        if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
15780        if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
15781        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
15782        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
15783
15784        // Disable listeners, watchers and apply/digest methods
15785        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
15786        this.$on = this.$watch = this.$watchGroup = function() { return noop; };
15787        this.$$listeners = {};
15788
15789        // All of the code below is bogus code that works around V8's memory leak via optimized code
15790        // and inline caches.
15791        //
15792        // see:
15793        // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15794        // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15795        // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15796
15797        this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
15798            this.$$childTail = this.$root = this.$$watchers = null;
15799      },
15800
15801      /**
15802       * @ngdoc method
15803       * @name $rootScope.Scope#$eval
15804       * @kind function
15805       *
15806       * @description
15807       * Executes the `expression` on the current scope and returns the result. Any exceptions in
15808       * the expression are propagated (uncaught). This is useful when evaluating Angular
15809       * expressions.
15810       *
15811       * # Example
15812       * ```js
15813           var scope = ng.$rootScope.Scope();
15814           scope.a = 1;
15815           scope.b = 2;
15816
15817           expect(scope.$eval('a+b')).toEqual(3);
15818           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
15819       * ```
15820       *
15821       * @param {(string|function())=} expression An angular expression to be executed.
15822       *
15823       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
15824       *    - `function(scope)`: execute the function with the current `scope` parameter.
15825       *
15826       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
15827       * @returns {*} The result of evaluating the expression.
15828       */
15829      $eval: function(expr, locals) {
15830        return $parse(expr)(this, locals);
15831      },
15832
15833      /**
15834       * @ngdoc method
15835       * @name $rootScope.Scope#$evalAsync
15836       * @kind function
15837       *
15838       * @description
15839       * Executes the expression on the current scope at a later point in time.
15840       *
15841       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
15842       * that:
15843       *
15844       *   - it will execute after the function that scheduled the evaluation (preferably before DOM
15845       *     rendering).
15846       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
15847       *     `expression` execution.
15848       *
15849       * Any exceptions from the execution of the expression are forwarded to the
15850       * {@link ng.$exceptionHandler $exceptionHandler} service.
15851       *
15852       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
15853       * will be scheduled. However, it is encouraged to always call code that changes the model
15854       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
15855       *
15856       * @param {(string|function())=} expression An angular expression to be executed.
15857       *
15858       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
15859       *    - `function(scope)`: execute the function with the current `scope` parameter.
15860       *
15861       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
15862       */
15863      $evalAsync: function(expr, locals) {
15864        // if we are outside of an $digest loop and this is the first time we are scheduling async
15865        // task also schedule async auto-flush
15866        if (!$rootScope.$$phase && !asyncQueue.length) {
15867          $browser.defer(function() {
15868            if (asyncQueue.length) {
15869              $rootScope.$digest();
15870            }
15871          });
15872        }
15873
15874        asyncQueue.push({scope: this, expression: expr, locals: locals});
15875      },
15876
15877      $$postDigest: function(fn) {
15878        postDigestQueue.push(fn);
15879      },
15880
15881      /**
15882       * @ngdoc method
15883       * @name $rootScope.Scope#$apply
15884       * @kind function
15885       *
15886       * @description
15887       * `$apply()` is used to execute an expression in angular from outside of the angular
15888       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
15889       * Because we are calling into the angular framework we need to perform proper scope life
15890       * cycle of {@link ng.$exceptionHandler exception handling},
15891       * {@link ng.$rootScope.Scope#$digest executing watches}.
15892       *
15893       * ## Life cycle
15894       *
15895       * # Pseudo-Code of `$apply()`
15896       * ```js
15897           function $apply(expr) {
15898             try {
15899               return $eval(expr);
15900             } catch (e) {
15901               $exceptionHandler(e);
15902             } finally {
15903               $root.$digest();
15904             }
15905           }
15906       * ```
15907       *
15908       *
15909       * Scope's `$apply()` method transitions through the following stages:
15910       *
15911       * 1. The {@link guide/expression expression} is executed using the
15912       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
15913       * 2. Any exceptions from the execution of the expression are forwarded to the
15914       *    {@link ng.$exceptionHandler $exceptionHandler} service.
15915       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
15916       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
15917       *
15918       *
15919       * @param {(string|function())=} exp An angular expression to be executed.
15920       *
15921       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
15922       *    - `function(scope)`: execute the function with current `scope` parameter.
15923       *
15924       * @returns {*} The result of evaluating the expression.
15925       */
15926      $apply: function(expr) {
15927        try {
15928          beginPhase('$apply');
15929          return this.$eval(expr);
15930        } catch (e) {
15931          $exceptionHandler(e);
15932        } finally {
15933          clearPhase();
15934          try {
15935            $rootScope.$digest();
15936          } catch (e) {
15937            $exceptionHandler(e);
15938            throw e;
15939          }
15940        }
15941      },
15942
15943      /**
15944       * @ngdoc method
15945       * @name $rootScope.Scope#$applyAsync
15946       * @kind function
15947       *
15948       * @description
15949       * Schedule the invocation of $apply to occur at a later time. The actual time difference
15950       * varies across browsers, but is typically around ~10 milliseconds.
15951       *
15952       * This can be used to queue up multiple expressions which need to be evaluated in the same
15953       * digest.
15954       *
15955       * @param {(string|function())=} exp An angular expression to be executed.
15956       *
15957       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
15958       *    - `function(scope)`: execute the function with current `scope` parameter.
15959       */
15960      $applyAsync: function(expr) {
15961        var scope = this;
15962        expr && applyAsyncQueue.push($applyAsyncExpression);
15963        scheduleApplyAsync();
15964
15965        function $applyAsyncExpression() {
15966          scope.$eval(expr);
15967        }
15968      },
15969
15970      /**
15971       * @ngdoc method
15972       * @name $rootScope.Scope#$on
15973       * @kind function
15974       *
15975       * @description
15976       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
15977       * discussion of event life cycle.
15978       *
15979       * The event listener function format is: `function(event, args...)`. The `event` object
15980       * passed into the listener has the following attributes:
15981       *
15982       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
15983       *     `$broadcast`-ed.
15984       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
15985       *     event propagates through the scope hierarchy, this property is set to null.
15986       *   - `name` - `{string}`: name of the event.
15987       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
15988       *     further event propagation (available only for events that were `$emit`-ed).
15989       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
15990       *     to true.
15991       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
15992       *
15993       * @param {string} name Event name to listen on.
15994       * @param {function(event, ...args)} listener Function to call when the event is emitted.
15995       * @returns {function()} Returns a deregistration function for this listener.
15996       */
15997      $on: function(name, listener) {
15998        var namedListeners = this.$$listeners[name];
15999        if (!namedListeners) {
16000          this.$$listeners[name] = namedListeners = [];
16001        }
16002        namedListeners.push(listener);
16003
16004        var current = this;
16005        do {
16006          if (!current.$$listenerCount[name]) {
16007            current.$$listenerCount[name] = 0;
16008          }
16009          current.$$listenerCount[name]++;
16010        } while ((current = current.$parent));
16011
16012        var self = this;
16013        return function() {
16014          var indexOfListener = namedListeners.indexOf(listener);
16015          if (indexOfListener !== -1) {
16016            namedListeners[indexOfListener] = null;
16017            decrementListenerCount(self, 1, name);
16018          }
16019        };
16020      },
16021
16022
16023      /**
16024       * @ngdoc method
16025       * @name $rootScope.Scope#$emit
16026       * @kind function
16027       *
16028       * @description
16029       * Dispatches an event `name` upwards through the scope hierarchy notifying the
16030       * registered {@link ng.$rootScope.Scope#$on} listeners.
16031       *
16032       * The event life cycle starts at the scope on which `$emit` was called. All
16033       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16034       * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16035       * registered listeners along the way. The event will stop propagating if one of the listeners
16036       * cancels it.
16037       *
16038       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16039       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16040       *
16041       * @param {string} name Event name to emit.
16042       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16043       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16044       */
16045      $emit: function(name, args) {
16046        var empty = [],
16047            namedListeners,
16048            scope = this,
16049            stopPropagation = false,
16050            event = {
16051              name: name,
16052              targetScope: scope,
16053              stopPropagation: function() {stopPropagation = true;},
16054              preventDefault: function() {
16055                event.defaultPrevented = true;
16056              },
16057              defaultPrevented: false
16058            },
16059            listenerArgs = concat([event], arguments, 1),
16060            i, length;
16061
16062        do {
16063          namedListeners = scope.$$listeners[name] || empty;
16064          event.currentScope = scope;
16065          for (i = 0, length = namedListeners.length; i < length; i++) {
16066
16067            // if listeners were deregistered, defragment the array
16068            if (!namedListeners[i]) {
16069              namedListeners.splice(i, 1);
16070              i--;
16071              length--;
16072              continue;
16073            }
16074            try {
16075              //allow all listeners attached to the current scope to run
16076              namedListeners[i].apply(null, listenerArgs);
16077            } catch (e) {
16078              $exceptionHandler(e);
16079            }
16080          }
16081          //if any listener on the current scope stops propagation, prevent bubbling
16082          if (stopPropagation) {
16083            event.currentScope = null;
16084            return event;
16085          }
16086          //traverse upwards
16087          scope = scope.$parent;
16088        } while (scope);
16089
16090        event.currentScope = null;
16091
16092        return event;
16093      },
16094
16095
16096      /**
16097       * @ngdoc method
16098       * @name $rootScope.Scope#$broadcast
16099       * @kind function
16100       *
16101       * @description
16102       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16103       * registered {@link ng.$rootScope.Scope#$on} listeners.
16104       *
16105       * The event life cycle starts at the scope on which `$broadcast` was called. All
16106       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16107       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16108       * scope and calls all registered listeners along the way. The event cannot be canceled.
16109       *
16110       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16111       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16112       *
16113       * @param {string} name Event name to broadcast.
16114       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16115       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16116       */
16117      $broadcast: function(name, args) {
16118        var target = this,
16119            current = target,
16120            next = target,
16121            event = {
16122              name: name,
16123              targetScope: target,
16124              preventDefault: function() {
16125                event.defaultPrevented = true;
16126              },
16127              defaultPrevented: false
16128            };
16129
16130        if (!target.$$listenerCount[name]) return event;
16131
16132        var listenerArgs = concat([event], arguments, 1),
16133            listeners, i, length;
16134
16135        //down while you can, then up and next sibling or up and next sibling until back at root
16136        while ((current = next)) {
16137          event.currentScope = current;
16138          listeners = current.$$listeners[name] || [];
16139          for (i = 0, length = listeners.length; i < length; i++) {
16140            // if listeners were deregistered, defragment the array
16141            if (!listeners[i]) {
16142              listeners.splice(i, 1);
16143              i--;
16144              length--;
16145              continue;
16146            }
16147
16148            try {
16149              listeners[i].apply(null, listenerArgs);
16150            } catch (e) {
16151              $exceptionHandler(e);
16152            }
16153          }
16154
16155          // Insanity Warning: scope depth-first traversal
16156          // yes, this code is a bit crazy, but it works and we have tests to prove it!
16157          // this piece should be kept in sync with the traversal in $digest
16158          // (though it differs due to having the extra check for $$listenerCount)
16159          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16160              (current !== target && current.$$nextSibling)))) {
16161            while (current !== target && !(next = current.$$nextSibling)) {
16162              current = current.$parent;
16163            }
16164          }
16165        }
16166
16167        event.currentScope = null;
16168        return event;
16169      }
16170    };
16171
16172    var $rootScope = new Scope();
16173
16174    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16175    var asyncQueue = $rootScope.$$asyncQueue = [];
16176    var postDigestQueue = $rootScope.$$postDigestQueue = [];
16177    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16178
16179    return $rootScope;
16180
16181
16182    function beginPhase(phase) {
16183      if ($rootScope.$$phase) {
16184        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16185      }
16186
16187      $rootScope.$$phase = phase;
16188    }
16189
16190    function clearPhase() {
16191      $rootScope.$$phase = null;
16192    }
16193
16194    function incrementWatchersCount(current, count) {
16195      do {
16196        current.$$watchersCount += count;
16197      } while ((current = current.$parent));
16198    }
16199
16200    function decrementListenerCount(current, count, name) {
16201      do {
16202        current.$$listenerCount[name] -= count;
16203
16204        if (current.$$listenerCount[name] === 0) {
16205          delete current.$$listenerCount[name];
16206        }
16207      } while ((current = current.$parent));
16208    }
16209
16210    /**
16211     * function used as an initial value for watchers.
16212     * because it's unique we can easily tell it apart from other values
16213     */
16214    function initWatchVal() {}
16215
16216    function flushApplyAsync() {
vendor: 4,838 bytes, lines 16217-16344
16217      while (applyAsyncQueue.length) {
16218        try {
16219          applyAsyncQueue.shift()();
16220        } catch (e) {
16221          $exceptionHandler(e);
16222        }
16223      }
16224      applyAsyncId = null;
16225    }
16226
16227    function scheduleApplyAsync() {
16228      if (applyAsyncId === null) {
16229        applyAsyncId = $browser.defer(function() {
16230          $rootScope.$apply(flushApplyAsync);
16231        });
16232      }
16233    }
16234  }];
16235}
16236
16237/**
16238 * @description
16239 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16240 */
16241function $$SanitizeUriProvider() {
16242  var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16243    imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16244
16245  /**
16246   * @description
16247   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16248   * urls during a[href] sanitization.
16249   *
16250   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16251   *
16252   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16253   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16254   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16255   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16256   *
16257   * @param {RegExp=} regexp New regexp to whitelist urls with.
16258   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16259   *    chaining otherwise.
16260   */
16261  this.aHrefSanitizationWhitelist = function(regexp) {
16262    if (isDefined(regexp)) {
16263      aHrefSanitizationWhitelist = regexp;
16264      return this;
16265    }
16266    return aHrefSanitizationWhitelist;
16267  };
16268
16269
16270  /**
16271   * @description
16272   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16273   * urls during img[src] sanitization.
16274   *
16275   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16276   *
16277   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16278   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16279   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16280   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16281   *
16282   * @param {RegExp=} regexp New regexp to whitelist urls with.
16283   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16284   *    chaining otherwise.
16285   */
16286  this.imgSrcSanitizationWhitelist = function(regexp) {
16287    if (isDefined(regexp)) {
16288      imgSrcSanitizationWhitelist = regexp;
16289      return this;
16290    }
16291    return imgSrcSanitizationWhitelist;
16292  };
16293
16294  this.$get = function() {
16295    return function sanitizeUri(uri, isImage) {
16296      var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16297      var normalizedVal;
16298      normalizedVal = urlResolve(uri).href;
16299      if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16300        return 'unsafe:' + normalizedVal;
16301      }
16302      return uri;
16303    };
16304  };
16305}
16306
16307/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16308 *     Any commits to this file should be reviewed with security in mind.  *
16309 *   Changes to this file can potentially create security vulnerabilities. *
16310 *          An approval from 2 Core members with history of modifying      *
16311 *                         this file is required.                          *
16312 *                                                                         *
16313 *  Does the change somehow allow for arbitrary javascript to be executed? *
16314 *    Or allows for someone to change the prototype of built-in objects?   *
16315 *     Or gives undesired access to variables likes document or window?    *
16316 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16317
16318var $sceMinErr = minErr('$sce');
16319
16320var SCE_CONTEXTS = {
16321  HTML: 'html',
16322  CSS: 'css',
16323  URL: 'url',
16324  // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16325  // url.  (e.g. ng-include, script src, templateUrl)
16326  RESOURCE_URL: 'resourceUrl',
16327  JS: 'js'
16328};
16329
16330// Helper functions follow.
16331
16332function adjustMatcher(matcher) {
16333  if (matcher === 'self') {
16334    return matcher;
16335  } else if (isString(matcher)) {
16336    // Strings match exactly except for 2 wildcards - '*' and '**'.
16337    // '*' matches any character except those from the set ':/.?&'.
16338    // '**' matches any character (like .* in a RegExp).
16339    // More than 2 *'s raises an error as it's ill defined.
16340    if (matcher.indexOf('***') > -1) {
16341      throw $sceMinErr('iwcard',
16342          'Illegal sequence *** in string matcher.  String: {0}', matcher);
16343    }
16344    matcher = escapeForRegexp(matcher).
16345                  replace('\\*\\*', '.*').
16346                  replace('\\*', '[^:/.?&;]*');
16347    return new RegExp('^' + matcher + '$');
16348  } else if (isRegExp(matcher)) {
16349    // The only other type of matcher allowed is a Regexp.
16350    // Match entire URL / disallow partial matches.
16351    // Flags are reset (i.e. no global, ignoreCase or multiline)
16352    return new RegExp('^' + matcher.source + '$');
16353  } else {
16354    throw $sceMinErr('imatcher',
16355        'Matchers may only be "self", string patterns or RegExp objects');
16356  }
16357}
16358
16359
16360function adjustMatchers(matchers) {
16361  var adjustedMatchers = [];
16362  if (isDefined(matchers)) {
16363    forEach(matchers, function(matcher) {
16364      adjustedMatchers.push(adjustMatcher(matcher));
16365    });
16366  }
16367  return adjustedMatchers;
16368}
16369
16370
16371/**
16372 * @ngdoc service
16373 * @name $sceDelegate
16374 * @kind function
16375 *
16376 * @description
16377 *
16378 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16379 * Contextual Escaping (SCE)} services to AngularJS.
16380 *
16381 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16382 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
16383 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16384 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16385 * work because `$sce` delegates to `$sceDelegate` for these operations.
16386 *
16387 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16388 *
16389 * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
16390 * can override it completely to change the behavior of `$sce`, the common case would
16391 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16392 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16393 * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16394 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16395 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16396 */
16397
16398/**
16399 * @ngdoc provider
16400 * @name $sceDelegateProvider
16401 * @description
16402 *
16403 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16404 * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
16405 * that the URLs used for sourcing Angular templates are safe.  Refer {@link
16406 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16407 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16408 *
16409 * For the general details about this service in Angular, read the main page for {@link ng.$sce
16410 * Strict Contextual Escaping (SCE)}.
16411 *
16412 * **Example**:  Consider the following case. <a name="example"></a>
16413 *
16414 * - your app is hosted at url `http://myapp.example.com/`
16415 * - but some of your templates are hosted on other domains you control such as
16416 *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
16417 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16418 *
16419 * Here is what a secure configuration for this scenario might look like:
16420 *
16421 * ```
16422 *  angular.module('myApp', []).config(function($sceDelegateProvider) {
16423 *    $sceDelegateProvider.resourceUrlWhitelist([
16424 *      // Allow same origin resource loads.
16425 *      'self',
16426 *      // Allow loading from our assets domain.  Notice the difference between * and **.
16427 *      'http://srv*.assets.example.com/**'
16428 *    ]);
16429 *
16430 *    // The blacklist overrides the whitelist so the open redirect here is blocked.
16431 *    $sceDelegateProvider.resourceUrlBlacklist([
16432 *      'http://myapp.example.com/clickThru**'
16433 *    ]);
16434 *  });
16435 * ```
16436 */
16437
16438function $SceDelegateProvider() {
16439  this.SCE_CONTEXTS = SCE_CONTEXTS;
16440
16441  // Resource URLs can also be trusted by policy.
16442  var resourceUrlWhitelist = ['self'],
16443      resourceUrlBlacklist = [];
16444
16445  /**
16446   * @ngdoc method
16447   * @name $sceDelegateProvider#resourceUrlWhitelist
16448   * @kind function
16449   *
16450   * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16451   *     provided.  This must be an array or null.  A snapshot of this array is used so further
16452   *     changes to the array are ignored.
16453   *
16454   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16455   *     allowed in this array.
vendor: 4,148 bytes, lines 16456-16580
16456   *
16457   *     Note: **an empty whitelist array will block all URLs**!
16458   *
16459   * @return {Array} the currently set whitelist array.
16460   *
16461   * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
16462   * same origin resource requests.
16463   *
16464   * @description
16465   * Sets/Gets the whitelist of trusted resource URLs.
16466   */
16467  this.resourceUrlWhitelist = function(value) {
16468    if (arguments.length) {
16469      resourceUrlWhitelist = adjustMatchers(value);
16470    }
16471    return resourceUrlWhitelist;
16472  };
16473
16474  /**
16475   * @ngdoc method
16476   * @name $sceDelegateProvider#resourceUrlBlacklist
16477   * @kind function
16478   *
16479   * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
16480   *     provided.  This must be an array or null.  A snapshot of this array is used so further
16481   *     changes to the array are ignored.
16482   *
16483   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16484   *     allowed in this array.
16485   *
16486   *     The typical usage for the blacklist is to **block
16487   *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
16488   *     these would otherwise be trusted but actually return content from the redirected domain.
16489   *
16490   *     Finally, **the blacklist overrides the whitelist** and has the final say.
16491   *
16492   * @return {Array} the currently set blacklist array.
16493   *
16494   * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
16495   * is no blacklist.)
16496   *
16497   * @description
16498   * Sets/Gets the blacklist of trusted resource URLs.
16499   */
16500
16501  this.resourceUrlBlacklist = function(value) {
16502    if (arguments.length) {
16503      resourceUrlBlacklist = adjustMatchers(value);
16504    }
16505    return resourceUrlBlacklist;
16506  };
16507
16508  this.$get = ['$injector', function($injector) {
16509
16510    var htmlSanitizer = function htmlSanitizer(html) {
16511      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16512    };
16513
16514    if ($injector.has('$sanitize')) {
16515      htmlSanitizer = $injector.get('$sanitize');
16516    }
16517
16518
16519    function matchUrl(matcher, parsedUrl) {
16520      if (matcher === 'self') {
16521        return urlIsSameOrigin(parsedUrl);
16522      } else {
16523        // definitely a regex.  See adjustMatchers()
16524        return !!matcher.exec(parsedUrl.href);
16525      }
16526    }
16527
16528    function isResourceUrlAllowedByPolicy(url) {
16529      var parsedUrl = urlResolve(url.toString());
16530      var i, n, allowed = false;
16531      // Ensure that at least one item from the whitelist allows this url.
16532      for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
16533        if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
16534          allowed = true;
16535          break;
16536        }
16537      }
16538      if (allowed) {
16539        // Ensure that no item from the blacklist blocked this url.
16540        for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
16541          if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
16542            allowed = false;
16543            break;
16544          }
16545        }
16546      }
16547      return allowed;
16548    }
16549
16550    function generateHolderType(Base) {
16551      var holderType = function TrustedValueHolderType(trustedValue) {
16552        this.$$unwrapTrustedValue = function() {
16553          return trustedValue;
16554        };
16555      };
16556      if (Base) {
16557        holderType.prototype = new Base();
16558      }
16559      holderType.prototype.valueOf = function sceValueOf() {
16560        return this.$$unwrapTrustedValue();
16561      };
16562      holderType.prototype.toString = function sceToString() {
16563        return this.$$unwrapTrustedValue().toString();
16564      };
16565      return holderType;
16566    }
16567
16568    var trustedValueHolderBase = generateHolderType(),
16569        byType = {};
16570
16571    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
16572    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
16573    byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
16574    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
16575    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
16576
16577    /**
16578     * @ngdoc method
16579     * @name $sceDelegate#trustAs
16580     *
16581     * @description
16582     * Returns an object that is trusted by angular for use in specified strict
16583     * contextual escaping contexts (such as ng-bind-html, ng-include, any src
16584     * attribute interpolation, any dom event binding attribute interpolation
16585     * such as for onclick,  etc.) that uses the provided value.
16586     * See {@link ng.$sce $sce} for enabling strict contextual escaping.
16587     *
16588     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
16589     *   resourceUrl, html, js and css.
16590     * @param {*} value The value that that should be considered trusted/safe.
16591     * @returns {*} A value that can be used to stand in for the provided `value` in places
16592     * where Angular expects a $sce.trustAs() return value.
16593     */
16594    function trustAs(type, trustedValue) {
16595      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16596      if (!Constructor) {
16597        throw $sceMinErr('icontext',
16598            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
16599            type, trustedValue);
16600      }
16601      if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
16602        return trustedValue;
16603      }
16604      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
16605      // mutable objects, we ensure here that the value passed in is actually a string.
16606      if (typeof trustedValue !== 'string') {
16607        throw $sceMinErr('itype',
16608            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
16609            type);
16610      }
16611      return new Constructor(trustedValue);
16612    }
16613
16614    /**
16615     * @ngdoc method
16616     * @name $sceDelegate#valueOf
16617     *
16618     * @description
16619     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
16620     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
16621     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
16622     *
16623     * If the passed parameter is not a value that had been returned by {@link
16624     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
16625     *
16626     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
16627     *      call or anything else.
16628     * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
16629     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
16630     *     `value` unchanged.
16631     */
16632    function valueOf(maybeTrusted) {
16633      if (maybeTrusted instanceof trustedValueHolderBase) {
16634        return maybeTrusted.$$unwrapTrustedValue();
16635      } else {
16636        return maybeTrusted;
16637      }
16638    }
16639
16640    /**
16641     * @ngdoc method
16642     * @name $sceDelegate#getTrusted
16643     *
16644     * @description
16645     * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
16646     * returns the originally supplied value if the queried context type is a supertype of the
16647     * created type.  If this condition isn't satisfied, throws an exception.
16648     *
16649     * @param {string} type The kind of context in which this value is to be used.
16650     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
16651     *     `$sceDelegate.trustAs`} call.
16652     * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
16653     *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
16654     */
16655    function getTrusted(type, maybeTrusted) {
16656      if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
16657        return maybeTrusted;
16658      }
16659      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16660      if (constructor && maybeTrusted instanceof constructor) {
16661        return maybeTrusted.$$unwrapTrustedValue();
16662      }
16663      // If we get here, then we may only take one of two actions.
16664      // 1. sanitize the value for the requested type, or
16665      // 2. throw an exception.
16666      if (type === SCE_CONTEXTS.RESOURCE_URL) {
16667        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
16668          return maybeTrusted;
16669        } else {
16670          throw $sceMinErr('insecurl',
16671              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
16672              maybeTrusted.toString());
16673        }
16674      } else if (type === SCE_CONTEXTS.HTML) {
16675        return htmlSanitizer(maybeTrusted);
16676      }
16677      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16678    }
16679
16680    return { trustAs: trustAs,
16681             getTrusted: getTrusted,
16682             valueOf: valueOf };
16683  }];
16684}
16685
16686
16687/**
16688 * @ngdoc provider
16689 * @name $sceProvider
16690 * @description
16691 *
16692 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
16693 * -   enable/disable Strict Contextual Escaping (SCE) in a module
16694 * -   override the default implementation with a custom delegate
16695 *
16696 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
16697 */
16698
16699/* jshint maxlen: false*/
16700
16701/**
16702 * @ngdoc service
16703 * @name $sce
16704 * @kind function
16705 *
16706 * @description
16707 *
16708 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
16709 *
16710 * # Strict Contextual Escaping
16711 *
16712 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
16713 * contexts to result in a value that is marked as safe to use for that context.  One example of
16714 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
16715 * to these contexts as privileged or SCE contexts.
16716 *
16717 * As of version 1.2, Angular ships with SCE enabled by default.
16718 *
16719 * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
16720 * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
16721 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
16722 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
16723 * to the top of your HTML document.
16724 *
16725 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
16726 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
16727 *
16728 * Here's an example of a binding in a privileged context:
16729 *
16730 * ```
16731 * <input ng-model="userHtml" aria-label="User input">
16732 * <div ng-bind-html="userHtml"></div>
16733 * ```
16734 *
16735 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
16736 * disabled, this application allows the user to render arbitrary HTML into the DIV.
16737 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
16738 * bindings.  (HTML is just one example of a context where rendering user controlled input creates
16739 * security vulnerabilities.)
16740 *
16741 * For the case of HTML, you might use a library, either on the client side, or on the server side,
16742 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
16743 *
16744 * How would you ensure that every place that used these types of bindings was bound to a value that
16745 * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
16746 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
16747 * properties/fields and forgot to update the binding to the sanitized value?
16748 *
16749 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
16750 * determine that something explicitly says it's safe to use a value for binding in that
16751 * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
16752 * for those values that you can easily tell are safe - because they were received from your server,
16753 * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
16754 * allowing only the files in a specific directory to do this.  Ensuring that the internal API
16755 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
16756 *
16757 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
16758 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
16759 * obtain values that will be accepted by SCE / privileged contexts.
16760 *
16761 *
16762 * ## How does it work?
16763 *
16764 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
16765 * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
16766 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
16767 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
16768 *
16769 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
16770 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
16771 * simplified):
16772 *
16773 * ```
16774 * var ngBindHtmlDirective = ['$sce', function($sce) {
16775 *   return function(scope, element, attr) {
16776 *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
16777 *       element.html(value || '');
16778 *     });
16779 *   };
16780 * }];
16781 * ```
16782 *
16783 * ## Impact on loading templates
16784 *
16785 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
16786 * `templateUrl`'s specified by {@link guide/directive directives}.
16787 *
16788 * By default, Angular only loads templates from the same domain and protocol as the application
16789 * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
16790 * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
16791 * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
16792 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
16793 *
16794 * *Please note*:
16795 * The browser's
16796 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
16797 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
16798 * policy apply in addition to this and may further restrict whether the template is successfully
16799 * loaded.  This means that without the right CORS policy, loading templates from a different domain
16800 * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
16801 * browsers.
16802 *
16803 * ## This feels like too much overhead
16804 *
16805 * It's important to remember that SCE only applies to interpolation expressions.
16806 *
16807 * If your expressions are constant literals, they're automatically trusted and you don't need to
16808 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
16809 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
16810 *
16811 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
16812 * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
16813 *
16814 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
16815 * templates in `ng-include` from your application's domain without having to even know about SCE.
16816 * It blocks loading templates from other domains or loading templates over http from an https
16817 * served document.  You can change these by setting your own custom {@link
16818 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
16819 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
16820 *
16821 * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
16822 * application that's secure and can be audited to verify that with much more ease than bolting
16823 * security onto an application later.
16824 *
16825 * <a name="contexts"></a>
16826 * ## What trusted context types are supported?
16827 *
16828 * | Context             | Notes          |
16829 * |---------------------|----------------|
16830 * | `$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. |
16831 * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
16832 * | `$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. |
16833 * | `$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. |
16834 * | `$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. |
16835 *
16836 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
16837 *
16838 *  Each element in these arrays must be one of the following:
16839 *
16840 *  - **'self'**
16841 *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
16842 *      domain** as the application document using the **same protocol**.
16843 *  - **String** (except the special value `'self'`)
16844 *    - The string is matched against the full *normalized / absolute URL* of the resource
16845 *      being tested (substring matches are not good enough.)
16846 *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
16847 *      match themselves.
16848 *    - `*`: matches zero or more occurrences of any character other than one of the following 6
16849 *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'.  It's a useful wildcard for use
16850 *      in a whitelist.
16851 *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
16852 *      not appropriate to use in for a scheme, domain, etc. as it would match too much.  (e.g.
16853 *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
16854 *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
16855 *      http://foo.example.com/templates/**).
16856 *  - **RegExp** (*see caveat below*)
16857 *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
16858 *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
16859 *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
16860 *      have good test coverage.).  For instance, the use of `.` in the regex is correct only in a
16861 *      small number of cases.  A `.` character in the regex used when matching the scheme or a
16862 *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
16863 *      is highly recommended to use the string patterns and only fall back to regular expressions
16864 *      if they as a last resort.
16865 *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
16866 *      matched against the **entire** *normalized / absolute URL* of the resource being tested
16867 *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
16868 *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
16869 *    - If you are generating your JavaScript from some other templating engine (not
16870 *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
16871 *      remember to escape your regular expression (and be aware that you might need more than
16872 *      one level of escaping depending on your templating engine and the way you interpolated
16873 *      the value.)  Do make use of your platform's escaping mechanism as it might be good
16874 *      enough before coding your own.  e.g. Ruby has
16875 *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
16876 *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
16877 *      Javascript lacks a similar built in function for escaping.  Take a look at Google
16878 *      Closure library's [goog.string.regExpEscape(s)](
16879 *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
16880 *
16881 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
16882 *
16883 * ## Show me an example using SCE.
16884 *
16885 * <example module="mySceApp" deps="angular-sanitize.js">
16886 * <file name="index.html">
16887 *   <div ng-controller="AppController as myCtrl">
16888 *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
16889 *     <b>User comments</b><br>
16890 *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
16891 *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
16892 *     exploit.
16893 *     <div class="well">
16894 *       <div ng-repeat="userComment in myCtrl.userComments">
16895 *         <b>{{userComment.name}}</b>:
16896 *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
16897 *         <br>
16898 *       </div>
16899 *     </div>
16900 *   </div>
16901 * </file>
16902 *
16903 * <file name="script.js">
16904 *   angular.module('mySceApp', ['ngSanitize'])
16905 *     .controller('AppController', ['$http', '$templateCache', '$sce',
16906 *       function($http, $templateCache, $sce) {
16907 *         var self = this;
16908 *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
16909 *           self.userComments = userComments;
16910 *         });
16911 *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
16912 *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
16913 *             'sanitization.&quot;">Hover over this text.</span>');
16914 *       }]);
16915 * </file>
16916 *
16917 * <file name="test_data.json">
16918 * [
16919 *   { "name": "Alice",
16920 *     "htmlComment":
16921 *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
16922 *   },
16923 *   { "name": "Bob",
16924 *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
16925 *   }
16926 * ]
16927 * </file>
16928 *
16929 * <file name="protractor.js" type="protractor">
16930 *   describe('SCE doc demo', function() {
16931 *     it('should sanitize untrusted values', function() {
16932 *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
16933 *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
16934 *     });
16935 *
16936 *     it('should NOT sanitize explicitly trusted values', function() {
16937 *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
16938 *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
16939 *           'sanitization.&quot;">Hover over this text.</span>');
16940 *     });
16941 *   });
16942 * </file>
16943 * </example>
16944 *
16945 *
16946 *
16947 * ## Can I disable SCE completely?
16948 *
16949 * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
16950 * for little coding overhead.  It will be much harder to take an SCE disabled application and
16951 * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
16952 * for cases where you have a lot of existing code that was written before SCE was introduced and
16953 * you're migrating them a module at a time.
16954 *
16955 * That said, here's how you can completely disable SCE:
16956 *
16957 * ```
16958 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
16959 *   // Completely disable SCE.  For demonstration purposes only!
16960 *   // Do not use in new projects.
16961 *   $sceProvider.enabled(false);
16962 * });
16963 * ```
16964 *
16965 */
16966/* jshint maxlen: 100 */
16967
16968function $SceProvider() {
16969  var enabled = true;
16970
16971  /**
16972   * @ngdoc method
16973   * @name $sceProvider#enabled
16974   * @kind function
16975   *
16976   * @param {boolean=} value If provided, then enables/disables SCE.
16977   * @return {boolean} true if SCE is enabled, false otherwise.
16978   *
16979   * @description
16980   * Enables/disables SCE and returns the current value.
16981   */
16982  this.enabled = function(value) {
16983    if (arguments.length) {
16984      enabled = !!value;
16985    }
16986    return enabled;
16987  };
16988
16989
16990  /* Design notes on the default implementation for SCE.
16991   *
16992   * The API contract for the SCE delegate
16993   * -------------------------------------
16994   * The SCE delegate object must provide the following 3 methods:
16995   *
16996   * - trustAs(contextEnum, value)
16997   *     This method is used to tell the SCE service that the provided value is OK to use in the
16998   *     contexts specified by contextEnum.  It must return an object that will be accepted by
16999   *     getTrusted() for a compatible contextEnum and return this value.
17000   *
17001   * - valueOf(value)
17002   *     For values that were not produced by trustAs(), return them as is.  For values that were
17003   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
17004   *     trustAs is wrapping the given values into some type, this operation unwraps it when given
17005   *     such a value.
17006   *
17007   * - getTrusted(contextEnum, value)
17008   *     This function should return the a value that is safe to use in the context specified by
17009   *     contextEnum or throw and exception otherwise.
17010   *
17011   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17012   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
17013   * instance, an implementation could maintain a registry of all trusted objects by context.  In
17014   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
17015   * return the same object passed in if it was found in the registry under a compatible context or
17016   * throw an exception otherwise.  An implementation might only wrap values some of the time based
17017   * on some criteria.  getTrusted() might return a value and not throw an exception for special
17018   * constants or objects even if not wrapped.  All such implementations fulfill this contract.
17019   *
17020   *
17021   * A note on the inheritance model for SCE contexts
17022   * ------------------------------------------------
17023   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
17024   * is purely an implementation details.
17025   *
17026   * The contract is simply this:
17027   *
17028   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17029   *     will also succeed.
17030   *
17031   * Inheritance happens to capture this in a natural way.  In some future, we
17032   * may not use inheritance anymore.  That is OK because no code outside of
17033   * sce.js and sceSpecs.js would need to be aware of this detail.
17034   */
17035
17036  this.$get = ['$parse', '$sceDelegate', function(
17037                $parse,   $sceDelegate) {
17038    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
17039    // the "expression(javascript expression)" syntax which is insecure.
17040    if (enabled && msie < 8) {
17041      throw $sceMinErr('iequirks',
17042        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17043        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17044        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
17045    }
17046
17047    var sce = shallowCopy(SCE_CONTEXTS);
17048
17049    /**
17050     * @ngdoc method
17051     * @name $sce#isEnabled
17052     * @kind function
17053     *
17054     * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
17055     * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17056     *
17057     * @description
17058     * Returns a boolean indicating if SCE is enabled.
17059     */
17060    sce.isEnabled = function() {
17061      return enabled;
17062    };
17063    sce.trustAs = $sceDelegate.trustAs;
17064    sce.getTrusted = $sceDelegate.getTrusted;
17065    sce.valueOf = $sceDelegate.valueOf;
17066
17067    if (!enabled) {
17068      sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17069      sce.valueOf = identity;
17070    }
17071
17072    /**
17073     * @ngdoc method
17074     * @name $sce#parseAs
17075     *
17076     * @description
17077     * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
17078     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
17079     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17080     * *result*)}
17081     *
17082     * @param {string} type The kind of SCE context in which this result will be used.
17083     * @param {string} expression String expression to compile.
17084     * @returns {function(context, locals)} a function which represents the compiled expression:
17085     *
17086     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17087     *      are evaluated against (typically a scope object).
17088     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17089     *      `context`.
17090     */
17091    sce.parseAs = function sceParseAs(type, expr) {
17092      var parsed = $parse(expr);
17093      if (parsed.literal && parsed.constant) {
17094        return parsed;
17095      } else {
17096        return $parse(expr, function(value) {
17097          return sce.getTrusted(type, value);
17098        });
17099      }
17100    };
17101
17102    /**
17103     * @ngdoc method
17104     * @name $sce#trustAs
17105     *
17106     * @description
17107     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
17108     * returns an object that is trusted by angular for use in specified strict contextual
17109     * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17110     * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
17111     * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
17112     * escaping.
17113     *
17114     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17115     *   resourceUrl, html, js and css.
17116     * @param {*} value The value that that should be considered trusted/safe.
17117     * @returns {*} A value that can be used to stand in for the provided `value` in places
17118     * where Angular expects a $sce.trustAs() return value.
17119     */
17120
17121    /**
17122     * @ngdoc method
17123     * @name $sce#trustAsHtml
17124     *
17125     * @description
17126     * Shorthand method.  `$sce.trustAsHtml(value)` →
17127     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17128     *
17129     * @param {*} value The value to trustAs.
17130     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17131     *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
17132     *     only accept expressions that are either literal constants or are the
17133     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17134     */
17135
17136    /**
17137     * @ngdoc method
17138     * @name $sce#trustAsUrl
17139     *
17140     * @description
17141     * Shorthand method.  `$sce.trustAsUrl(value)` →
17142     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17143     *
17144     * @param {*} value The value to trustAs.
17145     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17146     *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
17147     *     only accept expressions that are either literal constants or are the
17148     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17149     */
17150
17151    /**
17152     * @ngdoc method
17153     * @name $sce#trustAsResourceUrl
17154     *
17155     * @description
17156     * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
17157     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17158     *
17159     * @param {*} value The value to trustAs.
17160     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17161     *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
17162     *     only accept expressions that are either literal constants or are the return
17163     *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
17164     */
17165
17166    /**
17167     * @ngdoc method
17168     * @name $sce#trustAsJs
17169     *
17170     * @description
17171     * Shorthand method.  `$sce.trustAsJs(value)` →
17172     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17173     *
17174     * @param {*} value The value to trustAs.
17175     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17176     *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
17177     *     only accept expressions that are either literal constants or are the
17178     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17179     */
17180
17181    /**
17182     * @ngdoc method
17183     * @name $sce#getTrusted
17184     *
17185     * @description
17186     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
17187     * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17188     * originally supplied value if the queried context type is a supertype of the created type.
17189     * If this condition isn't satisfied, throws an exception.
17190     *
17191     * @param {string} type The kind of context in which this value is to be used.
17192     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17193     *                         call.
17194     * @returns {*} The value the was originally provided to
17195     *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17196     *              Otherwise, throws an exception.
17197     */
17198
17199    /**
17200     * @ngdoc method
17201     * @name $sce#getTrustedHtml
17202     *
17203     * @description
17204     * Shorthand method.  `$sce.getTrustedHtml(value)` →
17205     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17206     *
17207     * @param {*} value The value to pass to `$sce.getTrusted`.
17208     * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17209     */
17210
17211    /**
17212     * @ngdoc method
17213     * @name $sce#getTrustedCss
17214     *
17215     * @description
17216     * Shorthand method.  `$sce.getTrustedCss(value)` →
17217     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17218     *
17219     * @param {*} value The value to pass to `$sce.getTrusted`.
17220     * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17221     */
17222
17223    /**
17224     * @ngdoc method
17225     * @name $sce#getTrustedUrl
17226     *
17227     * @description
17228     * Shorthand method.  `$sce.getTrustedUrl(value)` →
17229     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17230     *
17231     * @param {*} value The value to pass to `$sce.getTrusted`.
17232     * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17233     */
17234
17235    /**
17236     * @ngdoc method
17237     * @name $sce#getTrustedResourceUrl
17238     *
17239     * @description
17240     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
17241     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17242     *
17243     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17244     * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17245     */
17246
17247    /**
17248     * @ngdoc method
17249     * @name $sce#getTrustedJs
17250     *
17251     * @description
17252     * Shorthand method.  `$sce.getTrustedJs(value)` →
17253     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17254     *
17255     * @param {*} value The value to pass to `$sce.getTrusted`.
17256     * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17257     */
17258
17259    /**
17260     * @ngdoc method
17261     * @name $sce#parseAsHtml
17262     *
17263     * @description
17264     * Shorthand method.  `$sce.parseAsHtml(expression string)` →
17265     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17266     *
17267     * @param {string} expression String expression to compile.
17268     * @returns {function(context, locals)} a function which represents the compiled expression:
17269     *
17270     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17271     *      are evaluated against (typically a scope object).
17272     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17273     *      `context`.
17274     */
17275
17276    /**
17277     * @ngdoc method
17278     * @name $sce#parseAsCss
17279     *
17280     * @description
17281     * Shorthand method.  `$sce.parseAsCss(value)` →
17282     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17283     *
17284     * @param {string} expression String expression to compile.
17285     * @returns {function(context, locals)} a function which represents the compiled expression:
17286     *
17287     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17288     *      are evaluated against (typically a scope object).
17289     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17290     *      `context`.
17291     */
17292
17293    /**
17294     * @ngdoc method
17295     * @name $sce#parseAsUrl
17296     *
17297     * @description
17298     * Shorthand method.  `$sce.parseAsUrl(value)` →
17299     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17300     *
17301     * @param {string} expression String expression to compile.
17302     * @returns {function(context, locals)} a function which represents the compiled expression:
17303     *
17304     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17305     *      are evaluated against (typically a scope object).
17306     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17307     *      `context`.
17308     */
17309
17310    /**
17311     * @ngdoc method
17312     * @name $sce#parseAsResourceUrl
17313     *
17314     * @description
17315     * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
17316     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17317     *
17318     * @param {string} expression String expression to compile.
17319     * @returns {function(context, locals)} a function which represents the compiled expression:
17320     *
17321     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17322     *      are evaluated against (typically a scope object).
17323     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17324     *      `context`.
17325     */
17326
17327    /**
17328     * @ngdoc method
17329     * @name $sce#parseAsJs
17330     *
17331     * @description
17332     * Shorthand method.  `$sce.parseAsJs(value)` →
17333     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17334     *
17335     * @param {string} expression String expression to compile.
17336     * @returns {function(context, locals)} a function which represents the compiled expression:
17337     *
17338     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17339     *      are evaluated against (typically a scope object).
17340     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17341     *      `context`.
17342     */
17343
17344    // Shorthand delegations.
17345    var parse = sce.parseAs,
17346        getTrusted = sce.getTrusted,
17347        trustAs = sce.trustAs;
17348
17349    forEach(SCE_CONTEXTS, function(enumValue, name) {
17350      var lName = lowercase(name);
17351      sce[camelCase("parse_as_" + lName)] = function(expr) {
17352        return parse(enumValue, expr);
17353      };
17354      sce[camelCase("get_trusted_" + lName)] = function(value) {
17355        return getTrusted(enumValue, value);
17356      };
17357      sce[camelCase("trust_as_" + lName)] = function(value) {
17358        return trustAs(enumValue, value);
17359      };
17360    });
17361
17362    return sce;
17363  }];
17364}
17365
17366/**
17367 * !!! This is an undocumented "private" service !!!
17368 *
17369 * @name $sniffer
17370 * @requires $window
17371 * @requires $document
17372 *
17373 * @property {boolean} history Does the browser support html5 history api ?
17374 * @property {boolean} transitions Does the browser support CSS transition events ?
17375 * @property {boolean} animations Does the browser support CSS animation events ?
17376 *
17377 * @description
17378 * This is very simple implementation of testing browser's features.
17379 */
17380function $SnifferProvider() {
17381  this.$get = ['$window', '$document', function($window, $document) {
17382    var eventSupport = {},
17383        android =
17384          toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17385        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17386        document = $document[0] || {},
17387        vendorPrefix,
17388        vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17389        bodyStyle = document.body && document.body.style,
17390        transitions = false,
17391        animations = false,
17392        match;
17393
17394    if (bodyStyle) {
17395      for (var prop in bodyStyle) {
17396        if (match = vendorRegex.exec(prop)) {
17397          vendorPrefix = match[0];
17398          vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17399          break;
17400        }
17401      }
17402
17403      if (!vendorPrefix) {
17404        vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17405      }
17406
17407      transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17408      animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17409
17410      if (android && (!transitions ||  !animations)) {
17411        transitions = isString(bodyStyle.webkitTransition);
17412        animations = isString(bodyStyle.webkitAnimation);
17413      }
17414    }
17415
17416
17417    return {
17418      // Android has history.pushState, but it does not update location correctly
17419      // so let's not use the history API at all.
17420      // http://code.google.com/p/android/issues/detail?id=17471
17421      // https://github.com/angular/angular.js/issues/904
17422
17423      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17424      // so let's not use the history API also
17425      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17426      // jshint -W018
17427      history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17428      // jshint +W018
17429      hasEvent: function(event) {
17430        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17431        // it. In particular the event is not fired when backspace or delete key are pressed or
17432        // when cut operation is performed.
17433        // IE10+ implements 'input' event but it erroneously fires under various situations,
17434        // e.g. when placeholder changes, or a form is focused.
17435        if (event === 'input' && msie <= 11) return false;
17436
17437        if (isUndefined(eventSupport[event])) {
17438          var divElm = document.createElement('div');
17439          eventSupport[event] = 'on' + event in divElm;
17440        }
17441
17442        return eventSupport[event];
17443      },
17444      csp: csp(),
17445      vendorPrefix: vendorPrefix,
17446      transitions: transitions,
17447      animations: animations,
17448      android: android
17449    };
17450  }];
17451}
17452
17453var $compileMinErr = minErr('$compile');
17454
17455/**
17456 * @ngdoc service
17457 * @name $templateRequest
17458 *
17459 * @description
17460 * The `$templateRequest` service downloads the provided template using `$http` and, upon success,
17461 * stores the contents inside of `$templateCache`. If the HTTP request fails or the response data
17462 * of the HTTP request is empty, a `$compile` error will be thrown (the exception can be thwarted
17463 * by setting the 2nd parameter of the function to true).
17464 *
17465 * @param {string} tpl The HTTP request template URL
17466 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
17467 *
17468 * @return {Promise} a promise for the HTTP response data of the given URL.
17469 *
17470 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
17471 */
17472function $TemplateRequestProvider() {
17473  this.$get = ['$templateCache', '$http', '$q', function($templateCache, $http, $q) {
17474    function handleRequestFn(tpl, ignoreRequestError) {
17475      handleRequestFn.totalPendingRequests++;
17476
17477      var transformResponse = $http.defaults && $http.defaults.transformResponse;
17478
17479      if (isArray(transformResponse)) {
17480        transformResponse = transformResponse.filter(function(transformer) {
17481          return transformer !== defaultHttpResponseTransform;
17482        });
17483      } else if (transformResponse === defaultHttpResponseTransform) {
17484        transformResponse = null;
17485      }
17486
17487      var httpOptions = {
17488        cache: $templateCache,
17489        transformResponse: transformResponse
17490      };
17491
17492      return $http.get(tpl, httpOptions)
17493        ['finally'](function() {
17494          handleRequestFn.totalPendingRequests--;
17495        })
17496        .then(function(response) {
17497          $templateCache.put(tpl, response.data);
17498          return response.data;
17499        }, handleError);
17500
17501      function handleError(resp) {
17502        if (!ignoreRequestError) {
17503          throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
17504            tpl, resp.status, resp.statusText);
17505        }
17506        return $q.reject(resp);
17507      }
17508    }
17509
17510    handleRequestFn.totalPendingRequests = 0;
17511
17512    return handleRequestFn;
17513  }];
17514}
17515
vendor: 5,440 bytes, lines 17516-17669
17516function $$TestabilityProvider() {
17517  this.$get = ['$rootScope', '$browser', '$location',
17518       function($rootScope,   $browser,   $location) {
17519
17520    /**
17521     * @name $testability
17522     *
17523     * @description
17524     * The private $$testability service provides a collection of methods for use when debugging
17525     * or by automated test and debugging tools.
17526     */
17527    var testability = {};
17528
17529    /**
17530     * @name $$testability#findBindings
17531     *
17532     * @description
17533     * Returns an array of elements that are bound (via ng-bind or {{}})
17534     * to expressions matching the input.
17535     *
17536     * @param {Element} element The element root to search from.
17537     * @param {string} expression The binding expression to match.
17538     * @param {boolean} opt_exactMatch If true, only returns exact matches
17539     *     for the expression. Filters and whitespace are ignored.
17540     */
17541    testability.findBindings = function(element, expression, opt_exactMatch) {
17542      var bindings = element.getElementsByClassName('ng-binding');
17543      var matches = [];
17544      forEach(bindings, function(binding) {
17545        var dataBinding = angular.element(binding).data('$binding');
17546        if (dataBinding) {
17547          forEach(dataBinding, function(bindingName) {
17548            if (opt_exactMatch) {
17549              var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
17550              if (matcher.test(bindingName)) {
17551                matches.push(binding);
17552              }
17553            } else {
17554              if (bindingName.indexOf(expression) != -1) {
17555                matches.push(binding);
17556              }
17557            }
17558          });
17559        }
17560      });
17561      return matches;
17562    };
17563
17564    /**
17565     * @name $$testability#findModels
17566     *
17567     * @description
17568     * Returns an array of elements that are two-way found via ng-model to
17569     * expressions matching the input.
17570     *
17571     * @param {Element} element The element root to search from.
17572     * @param {string} expression The model expression to match.
17573     * @param {boolean} opt_exactMatch If true, only returns exact matches
17574     *     for the expression.
17575     */
17576    testability.findModels = function(element, expression, opt_exactMatch) {
17577      var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
17578      for (var p = 0; p < prefixes.length; ++p) {
17579        var attributeEquals = opt_exactMatch ? '=' : '*=';
17580        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
17581        var elements = element.querySelectorAll(selector);
17582        if (elements.length) {
17583          return elements;
17584        }
17585      }
17586    };
17587
17588    /**
17589     * @name $$testability#getLocation
17590     *
17591     * @description
17592     * Shortcut for getting the location in a browser agnostic way. Returns
17593     *     the path, search, and hash. (e.g. /path?a=b#hash)
17594     */
17595    testability.getLocation = function() {
17596      return $location.url();
17597    };
17598
17599    /**
17600     * @name $$testability#setLocation
17601     *
17602     * @description
17603     * Shortcut for navigating to a location without doing a full page reload.
17604     *
17605     * @param {string} url The location url (path, search and hash,
17606     *     e.g. /path?a=b#hash) to go to.
17607     */
17608    testability.setLocation = function(url) {
17609      if (url !== $location.url()) {
17610        $location.url(url);
17611        $rootScope.$digest();
17612      }
17613    };
17614
17615    /**
17616     * @name $$testability#whenStable
17617     *
17618     * @description
17619     * Calls the callback when $timeout and $http requests are completed.
17620     *
17621     * @param {function} callback
17622     */
17623    testability.whenStable = function(callback) {
17624      $browser.notifyWhenNoOutstandingRequests(callback);
17625    };
17626
17627    return testability;
17628  }];
17629}
17630
17631function $TimeoutProvider() {
17632  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
17633       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
17634
17635    var deferreds = {};
17636
17637
17638     /**
17639      * @ngdoc service
17640      * @name $timeout
17641      *
17642      * @description
17643      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
17644      * block and delegates any exceptions to
17645      * {@link ng.$exceptionHandler $exceptionHandler} service.
17646      *
17647      * The return value of calling `$timeout` is a promise, which will be resolved when
17648      * the delay has passed and the timeout function, if provided, is executed.
17649      *
17650      * To cancel a timeout request, call `$timeout.cancel(promise)`.
17651      *
17652      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
17653      * synchronously flush the queue of deferred functions.
17654      *
17655      * If you only want a promise that will be resolved after some specified delay
17656      * then you can call `$timeout` without the `fn` function.
17657      *
17658      * @param {function()=} fn A function, whose execution should be delayed.
17659      * @param {number=} [delay=0] Delay in milliseconds.
17660      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
17661      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
17662      * @param {...*=} Pass additional parameters to the executed function.
17663      * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
17664      *   promise will be resolved with is the return value of the `fn` function.
17665      *
17666      */
17667    function timeout(fn, delay, invokeApply) {
17668      if (!isFunction(fn)) {
17669        invokeApply = delay;
17670        delay = fn;
17671        fn = noop;
17672      }
17673
17674      var args = sliceArgs(arguments, 3),
17675          skipApply = (isDefined(invokeApply) && !invokeApply),
17676          deferred = (skipApply ? $$q : $q).defer(),
17677          promise = deferred.promise,
17678          timeoutId;
17679
17680      timeoutId = $browser.defer(function() {
17681        try {
17682          deferred.resolve(fn.apply(null, args));
17683        } catch (e) {
17684          deferred.reject(e);
17685          $exceptionHandler(e);
17686        }
17687        finally {
17688          delete deferreds[promise.$$timeoutId];
17689        }
17690
17691        if (!skipApply) $rootScope.$apply();
17692      }, delay);
17693
17694      promise.$$timeoutId = timeoutId;
17695      deferreds[timeoutId] = deferred;
17696
17697      return promise;
17698    }
17699
17700
17701     /**
17702      * @ngdoc method
17703      * @name $timeout#cancel
17704      *
17705      * @description
17706      * Cancels a task associated with the `promise`. As a result of this, the promise will be
17707      * resolved with a rejection.
17708      *
17709      * @param {Promise=} promise Promise returned by the `$timeout` function.
17710      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
17711      *   canceled.
17712      */
17713    timeout.cancel = function(promise) {
17714      if (promise && promise.$$timeoutId in deferreds) {
17715        deferreds[promise.$$timeoutId].reject('canceled');
17716        delete deferreds[promise.$$timeoutId];
17717        return $browser.defer.cancel(promise.$$timeoutId);
17718      }
17719      return false;
17720    };
17721
17722    return timeout;
17723  }];
17724}
17725
17726// NOTE:  The usage of window and document instead of $window and $document here is
17727// deliberate.  This service depends on the specific behavior of anchor nodes created by the
17728// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
17729// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
17730// doesn't know about mocked locations and resolves URLs to the real document - which is
17731// exactly the behavior needed here.  There is little value is mocking these out for this
17732// service.
17733var urlParsingNode = document.createElement("a");
17734var originUrl = urlResolve(window.location.href);
17735
17736
17737/**
17738 *
17739 * Implementation Notes for non-IE browsers
17740 * ----------------------------------------
17741 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
17742 * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
17743 * URL will be resolved into an absolute URL in the context of the application document.
17744 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
17745 * properties are all populated to reflect the normalized URL.  This approach has wide
17746 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
17747 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
17748 *
17749 * Implementation Notes for IE
17750 * ---------------------------
17751 * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other
17752 * browsers.  However, the parsed components will not be set if the URL assigned did not specify
17753 * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
17754 * work around that by performing the parsing in a 2nd step by taking a previously normalized
17755 * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
17756 * properties such as protocol, hostname, port, etc.
17757 *
17758 * IE7 does not normalize the URL when assigned to an anchor node.  (Apparently, it does, if one
17759 * uses the inner HTML approach to assign the URL as part of an HTML snippet -
17760 * http://stackoverflow.com/a/472729)  However, setting img[src] does normalize the URL.
17761 * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception.
17762 * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that
17763 * method and IE < 8 is unsupported.
17764 *
17765 * References:
17766 *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
17767 *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
17768 *   http://url.spec.whatwg.org/#urlutils
17769 *   https://github.com/angular/angular.js/pull/2902
17770 *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
17771 *
17772 * @kind function
17773 * @param {string} url The URL to be parsed.
17774 * @description Normalizes and parses a URL.
17775 * @returns {object} Returns the normalized URL as a dictionary.
17776 *
17777 *   | member name   | Description    |
17778 *   |---------------|----------------|
17779 *   | href          | A normalized version of the provided URL if it was not an absolute URL |
17780 *   | protocol      | The protocol including the trailing colon                              |
17781 *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
17782 *   | search        | The search params, minus the question mark                             |
17783 *   | hash          | The hash string, minus the hash symbol
17784 *   | hostname      | The hostname
17785 *   | port          | The port, without ":"
17786 *   | pathname      | The pathname, beginning with "/"
17787 *
17788 */
17789function urlResolve(url) {
17790  var href = url;
17791
17792  if (msie) {
17793    // Normalize before parse.  Refer Implementation Notes on why this is
17794    // done in two steps on IE.
17795    urlParsingNode.setAttribute("href", href);
17796    href = urlParsingNode.href;
17797  }
17798
17799  urlParsingNode.setAttribute('href', href);
17800
17801  // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
17802  return {
17803    href: urlParsingNode.href,
17804    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
17805    host: urlParsingNode.host,
17806    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
17807    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
17808    hostname: urlParsingNode.hostname,
17809    port: urlParsingNode.port,
17810    pathname: (urlParsingNode.pathname.charAt(0) === '/')
17811      ? urlParsingNode.pathname
17812      : '/' + urlParsingNode.pathname
17813  };
17814}
17815
17816/**
17817 * Parse a request URL and determine whether this is a same-origin request as the application document.
17818 *
17819 * @param {string|object} requestUrl The url of the request as a string that will be resolved
17820 * or a parsed URL object.
17821 * @returns {boolean} Whether the request is for the same origin as the application document.
17822 */
17823function urlIsSameOrigin(requestUrl) {
17824  var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
17825  return (parsed.protocol === originUrl.protocol &&
17826          parsed.host === originUrl.host);
17827}
17828
17829/**
17830 * @ngdoc service
17831 * @name $window
17832 *
17833 * @description
17834 * A reference to the browser's `window` object. While `window`
17835 * is globally available in JavaScript, it causes testability problems, because
17836 * it is a global variable. In angular we always refer to it through the
17837 * `$window` service, so it may be overridden, removed or mocked for testing.
17838 *
17839 * Expressions, like the one defined for the `ngClick` directive in the example
17840 * below, are evaluated with respect to the current scope.  Therefore, there is
17841 * no risk of inadvertently coding in a dependency on a global value in such an
17842 * expression.
17843 *
17844 * @example
17845   <example module="windowExample">
17846     <file name="index.html">
17847       <script>
17848         angular.module('windowExample', [])
17849           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
17850             $scope.greeting = 'Hello, World!';
17851             $scope.doGreeting = function(greeting) {
17852               $window.alert(greeting);
17853             };
17854           }]);
17855       </script>
17856       <div ng-controller="ExampleController">
17857         <input type="text" ng-model="greeting" aria-label="greeting" />
17858         <button ng-click="doGreeting(greeting)">ALERT</button>
17859       </div>
17860     </file>
17861     <file name="protractor.js" type="protractor">
17862      it('should display the greeting in the input box', function() {
17863       element(by.model('greeting')).sendKeys('Hello, E2E Tests');
17864       // If we click the button it will block the test runner
17865       // element(':button').click();
17866      });
17867     </file>
17868   </example>
17869 */
17870function $WindowProvider() {
17871  this.$get = valueFn(window);
17872}
17873
17874/**
17875 * @name $$cookieReader
17876 * @requires $document
17877 *
17878 * @description
17879 * This is a private service for reading cookies used by $http and ngCookies
17880 *
17881 * @return {Object} a key/value map of the current cookies
17882 */
17883function $$CookieReader($document) {
17884  var rawDocument = $document[0] || {};
17885  var lastCookies = {};
17886  var lastCookieString = '';
17887
17888  function safeDecodeURIComponent(str) {
17889    try {
17890      return decodeURIComponent(str);
17891    } catch (e) {
17892      return str;
17893    }
17894  }
17895
17896  return function() {
17897    var cookieArray, cookie, i, index, name;
17898    var currentCookieString = rawDocument.cookie || '';
17899
17900    if (currentCookieString !== lastCookieString) {
17901      lastCookieString = currentCookieString;
17902      cookieArray = lastCookieString.split('; ');
17903      lastCookies = {};
17904
17905      for (i = 0; i < cookieArray.length; i++) {
17906        cookie = cookieArray[i];
17907        index = cookie.indexOf('=');
17908        if (index > 0) { //ignore nameless cookies
17909          name = safeDecodeURIComponent(cookie.substring(0, index));
17910          // the first value that is seen for a cookie is the most
17911          // specific one.  values for the same cookie name that
17912          // follow are for less specific paths.
17913          if (lastCookies[name] === undefined) {
17914            lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
17915          }
17916        }
17917      }
17918    }
17919    return lastCookies;
17920  };
17921}
17922
17923$$CookieReader.$inject = ['$document'];
17924
17925function $$CookieReaderProvider() {
17926  this.$get = $$CookieReader;
17927}
17928
17929/* global currencyFilter: true,
17930 dateFilter: true,
17931 filterFilter: true,
17932 jsonFilter: true,
17933 limitToFilter: true,
17934 lowercaseFilter: true,
17935 numberFilter: true,
17936 orderByFilter: true,
17937 uppercaseFilter: true,
17938 */
17939
17940/**
17941 * @ngdoc provider
17942 * @name $filterProvider
17943 * @description
17944 *
17945 * Filters are just functions which transform input to an output. However filters need to be
17946 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
17947 * annotated with dependencies and is responsible for creating a filter function.
17948 *
17949 * <div class="alert alert-warning">
17950 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
17951 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
17952 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
17953 * (`myapp_subsection_filterx`).
17954 * </div>
17955 *
17956 * ```js
17957 *   // Filter registration
17958 *   function MyModule($provide, $filterProvider) {
17959 *     // create a service to demonstrate injection (not always needed)
17960 *     $provide.value('greet', function(name){
17961 *       return 'Hello ' + name + '!';
17962 *     });
17963 *
17964 *     // register a filter factory which uses the
17965 *     // greet service to demonstrate DI.
17966 *     $filterProvider.register('greet', function(greet){
17967 *       // return the filter function which uses the greet service
17968 *       // to generate salutation
17969 *       return function(text) {
17970 *         // filters need to be forgiving so check input validity
17971 *         return text && greet(text) || text;
17972 *       };
17973 *     });
17974 *   }
17975 * ```
17976 *
17977 * The filter function is registered with the `$injector` under the filter name suffix with
17978 * `Filter`.
17979 *
17980 * ```js
17981 *   it('should be the same instance', inject(
17982 *     function($filterProvider) {
17983 *       $filterProvider.register('reverse', function(){
17984 *         return ...;
17985 *       });
17986 *     },
17987 *     function($filter, reverseFilter) {
17988 *       expect($filter('reverse')).toBe(reverseFilter);
17989 *     });
17990 * ```
17991 *
17992 *
17993 * For more information about how angular filters work, and how to create your own filters, see
17994 * {@link guide/filter Filters} in the Angular Developer Guide.
17995 */
17996
17997/**
17998 * @ngdoc service
17999 * @name $filter
18000 * @kind function
18001 * @description
18002 * Filters are used for formatting data displayed to the user.
18003 *
18004 * The general syntax in templates is as follows:
18005 *
18006 *         {{ expression [| filter_name[:parameter_value] ... ] }}
18007 *
18008 * @param {String} name Name of the filter function to retrieve
18009 * @return {Function} the filter function
18010 * @example
18011   <example name="$filter" module="filterExample">
18012     <file name="index.html">
18013       <div ng-controller="MainCtrl">
18014        <h3>{{ originalText }}</h3>
18015        <h3>{{ filteredText }}</h3>
18016       </div>
18017     </file>
18018
18019     <file name="script.js">
18020      angular.module('filterExample', [])
18021      .controller('MainCtrl', function($scope, $filter) {
18022        $scope.originalText = 'hello';
18023        $scope.filteredText = $filter('uppercase')($scope.originalText);
18024      });
18025     </file>
18026   </example>
18027  */
18028$FilterProvider.$inject = ['$provide'];
18029function $FilterProvider($provide) {
18030  var suffix = 'Filter';
18031
18032  /**
18033   * @ngdoc method
18034   * @name $filterProvider#register
18035   * @param {string|Object} name Name of the filter function, or an object map of filters where
18036   *    the keys are the filter names and the values are the filter factories.
18037   *
18038   *    <div class="alert alert-warning">
18039   *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18040   *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18041   *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18042   *    (`myapp_subsection_filterx`).
18043   *    </div>
18044   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18045   *    of the registered filter instances.
18046   */
18047  function register(name, factory) {
18048    if (isObject(name)) {
18049      var filters = {};
18050      forEach(name, function(filter, key) {
18051        filters[key] = register(key, filter);
18052      });
18053      return filters;
18054    } else {
18055      return $provide.factory(name + suffix, factory);
18056    }
18057  }
18058  this.register = register;
18059
18060  this.$get = ['$injector', function($injector) {
18061    return function(name) {
18062      return $injector.get(name + suffix);
18063    };
18064  }];
18065
18066  ////////////////////////////////////////
18067
18068  /* global
18069    currencyFilter: false,
18070    dateFilter: false,
18071    filterFilter: false,
18072    jsonFilter: false,
18073    limitToFilter: false,
18074    lowercaseFilter: false,
18075    numberFilter: false,
18076    orderByFilter: false,
18077    uppercaseFilter: false,
18078  */
18079
18080  register('currency', currencyFilter);
18081  register('date', dateFilter);
18082  register('filter', filterFilter);
18083  register('json', jsonFilter);
18084  register('limitTo', limitToFilter);
18085  register('lowercase', lowercaseFilter);
18086  register('number', numberFilter);
18087  register('orderBy', orderByFilter);
18088  register('uppercase', uppercaseFilter);
18089}
18090
18091/**
18092 * @ngdoc filter
18093 * @name filter
18094 * @kind function
18095 *
18096 * @description
18097 * Selects a subset of items from `array` and returns it as a new array.
18098 *
18099 * @param {Array} array The source array.
18100 * @param {string|Object|function()} expression The predicate to be used for selecting items from
18101 *   `array`.
18102 *
18103 *   Can be one of:
18104 *
18105 *   - `string`: The string is used for matching against the contents of the `array`. All strings or
18106 *     objects with string properties in `array` that match this string will be returned. This also
18107 *     applies to nested object properties.
18108 *     The predicate can be negated by prefixing the string with `!`.
18109 *
18110 *   - `Object`: A pattern object can be used to filter specific properties on objects contained
18111 *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18112 *     which have property `name` containing "M" and property `phone` containing "1". A special
18113 *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18114 *     property of the object or its nested object properties. That's equivalent to the simple
18115 *     substring match with a `string` as described above. The predicate can be negated by prefixing
18116 *     the string with `!`.
18117 *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
18118 *     not containing "M".
18119 *
18120 *     Note that a named property will match properties on the same level only, while the special
18121 *     `$` property will match properties on the same level or deeper. E.g. an array item like
18122 *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18123 *     **will** be matched by `{$: 'John'}`.
18124 *
18125 *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18126 *     The function is called for each element of the array, with the element, its index, and
18127 *     the entire array itself as arguments.
18128 *
18129 *     The final result is an array of those elements that the predicate returned true for.
18130 *
18131 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18132 *     determining if the expected value (from the filter expression) and actual value (from
18133 *     the object in the array) should be considered a match.
18134 *
18135 *   Can be one of:
18136 *
18137 *   - `function(actual, expected)`:
18138 *     The function will be given the object value and the predicate value to compare and
18139 *     should return true if both values should be considered equal.
18140 *
18141 *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18142 *     This is essentially strict comparison of expected and actual.
18143 *
18144 *   - `false|undefined`: A short hand for a function which will look for a substring match in case
18145 *     insensitive way.
18146 *
18147 *     Primitive values are converted to strings. Objects are not compared against primitives,
18148 *     unless they have a custom `toString` method (e.g. `Date` objects).
18149 *
18150 * @example
18151   <example>
18152     <file name="index.html">
18153       <div ng-init="friends = [{name:'John', phone:'555-1276'},
18154                                {name:'Mary', phone:'800-BIG-MARY'},
18155                                {name:'Mike', phone:'555-4321'},
18156                                {name:'Adam', phone:'555-5678'},
18157                                {name:'Julie', phone:'555-8765'},
18158                                {name:'Juliette', phone:'555-5678'}]"></div>
18159
18160       <label>Search: <input ng-model="searchText"></label>
18161       <table id="searchTextResults">
18162         <tr><th>
18162Name</th><th>Phone</th></tr>
18163         <tr ng-repeat="friend in friends | filter:searchText">
18164           <td>{{friend.name}}</td>
18165           <td>{{friend.phone}}</td>
18166         </tr>
18167       </table>
18168       <hr>
18169       <label>Any: <input ng-model="search.$"></label> <br>
18170       <label>Name only <input ng-model="search.name"></label><br>
18171       <label>Phone only <input ng-model="search.phone"></label><br>
18172       <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18173       <table id="searchObjResults">
18174         <tr><th>Name</th><th>Phone</th></tr>
18175         <tr ng-repeat="friendObj in friends | filter:search:strict">
18176           <td>{{friendObj.name}}</td>
18177           <td>{{friendObj.phone}}</td>
18178         </tr>
18179       </table>
18180     </file>
18181     <file name="protractor.js" type="protractor">
18182       var expectFriendNames = function(expectedNames, key) {
18183         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18184           arr.forEach(function(wd, i) {
18185             expect(wd.getText()).toMatch(expectedNames[i]);
18186           });
18187         });
18188       };
18189
18190       it('should search across all fields when filtering with a string', function() {
18191         var searchText = element(by.model('searchText'));
18192         searchText.clear();
18193         searchText.sendKeys('m');
18194         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18195
18196         searchText.clear();
18197         searchText.sendKeys('76');
18198         expectFriendNames(['John', 'Julie'], 'friend');
18199       });
18200
18201       it('should search in specific fields when filtering with a predicate object', function() {
18202         var searchAny = element(by.model('search.$'));
18203         searchAny.clear();
18204         searchAny.sendKeys('i');
18205         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18206       });
18207       it('should use a equal comparison when comparator is true', function() {
18208         var searchName = element(by.model('search.name'));
18209         var strict = element(by.model('strict'));
18210         searchName.clear();
18211         searchName.sendKeys('Julie');
18212         strict.click();
18213         expectFriendNames(['Julie'], 'friendObj');
18214       });
18215     </file>
18216   </example>
18217 */
18218function filterFilter() {
18219  return function(array, expression, comparator) {
18220    if (!isArrayLike(array)) {
18221      if (array == null) {
18222        return array;
18223      } else {
18224        throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18225      }
18226    }
18227
18228    var expressionType = getTypeForFilter(expression);
18229    var predicateFn;
18230    var matchAgainstAnyProp;
18231
18232    switch (expressionType) {
18233      case 'function':
18234        predicateFn = expression;
18235        break;
18236      case 'boolean':
18237      case 'null':
18238      case 'number':
18239      case 'string':
18240        matchAgainstAnyProp = true;
18241        //jshint -W086
18242      case 'object':
18243        //jshint +W086
18244        predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18245        break;
18246      default:
18247        return array;
18248    }
18249
18250    return Array.prototype.filter.call(array, predicateFn);
18251  };
18252}
18253
18254function hasCustomToString(obj) {
18255  return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
18256}
18257
18258// Helper functions for `filterFilter`
18259function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18260  var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18261  var predicateFn;
18262
18263  if (comparator === true) {
18264    comparator = equals;
18265  } else if (!isFunction(comparator)) {
18266    comparator = function(actual, expected) {
18267      if (isUndefined(actual)) {
18268        // No substring matching against `undefined`
18269        return false;
18270      }
18271      if ((actual === null) || (expected === null)) {
18272        // No substring matching against `null`; only match against `null`
18273        return actual === expected;
18274      }
18275      if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18276        // Should not compare primitives against objects, unless they have custom `toString` method
18277        return false;
18278      }
18279
18280      actual = lowercase('' + actual);
18281      expected = lowercase('' + expected);
18282      return actual.indexOf(expected) !== -1;
18283    };
18284  }
18285
18286  predicateFn = function(item) {
18287    if (shouldMatchPrimitives && !isObject(item)) {
18288      return deepCompare(item, expression.$, comparator, false);
18289    }
18290    return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18291  };
18292
18293  return predicateFn;
18294}
18295
18296function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18297  var actualType = getTypeForFilter(actual);
18298  var expectedType = getTypeForFilter(expected);
18299
18300  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18301    return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18302  } else if (isArray(actual)) {
18303    // In case `actual` is an array, consider it a match
18304    // if ANY of it's items matches `expected`
18305    return actual.some(function(item) {
18306      return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18307    });
18308  }
18309
18310  switch (actualType) {
18311    case 'object':
18312      var key;
18313      if (matchAgainstAnyProp) {
18314        for (key in actual) {
18315          if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18316            return true;
18317          }
18318        }
18319        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18320      } else if (expectedType === 'object') {
18321        for (key in expected) {
18322          var expectedVal = expected[key];
18323          if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18324            continue;
18325          }
18326
18327          var matchAnyProperty = key === '$';
18328          var actualVal = matchAnyProperty ? actual : actual[key];
18329          if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18330            return false;
18331          }
18332        }
18333        return true;
18334      } else {
18335        return comparator(actual, expected);
18336      }
18337      break;
18338    case 'function':
18339      return false;
18340    default:
18341      return comparator(actual, expected);
18342  }
18343}
18344
18345// Used for easily differentiating between `null` and actual `object`
18346function getTypeForFilter(val) {
18347  return (val === null) ? 'null' : typeof val;
18348}
18349
18350/**
18351 * @ngdoc filter
18352 * @name currency
18353 * @kind function
18354 *
18355 * @description
18356 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18357 * symbol for current locale is used.
18358 *
18359 * @param {number} amount Input to filter.
18360 * @param {string=} symbol Currency symbol or identifier to be displayed.
18361 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18362 * @returns {string} Formatted number.
18363 *
18364 *
18365 * @example
18366   <example module="currencyExample">
18367     <file name="index.html">
18368       <script>
18369         angular.module('currencyExample', [])
18370           .controller('ExampleController', ['$scope', function($scope) {
18371             $scope.amount = 1234.56;
18372           }]);
18373       </script>
18374       <div ng-controller="ExampleController">
18375         <input type="number" ng-model="amount" aria-label="amount"> <br>
18376         default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18377         custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18378         no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18379       </div>
18380     </file>
18381     <file name="protractor.js" type="protractor">
18382       it('should init with 1234.56', function() {
18383         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18384         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18385         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18386       });
18387       it('should update', function() {
18388         if (browser.params.browser == 'safari') {
18389           // Safari does not understand the minus key. See
18390           // https://github.com/angular/protractor/issues/481
18391           return;
18392         }
18393         element(by.model('amount')).clear();
18394         element(by.model('amount')).sendKeys('-1234');
18395         expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
18396         expect(element(by.id('currency-custom')).getText()).toBe('(USD$1,234.00)');
18397         expect(element(by.id('currency-no-fractions')).getText()).toBe('(USD$1,234)');
18398       });
18399     </file>
18400   </example>
18401 */
18402currencyFilter.$inject = ['$locale'];
18403function currencyFilter($locale) {
18404  var formats = $locale.NUMBER_FORMATS;
18405  return function(amount, currencySymbol, fractionSize) {
18406    if (isUndefined(currencySymbol)) {
18407      currencySymbol = formats.CURRENCY_SYM;
18408    }
18409
18410    if (isUndefined(fractionSize)) {
18411      fractionSize = formats.PATTERNS[1].maxFrac;
18412    }
18413
18414    // if null or undefined pass it through
18415    return (amount == null)
18416        ? amount
18417        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18418            replace(/\u00A4/g, currencySymbol);
18419  };
18420}
18421
18422/**
18423 * @ngdoc filter
18424 * @name number
18425 * @kind function
18426 *
18427 * @description
18428 * Formats a number as text.
18429 *
18430 * If the input is null or undefined, it will just be returned.
18431 * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
18432 * If the input is not a number an empty string is returned.
18433 *
18434 *
18435 * @param {number|string} number Number to format.
18436 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18437 * If this is not provided then the fraction size is computed from the current locale's number
18438 * formatting pattern. In the case of the default locale, it will be 3.
18439 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
18440 *
18441 * @example
18442   <example module="numberFilterExample">
18443     <file name="index.html">
18444       <script>
18445         angular.module('numberFilterExample', [])
18446           .controller('ExampleController', ['$scope', function($scope) {
18447             $scope.val = 1234.56789;
18448           }]);
18449       </script>
18450       <div ng-controller="ExampleController">
18451         <label>Enter number: <input ng-model='val'></label><br>
18452         Default formatting: <span id='number-default'>{{val | number}}</span><br>
18453         No fractions: <span>{{val | number:0}}</span><br>
18454         Negative number: <span>{{-val | number:4}}</span>
18455       </div>
18456     </file>
18457     <file name="protractor.js" type="protractor">
18458       it('should format numbers', function() {
18459         expect(element(by.id('number-default')).getText()).toBe('1,234.568');
18460         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
18461         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
18462       });
18463
18464       it('should update', function() {
18465         element(by.model('val')).clear();
18466         element(by.model('val')).sendKeys('3374.333');
18467         expect(element(by.id('number-default')).getText()).toBe('3,374.333');
18468         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
18469         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
18470      });
18471     </file>
18472   </example>
18473 */
18474
18475
18476numberFilter.$inject = ['$locale'];
18477function numberFilter($locale) {
18478  var formats = $locale.NUMBER_FORMATS;
18479  return function(number, fractionSize) {
18480
18481    // if null or undefined pass it through
18482    return (number == null)
18483        ? number
18484        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
18485                       fractionSize);
18486  };
18487}
18488
18489var DECIMAL_SEP = '.';
18490function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
18491  if (isObject(number)) return '';
18492
18493  var isNegative = number < 0;
18494  number = Math.abs(number);
18495
18496  var isInfinity = number === Infinity;
18497  if (!isInfinity && !isFinite(number)) return '';
18498
18499  var numStr = number + '',
18500      formatedText = '',
18501      hasExponent = false,
18502      parts = [];
18503
18504  if (isInfinity) formatedText = '\u221e';
18505
18506  if (!isInfinity && numStr.indexOf('e') !== -1) {
18507    var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
18508    if (match && match[2] == '-' && match[3] > fractionSize + 1) {
18509      number = 0;
18510    } else {
18511      formatedText = numStr;
18512      hasExponent = true;
18513    }
18514  }
18515
18516  if (!isInfinity && !hasExponent) {
18517    var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
18518
18519    // determine fractionSize if it is not specified
18520    if (isUndefined(fractionSize)) {
18521      fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
18522    }
18523
18524    // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
18525    // inspired by:
18526    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
18527    number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
18528
18529    var fraction = ('' + number).split(DECIMAL_SEP);
18530    var whole = fraction[0];
18531    fraction = fraction[1] || '';
18532
18533    var i, pos = 0,
18534        lgroup = pattern.lgSize,
18535        group = pattern.gSize;
18536
18537    if (whole.length >= (lgroup + group)) {
18538      pos = whole.length - lgroup;
18539      for (i = 0; i < pos; i++) {
18540        if ((pos - i) % group === 0 && i !== 0) {
18541          formatedText += groupSep;
18542        }
18543        formatedText += whole.charAt(i);
18544      }
18545    }
18546
18547    for (i = pos; i < whole.length; i++) {
18548      if ((whole.length - i) % lgroup === 0 && i !== 0) {
18549        formatedText += groupSep;
18550      }
18551      formatedText += whole.charAt(i);
18552    }
18553
18554    // format fraction part.
18555    while (fraction.length < fractionSize) {
18556      fraction += '0';
18557    }
18558
18559    if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
18560  } else {
18561    if (fractionSize > 0 && number < 1) {
18562      formatedText = number.toFixed(fractionSize);
18563      number = parseFloat(formatedText);
18564    }
18565  }
18566
18567  if (number === 0) {
18568    isNegative = false;
18569  }
18570
18571  parts.push(isNegative ? pattern.negPre : pattern.posPre,
18572             formatedText,
18573             isNegative ? pattern.negSuf : pattern.posSuf);
18574  return parts.join('');
18575}
18576
18577function padNumber(num, digits, trim) {
18578  var neg = '';
18579  if (num < 0) {
18580    neg =  '-';
18581    num = -num;
18582  }
18583  num = '' + num;
18584  while (num.length < digits) num = '0' + num;
18585  if (trim) {
18586    num = num.substr(num.length - digits);
18587  }
18588  return neg + num;
18589}
18590
18591
18592function dateGetter(name, size, offset, trim) {
18593  offset = offset || 0;
18594  return function(date) {
18595    var value = date['get' + name]();
18596    if (offset > 0 || value > -offset) {
18597      value += offset;
18598    }
18599    if (value === 0 && offset == -12) value = 12;
18600    return padNumber(value, size, trim);
18601  };
18602}
18603
18604function dateStrGetter(name, shortForm) {
18605  return function(date, formats) {
18606    var value = date['get' + name]();
18607    var get = uppercase(shortForm ? ('SHORT' + name) : name);
18608
18609    return formats[get][value];
18610  };
18611}
18612
18613function timeZoneGetter(date, formats, offset) {
18614  var zone = -1 * offset;
18615  var paddedZone = (zone >= 0) ? "+" : "";
18616
18617  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
18618                padNumber(Math.abs(zone % 60), 2);
18619
18620  return paddedZone;
18621}
18622
18623function getFirstThursdayOfYear(year) {
18624    // 0 = index of January
18625    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
18626    // 4 = index of Thursday (+1 to account for 1st = 5)
18627    // 11 = index of *next* Thursday (+1 account for 1st = 12)
18628    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
18629}
18630
18631function getThursdayThisWeek(datetime) {
18632    return new Date(datetime.getFullYear(), datetime.getMonth(),
18633      // 4 = index of Thursday
18634      datetime.getDate() + (4 - datetime.getDay()));
18635}
18636
18637function weekGetter(size) {
18638   return function(date) {
18639      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
18640         thisThurs = getThursdayThisWeek(date);
18641
18642      var diff = +thisThurs - +firstThurs,
18643         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
18644
18645      return padNumber(result, size);
18646   };
18647}
18648
18649function ampmGetter(date, formats) {
18650  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
18651}
18652
18653function eraGetter(date, formats) {
vendor: 5,157 bytes, lines 18654-18766
18654  return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
18655}
18656
18657function longEraGetter(date, formats) {
18658  return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
18659}
18660
18661var DATE_FORMATS = {
18662  yyyy: dateGetter('FullYear', 4),
18663    yy: dateGetter('FullYear', 2, 0, true),
18664     y: dateGetter('FullYear', 1),
18665  MMMM: dateStrGetter('Month'),
18666   MMM: dateStrGetter('Month', true),
18667    MM: dateGetter('Month', 2, 1),
18668     M: dateGetter('Month', 1, 1),
18669    dd: dateGetter('Date', 2),
18670     d: dateGetter('Date', 1),
18671    HH: dateGetter('Hours', 2),
18672     H: dateGetter('Hours', 1),
18673    hh: dateGetter('Hours', 2, -12),
18674     h: dateGetter('Hours', 1, -12),
18675    mm: dateGetter('Minutes', 2),
18676     m: dateGetter('Minutes', 1),
18677    ss: dateGetter('Seconds', 2),
18678     s: dateGetter('Seconds', 1),
18679     // while ISO 8601 requires fractions to be prefixed with `.` or `,`
18680     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
18681   sss: dateGetter('Milliseconds', 3),
18682  EEEE: dateStrGetter('Day'),
18683   EEE: dateStrGetter('Day', true),
18684     a: ampmGetter,
18685     Z: timeZoneGetter,
18686    ww: weekGetter(2),
18687     w: weekGetter(1),
18688     G: eraGetter,
18689     GG: eraGetter,
18690     GGG: eraGetter,
18691     GGGG: longEraGetter
18692};
18693
18694var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
18695    NUMBER_STRING = /^\-?\d+$/;
18696
18697/**
18698 * @ngdoc filter
18699 * @name date
18700 * @kind function
18701 *
18702 * @description
18703 *   Formats `date` to a string based on the requested `format`.
18704 *
18705 *   `format` string can be composed of the following elements:
18706 *
18707 *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
18708 *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
18709 *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
18710 *   * `'MMMM'`: Month in year (January-December)
18711 *   * `'MMM'`: Month in year (Jan-Dec)
18712 *   * `'MM'`: Month in year, padded (01-12)
18713 *   * `'M'`: Month in year (1-12)
18714 *   * `'dd'`: Day in month, padded (01-31)
18715 *   * `'d'`: Day in month (1-31)
18716 *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
18717 *   * `'EEE'`: Day in Week, (Sun-Sat)
18718 *   * `'HH'`: Hour in day, padded (00-23)
18719 *   * `'H'`: Hour in day (0-23)
18720 *   * `'hh'`: Hour in AM/PM, padded (01-12)
18721 *   * `'h'`: Hour in AM/PM, (1-12)
18722 *   * `'mm'`: Minute in hour, padded (00-59)
18723 *   * `'m'`: Minute in hour (0-59)
18724 *   * `'ss'`: Second in minute, padded (00-59)
18725 *   * `'s'`: Second in minute (0-59)
18726 *   * `'sss'`: Millisecond in second, padded (000-999)
18727 *   * `'a'`: AM/PM marker
18728 *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
18729 *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
18730 *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
18731 *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
18732 *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
18733 *
18734 *   `format` string can also be one of the following predefined
18735 *   {@link guide/i18n localizable formats}:
18736 *
18737 *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
18738 *     (e.g. Sep 3, 2010 12:05:08 PM)
18739 *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
18740 *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
18741 *     (e.g. Friday, September 3, 2010)
18742 *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
18743 *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
18744 *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
18745 *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
18746 *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
18747 *
18748 *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
18749 *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
18750 *   (e.g. `"h 'o''clock'"`).
18751 *
18752 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
18753 *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
18754 *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
18755 *    specified in the string input, the time is considered to be in the local timezone.
18756 * @param {string=} format Formatting rules (see Description). If not specified,
18757 *    `mediumDate` is used.
18758 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
18759 *    continental US time zone abbreviations, but for general use, use a time zone offset, for
18760 *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
18761 *    If not specified, the timezone of the browser will be used.
18762 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
18763 *
18764 * @example
18765   <example>
18766     <file name="index.html">
vendor: 4,078 bytes, lines 18767-18879
18767       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
18768           <span>{{1288323623006 | date:'medium'}}</span><br>
18769       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
18770          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
18771       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
18772          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
18773       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
18774          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
18775     </file>
18776     <file name="protractor.js" type="protractor">
18777       it('should format date', function() {
18778         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
18779            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
18780         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
18781            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
18782         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
18783            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
18784         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
18785            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
18786       });
18787     </file>
18788   </example>
18789 */
18790dateFilter.$inject = ['$locale'];
18791function dateFilter($locale) {
18792
18793
18794  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
18795                     // 1        2       3         4          5          6          7          8  9     10      11
18796  function jsonStringToDate(string) {
18797    var match;
18798    if (match = string.match(R_ISO8601_STR)) {
18799      var date = new Date(0),
18800          tzHour = 0,
18801          tzMin  = 0,
18802          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
18803          timeSetter = match[8] ? date.setUTCHours : date.setHours;
18804
18805      if (match[9]) {
18806        tzHour = toInt(match[9] + match[10]);
18807        tzMin = toInt(match[9] + match[11]);
18808      }
18809      dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
18810      var h = toInt(match[4] || 0) - tzHour;
18811      var m = toInt(match[5] || 0) - tzMin;
18812      var s = toInt(match[6] || 0);
18813      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
18814      timeSetter.call(date, h, m, s, ms);
18815      return date;
18816    }
18817    return string;
18818  }
18819
18820
18821  return function(date, format, timezone) {
18822    var text = '',
18823        parts = [],
18824        fn, match;
18825
18826    format = format || 'mediumDate';
18827    format = $locale.DATETIME_FORMATS[format] || format;
18828    if (isString(date)) {
18829      date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
18830    }
18831
18832    if (isNumber(date)) {
18833      date = new Date(date);
18834    }
18835
18836    if (!isDate(date) || !isFinite(date.getTime())) {
18837      return date;
18838    }
18839
18840    while (format) {
18841      match = DATE_FORMATS_SPLIT.exec(format);
18842      if (match) {
18843        parts = concat(parts, match, 1);
18844        format = parts.pop();
18845      } else {
18846        parts.push(format);
18847        format = null;
18848      }
18849    }
18850
18851    var dateTimezoneOffset = date.getTimezoneOffset();
18852    if (timezone) {
18853      dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
18854      date = convertTimezoneToLocal(date, timezone, true);
18855    }
18856    forEach(parts, function(value) {
18857      fn = DATE_FORMATS[value];
18858      text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
18859                 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
18860    });
18861
18862    return text;
18863  };
18864}
18865
18866
18867/**
18868 * @ngdoc filter
18869 * @name json
18870 * @kind function
18871 *
18872 * @description
18873 *   Allows you to convert a JavaScript object into JSON string.
18874 *
18875 *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
18876 *   the binding is automatically converted to JSON.
18877 *
18878 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
18879 * @param {number=}
18879 spacing The number of spaces to use per indentation, defaults to 2.
18880 * @returns {string} JSON string.
18881 *
18882 *
18883 * @example
18884   <example>
18885     <file name="index.html">
18886       <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
18887       <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
18888     </file>
18889     <file name="protractor.js" type="protractor">
18890       it('should jsonify filtered objects', function() {
18891         expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
18892         expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
18893       });
18894     </file>
18895   </example>
18896 *
18897 */
18898function jsonFilter() {
18899  return function(object, spacing) {
18900    if (isUndefined(spacing)) {
18901        spacing = 2;
18902    }
18903    return toJson(object, spacing);
18904  };
18905}
18906
18907
18908/**
18909 * @ngdoc filter
18910 * @name lowercase
18911 * @kind function
18912 * @description
18913 * Converts string to lowercase.
18914 * @see angular.lowercase
18915 */
18916var lowercaseFilter = valueFn(lowercase);
18917
18918
18919/**
18920 * @ngdoc filter
18921 * @name uppercase
18922 * @kind function
18923 * @description
18924 * Converts string to uppercase.
18925 * @see angular.uppercase
18926 */
18927var uppercaseFilter = valueFn(uppercase);
18928
18929/**
18930 * @ngdoc filter
18931 * @name limitTo
18932 * @kind function
18933 *
18934 * @description
18935 * Creates a new array or string containing only a specified number of elements. The elements
18936 * are taken from either the beginning or the end of the source array, string or number, as specified by
18937 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
18938 * converted to a string.
18939 *
18940 * @param {Array|string|number} input Source array, string or number to be limited.
18941 * @param {string|number} limit The length of the returned array or string. If the `limit` number
18942 *     is positive, `limit` number of items from the beginning of the source array/string are copied.
18943 *     If the number is negative, `limit` number  of items from the end of the source array/string
18944 *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
18945 *     the input will be returned unchanged.
18946 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
18947 *     indicates an offset from the end of `input`. Defaults to `0`.
18948 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
18949 *     had less than `limit` elements.
18950 *
18951 * @example
18952   <example module="limitToExample">
18953     <file name="index.html">
18954       <script>
18955         angular.module('limitToExample', [])
18956           .controller('ExampleController', ['$scope', function($scope) {
18957             $scope.numbers = [1,2,3,4,5,6,7,8,9];
18958             $scope.letters = "abcdefghi";
18959             $scope.longNumber = 2345432342;
18960             $scope.numLimit = 3;
18961             $scope.letterLimit = 3;
18962             $scope.longNumberLimit = 3;
18963           }]);
18964       </script>
18965       <div ng-controller="ExampleController">
18966         <label>
18967            Limit {{numbers}} to:
18968            <input type="number" step="1" ng-model="numLimit">
18969         </label>
18970         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
18971         <label>
18972            Limit {{letters}} to:
18973            <input type="number" step="1" ng-model="letterLimit">
18974         </label>
18975         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
18976         <label>
18977            Limit {{longNumber}} to:
18978            <input type="number" step="1" ng-model="longNumberLimit">
18979         </label>
18980         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
18981       </div>
18982     </file>
18983     <file name="protractor.js" type="protractor">
18984       var numLimitInput = element(by.model('numLimit'));
18985       var letterLimitInput = element(by.model('letterLimit'));
18986       var longNumberLimitInput = element(by.model('longNumberLimit'));
18987       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
18988       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
18989       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
18990
18991       it('should limit the number array to first three items', function() {
18992         expect(numLimitInput.getAttribute('value')).toBe('3');
18993         expect(letterLimitInput.getAttribute('value')).toBe('3');
18994         expect(longNumberLimitInput.getAttribute('value')).toBe('3');
18995         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
18996         expect(limitedLetters.getText()).toEqual('Output letters: abc');
18997         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
18998       });
18999
19000       // There is a bug in safari and protractor that doesn't like the minus key
19001       // it('should update the output when -3 is entered', function() {
19002       //   numLimitInput.clear();
19003       //   numLimitInput.sendKeys('-3');
19004       //   letterLimitInput.clear();
19005       //   letterLimitInput.sendKeys('-3');
19006       //   longNumberLimitInput.clear();
19007       //   longNumberLimitInput.sendKeys('-3');
19008       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19009       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19010       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19011       // });
19012
19013       it('should not exceed the maximum size of input array', function() {
19014         numLimitInput.clear();
19015         numLimitInput.sendKeys('100');
19016         letterLimitInput.clear();
19017         letterLimitInput.sendKeys('100');
19018         longNumberLimitInput.clear();
19019         longNumberLimitInput.sendKeys('100');
19020         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19021         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19022         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19023       });
19024     </file>
19025   </example>
19026*/
19027function limitToFilter() {
19028  return function(input, limit, begin) {
19029    if (Math.abs(Number(limit)) === Infinity) {
19030      limit = Number(limit);
19031    } else {
19032      limit = toInt(limit);
19033    }
19034    if (isNaN(limit)) return input;
19035
19036    if (isNumber(input)) input = input.toString();
19037    if (!isArray(input) && !isString(input)) return input;
19038
19039    begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19040    begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin;
19041
19042    if (limit >= 0) {
19043      return input.slice(begin, begin + limit);
19044    } else {
19045      if (begin === 0) {
19046        return input.slice(limit, input.length);
19047      } else {
19048        return input.slice(Math.max(0, begin + limit), begin);
19049      }
19050    }
19051  };
19052}
19053
19054/**
19055 * @ngdoc filter
19056 * @name orderBy
19057 * @kind function
19058 *
19059 * @description
19060 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19061 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19062 * as expected, make sure they are actually being saved as numbers and not strings.
19063 *
19064 * @param {Array} array The array to sort.
19065 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19066 *    used by the comparator to determine the order of elements.
19067 *
19068 *    Can be one of:
19069 *
19070 *    - `function`: Getter function. The result of this function will be sorted using the
19071 *      `<`, `===`, `>` operator.
19072 *    - `string`: An Angular expression. The result of this expression is used to compare elements
19073 *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19074 *      3 first characters of a property called `name`). The result of a constant expression
19075 *      is interpreted as a property name to be used in comparisons (for example `"special name"`
19076 *      to sort object by the value of their `special name` property). An expression can be
19077 *      optionally prefixed with `+` or `-` to control ascending or descending sort order
19078 *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19079 *      element itself is used to compare where sorting.
19080 *    - `Array`: An array of function or string predicates. The first predicate in the array
19081 *      is used for sorting, but when two items are equivalent, the next predicate is used.
19082 *
19083 *    If the predicate is missing or empty then it defaults to `'+'`.
19084 *
19085 * @param {boolean=} reverse Reverse the order of the array.
19086 * @returns {Array} Sorted copy of the source array.
19087 *
19088 *
19089 * @example
19090 * The example below demonstrates a simple ngRepeat, where the data is sorted
19091 * by age in descending order (predicate is set to `'-age'`).
19092 * `reverse` is not set, which means it defaults to `false`.
19093   <example module="orderByExample">
19094     <file name="index.html">
19095       <script>
19096         angular.module('orderByExample', [])
19097           .controller('ExampleController', ['$scope', function($scope) {
19098             $scope.friends =
19099                 [{name:'John', phone:'555-1212', age:10},
19100                  {name:'Mary', phone:'555-9876', age:19},
19101                  {name:'Mike', phone:'555-4321', age:21},
19102                  {name:'Adam', phone:'555-5678', age:35},
19103                  {name:'Julie', phone:'555-8765', age:29}];
19104           }]);
19105       </script>
19106       <div ng-controller="ExampleController">
19107         <table class="friend">
19108           <tr>
19109             <th>Name</th>
19110             <th>Phone Number</th>
19111             <th>Age</th>
19112           </tr>
19113           <tr ng-repeat="friend in friends | orderBy:'-age'">
19114             <td>{{friend.name}}</td>
19115             <td>{{friend.phone}}</td>
19116             <td>{{friend.age}}</td>
19117           </tr>
19118         </table>
19119       </div>
19120     </file>
19121   </example>
19122 *
19123 * The predicate and reverse parameters can be controlled dynamically through scope properties,
19124 * as shown in the next example.
19125 * @example
19126   <example module="orderByExample">
19127     <file name="index.html">
19128       <script>
19129         angular.module('orderByExample', [])
19130           .controller('ExampleController', ['$scope', function($scope) {
19131             $scope.friends =
19132                 [{name:'John', phone:'555-1212', age:10},
19133                  {name:'Mary', phone:'555-9876', age:19},
19134                  {name:'Mike', phone:'555-4321', age:21},
19135                  {name:'Adam', phone:'555-5678', age:35},
19136                  {name:'Julie', phone:'555-8765', age:29}];
19137             $scope.predicate = 'age';
19138             $scope.reverse = true;
19139             $scope.order = function(predicate) {
19140               $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19141               $scope.predicate = predicate;
19142             };
19143           }]);
19144       </script>
19145       <style type="text/css">
19146         .sortorder:after {
19147           content: '\25b2';
19148         }
19149         .sortorder.reverse:after {
19150           content: '\25bc';
19151         }
19152       </style>
19153       <div ng-controller="ExampleController">
19154         <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19155         <hr/>
19156         [ <a href="" ng-click="predicate=''">unsorted</a> ]
19157         <table class="friend">
19158           <tr>
19159             <th>
19160               <a href="" ng-click="order('name')">Name</a>
19161               <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19162             </th>
19163             <th>
19164               <a href="" ng-click="order('phone')">Phone Number</a>
19165               <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19166             </th>
19167             <th>
19168               <a href="" ng-click="order('age')">Age</a>
19169               <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19170             </th>
19171           </tr>
19172           <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19173             <td>{{friend.name}}</td>
19174             <td>{{friend.phone}}</td>
19175             <td>{{friend.age}}</td>
19176           </tr>
19177         </table>
19178       </div>
19179     </file>
19180   </example>
19181 *
19182 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19183 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19184 * desired parameters.
19185 *
19186 * Example:
19187 *
19188 * @example
19189  <example module="orderByExample">
19190    <file name="index.html">
19191      <div ng-controller="ExampleController">
19192        <table class="friend">
19193          <tr>
19194            <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
19195              (<a href="" ng-click="order('-name',false)">^</a>)</th>
19196            <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
19197            <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
19198          </tr>
19199          <tr ng-repeat="friend in friends">
19200            <td>{{friend.name}}</td>
19201            <td>{{friend.phone}}</td>
19202            <td>{{friend.age}}</td>
19203          </tr>
19204        </table>
19205      </div>
19206    </file>
19207
19208    <file name="script.js">
19209      angular.module('orderByExample', [])
19210        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19211          var orderBy = $filter('orderBy');
19212          $scope.friends = [
19213            { name: 'John',    phone: '555-1212',    age: 10 },
19214            { name: 'Mary',    phone: '555-9876',    age: 19 },
19215            { name: 'Mike',    phone: '555-4321',    age: 21 },
19216            { name: 'Adam',    phone: '555-5678',    age: 35 },
19217            { name: 'Julie',   phone: '555-8765',    age: 29 }
19218          ];
19219          $scope.order = function(predicate, reverse) {
19220            $scope.friends = orderBy($scope.friends, predicate, reverse);
19221          };
19222          $scope.order('-age',false);
19223        }]);
19224    </file>
19225</example>
19226 */
19227orderByFilter.$inject = ['$parse'];
19228function orderByFilter($parse) {
19229  return function(array, sortPredicate, reverseOrder) {
19230    if (!(isArrayLike(array))) return array;
19231    sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
19232    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19233    sortPredicate = sortPredicate.map(function(predicate) {
19234      var descending = false, get = predicate || identity;
19235      if (isString(predicate)) {
19236        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19237          descending = predicate.charAt(0) == '-';
19238          predicate = predicate.substring(1);
19239        }
19240        if (predicate === '') {
19241          // Effectively no predicate was passed so we compare identity
19242          return reverseComparator(compare, descending);
19243        }
19244        get = $parse(predicate);
19245        if (get.constant) {
19246          var key = get();
19247          return reverseComparator(function(a, b) {
19248            return compare(a[key], b[key]);
19249          }, descending);
19250        }
19251      }
19252      return reverseComparator(function(a, b) {
19253        return compare(get(a),get(b));
19254      }, descending);
19255    });
19256    return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
19257
19258    function comparator(o1, o2) {
19259      for (var i = 0; i < sortPredicate.length; i++) {
19260        var comp = sortPredicate[i](o1, o2);
19261        if (comp !== 0) return comp;
19262      }
19263      return 0;
19264    }
19265    function reverseComparator(comp, descending) {
19266      return descending
19267          ? function(a, b) {return comp(b,a);}
19268          : comp;
19269    }
19270
19271    function isPrimitive(value) {
19272      switch (typeof value) {
19273        case 'number': /* falls through */
19274        case 'boolean': /* falls through */
19275        case 'string':
19276          return true;
19277        default:
19278          return false;
19279      }
19280    }
19281
19282    function objectToString(value) {
19283      if (value === null) return 'null';
19284      if (typeof value.valueOf === 'function') {
19285        value = value.valueOf();
19286        if (isPrimitive(value)) return value;
19287      }
19288      if (typeof value.toString === 'function') {
19289        value = value.toString();
19290        if (isPrimitive(value)) return value;
19291      }
19292      return '';
19293    }
19294
19295    function compare(v1, v2) {
19296      var t1 = typeof v1;
19297      var t2 = typeof v2;
19298      if (t1 === t2 && t1 === "object") {
19299        v1 = objectToString(v1);
19300        v2 = objectToString(v2);
19301      }
19302      if (t1 === t2) {
19303        if (t1 === "string") {
19304           v1 = v1.toLowerCase();
19305           v2 = v2.toLowerCase();
19306        }
19307        if (v1 === v2) return 0;
19308        return v1 < v2 ? -1 : 1;
19309      } else {
19310        return t1 < t2 ? -1 : 1;
19311      }
19312    }
19313  };
19314}
19315
19316function ngDirective(directive) {
19317  if (isFunction(directive)) {
19318    directive = {
19319      link: directive
19320    };
19321  }
19322  directive.restrict = directive.restrict || 'AC';
19323  return valueFn(directive);
19324}
19325
19326/**
19327 * @ngdoc directive
19328 * @name a
19329 * @restrict E
19330 *
19331 * @description
19332 * Modifies the default behavior of the html A tag so that the default action is prevented when
19333 * the href attribute is empty.
19334 *
19335 * This change permits the easy creation of action links with the `ngClick` directive
19336 * without changing the location or causing page reloads, e.g.:
19337 * `<a href="" ng-click="list.addItem()">Add Item</a>`
19338 */
19339var htmlAnchorDirective = valueFn({
19340  restrict: 'E',
19341  compile: function(element, attr) {
19342    if (!attr.href && !attr.xlinkHref) {
19343      return function(scope, element) {
19344        // If the linked element is not an anchor tag anymore, do nothing
19345        if (element[0].nodeName.toLowerCase() !== 'a') return;
19346
19347        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
19348        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
19349                   'xlink:href' : 'href';
19350        element.on('click', function(event) {
19351          // if we have no href url, then don't navigate anywhere.
19352          if (!element.attr(href)) {
19353            event.preventDefault();
19354          }
19355        });
19356      };
19357    }
19358  }
19359});
19360
19361/**
19362 * @ngdoc directive
19363 * @name ngHref
19364 * @restrict A
19365 * @priority 99
19366 *
19367 * @description
19368 * Using Angular markup like `{{hash}}` in an href attribute will
19369 * make the link go to the wrong URL if the user clicks it before
19370 * Angular has a chance to replace the `{{hash}}
19370` markup with its
19371 * value. Until Angular replaces the markup the link will be broken
19372 * and will most likely return a 404 error. The `ngHref` directive
19373 * solves this problem.
19374 *
19375 * The wrong way to write it:
19376 * ```html
19377 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19378 * ```
19379 *
19380 * The correct way to write it:
19381 * ```html
19382 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19383 * ```
19384 *
19385 * @element A
19386 * @param {template} ngHref any string which can contain `{{}}` markup.
19387 *
19388 * @example
19389 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
19390 * in links and their different behaviors:
19391    <example>
19392      <file name="index.html">
19393        <input ng-model="value" /><br />
19394        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
19395        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
19396        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
19397        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
19398        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
19399        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
19400      </file>
19401      <file name="protractor.js" type="protractor">
19402        it('should execute ng-click but not reload when href without value', function() {
19403          element(by.id('link-1')).click();
19404          expect(element(by.model('value')).getAttribute('value')).toEqual('1');
19405          expect(element(by.id('link-1')).getAttribute('href')).toBe('');
19406        });
19407
19408        it('should execute ng-click but not reload when href empty string', function() {
19409          element(by.id('link-2')).click();
19410          expect(element(by.model('value')).getAttribute('value')).toEqual('2');
19411          expect(element(by.id('link-2')).getAttribute('href')).toBe('');
19412        });
19413
19414        it('should execute ng-click and change url when ng-href specified', function() {
19415          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
19416
19417          element(by.id('link-3')).click();
19418
19419          // At this point, we navigate away from an Angular page, so we need
19420          // to use browser.driver to get the base webdriver.
19421
19422          browser.wait(function() {
19423            return browser.driver.getCurrentUrl().then(function(url) {
19424              return url.match(/\/123$/);
19425            });
19426          }, 5000, 'page should navigate to /123');
19427        });
19428
19429        it('should execute ng-click but not reload when href empty string and name specified', function() {
19430          element(by.id('link-4')).click();
19431          expect(element(by.model('value')).getAttribute('value')).toEqual('4');
19432          expect(element(by.id('link-4')).getAttribute('href')).toBe('');
19433        });
19434
19435        it('should execute ng-click but not reload when no href but name specified', function() {
19436          element(by.id('link-5')).click();
19437          expect(element(by.model('value')).getAttribute('value')).toEqual('5');
19438          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
19439        });
19440
19441        it('should only change url when only ng-href', function() {
19442          element(by.model('value')).clear();
19443          element(by.model('value')).sendKeys('6');
19444          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
19445
19446          element(by.id('link-6')).click();
19447
19448          // At this point, we navigate away from an Angular page, so we need
19449          // to use browser.driver to get the base webdriver.
19450          browser.wait(function() {
19451            return browser.driver.getCurrentUrl().then(function(url) {
19452              return url.match(/\/6$/);
19453            });
19454          }, 5000, 'page should navigate to /6');
19455        });
19456      </file>
19457    </example>
19458 */
19459
19460/**
19461 * @ngdoc directive
19462 * @name ngSrc
19463 * @restrict A
19464 * @priority 99
19465 *
19466 * @description
19467 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
19468 * work right: The browser will fetch from the URL with the literal
19469 * text `{{hash}}` until Angular replaces the expression inside
19470 * `{{hash}}`. The `ngSrc` directive solves this problem.
19471 *
19472 * The buggy way to write it:
19473 * ```html
19474 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
19475 * ```
19476 *
19477 * The correct way to write it:
19478 * ```html
19479 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
19480 * ```
19481 *
19482 * @element IMG
19483 * @param {template} ngSrc any string which can contain `{{}}` markup.
19484 */
19485
19486/**
19487 * @ngdoc directive
19488 * @name ngSrcset
19489 * @restrict A
19490 * @priority 99
19491 *
19492 * @description
19493 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
19494 * work right: The browser will fetch from the URL with the literal
19495 * text `{{hash}}` until Angular replaces the expression inside
19496 * `{{hash}}`. The `ngSrcset` directive solves this problem.
19497 *
19498 * The buggy way to write it:
19499 * ```html
19500 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
19501 * ```
19502 *
19503 * The correct way to write it:
19504 * ```html
19505 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
19506 * ```
19507 *
19508 * @element IMG
19509 * @param {template} ngSrcset any string which can contain `{{}}` markup.
19510 */
19511
19512/**
19513 * @ngdoc directive
19514 * @name ngDisabled
19515 * @restrict A
19516 * @priority 100
19517 *
19518 * @description
19519 *
19520 * This directive sets the `disabled` attribute on the element if the
19521 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
19522 *
19523 * A special directive is necessary because we cannot use interpolation inside the `disabled`
19524 * attribute.  The following example would make the button enabled on Chrome/Firefox
19525 * but not on older IEs:
19526 *
19527 * ```html
19528 * <!-- See below for an example of ng-disabled being used correctly -->
19529 * <div ng-init="isDisabled = false">
19530 *  <button disabled="{{isDisabled}}">Disabled</button>
19531 * </div>
19532 * ```
19533 *
19534 * This is because the HTML specification does not require browsers to preserve the values of
19535 * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
19536 * If we put an Angular interpolation expression into such an attribute then the
19537 * binding information would be lost when the browser removes the attribute.
19538 *
19539 * @example
19540    <example>
19541      <file name="index.html">
19542        <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
19543        <button ng-model="button" ng-disabled="checked">Button</button>
19544      </file>
19545      <file name="protractor.js" type="protractor">
19546        it('should toggle button', function() {
19547          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
19548          element(by.model('checked')).click();
19549          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
19550        });
19551      </file>
19552    </example>
19553 *
19554 * @element INPUT
19555 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
19556 *     then the `disabled` attribute will be set on the element
19557 */
19558
19559
19560/**
19561 * @ngdoc directive
19562 * @name ngChecked
19563 * @restrict A
19564 * @priority 100
19565 *
19566 * @description
19567 * The HTML specification does not require browsers to preserve the values of boolean attributes
19568 * such as checked. (Their presence means true and their absence means false.)
19569 * If we put an Angular interpolation expression into such an attribute then the
19570 * binding information would be lost when the browser removes the attribute.
19571 * The `ngChecked` directive solves this problem for the `checked` attribute.
19572 * This complementary directive is not removed by the browser and so provides
19573 * a permanent reliable place to store the binding information.
19574 * @example
19575    <example>
19576      <file name="index.html">
19577        <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
19578        <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
19579      </file>
19580      <file name="protractor.js" type="protractor">
19581        it('should check both checkBoxes', function() {
19582          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
19583          element(by.model('master')).click();
19584          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
19585        });
19586      </file>
19587    </example>
19588 *
19589 * @element INPUT
19590 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
19591 *     then special attribute "checked" will be set on the element
19592 */
19593
19594
19595/**
19596 * @ngdoc directive
19597 * @name ngReadonly
19598 * @restrict A
19599 * @priority 100
19600 *
19601 * @description
19602 * The HTML specification does not require browsers to preserve the values of boolean attributes
19603 * such as readonly. (Their presence means true and their absence means false.)
19604 * If we put an Angular interpolation expression into such an attribute then the
19605 * binding information would be lost when the browser removes the attribute.
19606 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
19607 * This complementary directive is not removed by the browser and so provides
19608 * a permanent reliable place to store the binding information.
19609 * @example
19610    <example>
19611      <file name="index.html">
19612        <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
19613        <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
19614      </file>
19615      <file name="protractor.js" type="protractor">
19616        it('should toggle readonly attr', function() {
19617          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
19618          element(by.model('checked')).click();
19619          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
19620        });
19621      </file>
19622    </example>
19623 *
19624 * @element INPUT
19625 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
19626 *     then special attribute "readonly" will be set on the element
19627 */
19628
19629
19630/**
19631 * @ngdoc directive
19632 * @name ngSelected
19633 * @restrict A
19634 * @priority 100
19635 *
19636 * @description
19637 * The HTML specification does not require browsers to preserve the values of boolean attributes
19638 * such as selected. (Their presence means true and their absence means false.)
19639 * If we put an Angular interpolation expression into such an attribute then the
19640 * binding information would be lost when the browser removes the attribute.
19641 * The `ngSelected` directive solves this problem for the `selected` attribute.
19642 * This complementary directive is not removed by the browser and so provides
19643 * a permanent reliable place to store the binding information.
19644 *
19645 * @example
19646    <example>
19647      <file name="index.html">
19648        <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
19649        <select aria-label="ngSelected demo">
19650          <option>Hello!</option>
19651          <option id="greet" ng-selected="selected">Greetings!</option>
19652        </select>
19653      </file>
19654      <file name="protractor.js" type="protractor">
19655        it('should select Greetings!', function() {
19656          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
19657          element(by.model('selected')).click();
19658          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
19659        });
19660      </file>
19661    </example>
19662 *
19663 * @element OPTION
19664 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
19665 *     then special attribute "selected" will be set on the element
19666 */
19667
19668/**
19669 * @ngdoc directive
19670 * @name ngOpen
19671 * @restrict A
19672 * @priority 100
19673 *
19674 * @description
19675 * The HTML specification does not require browsers to preserve the values of boolean attributes
19676 * such as open. (Their presence means true and their absence means false.)
19677 * If we put an Angular interpolation expression into such an attribute then the
19678 * binding information would be lost when the browser removes the attribute.
19679 * The `ngOpen` directive solves this problem for the `open` attribute.
19680 * This complementary directive is not removed by the browser and so provides
19681 * a permanent reliable place to store the binding information.
19682 * @example
19683     <example>
19684       <file name="index.html">
19685         <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
19686         <details id="details" ng-open="open">
19687            <summary>Show/Hide me</summary>
19688         </details>
19689       </file>
19690       <file name="protractor.js" type="protractor">
19691         it('should toggle open', function() {
19692           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
19693           element(by.model('open')).click();
19694           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
19695         });
19696       </file>
19697     </example>
19698 *
19699 * @element DETAILS
19700 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
19701 *     then special attribute "open" will be set on the element
19702 */
19703
19704var ngAttributeAliasDirectives = {};
19705
19706// boolean attrs are evaluated
19707forEach(BOOLEAN_ATTR, function(propName, attrName) {
19708  // binding to multiple is not supported
19709  if (propName == "multiple") return;
19710
19711  function defaultLinkFn(scope, element, attr) {
19712    scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
19713      attr.$set(attrName, !!value);
19714    });
19715  }
19716
19717  var normalized = directiveNormalize('ng-' + attrName);
19718  var linkFn = defaultLinkFn;
19719
19720  if (propName === 'checked') {
19721    linkFn = function(scope, element, attr) {
19722      // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
19723      if (attr.ngModel !== attr[normalized]) {
19724        defaultLinkFn(scope, element, attr);
19725      }
19726    };
19727  }
19728
19729  ngAttributeAliasDirectives[normalized] = function() {
19730    return {
19731      restrict: 'A',
19732      priority: 100,
19733      link: linkFn
19734    };
19735  };
19736});
19737
19738// aliased input attrs are evaluated
19739forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
19740  ngAttributeAliasDirectives[ngAttr] = function() {
19741    return {
19742      priority: 100,
19743      link: function(scope, element, attr) {
19744        //special case ngPattern when a literal regular expression value
19745        //is used as the expression (this way we don't have to watch anything).
19746        if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
19747          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
19748          if (match) {
19749            attr.$set("ngPattern", new RegExp(match[1], match[2]));
19750            return;
19751          }
19752        }
19753
19754        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
19755          attr.$set(ngAttr, value);
19756        });
19757      }
19758    };
19759  };
19760});
19761
19762// ng-src, ng-srcset, ng-href are interpolated
19763forEach(['src', 'srcset', 'href'], function(attrName) {
19764  var normalized = directiveNormalize('ng-' + attrName);
19765  ngAttributeAliasDirectives[normalized] = function() {
19766    return {
19767      priority: 99, // it needs to run after the attributes are interpolated
19768      link: function(scope, element, attr) {
19769        var propName = attrName,
19770            name = attrName;
19771
19772        if (attrName === 'href' &&
19773            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
19774          name = 'xlinkHref';
19775          attr.$attr[name] = 'xlink:href';
19776          propName = null;
19777        }
19778
19779        attr.$observe(normalized, function(value) {
19780          if (!value) {
19781            if (attrName === 'href') {
19782              attr.$set(name, null);
19783            }
19784            return;
19785          }
19786
19787          attr.$set(name, value);
19788
19789          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
19790          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
19791          // to set the property as well to achieve the desired effect.
19792          // we use attr[attrName] value since $set can sanitize the url.
19793          if (msie && propName) element.prop(propName, attr[name]);
19794        });
19795      }
19796    };
19797  };
19798});
19799
19800/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
19801 */
19802var nullFormCtrl = {
19803  $addControl: noop,
19804  $$renameControl: nullFormRenameControl,
19805  $removeControl: noop,
19806  $setValidity: noop,
19807  $setDirty: noop,
19808  $setPristine: noop,
19809  $setSubmitted: noop
19810},
19811SUBMITTED_CLASS = 'ng-submitted';
19812
19813function nullFormRenameControl(control, name) {
19814  control.$name = name;
19815}
19816
19817/**
19818 * @ngdoc type
19819 * @name form.FormController
19820 *
19821 * @property {boolean} $pristine True if user has not interacted with the form yet.
19822 * @property {boolean} $dirty True if user has already interacted with the form.
19823 * @property {boolean} $valid True if all of the containing forms and controls are valid.
19824 * @property {boolean} $invalid True if at least one containing control or form is invalid.
19825 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
19826 *
19827 * @property {Object} $error Is an object hash, containing references to controls or
19828 *  forms with failing validators, where:
19829 *
19830 *  - keys are validation tokens (error names),
19831 *  - values are arrays of controls or forms that have a failing validator for given error name.
19832 *
19833 *  Built-in validation tokens:
19834 *
19835 *  - `email`
19836 *  - `max`
19837 *  - `maxlength`
19838 *  - `min`
19839 *  - `minlength`
19840 *  - `number`
19841 *  - `pattern`
19842 *  - `required`
19843 *  - `url`
19844 *  - `date`
19845 *  - `datetimelocal`
19846 *  - `time`
19847 *  - `week`
19848 *  - `month`
19849 *
19850 * @description
19851 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
19852 * such as being valid/invalid or dirty/pristine.
19853 *
19854 * Each {@link ng.directive:form form} directive creates an instance
19855 * of `FormController`.
19856 *
19857 */
19858//asks for $scope to fool the BC controller module
19859FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
19860function FormController(element, attrs, $scope, $animate, $interpolate) {
19861  var form = this,
19862      controls = [];
19863
19864  var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl;
19865
19866  // init state
19867  form.$error = {};
19868  form.$$success = {};
19869  form.$pending = undefined;
19870  form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
19871  form.$dirty = false;
19872  form.$pristine = true;
19873  form.$valid = true;
19874  form.$invalid = false;
19875  form.$submitted = false;
19876
19877  parentForm.$addControl(form);
19878
19879  /**
19880   * @ngdoc method
19881   * @name form.FormController#$rollbackViewValue
19882   *
19883   * @description
19884   * Rollback all form controls pending updates to the `$modelValue`.
19885   *
19886   * Updates may be pending by a debounced event or because the input is waiting for a some future
19887   * event defined in `ng-model-options`. This method is typically needed by the reset button of
19888   * a form that uses `ng-model-options` to pend updates.
19889   */
19890  form.$rollbackViewValue = function() {
19891    forEach(controls, function(control) {
19892      control.$rollbackViewValue();
19893    });
19894  };
19895
19896  /**
19897   * @ngdoc method
19898   * @name form.FormController#$commitViewValue
19899   *
19900   * @description
19901   * Commit all form controls pending updates to the `$modelValue`.
19902   *
19903   * Updates may be pending by a debounced event or because the input is waiting for a some future
19904   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
19905   * usually handles calling this in response to input events.
19906   */
19907  form.$commitViewValue = function() {
19908    forEach(controls, function(control) {
19909      control.$commitViewValue();
19910    });
19911  };
19912
19913  /**
19914   * @ngdoc method
19915   * @name form.FormController#$addControl
19916   *
19917   * @description
19918   * Register a control with the form.
19919   *
19920   * Input elements using ngModelController do this automatically when they are linked.
19921   */
19922  form.$addControl = function(control) {
19923    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
19924    // and not added to the scope.  Now we throw an error.
19925    assertNotHasOwnProperty(control.$name, 'input');
19926    controls.push(control);
19927
19928    if (control.$name) {
19929      form[control.$name] = control;
19930    }
19931  };
19932
19933  // Private API: rename a form control
19934  form.$$renameControl = function(control, newName) {
19935    var oldName = control.$name;
19936
19937    if (form[oldName] === control) {
19938      delete form[oldName];
19939    }
19940    form[newName] = control;
19941    control.$name = newName;
19942  };
19943
19944  /**
19945   * @ngdoc method
19946   * @name form.FormController#$removeControl
19947   *
19948   * @description
19949   * Deregister a control from the form.
19950   *
19951   * Input elements using ngModelController do this automatically when they are destroyed.
19952   */
19953  form.$removeControl = function(control) {
19954    if (control.$name && form[control.$name] === control) {
19955      delete form[control.$name];
19956    }
19957    forEach(form.$pending, function(value, name) {
19958      form.$setValidity(name, null, control);
19959    });
19960    forEach(form.$error, function(value, name) {
19961      form.$setValidity(name, null, control);
19962    });
19963    forEach(form.$$success, function(value, name) {
19964      form.$setValidity(name, null, control);
19965    });
19966
19967    arrayRemove(controls, control);
19968  };
19969
19970
19971  /**
19972   * @ngdoc method
19973   * @name form.FormController#$setValidity
19974   *
19975   * @description
19976   * Sets the validity of a form control.
19977   *
19978   * This method will also propagate to parent forms.
19979   */
19980  addSetValidityMethod({
19981    ctrl: this,
19982    $element: element,
19983    set: function(object, property, controller) {
19984      var list = object[property];
19985      if (!list) {
19986        object[property] = [controller];
19987      } else {
19988        var index = list.indexOf(controller);
19989        if (index === -1) {
19990          list.push(controller);
19991        }
19992      }
19993    },
19994    unset: function(object, property, controller) {
19995      var list = object[property];
19996      if (!list) {
19997        return;
19998      }
19999      arrayRemove(list, controller);
20000      if (list.length === 0) {
20001        delete object[property];
20002      }
20003    },
20004    parentForm: parentForm,
20005    $animate: $animate
20006  });
20007
20008  /**
20009   * @ngdoc method
20010   * @name form.FormController#$setDirty
20011   *
20012   * @description
20013   * Sets the form to a dirty state.
20014   *
20015   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20016   * state (ng-dirty class). This method will also propagate to parent forms.
20017   */
20018  form.$setDirty = function() {
20019    $animate.removeClass(element, PRISTINE_CLASS);
20020    $animate.addClass(element, DIRTY_CLASS);
20021    form.$dirty = true;
20022    form.$pristine = false;
20023    parentForm.$setDirty();
20024  };
20025
20026  /**
20027   * @ngdoc method
20028   * @name form.FormController#$setPristine
20029   *
20030   * @description
20031   * Sets the form to its pristine state.
20032   *
20033   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20034   * state (ng-pristine class). This method will also propagate to all the controls contained
20035   * in this form.
20036   *
20037   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20038   * saving or resetting it.
20039   */
20040  form.$setPristine = function() {
20041    $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20042    form.$dirty = false;
20043    form.$pristine = true;
20044    form.$submitted = false;
20045    forEach(controls, function(control) {
20046      control.$setPristine();
20047    });
20048  };
20049
20050  /**
20051   * @ngdoc method
20052   * @name form.FormController#$setUntouched
20053   *
20054   * @description
20055   * Sets the form to its untouched state.
20056   *
20057   * This method can be called to remove the 'ng-touched' class and set the form controls to their
20058   * untouched state (ng-untouched class).
20059   *
20060   * Setting a form controls back to their untouched state is often useful when setting the form
20061   * back to its pristine state.
20062   */
20063  form.$setUntouched = function() {
20064    forEach(controls, function(control) {
20065      control.$setUntouched();
20066    });
20067  };
20068
20069  /**
20070   * @ngdoc method
20071   * @name form.FormController#$setSubmitted
20072   *
20073   * @description
20074   * Sets the form to its submitted state.
20075   */
20076  form.$setSubmitted = function() {
20077    $animate.addClass(element, SUBMITTED_CLASS);
20078    form.$submitted = true;
20079    parentForm.$setSubmitted();
20080  };
20081}
20082
20083/**
20084 * @ngdoc directive
20085 * @name ngForm
20086 * @restrict EAC
20087 *
20088 * @description
20089 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20090 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20091 * sub-group of controls needs to be determined.
20092 *
20093 * Note: the purpose of `ngForm` is to group controls,
20094 * but not to be a replacement for the `<form>` tag with all of its capabilities
20095 * (e.g. posting to the server, ...).
20096 *
20097 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20098 *                       related scope, under this name.
20099 *
20100 */
20101
20102 /**
20103 * @ngdoc directive
20104 * @name form
20105 * @restrict E
20106 *
20107 * @description
20108 * Directive that instantiates
20109 * {@link form.FormController FormController}.
20110 *
20111 * If the `name` attribute is specified, the form controller is published onto the current scope under
20112 * this name.
20113 *
20114 * # Alias: {@link ng.directive:ngForm `ngForm`}
20115 *
20116 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20117 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20118 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
20119 * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
20120 * using Angular validation directives in forms that are dynamically generated using the
20121 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
20122 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
20123 * `ngForm` directive and nest these in an outer `form` element.
20124 *
20125 *
20126 * # CSS classes
20127 *  - `ng-valid` is set if the form is valid.
20128 *  - `ng-invalid` is set if the form is invalid.
20129 *  - `ng-pristine` is set if the form is pristine.
20130 *  - `ng-dirty` is set if the form is dirty.
20131 *  - `ng-submitted` is set if the form was submitted.
20132 *
20133 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20134 *
20135 *
20136 * # Submitting a form and preventing the default action
20137 *
20138 * Since the role of forms in client-side Angular applications is different than in classical
20139 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20140 * page reload that sends the data to the server. Instead some javascript logic should be triggered
20141 * to handle the form submission in an application-specific way.
20142 *
20143 * For this reason, Angular prevents the default action (form submission to the server) unless the
20144 * `<form>` element has an `action` attribute specified.
20145 *
20146 * You can use one of the following two ways to specify what javascript method should be called when
20147 * a form is submitted:
20148 *
20149 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20150 * - {@link ng.directive:ngClick ngClick} directive on the first
20151  *  button or input field of type submit (input[type=submit])
20152 *
20153 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20154 * or {@link ng.directive:ngClick ngClick} directives.
20155 * This is because of the following form submission rules in the HTML specification:
20156 *
20157 * - If a form has only one input field then hitting enter in this field triggers form submit
20158 * (`ngSubmit`)
20159 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20160 * doesn't trigger submit
20161 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20162 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20163 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20164 *
20165 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20166 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20167 * to have access to the updated model.
20168 *
20169 * ## Animation Hooks
20170 *
20171 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20172 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20173 * other validations that are performed within the form. Animations in ngForm are similar to how
20174 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20175 * as JS animations.
20176 *
20177 * The following example shows a simple way to utilize CSS transitions to style a form element
20178 * that has been rendered as invalid after it has been validated:
20179 *
20180 * <pre>
20181 * //be sure to include ngAnimate as a module to hook into more
20182 * //advanced animations
20183 * .my-form {
20184 *   transition:0.5s linear all;
20185 *   background: white;
20186 * }
20187 * .my-form.ng-invalid {
20188 *   background: red;
20189 *   color:white;
20190 * }
20191 * </pre>
20192 *
20193 * @example
20194    <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20195      <file name="index.html">
20196       <script>
20197         angular.module('formExample', [])
20198           .controller('FormController', ['$scope', function($scope) {
20199             $scope.userType = 'guest';
20200           }]);
20201       </script>
20202       <style>
20203        .my-form {
20204          -webkit-transition:all linear 0.5s;
20205          transition:all linear 0.5s;
20206          background: transparent;
20207        }
20208        .my-form.ng-invalid {
20209          background: red;
20210        }
20211       </style>
20212       <form name="myForm" ng-controller="FormController" class="my-form">
20213         userType: <input name="input" ng-model="userType" required>
20214         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20215         <code>userType = {{userType}}</code><br>
20216         <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20217         <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20218         <code>myForm.$valid = {{myForm.$valid}}</code><br>
20219         <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20220        </form>
20221      </file>
20222      <file name="protractor.js" type="protractor">
20223        it('should initialize to model', function() {
20224          var userType = element(by.binding('userType'));
20225          var valid = element(by.binding('myForm.input.$valid'));
20226
20227          expect(userType.getText()).toContain('guest');
20228          expect(valid.getText()).toContain('true');
20229        });
20230
20231        it('should be invalid if empty', function() {
20232          var userType = element(by.binding('userType'));
20233          var valid = element(by.binding('myForm.input.$valid'));
20234          var userInput = element(by.model('userType'));
20235
20236          userInput.clear();
20237          userInput.sendKeys('');
20238
20239          expect(userType.getText()).toEqual('userType =');
20240          expect(valid.getText()).toContain('false');
20241        });
20242      </file>
20243    </example>
20244 *
20245 * @param {string=} name Name of the form. If specified, the form controller will be published into
20246 *                       related scope, under this name.
20247 */
20248var formDirectiveFactory = function(isNgForm) {
20249  return ['$timeout', function($timeout) {
20250    var formDirective = {
20251      name: 'form',
20252      restrict: isNgForm ? 'EAC' : 'E',
20253      controller: FormController,
20254      compile: function ngFormCompile(formElement, attr) {
20255        // Setup initial state of the control
20256        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20257
20258        var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20259
20260        return {
20261          pre: function ngFormPreLink(scope, formElement, attr, controller) {
20262            // if `action` attr is not present on the form, prevent the default action (submission)
20263            if (!('action' in attr)) {
20264              // we can't use jq events because if a form is destroyed during submission the default
20265              // action is not prevented. see #1238
20266              //
20267              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20268              // page reload if the form was destroyed by submission of the form via a click handler
20269              // on a button in the form. Looks like an IE9 specific bug.
20270              var handleFormSubmission = function(event) {
20271                scope.$apply(function() {
20272                  controller.$commitViewValue();
20273                  controller.$setSubmitted();
20274                });
20275
20276                event.preventDefault();
20277              };
20278
20279              addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20280
20281              // unregister the preventDefault listener so that we don't not leak memory but in a
20282              // way that will achieve the prevention of the default action.
20283              formElement.on('$destroy', function() {
20284                $timeout(function() {
20285                  removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20286                }, 0, false);
20287              });
20288            }
20289
20290            var parentFormCtrl = controller.$$parentForm;
20291
20292            if (nameAttr) {
20293              setter(scope, controller.$name, controller, controller.$name);
20294              attr.$observe(nameAttr, function(newValue) {
20295                if (controller.$name === newValue) return;
20296                setter(scope, controller.$name, undefined, controller.$name);
20297                parentFormCtrl.$$renameControl(controller, newValue);
20298                setter(scope, controller.$name, controller, controller.$name);
20299              });
20300            }
20301            formElement.on('$destroy', function() {
20302              parentFormCtrl.$removeControl(controller);
20303              if (nameAttr) {
20304                setter(scope, attr[nameAttr], undefined, controller.$name);
20305              }
20306              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
20307            });
20308          }
20309        };
20310      }
20311    };
20312
20313    return formDirective;
20314  }];
20315};
20316
20317var formDirective = formDirectiveFactory();
20318var ngFormDirective = formDirectiveFactory(true);
20319
20320/* global VALID_CLASS: false,
20321  INVALID_CLASS: false,
20322  PRISTINE_CLASS: false,
20323  DIRTY_CLASS: false,
20324  UNTOUCHED_CLASS: false,
20325  TOUCHED_CLASS: false,
20326  $ngModelMinErr: false,
20327*/
20328
20329// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
20330var 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)/;
20331var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
20332var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
20333var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
20334var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
20335var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20336var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
20337var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
20338var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20339
20340var inputType = {
20341
20342  /**
20343   * @ngdoc input
20344   * @name input[text]
20345   *
20346   * @description
20347   * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
20348   *
20349   *
20350   * @param {string} ngModel Assignable angular expression to data-bind to.
20351   * @param {string=} name Property name of the form under which the control is published.
20352   * @param {string=} required Adds `required` validation error key if the value is not entered.
20353   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20354   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20355   *    `required` when you want to data-bind to the `required` attribute.
20356   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20357   *    minlength.
20358   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20359   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
20360   *    any length.
20361   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
20362   *    that contains the regular expression body that will be converted to a regular expression
20363   *    as in the ngPattern directive.
20364   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
20365   *    a RegExp found by evaluating the Angular expression given in the attribute value.
20366   *    If the expression evaluates to a RegExp object, then this is used directly.
20367   *    If the expression evaluates to a string, then it will be converted to a RegExp
20368   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
20369   *    `new RegExp('^abc$')`.<br />
20370   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
20371   *    start at the index of the last search's match, thus not taking the whole input value into
20372   *    account.
20373   * @param {string=} ngChange Angular expression to be executed when input changes due to user
20374   *    interaction with the input element.
20375   * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20376   *    This parameter is ignored for input[type=password] controls, which will never trim the
20377   *    input.
20378   *
20379   * @example
20380      <example name="text-input-directive" module="textInputExample">
20381        <file name="index.html">
20382         <script>
20383           angular.module('textInputExample', [])
20384             .controller('ExampleController', ['$scope', function($scope) {
20385               $scope.example = {
20386                 text: 'guest',
20387                 word: /^\s*\w*\s*$/
20388               };
20389             }]);
20390         </script>
20391         <form name="myForm" ng-controller="ExampleController">
20392           <label>Single word:
20393             <input type="text" name="input" ng-model="example.text"
20394                    ng-pattern="example.word" required ng-trim="false">
20395           </label>
20396           <div role="alert">
20397             <span class="error" ng-show="myForm.input.$error.required">
20398               Required!</span>
20399             <span class="error" ng-show="myForm.input.$error.pattern">
20400               Single word only!</span>
20401           </div>
20402           <tt>text = {{example.text}}</tt><br/>
20403           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20404           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20405           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20406           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20407          </form>
20408        </file>
20409        <file name="protractor.js" type="protractor">
20410          var text = element(by.binding('example.text'));
20411          var valid = element(by.binding('myForm.input.$valid'));
20412          var input = element(by.model('example.text'));
20413
20414          it('should initialize to model', function() {
20415            expect(text.getText()).toContain('guest');
20416            expect(valid.getText()).toContain('true');
20417          });
20418
20419          it('should be invalid if empty', function() {
20420            input.clear();
20421            input.sendKeys('');
20422
20423            expect(text.getText()).toEqual('text =');
20424            expect(valid.getText()).toContain('false');
20425          });
20426
20427          it('should be invalid if multi word', function() {
20428            input.clear();
20429            input.sendKeys('hello world');
20430
20431            expect(valid.getText()).toContain('false');
20432          });
20433        </file>
20434      </example>
20435   */
20436  'text': textInputType,
20437
20438    /**
20439     * @ngdoc input
20440     * @name input[date]
20441     *
20442     * @description
20443     * Input with date validation and transformation. In browsers that do not yet support
20444     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
20445     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
20446     * modern browsers do not yet support this input type, it is important to provide cues to users on the
20447     * expected input format via a placeholder or label.
20448     *
20449     * The model must always be a Date object, otherwise Angular will throw an error.
20450     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20451     *
20452     * The timezone to be used to read/write the `Date` instance in the model can be defined using
20453     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20454     *
20455     * @param {string} ngModel Assignable angular expression to data-bind to.
20456     * @param {string=} name Property name of the form under which the control is published.
20457     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20458     * valid ISO date string (yyyy-MM-dd).
20459     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20460     * a valid ISO date string (yyyy-MM-dd).
20461     * @param {string=} required Sets `required` validation error key if the value is not entered.
20462     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20463     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20464     *    `required` when you want to data-bind to the `required` attribute.
20465     * @param {string=} ngChange Angular expression to be executed when input changes due to user
20466     *    interaction with the input element.
20467     *
20468     * @example
20469     <example name="date-input-directive" module="dateInputExample">
20470     <file name="index.html">
20471       <script>
20472          angular.module('dateInputExample', [])
20473            .controller('DateController', ['$scope', function($scope) {
20474              $scope.example = {
20475                value: new Date(2013, 9, 22)
20476              };
20477            }]);
20478       </script>
20479       <form name="myForm" ng-controller="DateController as dateCtrl">
20480          <label for="exampleInput">Pick a date in 2013:</label>
20481          <input type="date" id="exampleInput" name="input" ng-model="example.value"
20482              placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
20483          <div role="alert">
20484            <span class="error" ng-show="myForm.input.$error.required">
20485                Required!</span>
20486            <span class="error" ng-show="myForm.input.$error.date">
20487                Not a valid date!</span>
20488           </div>
20489           <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
20490           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20491           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20492           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20493           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20494       </form>
20495     </file>
20496     <file name="protractor.js" type="protractor">
20497        var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
20498        var valid = element(by.binding('myForm.input.$valid'));
20499        var input = element(by.model('example.value'));
20500
20501        // currently protractor/webdriver does not support
20502        // sending keys to all known HTML5 input controls
20503        // for various browsers (see https://github.com/angular/protractor/issues/562).
20504        function setInput(val) {
20505          // set the value of the element and force validation.
20506          var scr = "var ipt = document.getElementById('exampleInput'); " +
20507          "ipt.value = '" + val + "';" +
20508          "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20509          browser.executeScript(scr);
20510        }
20511
20512        it('should initialize to model', function() {
20513          expect(value.getText()).toContain('2013-10-22');
20514          expect(valid.getText()).toContain('myForm.input.$valid = true');
20515        });
20516
20517        it('should be invalid if empty', function() {
20518          setInput('');
20519          expect(value.getText()).toEqual('value =');
20520          expect(valid.getText()).toContain('myForm.input.$valid = false');
20521        });
20522
20523        it('should be invalid if over max', function() {
20524          setInput('2015-01-01');
20525          expect(value.getText()).toContain('');
20526          expect(valid.getText()).toContain('myForm.input.$valid = false');
20527        });
20528     </file>
20529     </example>
20530     */
20531  'date': createDateInputType('date', DATE_REGEXP,
20532         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
20533         'yyyy-MM-dd'),
20534
20535   /**
20536    * @ngdoc input
20537    * @name input[datetime-local]
20538    *
20539    * @description
20540    * Input with datetime validation and transformation. In browsers that do not yet support
20541    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20542    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
20543    *
20544    * The model must always be a Date object, otherwise Angular will throw an error.
20545    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20546    *
20547    * The timezone to be used to read/write the `Date` instance in the model can be defined using
20548    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20549    *
20550    * @param {string} ngModel Assignable angular expression to data-bind to.
20551    * @param {string=} name Property name of the form under which the control is published.
20552    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20553    * valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
20554    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20555    * a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
20556    * @param {string=} required Sets `required` validation error key if the value is not entered.
20557    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20558    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20559    *    `required` when you want to data-bind to the `required` attribute.
20560    * @param {string=} ngChange Angular expression to be executed when input changes due to user
20561    *    interaction with the input element.
20562    *
20563    * @example
20564    <example name="datetimelocal-input-directive" module="dateExample">
20565    <file name="index.html">
20566      <script>
20567        angular.module('dateExample', [])
20568          .controller('DateController', ['$scope', function($scope) {
20569            $scope.example = {
20570              value: new Date(2010, 11, 28, 14, 57)
20571            };
20572          }]);
20573      </script>
20574      <form name="myForm" ng-controller="DateController as dateCtrl">
20575        <label for="exampleInput">Pick a date between in 2013:</label>
20576        <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
20577            placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
20578        <div role="alert">
20579          <span class="error" ng-show="myForm.input.$error.required">
20580              Required!</span>
20581          <span class="error" ng-show="myForm.input.$error.datetimelocal">
20582              Not a valid date!</span>
20583        </div>
20584        <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
20585        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20586        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20587        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20588        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20589      </form>
20590    </file>
20591    <file name="protractor.js" type="protractor">
20592      var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
20593      var valid = element(by.binding('myForm.input.$valid'));
20594      var input = element(by.model('example.value'));
20595
20596      // currently protractor/webdriver does not support
20597      // sending keys to all known HTML5 input controls
20598      // for various browsers (https://github.com/angular/protractor/issues/562).
20599      function setInput(val) {
20600        // set the value of the element and force validation.
20601        var scr = "var ipt = document.getElementById('exampleInput'); " +
20602        "ipt.value = '" + val + "';" +
20603        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20604        browser.executeScript(scr);
20605      }
20606
20607      it('should initialize to model', function() {
20608        expect(value.getText()).toContain('2010-12-28T14:57:00');
20609        expect(valid.getText()).toContain('myForm.input.$valid = true');
20610      });
20611
20612      it('should be invalid if empty', function() {
20613        setInput('');
20614        expect(value.getText()).toEqual('value =');
20615        expect(valid.getText()).toContain('myForm.input.$valid = false');
20616      });
20617
20618      it('should be invalid if over max', function() {
20619        setInput('2015-01-01T23:59:00');
20620        expect(value.getText()).toContain('');
20621        expect(valid.getText()).toContain('myForm.input.$valid = false');
20622      });
20623    </file>
20624    </example>
20625    */
20626  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
20627      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
20628      'yyyy-MM-ddTHH:mm:ss.sss'),
20629
20630  /**
20631   * @ngdoc input
20632   * @name input[time]
20633   *
20634   * @description
20635   * Input with time validation and transformation. In browsers that do not yet support
20636   * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20637   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
20638   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
20639   *
20640   * The model must always be a Date object, otherwise Angular will throw an error.
20641   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20642   *
20643   * The timezone to be used to read/write the `Date` instance in the model can be defined using
20644   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20645   *
20646   * @param {string} ngModel Assignable angular expression to data-bind to.
20647   * @param {string=} name Property name of the form under which the control is published.
20648   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20649   * valid ISO time format (HH:mm:ss).
20650   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a
20651   * valid ISO time format (HH:mm:ss).
20652   * @param {string=} required Sets `required` validation error key if the value is not entered.
20653   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20654   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20655   *    `required` when you want to data-bind to the `required` attribute.
20656   * @param {string=} ngChange Angular expression to be executed when input changes due to user
20657   *    interaction with the input element.
20658   *
20659   * @example
20660   <example name="time-input-directive" module="timeExample">
20661   <file name="index.html">
20662     <script>
20663      angular.module('timeExample', [])
20664        .controller('DateController', ['$scope', function($scope) {
20665          $scope.example = {
20666            value: new Date(1970, 0, 1, 14, 57, 0)
20667          };
20668        }]);
20669     </script>
20670     <form name="myForm" ng-controller="DateController as dateCtrl">
20671        <label for="exampleInput">Pick a between 8am and 5pm:</label>
20672        <input type="time" id="exampleInput" name="input" ng-model="example.value"
20673            placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
20674        <div role="alert">
20675          <span class="error" ng-show="myForm.input.$error.required">
20676              Required!</span>
20677          <span class="error" ng-show="myForm.input.$error.time">
20678              Not a valid date!</span>
20679        </div>
20680        <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
20681        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20682        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20683        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20684        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20685     </form>
20686   </file>
20687   <file name="protractor.js" type="protractor">
20688      var value = element(by.binding('example.value | date: "HH:mm:ss"'));
20689      var valid = element(by.binding('myForm.input.$valid'));
20690      var input = element(by.model('example.value'));
20691
20692      // currently protractor/webdriver does not support
20693      // sending keys to all known HTML5 input controls
20694      // for various browsers (https://github.com/angular/protractor/issues/562).
20695      function setInput(val) {
20696        // set the value of the element and force validation.
20697        var scr = "var ipt = document.getElementById('exampleInput'); " +
20698        "ipt.value = '" + val + "';" +
20699        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20700        browser.executeScript(scr);
20701      }
20702
20703      it('should initialize to model', function() {
20704        expect(value.getText()).toContain('14:57:00');
20705        expect(valid.getText()).toContain('myForm.input.$valid = true');
20706      });
20707
20708      it('should be invalid if empty', function() {
20709        setInput('');
20710        expect(value.getText()).toEqual('value =');
20711        expect(valid.getText()).toContain('myForm.input.$valid = false');
20712      });
20713
20714      it('should be invalid if over max', function() {
20715        setInput('23:59:00');
20716        expect(value.getText()).toContain('');
20717        expect(valid.getText()).toContain('myForm.input.$valid = false');
20718      });
20719   </file>
20720   </example>
20721   */
20722  'time': createDateInputType('time', TIME_REGEXP,
20723      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
20724     'HH:mm:ss.sss'),
20725
20726   /**
20727    * @ngdoc input
20728    * @name input[week]
20729    *
20730    * @description
20731    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
20732    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20733    * week format (yyyy-W##), for example: `2013-W02`.
20734    *
20735    * The model must always be a Date object, otherwise Angular will throw an error.
20736    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20737    *
20738    * The timezone to be used to read/write the `Date` instance in the model can be defined using
20739    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20740    *
20741    * @param {string} ngModel Assignable angular expression to data-bind to.
20742    * @param {string=} name Property name of the form under which the control is published.
20743    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20744    * valid ISO week format (yyyy-W##).
20745    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20746    * a valid ISO week format (yyyy-W##).
20747    * @param {string=} required Sets `required` validation error key if the value is not entered.
20748    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20749    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20750    *    `required` when you want to data-bind to the `required` attribute.
20751    * @param {string=} ngChange Angular expression to be executed when input changes due to user
20752    *    interaction with the input element.
20753    *
20754    * @example
20755    <example name="week-input-directive" module="weekExample">
20756    <file name="index.html">
20757      <script>
20758      angular.module('weekExample', [])
20759        .controller('DateController', ['$scope', function($scope) {
20760          $scope.example = {
20761            value: new Date(2013, 0, 3)
20762          };
20763        }]);
20764      </script>
20765      <form name="myForm" ng-controller="DateController as dateCtrl">
20766        <label>Pick a date between in 2013:
20767          <input id="exampleInput" type="week" name="input" ng-model="example.value"
20768                 placeholder="YYYY-W##" min="2012-W32"
20769                 max="2013-W52" required />
20770        </label>
20771        <div role="alert">
20772          <span class="error" ng-show="myForm.input.$error.required">
20773              Required!</span>
20774          <span class="error" ng-show="myForm.input.$error.week">
20775              Not a valid date!</span>
20776        </div>
20777        <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
20778        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20779        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20780        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20781        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20782      </form>
20783    </file>
20784    <file name="protractor.js" type="protractor">
20785      var value = element(by.binding('example.value | date: "yyyy-Www"'));
20786      var valid = element(by.binding('myForm.input.$valid'));
20787      var input = element(by.model('example.value'));
20788
20789      // currently protractor/webdriver does not support
20790      // sending keys to all known HTML5 input controls
20791      // for various browsers (https://github.com/angular/protractor/issues/562).
20792      function setInput(val) {
20793        // set the value of the element and force validation.
20794        var scr = "var ipt = document.getElementById('exampleInput'); " +
20795        "ipt.value = '" + val + "';" +
20796        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20797        browser.executeScript(scr);
20798      }
20799
20800      it('should initialize to model', function() {
20801        expect(value.getText()).toContain('2013-W01');
20802        expect(valid.getText()).toContain('myForm.input.$valid = true');
20803      });
20804
20805      it('should be invalid if empty', function() {
20806        setInput('');
20807        expect(value.getText()).toEqual('value =');
20808        expect(valid.getText()).toContain('myForm.input.$valid = false');
20809      });
20810
20811      it('should be invalid if over max', function() {
20812        setInput('2015-W01');
20813        expect(value.getText()).toContain('');
20814        expect(valid.getText()).toContain('myForm.input.$valid = false');
20815      });
20816    </file>
20817    </example>
20818    */
20819  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
20820
20821  /**
20822   * @ngdoc input
20823   * @name input[month]
20824   *
20825   * @description
20826   * Input with month validation and transformation. In browsers that do not yet support
20827   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20828   * month format (yyyy-MM), for example: `2009-01`.
20829   *
20830   * The model must always be a Date object, otherwise Angular will throw an error.
20831   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20832   * If the model is not set to the first of the month, the next view to model update will set it
20833   * to the first of the month.
20834   *
20835   * The timezone to be used to read/write the `Date` instance in the model can be defined using
20836   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20837   *
20838   * @param {string} ngModel Assignable angular expression to data-bind to.
20839   * @param {string=} name Property name of the form under which the control is published.
20840   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be
20841   * a valid ISO month format (yyyy-MM).
20842   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must
20843   * be a valid ISO month format (yyyy-MM).
20844   * @param {string=} required Sets `required` validation error key if the value is not entered.
20845   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20846   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20847   *    `required` when you want to data-bind to the `required` attribute.
20848   * @param {string=} ngChange Angular expression to be executed when input changes due to user
20849   *    interaction with the input element.
20850   *
20851   * @example
20852   <example name="month-input-directive" module="monthExample">
20853   <file name="index.html">
20854     <script>
20855      angular.module('monthExample', [])
20856        .controller('DateController', ['$scope', function($scope) {
20857          $scope.example = {
20858            value: new Date(2013, 9, 1)
20859          };
20860        }]);
20861     </script>
20862     <form name="myForm" ng-controller="DateController as dateCtrl">
20863       <label for="exampleInput">Pick a month in 2013:</label>
20864       <input id="exampleInput" type="month" name="input" ng-model="example.value"
20865          placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
20866       <div role="alert">
20867         <span class="error" ng-show="myForm.input.$error.required">
20868            Required!</span>
20869         <span class="error" ng-show="myForm.input.$error.month">
20870            Not a valid month!</span>
20871       </div>
20872       <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
20873       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20874       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20875       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20876       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20877     </form>
20878   </file>
20879   <file name="protractor.js" type="protractor">
20880      var value = element(by.binding('example.value | date: "yyyy-MM"'));
20881      var valid = element(by.binding('myForm.input.$valid'));
20882      var input = element(by.model('example.value'));
20883
20884      // currently protractor/webdriver does not support
20885      // sending keys to all known HTML5 input controls
20886      // for various browsers (https://github.com/angular/protractor/issues/562).
20887      function setInput(val) {
20888        // set the value of the element and force validation.
20889        var scr = "var ipt = document.getElementById('exampleInput'); " +
20890        "ipt.value = '" + val + "';" +
20891        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20892        browser.executeScript(scr);
20893      }
20894
20895      it('should initialize to model', function() {
20896        expect(value.getText()).toContain('2013-10');
20897        expect(valid.getText()).toContain('myForm.input.$valid = true');
20898      });
20899
20900      it('should be invalid if empty', function() {
20901        setInput('');
20902        expect(value.getText()).toEqual('value =');
20903        expect(valid.getText()).toContain('myForm.input.$valid = false');
20904      });
20905
20906      it('should be invalid if over max', function() {
20907        setInput('2015-01');
20908        expect(value.getText()).toContain('');
20909        expect(valid.getText()).toContain('myForm.input.$valid = false');
20910      });
20911   </file>
20912   </example>
20913   */
20914  'month': createDateInputType('month', MONTH_REGEXP,
20915     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
20916     'yyyy-MM'),
20917
20918  /**
20919   * @ngdoc input
20920   * @name input[number]
20921   *
20922   * @description
20923   * Text input with number validation and transformation. Sets the `number` validation
20924   * error if not a valid number.
20925   *
20926   * <div class="alert alert-warning">
20927   * The model must always be of type `number` otherwise Angular will throw an error.
20928   * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
20929   * error docs for more information and an example of how to convert your model if necessary.
20930   * </div>
20931   *
20932   * ## Issues with HTML5 constraint validation
20933   *
20934   * In browsers that follow the
20935   * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
20936   * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
20937   * If a non-number is entered in the input, the browser will report the value as an empty string,
20938   * which means the view / model values in `ngModel` and subsequently the scope value
20939   * will also be an empty string.
20940   *
20941   *
20942   * @param {string} ngModel Assignable angular expression to data-bind to.
20943   * @param {string=} name Property name of the form under which the control is published.
20944   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20945   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20946   * @param {string=} required Sets `required` validation error key if the value is not entered.
20947   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20948   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20949   *    `required` when you want to data-bind to the `required` attribute.
20950   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20951   *    minlength.
20952   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20953   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
20954   *    any length.
20955   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
20956   *    that contains the regular expression body that will be converted to a regular expression
20957   *    as in the ngPattern directive.
20958   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
20959   *    a RegExp found by evaluating the Angular expression given in the attribute value.
20960   *    If the expression evaluates to a RegExp object, then this is used directly.
20961   *    If the expression evaluates to a string, then it will be converted to a RegExp
20962   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
20963   *    `new RegExp('^abc$')`.<br />
20964   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
20965   *    start at the index of the last search's match, thus not taking the whole input value into
20966   *    account.
20967   * @param {string=} ngChange Angular expression to be executed when input changes due to user
20968   *    interaction with the input element.
20969   *
20970   * @example
20971      <example name="number-input-directive" module="numberExample">
20972        <file name="index.html">
20973         <script>
20974           angular.module('numberExample', [])
20975             .controller('ExampleController', ['$scope', function($scope) {
20976               $scope.example = {
20977                 value: 12
20978               };
20979             }]);
20980         </script>
20981         <form name="myForm" ng-controller="ExampleController">
20982           <label>Number:
20983             <input type="number" name="input" ng-model="example.value"
20984                    min="0" max="99" required>
20985          </label>
20986           <div role="alert">
20987             <span class="error" ng-show="myForm.input.$error.required">
20988               Required!</span>
20989             <span class="error" ng-show="myForm.input.$error.number">
20990               Not valid number!</span>
20991           </div>
20992           <tt>value = {{example.value}}</tt><br/>
20993           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20994           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20995           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20996           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20997          </form>
20998        </file>
20999        <file name="protractor.js" type="protractor">
21000          var value = element(by.binding('example.value'));
21001          var valid = element(by.binding('myForm.input.$valid'));
21002          var input = element(by.model('example.value'));
21003
21004          it('should initialize to model', function() {
21005            expect(value.getText()).toContain('12');
21006            expect(valid.getText()).toContain('true');
21007          });
21008
21009          it('should be invalid if empty', function() {
21010            input.clear();
21011            input.sendKeys('');
21012            expect(value.getText()).toEqual('value =');
21013            expect(valid.getText()).toContain('false');
21014          });
21015
21016          it('should be invalid if over max', function() {
21017            input.clear();
21018            input.sendKeys('123');
21019            expect(value.getText()).toEqual('value =');
21020            expect(valid.getText()).toContain('false');
21021          });
21022        </file>
21023      </example>
21024   */
21025  'number': numberInputType,
21026
21027
21028  /**
21029   * @ngdoc input
21030   * @name input[url]
21031   *
21032   * @description
21033   * Text input with URL validation. Sets the `url` validation error key if the content is not a
21034   * valid URL.
21035   *
21036   * <div class="alert alert-warning">
21037   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21038   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21039   * the built-in validators (see the {@link guide/forms Forms guide})
21040   * </div>
21041   *
21042   * @param {string} ngModel Assignable angular expression to data-bind to.
21043   * @param {string=} name Property name of the form under which the control is published.
21044   * @param {string=} required Sets `required` validation error key if the value is not entered.
21045   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21046   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21047   *    `required` when you want to data-bind to the `required` attribute.
21048   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21049   *    minlength.
21050   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21051   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21052   *    any length.
21053   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21054   *    that contains the regular expression body that will be converted to a regular expression
21055   *    as in the ngPattern directive.
21056   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21057   *    a RegExp found by evaluating the Angular expression given in the attribute value.
21058   *    If the expression evaluates to a RegExp object, then this is used directly.
21059   *    If the expression evaluates to a string, then it will be converted to a RegExp
21060   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21061   *    `new RegExp('^abc$')`.<br />
21062   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21063   *    start at the index of the last search's match, thus not taking the whole input value into
21064   *    account.
21065   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21066   *    interaction with the input element.
21067   *
21068   * @example
21069      <example name="url-input-directive" module="urlExample">
21070        <file name="index.html">
21071         <script>
21072           angular.module('urlExample', [])
21073             .controller('ExampleController', ['$scope', function($scope) {
21074               $scope.url = {
21075                 text: 'http://google.com'
21076               };
21077             }]);
21078         </script>
21079         <form name="myForm" ng-controller="ExampleController">
21080           <label>URL:
21081             <input type="url" name="input" ng-model="url.text" required>
21082           <label>
21083           <div role="alert">
21084             <span class="error" ng-show="myForm.input.$error.required">
21085               Required!</span>
21086             <span class="error" ng-show="myForm.input.$error.url">
21087               Not valid url!</span>
21088           </div>
21089           <tt>text = {{url.text}}</tt><br/>
21090           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21091           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21092           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21093           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21094           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21095          </form>
21096        </file>
21097        <file name="protractor.js" type="protractor">
21098          var text = element(by.binding('url.text'));
21099          var valid = element(by.binding('myForm.input.$valid'));
21100          var input = element(by.model('url.text'));
21101
21102          it('should initialize to model', function() {
21103            expect(text.getText()).toContain('http://google.com');
21104            expect(valid.getText()).toContain('true');
21105          });
21106
21107          it('should be invalid if empty', function() {
21108            input.clear();
21109            input.sendKeys('');
21110
21111            expect(text.getText()).toEqual('text =');
21112            expect(valid.getText()).toContain('false');
21113          });
21114
21115          it('should be invalid if not url', function() {
21116            input.clear();
21117            input.sendKeys('box');
21118
21119            expect(valid.getText()).toContain('false');
21120          });
21121        </file>
21122      </example>
21123   */
21124  'url': urlInputType,
21125
21126
21127  /**
21128   * @ngdoc input
21129   * @name input[email]
21130   *
21131   * @description
21132   * Text input with email validation. Sets the `email` validation error key if not a valid email
21133   * address.
21134   *
21135   * <div class="alert alert-warning">
21136   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21137   * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21138   * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21139   * </div>
21140   *
21141   * @param {string} ngModel Assignable angular expression to data-bind to.
21142   * @param {string=} name Property name of the form under which the control is published.
21143   * @param {string=} required Sets `required` validation error key if the value is not entered.
21144   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21145   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21146   *    `required` when you want to data-bind to the `required` attribute.
21147   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21148   *    minlength.
21149   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21150   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21151   *    any length.
21152   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21153   *    that contains the regular expression body that will be converted to a regular expression
21154   *    as in the ngPattern directive.
21155   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21156   *    a RegExp found by evaluating the Angular expression given in the attribute value.
21157   *    If the expression evaluates to a RegExp object, then this is used directly.
21158   *    If the expression evaluates to a string, then it will be converted to a RegExp
21159   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21160   *    `new RegExp('^abc$')`.<br />
21161   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21162   *    start at the index of the last search's match, thus not taking the whole input value into
21163   *    account.
21164   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21165   *    interaction with the input element.
21166   *
21167   * @example
21168      <example name="email-input-directive" module="emailExample">
21169        <file name="index.html">
21170         <script>
21171           angular.module('emailExample', [])
21172             .controller('ExampleController', ['$scope', function($scope) {
21173               $scope.email = {
21174                 text: '[email protected]'
21175               };
21176             }]);
21177         </script>
21178           <form name="myForm" ng-controller="ExampleController">
21179             <label>Email:
21180               <input type="email" name="input" ng-model="email.text" required>
21181             </label>
21182             <div role="alert">
21183               <span class="error" ng-show="myForm.input.$error.required">
21184                 Required!</span>
21185               <span class="error" ng-show="myForm.input.$error.email">
21186                 Not valid email!</span>
21187             </div>
21188             <tt>text = {{email.text}}</tt><br/>
21189             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21190             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21191             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21192             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21193             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21194           </form>
21195         </file>
21196        <file name="protractor.js" type="protractor">
21197          var text = element(by.binding('email.text'));
21198          var valid = element(by.binding('myForm.input.$valid'));
21199          var input = element(by.model('email.text'));
21200
21201          it('should initialize to model', function() {
21202            expect(text.getText()).toContain('[email protected]');
21203            expect(valid.getText()).toContain('true');
21204          });
21205
21206          it('should be invalid if empty', function() {
21207            input.clear();
21208            input.sendKeys('');
21209            expect(text.getText()).toEqual('text =');
21210            expect(valid.getText()).toContain('false');
21211          });
21212
21213          it('should be invalid if not email', function() {
21214            input.clear();
21215            input.sendKeys('xxx');
21216
21217            expect(valid.getText()).toContain('false');
21218          });
21219        </file>
21220      </example>
21221   */
21222  'email': emailInputType,
21223
21224
21225  /**
21226   * @ngdoc input
21227   * @name input[radio]
21228   *
21229   * @description
21230   * HTML radio button.
21231   *
21232   * @param {string} ngModel Assignable angular expression to data-bind to.
21233   * @param {string} value The value to which the expression should be set when selected.
21234   * @param {string=} name Property name of the form under which the control is published.
21235   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21236   *    interaction with the input element.
21237   * @param {string} ngValue Angular expression which sets the value to which the expression should
21238   *    be set when selected.
21239   *
21240   * @example
21241      <example name="radio-input-directive" module="radioExample">
21242        <file name="index.html">
21243         <script>
21244           angular.module('radioExample', [])
21245             .controller('ExampleController', ['$scope', function($scope) {
21246               $scope.color = {
21247                 name: 'blue'
21248               };
21249               $scope.specialValue = {
21250                 "id": "12345",
21251                 "value": "green"
21252               };
21253             }]);
21254         </script>
21255         <form name="myForm" ng-controller="ExampleController">
21256           <label>
21257             <input type="radio" ng-model="color.name" value="red">
21258             Red
21259           </label><br/>
21260           <label>
21261             <input type="radio" ng-model="color.name" ng-value="specialValue">
21262             Green
21263           </label><br/>
21264           <label>
21265             <input type="radio" ng-model="color.name" value="blue">
21266             Blue
21267           </label><br/>
21268           <tt>color = {{color.name | json}}</tt><br/>
21269          </form>
21270          Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
21271        </file>
21272        <file name="protractor.js" type="protractor">
21273          it('should change state', function() {
21274            var color = element(by.binding('color.name'));
21275
21276            expect(color.getText()).toContain('blue');
21277
21278            element.all(by.model('color.name')).get(0).click();
21279
21280            expect(color.getText()).toContain('red');
21281          });
21282        </file>
21283      </example>
21284   */
21285  'radio': radioInputType,
21286
21287
21288  /**
21289   * @ngdoc input
21290   * @name input[checkbox]
21291   *
21292   * @description
21293   * HTML checkbox.
21294   *
21295   * @param {string} ngModel Assignable angular expression to data-bind to.
21296   * @param {string=} name Property name of the form under which the control is published.
21297   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
21298   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
21299   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21300   *    interaction with the input element.
21301   *
21302   * @example
21303      <example name="checkbox-input-directive" module="checkboxExample">
21304        <file name="index.html">
21305         <script>
21306           angular.module('checkboxExample', [])
21307             .controller('ExampleController', ['$scope', function($scope) {
21308               $scope.checkboxModel = {
21309                value1 : true,
21310                value2 : 'YES'
21311              };
21312             }]);
21313         </script>
21314         <form name="myForm" ng-controller="ExampleController">
21315           <label>Value1:
21316             <input type="checkbox" ng-model="checkboxModel.value1">
21317           </label><br/>
21318           <label>Value2:
21319             <input type="checkbox" ng-model="checkboxModel.value2"
21320                    ng-true-value="'YES'" ng-false-value="'NO'">
21321            </label><br/>
21322           <tt>value1 = {{checkboxModel.value1}}</tt><br/>
21323           <tt>value2 = {{checkboxModel.value2}}</tt><br/>
21324          </form>
21325        </file>
21326        <file name="protractor.js" type="protractor">
21327          it('should change state', function() {
21328            var value1 = element(by.binding('checkboxModel.value1'));
21329            var value2 = element(by.binding('checkboxModel.value2'));
21330
21331            expect(value1.getText()).toContain('true');
21332            expect(value2.getText()).toContain('YES');
21333
21334            element(by.model('checkboxModel.value1')).click();
21335            element(by.model('checkboxModel.value2')).click();
21336
21337            expect(value1.getText()).toContain('false');
21338            expect(value2.getText()).toContain('NO');
21339          });
21340        </file>
21341      </example>
21342   */
21343  'checkbox': checkboxInputType,
21344
21345  'hidden': noop,
21346  'button': noop,
21347  'submit': noop,
21348  'reset': noop,
21349  'file': noop
21350};
21351
21352function stringBasedInputType(ctrl) {
21353  ctrl.$formatters.push(function(value) {
21354    return ctrl.$isEmpty(value) ? value : value.toString();
21355  });
21356}
21357
21358function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21359  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21360  stringBasedInputType(ctrl);
21361}
21362
21363function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21364  var type = lowercase(element[0].type);
21365
21366  // In composition mode, users are still inputing intermediate text buffer,
21367  // hold the listener until composition is done.
21368  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
21369  if (!$sniffer.android) {
21370    var composing = false;
21371
21372    element.on('compositionstart', function(data) {
21373      composing = true;
21374    });
21375
21376    element.on('compositionend', function() {
21377      composing = false;
21378      listener();
21379    });
21380  }
21381
21382  var listener = function(ev) {
21383    if (timeout) {
21384      $browser.defer.cancel(timeout);
21385      timeout = null;
21386    }
21387    if (composing) return;
21388    var value = element.val(),
21389        event = ev && ev.type;
21390
21391    // By default we will trim the value
21392    // If the attribute ng-trim exists we will avoid trimming
21393    // If input type is 'password', the value is never trimmed
21394    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
21395      value = trim(value);
21396    }
21397
21398    // If a control is suffering from bad input (due to native validators), browsers discard its
21399    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
21400    // control's value is the same empty value twice in a row.
21401    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
21402      ctrl.$setViewValue(value, event);
21403    }
21404  };
21405
21406  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
21407  // input event on backspace, delete or cut
21408  if ($sniffer.hasEvent('input')) {
21409    element.on('input', listener);
21410  } else {
21411    var timeout;
21412
21413    var deferListener = function(ev, input, origValue) {
21414      if (!timeout) {
21415        timeout = $browser.defer(function() {
21416          timeout = null;
21417          if (!input || input.value !== origValue) {
21418            listener(ev);
21419          }
21420        });
21421      }
21422    };
21423
21424    element.on('keydown', function(event) {
21425      var key = event.keyCode;
21426
21427      // ignore
21428      //    command            modifiers                   arrows
21429      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
21430
21431      deferListener(event, this, this.value);
21432    });
21433
21434    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
21435    if ($sniffer.hasEvent('paste')) {
21436      element.on('paste cut', deferListener);
21437    }
21438  }
21439
21440  // if user paste into input using mouse on older browser
21441  // or form autocomplete on newer browser, we need "change" event to catch it
21442  element.on('change', listener);
21443
21444  ctrl.$render = function() {
21445    element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
21446  };
21447}
21448
21449function weekParser(isoWeek, existingDate) {
21450  if (isDate(isoWeek)) {
21451    return isoWeek;
21452  }
21453
21454  if (isString(isoWeek)) {
21455    WEEK_REGEXP.lastIndex = 0;
21456    var parts = WEEK_REGEXP.exec(isoWeek);
21457    if (parts) {
21458      var year = +parts[1],
21459          week = +parts[2],
21460          hours = 0,
21461          minutes = 0,
21462          seconds = 0,
21463          milliseconds = 0,
21464          firstThurs = getFirstThursdayOfYear(year),
21465          addDays = (week - 1) * 7;
21466
21467      if (existingDate) {
21468        hours = existingDate.getHours();
21469        minutes = existingDate.getMinutes();
21470        seconds = existingDate.getSeconds();
21471        milliseconds = existingDate.getMilliseconds();
21472      }
21473
21474      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
21475    }
21476  }
21477
21478  return NaN;
21479}
21480
21481function createDateParser(regexp, mapping) {
21482  return function(iso, date) {
21483    var parts, map;
21484
21485    if (isDate(iso)) {
21486      return iso;
21487    }
21488
21489    if (isString(iso)) {
21490      // When a date is JSON'ified to wraps itself inside of an extra
21491      // set of double quotes. This makes the date parsing code unable
21492      // to match the date string and parse it as a date.
21493      if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
21494        iso = iso.substring(1, iso.length - 1);
21495      }
21496      if (ISO_DATE_REGEXP.test(iso)) {
21497        return new Date(iso);
21498      }
21499      regexp.lastIndex = 0;
21500      parts = regexp.exec(iso);
21501
21502      if (parts) {
21503        parts.shift();
21504        if (date) {
21505          map = {
21506            yyyy: date.getFullYear(),
21507            MM: date.getMonth() + 1,
21508            dd: date.getDate(),
21509            HH: date.getHours(),
21510            mm: date.getMinutes(),
21511            ss: date.getSeconds(),
21512            sss: date.getMilliseconds() / 1000
21513          };
21514        } else {
21515          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
21516        }
21517
21518        forEach(parts, function(part, index) {
21519          if (index < mapping.length) {
21520            map[mapping[index]] = +part;
21521          }
21522        });
21523        return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
21524      }
21525    }
21526
21527    return NaN;
21528  };
21529}
21530
21531function createDateInputType(type, regexp, parseDate, format) {
21532  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
21533    badInputChecker(scope, element, attr, ctrl);
21534    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21535    var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
21536    var previousDate;
21537
21538    ctrl.$$parserName = type;
21539    ctrl.$parsers.push(function(value) {
21540      if (ctrl.$isEmpty(value)) return null;
21541      if (regexp.test(value)) {
21542        // Note: We cannot read ctrl.$modelValue, as there might be a different
21543        // parser/formatter in the processing chain so that the model
21544        // contains some different data format!
21545        var parsedDate = parseDate(value, previousDate);
21546        if (timezone) {
21547          parsedDate = convertTimezoneToLocal(parsedDate, timezone);
21548        }
21549        return parsedDate;
21550      }
21551      return undefined;
21552    });
21553
21554    ctrl.$formatters.push(function(value) {
21555      if (value && !isDate(value)) {
21556        throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
21557      }
21558      if (isValidDate(value)) {
21559        previousDate = value;
21560        if (previousDate && timezone) {
21561          previousDate = convertTimezoneToLocal(previousDate, timezone, true);
21562        }
21563        return $filter('date')(value, format, timezone);
21564      } else {
21565        previousDate = null;
21566        return '';
21567      }
21568    });
21569
21570    if (isDefined(attr.min) || attr.ngMin) {
21571      var minVal;
21572      ctrl.$validators.min = function(value) {
21573        return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
21574      };
21575      attr.$observe('min', function(val) {
21576        minVal = parseObservedDateValue(val);
21577        ctrl.$validate();
21578      });
21579    }
21580
21581    if (isDefined(attr.max) || attr.ngMax) {
21582      var maxVal;
21583      ctrl.$validators.max = function(value) {
21584        return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
21585      };
21586      attr.$observe('max', function(val) {
21587        maxVal = parseObservedDateValue(val);
21588        ctrl.$validate();
21589      });
21590    }
21591
21592    function isValidDate(value) {
21593      // Invalid Date: getTime() returns NaN
21594      return value && !(value.getTime && value.getTime() !== value.getTime());
21595    }
21596
21597    function parseObservedDateValue(val) {
21598      return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
21599    }
21600  };
21601}
21602
21603function badInputChecker(scope, element, attr, ctrl) {
21604  var node = element[0];
21605  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
21606  if (nativeValidation) {
21607    ctrl.$parsers.push(function(value) {
21608      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
21609      // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
21610      // - also sets validity.badInput (should only be validity.typeMismatch).
21611      // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
21612      // - can ignore this case as we can still read out the erroneous email...
21613      return validity.badInput && !validity.typeMismatch ? undefined : value;
21614    });
21615  }
21616}
21617
21618function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21619  badInputChecker(scope, element, attr, ctrl);
21620  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21621
21622  ctrl.$$parserName = 'number';
21623  ctrl.$parsers.push(function(value) {
21624    if (ctrl.$isEmpty(value))      return null;
21625    if (NUMBER_REGEXP.test(value)) return parseFloat(value);
21626    return undefined;
21627  });
21628
21629  ctrl.$formatters.push(function(value) {
21630    if (!ctrl.$isEmpty(value)) {
21631      if (!isNumber(value)) {
21632        throw $ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
21633      }
21634      value = value.toString();
21635    }
21636    return value;
21637  });
21638
21639  if (isDefined(attr.min) || attr.ngMin) {
21640    var minVal;
21641    ctrl.$validators.min = function(value) {
21642      return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
21643    };
21644
21645    attr.$observe('min', function(val) {
21646      if (isDefined(val) && !isNumber(val)) {
21647        val = parseFloat(val, 10);
21648      }
21649      minVal = isNumber(val) && !isNaN(val) ? val : undefined;
21650      // TODO(matsko): implement validateLater to reduce number of validations
21651      ctrl.$validate();
21652    });
21653  }
21654
21655  if (isDefined(attr.max) || attr.ngMax) {
21656    var maxVal;
21657    ctrl.$validators.max = function(value) {
21658      return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
21659    };
21660
21661    attr.$observe('max', function(val) {
21662      if (isDefined(val) && !isNumber(val)) {
21663        val = parseFloat(val, 10);
21664      }
21665      maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
21666      // TODO(matsko): implement validateLater to reduce number of validations
21667      ctrl.$validate();
21668    });
21669  }
21670}
21671
21672function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21673  // Note: no badInputChecker here by purpose as `url` is only a validation
21674  // in browsers, i.e. we can always read out input.value even if it is not val
21674id!
21675  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21676  stringBasedInputType(ctrl);
21677
21678  ctrl.$$parserName = 'url';
21679  ctrl.$validators.url = function(modelValue, viewValue) {
21680    var value = modelValue || viewValue;
21681    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
21682  };
21683}
21684
21685function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21686  // Note: no badInputChecker here by purpose as `url` is only a validation
21687  // in browsers, i.e. we can always read out input.value even if it is not valid!
21688  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21689  stringBasedInputType(ctrl);
21690
21691  ctrl.$$parserName = 'email';
21692  ctrl.$validators.email = function(modelValue, viewValue) {
21693    var value = modelValue || viewValue;
21694    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
21695  };
21696}
21697
21698function radioInputType(scope, element, attr, ctrl) {
21699  // make the name unique, if not defined
21700  if (isUndefined(attr.name)) {
21701    element.attr('name', nextUid());
21702  }
21703
21704  var listener = function(ev) {
21705    if (element[0].checked) {
21706      ctrl.$setViewValue(attr.value, ev && ev.type);
21707    }
21708  };
21709
21710  element.on('click', listener);
21711
21712  ctrl.$render = function() {
21713    var value = attr.value;
21714    element[0].checked = (value == ctrl.$viewValue);
21715  };
21716
21717  attr.$observe('value', ctrl.$render);
21718}
21719
21720function parseConstantExpr($parse, context, name, expression, fallback) {
21721  var parseFn;
21722  if (isDefined(expression)) {
21723    parseFn = $parse(expression);
21724    if (!parseFn.constant) {
21725      throw minErr('ngModel')('constexpr', 'Expected constant expression for `{0}`, but saw ' +
21726                                   '`{1}`.', name, expression);
21727    }
21728    return parseFn(context);
21729  }
21730  return fallback;
21731}
21732
21733function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
21734  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
21735  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
21736
21737  var listener = function(ev) {
21738    ctrl.$setViewValue(element[0].checked, ev && ev.type);
21739  };
21740
21741  element.on('click', listener);
21742
21743  ctrl.$render = function() {
21744    element[0].checked = ctrl.$viewValue;
21745  };
21746
21747  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
21748  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
21749  // it to a boolean.
21750  ctrl.$isEmpty = function(value) {
21751    return value === false;
21752  };
21753
21754  ctrl.$formatters.push(function(value) {
21755    return equals(value, trueValue);
21756  });
21757
21758  ctrl.$parsers.push(function(value) {
21759    return value ? trueValue : falseValue;
21760  });
21761}
21762
21763
21764/**
21765 * @ngdoc directive
21766 * @name textarea
21767 * @restrict E
21768 *
21769 * @description
21770 * HTML textarea element control with angular data-binding. The data-binding and validation
21771 * properties of this element are exactly the same as those of the
21772 * {@link ng.directive:input input element}.
21773 *
21774 * @param {string} ngModel Assignable angular expression to data-bind to.
21775 * @param {string=} name Property name of the form under which the control is published.
21776 * @param {string=} required Sets `required` validation error key if the value is not entered.
21777 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21778 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21779 *    `required` when you want to data-bind to the `required` attribute.
21780 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21781 *    minlength.
21782 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21783 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
21784 *    length.
21785 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21786 *    a RegExp found by evaluating the Angular expression given in the attribute value.
21787 *    If the expression evaluates to a RegExp object, then this is used directly.
21788 *    If the expression evaluates to a string, then it will be converted to a RegExp
21789 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21790 *    `new RegExp('^abc$')`.<br />
21791 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21792 *    start at the index of the last search's match, thus not taking the whole input value into
21793 *    account.
21794 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21795 *    interaction with the input element.
21796 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
21797 */
21798
21799
21800/**
21801 * @ngdoc directive
21802 * @name input
21803 * @restrict E
21804 *
21805 * @description
21806 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
21807 * input state control, and validation.
21808 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
21809 *
21810 * <div class="alert alert-warning">
21811 * **Note:** Not every feature offered is available for all input types.
21812 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
21813 * </div>
21814 *
21815 * @param {string} ngModel Assignable angular expression to data-bind to.
21816 * @param {string=} name Property name of the form under which the control is published.
21817 * @param {string=} required Sets `required` validation error key if the value is not entered.
21818 * @param {boolean=} ngRequired Sets `required` attribute if set to true
21819 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21820 *    minlength.
21821 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21822 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
21823 *    length.
21824 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21825 *    a RegExp found by evaluating the Angular expression given in the attribute value.
21826 *    If the expression evaluates to a RegExp object, then this is used directly.
21827 *    If the expression evaluates to a string, then it will be converted to a RegExp
21828 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21829 *    `new RegExp('^abc$')`.<br />
21830 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21831 *    start at the index of the last search's match, thus not taking the whole input value into
21832 *    account.
21833 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21834 *    interaction with the input element.
21835 * @param {boolean=} [ngTrim=true] If set to false Angular
vendor: 4,842 bytes, lines 21835-21953
21835 will not automatically trim the input.
21836 *    This parameter is ignored for input[type=password] controls, which will never trim the
21837 *    input.
21838 *
21839 * @example
21840    <example name="input-directive" module="inputExample">
21841      <file name="index.html">
21842       <script>
21843          angular.module('inputExample', [])
21844            .controller('ExampleController', ['$scope', function($scope) {
21845              $scope.user = {name: 'guest', last: 'visitor'};
21846            }]);
21847       </script>
21848       <div ng-controller="ExampleController">
21849         <form name="myForm">
21850           <label>
21851              User name:
21852              <input type="text" name="userName" ng-model="user.name" required>
21853           </label>
21854           <div role="alert">
21855             <span class="error" ng-show="myForm.userName.$error.required">
21856              Required!</span>
21857           </div>
21858           <label>
21859              Last name:
21860              <input type="text" name="lastName" ng-model="user.last"
21861              ng-minlength="3" ng-maxlength="10">
21862           </label>
21863           <div role="alert">
21864             <span class="error" ng-show="myForm.lastName.$error.minlength">
21865               Too short!</span>
21866             <span class="error" ng-show="myForm.lastName.$error.maxlength">
21867               Too long!</span>
21868           </div>
21869         </form>
21870         <hr>
21871         <tt>user = {{user}}</tt><br/>
21872         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
21873         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
21874         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
21875         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
21876         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21877         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21878         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
21879         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
21880       </div>
21881      </file>
21882      <file name="protractor.js" type="protractor">
21883        var user = element(by.exactBinding('user'));
21884        var userNameValid = element(by.binding('myForm.userName.$valid'));
21885        var lastNameValid = element(by.binding('myForm.lastName.$valid'));
21886        var lastNameError = element(by.binding('myForm.lastName.$error'));
21887        var formValid = element(by.binding('myForm.$valid'));
21888        var userNameInput = element(by.model('user.name'));
21889        var userLastInput = element(by.model('user.last'));
21890
21891        it('should initialize to model', function() {
21892          expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
21893          expect(userNameValid.getText()).toContain('true');
21894          expect(formValid.getText()).toContain('true');
21895        });
21896
21897        it('should be invalid if empty when required', function() {
21898          userNameInput.clear();
21899          userNameInput.sendKeys('');
21900
21901          expect(user.getText()).toContain('{"last":"visitor"}');
21902          expect(userNameValid.getText()).toContain('false');
21903          expect(formValid.getText()).toContain('false');
21904        });
21905
21906        it('should be valid if empty when min length is set', function() {
21907          userLastInput.clear();
21908          userLastInput.sendKeys('');
21909
21910          expect(user.getText()).toContain('{"name":"guest","last":""}');
21911          expect(lastNameValid.getText()).toContain('true');
21912          expect(formValid.getText()).toContain('true');
21913        });
21914
21915        it('should be invalid if less than required min length', function() {
21916          userLastInput.clear();
21917          userLastInput.sendKeys('xx');
21918
21919          expect(user.getText()).toContain('{"name":"guest"}');
21920          expect(lastNameValid.getText()).toContain('false');
21921          expect(lastNameError.getText()).toContain('minlength');
21922          expect(formValid.getText()).toContain('false');
21923        });
21924
21925        it('should be invalid if longer than max length', function() {
21926          userLastInput.clear();
21927          userLastInput.sendKeys('some ridiculously long name');
21928
21929          expect(user.getText()).toContain('{"name":"guest"}');
21930          expect(lastNameValid.getText()).toContain('false');
21931          expect(lastNameError.getText()).toContain('maxlength');
21932          expect(formValid.getText()).toContain('false');
21933        });
21934      </file>
21935    </example>
21936 */
21937var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
21938    function($browser, $sniffer, $filter, $parse) {
21939  return {
21940    restrict: 'E',
21941    require: ['?ngModel'],
21942    link: {
21943      pre: function(scope, element, attr, ctrls) {
21944        if (ctrls[0]) {
21945          (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
21946                                                              $browser, $filter, $parse);
21947        }
21948      }
21949    }
21950  };
21951}];
21952
21953
21954
21955var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
21956/**
21957 * @ngdoc directive
21958 * @name ngValue
21959 *
21960 * @description
21961 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
21962 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
21963 * the bound value.
21964 *
21965 * `ngValue` is useful when dynamically generating lists of radio buttons using
21966 * {@link ngRepeat `ngRepeat`}, as shown below.
21967 *
21968 * Likewise, `ngValue` can be used to generate `<option>` elements for
21969 * the {@link select `select`} element. In that case however, only strings are supported
21970 * for the `value `attribute, so the resulting `ngModel` will always be a string.
21971 * Support for `select` models with non-string values is available via `ngOptions`.
21972 *
21973 * @element input
21974 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
21975 *   of the `input` element
21976 *
21977 * @example
21978    <example name="ngValue-directive" module="valueExample">
21979      <file name="index.html">
21980       <script>
21981          angular.module('valueExample', [])
21982            .controller('ExampleController', ['$scope', function($scope) {
21983              $scope.names = ['pizza', 'unicorns', 'robots'];
21984              $scope.my = { favorite: 'unicorns' };
21985            }]);
21986       </script>
21987        <form ng-controller="ExampleController">
21988          <h2>Which is your favorite?</h2>
21989            <label ng-repeat="name in names" for="{{name}}">
21990              {{name}}
21991              <input type="radio"
21992                     ng-model="my.favorite"
21993                     ng-value="name"
21994                     id="{{name}}"
21995                     name="favorite">
21996            </label>
21997          <div>You chose {{my.favorite}}</div>
21998        </form>
21999      </file>
22000      <file name="protractor.js" type="protractor">
22001        var favorite = element(by.binding('my.favorite'));
22002
22003        it('should initialize to model', function() {
22004          expect(favorite.getText()).toContain('unicorns');
22005        });
22006        it('should bind the values to the inputs', function() {
22007          element.all(by.model('my.favorite')).get(0).click();
22008          expect(favorite.getText()).toContain('pizza');
22009        });
22010      </file>
22011    </example>
22012 */
22013var ngValueDirective = function() {
22014  return {
22015    restrict: 'A',
22016    priority: 100,
22017    compile: function(tpl, tplAttr) {
22018      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22019        return function ngValueConstantLink(scope, elm, attr) {
22020          attr.$set('value', scope.$eval(attr.ngValue));
22021        };
22022      } else {
22023        return function ngValueLink(scope, elm, attr) {
22024          scope.$watch(attr.ngValue, function valueWatchAction(value) {
22025            attr.$set('value', value);
22026          });
22027        };
22028      }
22029    }
22030  };
22031};
22032
22033/**
22034 * @ngdoc directive
22035 * @name ngBind
22036 * @restrict AC
22037 *
22038 * @description
22039 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22040 * with the value of a given expression, and to update the text content when the value of that
22041 * expression changes.
22042 *
22043 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22044 * `{{ expression }}` which is similar but less verbose.
22045 *
22046 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22047 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22048 * element attribute, it makes the bindings invisible to the user while the page is loading.
22049 *
22050 * An alternative solution to this problem would be using the
22051 * {@link ng.directive:ngCloak ngCloak} directive.
22052 *
22053 *
22054 * @element ANY
22055 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22056 *
22057 * @example
22058 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22059   <example module="bindExample">
22060     <file name="index.html">
22061       <script>
22062         angular.module('bindExample', [])
22063           .controller('ExampleController', ['$scope', function($scope) {
22064             $scope.name = 'Whirled';
22065           }]);
22066       </script>
22067       <div ng-controller="ExampleController">
22068         <label>Enter name: <input type="text" ng-model="name"></label><br>
22069         Hello <span ng-bind="name"></span>!
22070       </div>
22071     </file>
22072     <file name="protractor.js" type="protractor">
22073       it('should check ng-bind', function() {
22074         var nameInput = element(by.model('name'));
22075
22076         expect(element(by.binding('name')).getText()).toBe('Whirled');
22077         nameInput.clear();
22078         nameInput.sendKeys('world');
22079         expect(element(by.binding('name')).getText()).toBe('world');
22080       });
22081     </file>
22082   </example>
22083 */
22084var ngBindDirective = ['$compile', function($compile) {
22085  return {
22086    restrict: 'AC',
22087    compile: function ngBindCompile(templateElement) {
22088      $compile.$$addBindingClass(templateElement);
22089      return function ngBindLink(scope, element, attr) {
22090        $compile.$$addBindingInfo(element, attr.ngBind);
22091        element = element[0];
22092        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22093          element.textContent = value === undefined ? '' : value;
22094        });
22095      };
22096    }
22097  };
22098}];
22099
22100
22101/**
22102 * @ngdoc directive
22103 * @name ngBindTemplate
22104 *
22105 * @description
22106 * The `ngBindTemplate` directive specifies that the element
22107 * text content should be replaced with the interpolation of the template
22108 * in the `ngBindTemplate` attribute.
22109 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22110 * expressions. This directive is needed since some HTML elements
22111 * (such as TITLE and OPTION) cannot contain SPAN elements.
22112 *
22113 * @element ANY
22114 * @param {string} ngBindTemplate template of form
22115 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22116 *
22117 * @example
22118 * Try it here: enter text in text box and watch the greeting change.
22119   <example module="bindExample">
22120     <file name="index.html">
22121       <script>
22122         angular.module('bindExample', [])
22123           .controller('ExampleController', ['$scope', function($scope) {
22124             $scope.salutation = 'Hello';
22125             $scope.name = 'World';
22126           }]);
22127       </script>
22128       <div ng-controller="ExampleController">
22129        <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22130        <label>Name: <input type="text" ng-model="name"></label><br>
22131        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22132       </div>
22133     </file>
22134     <file name="protractor.js" type="protractor">
22135       it('should check ng-bind', function() {
22136         var salutationElem = element(by.binding('salutation'));
22137         var salutationInput = element(by.model('salutation'));
22138         var nameInput = element(by.model('name'));
22139
22140         expect(salutationElem.getText()).toBe('Hello World!');
22141
22142         salutationInput.clear();
22143         salutationInput.sendKeys('Greetings');
22144         nameInput.clear();
22145         nameInput.sendKeys('user');
22146
22147         expect(salutationElem.getText()).toBe('Greetings user!');
22148       });
22149     </file>
22150   </example>
22151 */
22152var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22153  return {
22154    compile: function ngBindTemplateCompile(templateElement) {
22155      $compile.$$addBindingClass(templateElement);
22156      return function ngBindTemplateLink(scope, element, attr) {
22157        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22158        $compile.$$addBindingInfo(element, interpolateFn.expressions);
22159        element = element[0];
22160        attr.$observe('ngBindTemplate', function(value) {
22161          element.textContent = value === undefined ? '' : value;
22162        });
22163      };
22164    }
22165  };
22166}];
22167
22168
22169/**
22170 * @ngdoc directive
22171 * @name ngBindHtml
22172 *
22173 * @description
22174 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22175 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22176 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22177 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22178 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22179 *
22180 * You may also bypass sanitization for values you know are safe. To do so, bind to
22181 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
22182 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22183 *
22184 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22185 * will have an exception (instead of an exploit.)
22186 *
22187 * @element ANY
22188 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22189 *
22190 * @example
22191
22192   <example module="bindHtmlExample" deps="angular-sanitize.js">
22193     <file name="index.html">
22194       <div ng-controller="ExampleController">
22195        <p ng-bind-html="myHTML"></p>
22196       </div>
22197     </file>
22198
22199     <file name="script.js">
22200       angular.module('bindHtmlExample', ['ngSanitize'])
22201         .controller('ExampleController', ['$scope', function($scope) {
22202           $scope.myHTML =
22203              'I am an <code>HTML</code>string with ' +
22204              '<a href="#">links!</a> and other <em>stuff</em>';
22205         }]);
22206     </file>
22207
22208     <file name="protractor.js" type="protractor">
22209       it('should check ng-bind-html', function() {
22210         expect(element(by.binding('myHTML')).getText()).toBe(
22211             'I am an HTMLstring with links! and other stuff');
22212       });
22213     </file>
22214   </example>
22215 */
22216var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
22217  return {
22218    restrict: 'A',
22219    compile: function ngBindHtmlCompile(tElement, tAttrs) {
22220      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
22221      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
22222        return (value || '').toString();
22223      });
22224      $compile.$$addBindingClass(tElement);
22225
22226      return function ngBindHtmlLink(scope, element, attr) {
22227        $compile.$$addBindingInfo(element, attr.ngBindHtml);
22228
22229        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
22230          // we re-evaluate the expr because we want a TrustedValueHolderType
22231          // for $sce, not a string
22232          element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
22233        });
22234      };
22235    }
22236  };
22237}];
22238
22239/**
22240 * @ngdoc directive
22241 * @name ngChange
22242 *
22243 * @description
22244 * Evaluate the given expression when the user changes the input.
22245 * The expression is evaluated immediately, unlike the JavaScript onchange event
22246 * which only triggers at the end of a change (usually, when the user leaves the
22247 * form element or presses the return key).
22248 *
22249 * The `ngChange` expression is only evaluated when a change in the input value causes
22250 * a new value to be committed to the model.
22251 *
22252 * It will not be evaluated:
22253 * * if the value returned from the `$parsers` transformation pipeline has not changed
22254 * * if the input has continued to be invalid since the model will stay `null`
22255 * * if the model is changed programmatically and not by a change to the input value
22256 *
22257 *
22258 * Note, this directive requires `ngModel` to be present.
22259 *
22260 * @element input
22261 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
22262 * in input value.
22263 *
22264 * @example
22265 * <example name="ngChange-directive" module="changeExample">
22266 *   <file name="index.html">
22267 *     <script>
22268 *       angular.module('changeExample', [])
22269 *         .controller('ExampleController', ['$scope', function($scope) {
22270 *           $scope.counter = 0;
22271 *           $scope.change = function() {
22272 *             $scope.counter++;
22273 *           };
22274 *         }]);
22275 *     </script>
22276 *     <div ng-controller="ExampleController">
22277 *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
22278 *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
22279 *       <label for="ng-change-example2">Confirmed</label><br />
22280 *       <tt>debug = {{confirmed}}</tt><br/>
22281 *       <tt>counter = {{counter}}</tt><br/>
22282 *     </div>
22283 *   </file>
22284 *   <file name="protractor.js" type="protractor">
22285 *     var counter = element(by.binding('counter'));
22286 *     var debug = element(by.binding('confirmed'));
22287 *
22288 *     it('should evaluate the expression if changing from view', function() {
22289 *       expect(counter.getText()).toContain('0');
22290 *
22291 *       element(by.id('ng-change-example1')).click();
22292 *
22293 *       expect(counter.getText()).toContain('1');
22294 *       expect(debug.getText()).toContain('true');
22295 *     });
22296 *
22297 *     it('should not evaluate the expression if changing from model', function() {
22298 *       element(by.id('ng-change-example2')).click();
22299
22300 *       expect(counter.getText()).toContain('0');
22301 *       expect(debug.getText()).toContain('true');
22302 *     });
22303 *   </file>
22304 * </example>
22305 */
22306var ngChangeDirective = valueFn({
22307  restrict: 'A',
22308  require: 'ngModel',
22309  link: function(scope, element, attr, ctrl) {
22310    ctrl.$viewChangeListeners.push(function() {
22311      scope.$eval(attr.ngChange);
22312    });
22313  }
22314});
22315
22316function classDirective(name, selector) {
22317  name = 'ngClass' + name;
22318  return ['$animate', function($animate) {
22319    return {
22320      restrict: 'AC',
22321      link: function(scope, element, attr) {
22322        var oldVal;
22323
22324        scope.$watch(attr[name], ngClassWatchAction, true);
22325
22326        attr.$observe('class', function(value) {
22327          ngClassWatchAction(scope.$eval(attr[name]));
22328        });
22329
22330
22331        if (name !== 'ngClass') {
22332          scope.$watch('$index', function($index, old$index) {
22333            // jshint bitwise: false
22334            var mod = $index & 1;
22335            if (mod !== (old$index & 1)) {
22336              var classes = arrayClasses(scope.$eval(attr[name]));
22337              mod === selector ?
22338                addClasses(classes) :
22339                removeClasses(classes);
22340            }
22341          });
22342        }
22343
22344        function addClasses(classes) {
22345          var newClasses = digestClassCounts(classes, 1);
22346          attr.$addClass(newClasses);
22347        }
22348
22349        function removeClasses(classes) {
22350          var newClasses = digestClassCounts(classes, -1);
22351          attr.$removeClass(newClasses);
22352        }
22353
22354        function digestClassCounts(classes, count) {
22355          // Use createMap() to prevent class assumptions involving property
22356          // names in Object.prototype
22357          var classCounts = element.data('$classCounts') || createMap();
22358          var classesToUpdate = [];
22359          forEach(classes, function(className) {
22360            if (count > 0 || classCounts[className]) {
22361              classCounts[className] = (classCounts[className] || 0) + count;
22362              if (classCounts[className] === +(count > 0)) {
22363                classesToUpdate.push(className);
22364              }
22365            }
22366          });
22367          element.data('$classCounts', classCounts);
22368          return classesToUpdate.join(' ');
22369        }
22370
22371        function updateClasses(oldClasses, newClasses) {
22372          var toAdd = arrayDifference(newClasses, oldClasses);
22373          var toRemove = arrayDifference(oldClasses, newClasses);
22374          toAdd = digestClassCounts(toAdd, 1);
22375          toRemove = digestClassCounts(toRemove, -1);
22376          if (toAdd && toAdd.length) {
22377            $animate.addClass(element, toAdd);
22378          }
22379          if (toRemove && toRemove.length) {
22380            $animate.removeClass(element, toRemove);
22381          }
22382        }
22383
22384        function ngClassWatchAction(newVal) {
22385          if (selector === true || scope.$index % 2 === selector) {
22386            var newClasses = arrayClasses(newVal || []);
22387            if (!oldVal) {
22388              addClasses(newClasses);
22389            } else if (!equals(newVal,oldVal)) {
22390              var oldClasses = arrayClasses(oldVal);
22391              updateClasses(oldClasses, newClasses);
22392            }
22393          }
22394          oldVal = shallowCopy(newVal);
22395        }
22396      }
22397    };
22398
22399    function arrayDifference(tokens1, tokens2) {
22400      var values = [];
22401
22402      outer:
22403      for (var i = 0; i < tokens1.length; i++) {
22404        var token = tokens1[i];
22405        for (var j = 0; j < tokens2.length; j++) {
22406          if (token == tokens2[j]) continue outer;
22407        }
22408        values.push(token);
22409      }
22410      return values;
22411    }
22412
22413    function arrayClasses(classVal) {
22414      var classes = [];
22415      if (isArray(classVal)) {
22416        forEach(classVal, function(v) {
22417          classes = classes.concat(arrayClasses(v));
22418        });
22419        return classes;
22420      } else if (isString(classVal)) {
22421        return classVal.split(' ');
22422      } else if (isObject(classVal)) {
22423        forEach(classVal, function(v, k) {
22424          if (v) {
22425            classes = classes.concat(k.split(' '));
22426          }
22427        });
22428        return classes;
22429      }
22430      return classVal;
22431    }
22432  }];
22433}
22434
22435/**
22436 * @ngdoc directive
22437 * @name ngClass
22438 * @restrict AC
22439 *
22440 * @description
22441 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
22442 * an expression that represents all classes to be added.
22443 *
22444 * The directive operates in three different ways, depending on which of three types the expression
22445 * evaluates to:
22446 *
22447 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
22448 * names.
22449 *
22450 * 2. If the expression evaluates to an object, then for each key-value pair of the
22451 * object with a truthy value the corresponding key is used as a class name.
22452 *
22453 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
22454 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
22455 * to give you more control over what CSS classes appear. See the code below for an example of this.
22456 *
22457 *
22458 * The directive won't add duplicate classes if a particular class was already set.
22459 *
22460 * When the expression changes, the previously added classes are removed and only then are the
22461 * new classes added.
22462 *
22463 * @animations
22464 * **add** - happens just before the class is applied to the elements
22465 *
22466 * **remove** - happens just before the class is removed from the element
22467 *
22468 * @element ANY
22469 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
22470 *   of the evaluation can be a string representing space delimited class
22471 *   names, an array, or a map of class names to boolean values. In the case of a map, the
22472 *   names of the properties whose values are truthy will be added as css classes to the
22473 *   element.
22474 *
22475 * @example Example that demonstrates basic bindings via ngClass directive.
22476   <example>
22477     <file name="index.html">
22478       <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
22479       <label>
22480          <input type="checkbox" ng-model="deleted">
22481          deleted (apply "strike" class)
22482       </label><br>
22483       <label>
22484          <input type="checkbox" ng-model="important">
22485          important (apply "bold" class)
22486       </label><br>
22487       <label>
22488          <input type="checkbox" ng-model="error">
22489          error (apply "has-error" class)
22490       </label>
22491       <hr>
22492       <p ng-class="style">Using String Syntax</p>
22493       <input type="text" ng-model="style"
22494              placeholder="Type: bold strike red" aria-label="Type: bold strike red">
22495       <hr>
22496       <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
22497       <input ng-model="style1"
22498              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
22499       <input ng-model="style2"
22500              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
22501       <input ng-model="style3"
22502              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
22503       <hr>
22504       <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
22505       <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
22506       <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
22507     </file>
22508     <file name="style.css">
22509       .strike {
22510           text-decoration: line-through;
22511       }
22512       .bold {
22513           font-weight: bold;
22514       }
22515       .red {
22516           color: red;
22517       }
22518       .has-error {
22519           color: red;
22520           background-color: yellow;
22521       }
22522       .orange {
22523           color: orange;
22524       }
22525     </file>
22526     <file name="protractor.js" type="protractor">
22527       var ps = element.all(by.css('p'));
22528
22529       it('should let you toggle the class', function() {
22530
22531         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
22532         expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
22533
22534         element(by.model('important')).click();
22535         expect(ps.first().getAttribute('class')).toMatch(/bold/);
22536
22537         element(by.model('error')).click();
22538         expect(ps.first().getAttribute('class')).toMatch(/has-error/);
22539       });
22540
22541       it('should let you toggle string example', function() {
22542         expect(ps.get(1).getAttribute('class')).toBe('');
22543         element(by.model('style')).clear();
22544         element(by.model('style')).sendKeys('red');
22545         expect(ps.get(1).getAttribute('class')).toBe('red');
22546       });
22547
22548       it('array example should have 3 classes', function() {
22549         expect(ps.get(2).getAttribute('class')).toBe('');
22550         element(by.model('style1')).sendKeys('bold');
22551         element(by.model('style2')).sendKeys('strike');
22552         element(by.model('style3')).sendKeys('red');
22553         expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
22554       });
22555
22556       it('array with map example should have 2 classes', function() {
22557         expect(ps.last().getAttribute('class')).toBe('');
22558         element(by.model('style4')).sendKeys('bold');
22559         element(by.model('warning')).click();
22560         expect(ps.last().getAttribute('class')).toBe('bold orange');
22561       });
22562     </file>
22563   </example>
22564
22565   ## Animations
22566
22567   The example below demonstrates how to perform animations using ngClass.
22568
22569   <example module="ngAnimate" deps="angular-animate.js" animations="true">
22570     <file name="index.html">
22571      <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
22572      <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
22573      <br>
22574      <span class="base-class" ng-class="myVar">Sample Text</span>
22575     </file>
22576     <file name="style.css">
22577       .base-class {
22578         -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22579         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22580       }
22581
22582       .base-class.my-class {
22583         color: red;
22584         font-size:3em;
22585       }
22586     </file>
22587     <file name="protractor.js" type="protractor">
22588       it('should check ng-class', function() {
22589         expect(element(by.css('.base-class')).getAttribute('class')).not.
22590           toMatch(/my-class/);
22591
22592         element(by.id('setbtn')).click();
22593
22594         expect(element(by.css('.base-class')).getAttribute('class')).
22595           toMatch(/my-class/);
22596
22597         element(by.id('clearbtn')).click();
22598
22599         expect(element(by.css('.base-class')).getAttribute('class')).not.
22600           toMatch(/my-class/);
22601       });
22602     </file>
22603   </example>
22604
22605
22606   ## ngClass and pre-existing CSS3 Transitions/Animations
22607   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
22608   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
22609   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
22610   to view the step by step details of {@link $animate#addClass $animate.addClass} and
22611   {@link $animate#removeClass $animate.removeClass}.
22612 */
22613var ngClassDirective = classDirective('', true);
22614
22615/**
22616 * @ngdoc directive
22617 * @name ngClassOdd
22618 * @restrict AC
22619 *
22620 * @description
22621 * The `ngClassOdd` and `ngClassEven` directives work exactly as
22622 * {@link ng.directive:ngClass ngClass}, except they work in
22623 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
22624 *
22625 * This directive can be applied only within the scope of an
22626 * {@link ng.directive:ngRepeat ngRepeat}.
22627 *
22628 * @element ANY
22629 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
22630 *   of the evaluation can be a string representing space delimited class names or an array.
22631 *
22632 * @example
22633   <example>
22634     <file name="index.html">
22635        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
22636          <li ng-repeat="name in names">
22637           <span ng-class-odd="'odd'" ng-class-even="'even'">
22638             {{name}}
22639           </span>
22640          </li>
22641        </ol>
22642     </file>
22643     <file name="style.css">
22644       .odd {
22645         color: red;
22646       }
22647       .even {
22648         color: blue;
22649       }
22650     </file>
22651     <file name="protractor.js" type="protractor">
22652       it('should check ng-class-odd and ng-class-even', function() {
22653         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
22654           toMatch(/odd/);
22655         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
22656           toMatch(/even/);
22657       });
22658     </file>
22659   </example>
22660 */
22661var ngClassOddDirective = classDirective('Odd', 0);
22662
22663/**
22664 * @ngdoc directive
22665 * @name ngClassEven
22666 * @restrict AC
22667 *
22668 * @description
22669 * The `ngClassOdd` and `ngClassEven` directives work exactly as
22670 * {@link ng.directive:ngClass ngClass}, except they work in
22671 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
22672 *
22673 * This directive can be applied only within the scope of an
22674 * {@link ng.directive:ngRepeat ngRepeat}.
22675 *
22676 * @element ANY
22677 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
22678 *   result of the evaluation can be a string representing space delimited class names or an array.
22679 *
22680 * @example
22681   <example>
22682     <file name="index.html">
22683        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
22684          <li ng-repeat="name in names">
22685           <span ng-class-odd="'odd'" ng-class-even="'even'">
22686             {{name}} &nbsp; &nbsp; &nbsp;
22687           </span>
22688          </li>
22689        </ol>
22690     </file>
22691     <file name="style.css">
22692       .odd {
22693         color: red;
22694       }
22695       .even {
22696         color: blue;
22697       }
22698     </file>
22699     <file name="protractor.js" type="protractor">
22700       it('should check ng-class-odd and ng-class-even', function() {
22701         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
22702           toMatch(/odd/);
22703         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
22704           toMatch(/even/);
22705       });
22706     </file>
22707   </example>
22708 */
22709var ngClassEvenDirective = classDirective('Even', 1);
22710
22711/**
22712 * @ngdoc directive
22713 * @name ngCloak
22714 * @restrict AC
22715 *
22716 * @description
22717 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
22718 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
22719 * directive to avoid the undesirable flicker effect caused by the html template display.
22720 *
22721 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
22722 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
22723 * of the browser view.
22724 *
22725 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
22726 * `angular.min.js`.
22727 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
22728 *
22729 * ```css
22730 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
22731 *   display: none !important;
22732 * }
22733 * ```
22734 *
22735 * When this css rule is loaded by the browser, all html elements (including their children) that
22736 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
22737 * during the compilation of the template it deletes the `ngCloak` element attribute, making
22738 * the compiled element visible.
22739 *
22740 * For the best result, the `angular.js` script must be loaded in the head section of the html
22741 * document; alternatively, the css rule above must be included in the external stylesheet of the
22742 * application.
22743 *
22744 * @element ANY
22745 *
22746 * @example
22747   <example>
22748     <file name="index.html">
22749        <div id="template1" ng-cloak>{{ 'hello' }}</div>
22750        <div id="template2" class="ng-cloak">{{ 'world' }}</div>
22751     </file>
22752     <file name="protractor.js" type="protractor">
22753       it('should remove the template directive and css class', function() {
22754         expect($('#template1').getAttribute('ng-cloak')).
22755           toBeNull();
22756         expect($('#template2').getAttribute('ng-cloak')).
22757           toBeNull();
22758       });
22759     </file>
22760   </example>
22761 *
22762 */
22763var ngCloakDirective = ngDirective({
22764  compile: function(element, attr) {
22765    attr.$set('ngCloak', undefined);
22766    element.removeClass('ng-cloak');
22767  }
22768});
22769
22770/**
22771 * @ngdoc directive
22772 * @name ngController
22773 *
22774 * @description
22775 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
22776 * supports the principles behind the Model-View-Controller design pattern.
22777 *
22778 * MVC components in angular:
22779 *
22780 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
22781 *   are accessed through bindings.
22782 * * View — The template (HTML with data bindings) that is rendered into the View.
22783 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
22784 *   logic behind the application to decorate the scope with functions and values
22785 *
22786 * Note that you can also attach controllers to the DOM by declaring it in a route definition
22787 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
22788 * again using `ng-controller` in the template itself.  This will cause the controller to be attached
22789 * and executed twice.
22790 *
22791 * @element ANY
22792 * @scope
22793 * @priority 500
22794 * @param {expression} ngController Name of a constructor function registered with the current
22795 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
22796 * that on the current scope evaluates to a constructor function.
22797 *
22798 * The controller instance can be published into a scope property by specifying
22799 * `ng-controller="as propertyName"`.
22800 *
22801 * If the current `$controllerProvider` is configured to use globals (via
22802 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
22803 * also be the name of a globally accessible constructor function (not recommended).
22804 *
22805 * @example
22806 * Here is a simple form for editing user contact information. Adding, remov
22806ing, clearing, and
22807 * greeting are methods declared on the controller (see source tab). These methods can
22808 * easily be called from the angular markup. Any changes to the data are automatically reflected
22809 * in the View without the need for a manual update.
22810 *
22811 * Two different declaration styles are included below:
22812 *
22813 * * one binds methods and properties directly onto the controller using `this`:
22814 * `ng-controller="SettingsController1 as settings"`
22815 * * one injects `$scope` into the controller:
22816 * `ng-controller="SettingsController2"`
22817 *
22818 * The second option is more common in the Angular community, and is generally used in boilerplates
22819 * and in this guide. However, there are advantages to binding properties directly to the controller
22820 * and avoiding scope.
22821 *
22822 * * Using `controller as` makes it obvious which controller you are accessing in the template when
22823 * multiple controllers apply to an element.
22824 * * If you are writing your controllers as classes you have easier access to the properties and
22825 * methods, which will appear on the scope, from inside the controller code.
22826 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
22827 * inheritance masking primitives.
22828 *
22829 * This example demonstrates the `controller as` syntax.
22830 *
22831 * <example name="ngControllerAs" module="controllerAsExample">
22832 *   <file name="index.html">
22833 *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
22834 *      <label>Name: <input type="text" ng-model="settings.name"/></label>
22835 *      <button ng-click="settings.greet()">greet</button><br/>
22836 *      Contact:
22837 *      <ul>
22838 *        <li ng-repeat="contact in settings.contacts">
22839 *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
22840 *             <option>phone</option>
22841 *             <option>email</option>
22842 *          </select>
22843 *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
22844 *          <button ng-click="settings.clearContact(contact)">clear</button>
22845 *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
22846 *        </li>
22847 *        <li><button ng-click="settings.addContact()">add</button></li>
22848 *     </ul>
22849 *    </div>
22850 *   </file>
22851 *   <file name="app.js">
22852 *    angular.module('controllerAsExample', [])
22853 *      .controller('SettingsController1', SettingsController1);
22854 *
22855 *    function SettingsController1() {
22856 *      this.name = "John Smith";
22857 *      this.contacts = [
22858 *        {type: 'phone', value: '408 555 1212'},
22859 *        {type: 'email', value: '[email protected]'} ];
22860 *    }
22861 *
22862 *    SettingsController1.prototype.greet = function() {
22863 *      alert(this.name);
22864 *    };
22865 *
22866 *    SettingsController1.prototype.addContact = function() {
22867 *      this.contacts.push({type: 'email', value: '[email protected]'});
22868 *    };
22869 *
22870 *    SettingsController1.prototype.removeContact = function(contactToRemove) {
22871 *     var index = this.contacts.indexOf(contactToRemove);
22872 *      this.contacts.splice(index, 1);
22873 *    };
22874 *
22875 *    SettingsController1.prototype.clearContact = function(contact) {
22876 *      contact.type = 'phone';
22877 *      contact.value = '';
22878 *    };
22879 *   </file>
22880 *   <file name="protractor.js" type="protractor">
22881 *     it('should check controller as', function() {
22882 *       var container = element(by.id('ctrl-as-exmpl'));
22883 *         expect(container.element(by.model('settings.name'))
22884 *           .getAttribute('value')).toBe('John Smith');
22885 *
22886 *       var firstRepeat =
22887 *           container.element(by.repeater('contact in settings.contacts').row(0));
22888 *       var secondRepeat =
22889 *           container.element(by.repeater('contact in settings.contacts').row(1));
22890 *
22891 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22892 *           .toBe('408 555 1212');
22893 *
22894 *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
22895 *           .toBe('[email protected]');
22896 *
22897 *       firstRepeat.element(by.buttonText('clear')).click();
22898 *
22899 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22900 *           .toBe('');
22901 *
22902 *       container.element(by.buttonText('add')).click();
22903 *
22904 *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
22905 *           .element(by.model('contact.value'))
22906 *           .getAttribute('value'))
22907 *           .toBe('[email protected]');
22908 *     });
22909 *   </file>
22910 * </example>
22911 *
22912 * This example demonstrates the "attach to `$scope`" style of controller.
22913 *
22914 * <example name="ngController" module="controllerExample">
22915 *  <file name="index.html">
22916 *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
22917 *     <label>Name: <input type="text" ng-model="name"/></label>
22918 *     <button ng-click="greet()">greet</button><br/>
22919 *     Contact:
22920 *     <ul>
22921 *       <li ng-repeat="contact in contacts">
22922 *         <select ng-model="contact.type" id="select_{{$index}}">
22923 *            <option>phone</option>
22924 *            <option>email</option>
22925 *         </select>
22926 *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
22927 *         <button ng-click="clearContact(contact)">clear</button>
22928 *         <button ng-click="removeContact(contact)">X</button>
22929 *       </li>
22930 *       <li>[ <button ng-click="addContact()">add</button> ]</li>
22931 *    </ul>
22932 *   </div>
22933 *  </file>
22934 *  <file name="app.js">
22935 *   angular.module('controllerExample', [])
22936 *     .controller('SettingsController2', ['$scope', SettingsController2]);
22937 *
22938 *   function SettingsController2($scope) {
22939 *     $scope.name = "John Smith";
22940 *     $scope.contacts = [
22941 *       {type:'phone', value:'408 555 1212'},
22942 *       {type:'email', value:'[email protected]'} ];
22943 *
22944 *     $scope.greet = function() {
22945 *       alert($scope.name);
22946 *     };
22947 *
22948 *     $scope.addContact = function() {
22949 *       $scope.contacts.push({type:'email', value:'[email protected]'});
22950 *     };
22951 *
22952 *     $scope.removeContact = function(contactToRemove) {
22953 *       var index = $scope.contacts.indexOf(contactToRemove);
22954 *       $scope.contacts.splice(index, 1);
22955 *     };
22956 *
22957 *     $scope.clearContact = function(contact) {
22958 *       contact.type = 'phone';
22959 *       contact.value = '';
22960 *     };
22961 *   }
22962 *  </file>
22963 *  <file name="protractor.js" type="protractor">
22964 *    it('should check controller', function() {
22965 *      var container = element(by.id('ctrl-exmpl'));
22966 *
22967 *      expect(container.element(by.model('name'))
22968 *          .getAttribute('value')).toBe('John Smith');
22969 *
22970 *      var firstRepeat =
22971 *          container.element(by.repeater('contact in contacts').row(0));
22972 *      var secondRepeat =
22973 *          container.element(by.repeater('contact in contacts').row(1));
22974 *
22975 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22976 *          .toBe('408 555 1212');
22977 *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
22978 *          .toBe('[email protected]');
22979 *
22980 *      firstRepeat.element(by.buttonText('clear')).click();
22981 *
22982 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22983 *          .toBe('');
22984 *
22985 *      container.element(by.buttonText('add')).click();
22986 *
22987 *      expect(container.element(by.repeater('contact in contacts').row(2))
22988 *          .element(by.model('contact.value'))
22989 *          .getAttribute('value'))
22990 *          .toBe('[email protected]');
22991 *    });
22992 *  </file>
22993 *</example>
22994
22995 */
22996var ngControllerDirective = [function() {
22997  return {
22998    restrict: 'A',
22999    scope: true,
23000    controller: '@',
23001    priority: 500
23002  };
23003}];
23004
23005/**
23006 * @ngdoc directive
23007 * @name ngCsp
23008 *
23009 * @element html
23010 * @description
23011 * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
23012 *
23013 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23014 *
23015 * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
23016 * For Angular to be CSP compatible there are only two things that we need to do differently:
23017 *
23018 * - don't use `Function` constructor to generate optimized value getters
23019 * - don't inject custom stylesheet into the document
23020 *
23021 * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
23022 * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
23023 * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
23024 * be raised.
23025 *
23026 * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically
23027 * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
23028 * To make those directives work in CSP mode, include the `angular-csp.css` manually.
23029 *
23030 * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This
23031 * autodetection however triggers a CSP error to be logged in the console:
23032 *
23033 * ```
23034 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23035 * script in the following Content Security Policy directive: "default-src 'self'". Note that
23036 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23037 * ```
23038 *
23039 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23040 * directive on the root element of the application or on the `angular.js` script tag, whichever
23041 * appears first in the html document.
23042 *
23043 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23044 *
23045 * @example
23046 * This example shows how to apply the `ngCsp` directive to the `html` tag.
23047   ```html
23048     <!doctype html>
23049     <html ng-app ng-csp>
23050     ...
23051     ...
23052     </html>
23053   ```
23054  * @example
23055      // Note: the suffix `.csp` in the example name triggers
23056      // csp mode in our http server!
23057      <example name="example.csp" module="cspExample" ng-csp="true">
23058        <file name="index.html">
23059          <div ng-controller="MainController as ctrl">
23060            <div>
23061              <button ng-click="ctrl.inc()" id="inc">Increment</button>
23062              <span id="counter">
23063                {{ctrl.counter}}
23064              </span>
23065            </div>
23066
23067            <div>
23068              <button ng-click="ctrl.evil()" id="evil">Evil</button>
23069              <span id="evilError">
23070                {{ctrl.evilError}}
23071              </span>
23072            </div>
23073          </div>
23074        </file>
23075        <file name="script.js">
23076           angular.module('cspExample', [])
23077             .controller('MainController', function() {
23078                this.counter = 0;
23079                this.inc = function() {
23080                  this.counter++;
23081                };
23082                this.evil = function() {
23083                  // jshint evil:true
23084                  try {
23085                    eval('1+2');
23086                  } catch (e) {
23087                    this.evilError = e.message;
23088                  }
23089                };
23090              });
23091        </file>
23092        <file name="protractor.js" type="protractor">
23093          var util, webdriver;
23094
23095          var incBtn = element(by.id('inc'));
23096          var counter = element(by.id('counter'));
23097          var evilBtn = element(by.id('evil'));
23098          var evilError = element(by.id('evilError'));
23099
23100          function getAndClearSevereErrors() {
23101            return browser.manage().logs().get('browser').then(function(browserLog) {
23102              return browserLog.filter(function(logEntry) {
23103                return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23104              });
23105            });
23106          }
23107
23108          function clearErrors() {
23109            getAndClearSevereErrors();
23110          }
23111
23112          function expectNoErrors() {
23113            getAndClearSevereErrors().then(function(filteredLog) {
23114              expect(filteredLog.length).toEqual(0);
23115              if (filteredLog.length) {
23116                console.log('browser console errors: ' + util.inspect(filteredLog));
23117              }
23118            });
23119          }
23120
23121          function expectError(regex) {
23122            getAndClearSevereErrors().then(function(filteredLog) {
23123              var found = false;
23124              filteredLog.forEach(function(log) {
23125                if (log.message.match(regex)) {
23126                  found = true;
23127                }
23128              });
23129              if (!found) {
23130                throw new Error('expected an error that matches ' + regex);
23131              }
23132            });
23133          }
23134
23135          beforeEach(function() {
23136            util = require('util');
23137            webdriver = require('protractor/node_modules/selenium-webdriver');
23138          });
23139
23140          // For now, we only test on Chrome,
23141          // as Safari does not load the page with Protractor's injected scripts,
23142          // and Firefox webdriver always disables content security policy (#6358)
23143          if (browser.params.browser !== 'chrome') {
23144            return;
23145          }
23146
23147          it('should not report errors when the page is loaded', function() {
23148            // clear errors so we are not dependent on previous tests
23149            clearErrors();
23150            // Need to reload the page as the page is already loaded when
23151            // we come here
23152            browser.driver.getCurrentUrl().then(function(url) {
23153              browser.get(url);
23154            });
23155            expectNoErrors();
23156          });
23157
23158          it('should evaluate expressions', function() {
23159            expect(counter.getText()).toEqual('0');
23160            incBtn.click();
23161            expect(counter.getText()).toEqual('1');
23162            expectNoErrors();
23163          });
23164
23165          it('should throw and report an error when using "eval"', function() {
23166            evilBtn.click();
23167            expect(evilError.getText()).toMatch(/Content Security Policy/);
23168            expectError(/Content Security Policy/);
23169          });
23170        </file>
23171      </example>
23172  */
23173
23174// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
23175// bootstrap the system (before $parse is instantiated), for this reason we just have
23176// the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc
23177
23178/**
23179 * @ngdoc directive
23180 * @name ngClick
23181 *
23182 * @description
23183 * The ngClick directive allows you to specify custom behavior when
23184 * an element is clicked.
23185 *
23186 * @element ANY
23187 * @priority 0
23188 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
23189 * click. ({@link guide/expression#-event- Event object is available as `$event`})
23190 *
23191 * @example
23192   <example>
23193     <file name="index.html">
23194      <button ng-click="count = count + 1" ng-init="count=0">
23195        Increment
23196      </button>
23197      <span>
23198        count: {{count}}
23199      </span>
23200     </file>
23201     <file name="protractor.js" type="protractor">
23202       it('should check ng-click', function() {
23203         expect(element(by.binding('count')).getText()).toMatch('0');
23204         element(by.css('button')).click();
23205         expect(element(by.binding('count')).getText()).toMatch('1');
23206       });
23207     </file>
23208   </example>
23209 */
23210/*
23211 * A collection of directives that allows creation of custom event handlers that are defined as
23212 * angular expressions and are compiled and executed within the current scope.
23213 */
23214var ngEventDirectives = {};
23215
23216// For events that might fire synchronously during DOM manipulation
23217// we need to execute their event handlers asynchronously using $evalAsync,
23218// so that they are not executed in an inconsistent state.
23219var forceAsyncEvents = {
23220  'blur': true,
23221  'focus': true
23222};
23223forEach(
23224  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
23225  function(eventName) {
23226    var directiveName = directiveNormalize('ng-' + eventName);
23227    ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
23228      return {
23229        restrict: 'A',
23230        compile: function($element, attr) {
23231          // We expose the powerful $event object on the scope that provides access to the Window,
23232          // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
23233          // checks at the cost of speed since event handler expressions are not executed as
23234          // frequently as regular change detection.
23235          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
23236          return function ngEventHandler(scope, element) {
23237            element.on(eventName, function(event) {
23238              var callback = function() {
23239                fn(scope, {$event:event});
23240              };
23241              if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
23242                scope.$evalAsync(callback);
23243              } else {
23244                scope.$apply(callback);
23245              }
23246            });
23247          };
23248        }
23249      };
23250    }];
23251  }
23252);
23253
23254/**
23255 * @ngdoc directive
23256 * @name ngDblclick
23257 *
23258 * @description
23259 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
23260 *
23261 * @element ANY
23262 * @priority 0
23263 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
23264 * a dblclick. (The Event object is available as `$event`)
23265 *
23266 * @example
23267   <example>
23268     <file name="index.html">
23269      <button ng-dblclick="count = count + 1" ng-init="count=0">
23270        Increment (on double click)
23271      </button>
23272      count: {{count}}
23273     </file>
23274   </example>
23275 */
23276
23277
23278/**
23279 * @ngdoc directive
23280 * @name ngMousedown
23281 *
23282 * @description
23283 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
23284 *
23285 * @element ANY
23286 * @priority 0
23287 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
23288 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
23289 *
23290 * @example
23291   <example>
23292     <file name="index.html">
23293      <button ng-mousedown="count = count + 1" ng-init="count=0">
23294        Increment (on mouse down)
23295      </button>
23296      count: {{count}}
23297     </file>
23298   </example>
23299 */
23300
23301
23302/**
23303 * @ngdoc directive
23304 * @name ngMouseup
23305 *
23306 * @description
23307 * Specify custom behavior on mouseup event.
23308 *
23309 * @element ANY
23310 * @priority 0
23311 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
23312 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
23313 *
23314 * @example
23315   <example>
23316     <file name="index.html">
23317      <button ng-mouseup="count = count + 1" ng-init="count=0">
23318        Increment (on mouse up)
23319      </button>
23320      count: {{count}}
23321     </file>
23322   </example>
23323 */
23324
23325/**
23326 * @ngdoc directive
23327 * @name ngMouseover
23328 *
23329 * @description
23330 * Specify custom behavior on mouseover event.
23331 *
23332 * @element ANY
23333 * @priority 0
23334 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
23335 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
23336 *
23337 * @example
23338   <example>
23339     <file name="index.html">
23340      <button ng-mouseover="count = count + 1" ng-init="count=0">
23341        Increment (when mouse is over)
23342      </button>
23343      count: {{count}}
23344     </file>
23345   </example>
23346 */
23347
23348
23349/**
23350 * @ngdoc directive
23351 * @name ngMouseenter
23352 *
23353 * @description
23354 * Specify custom behavior on mouseenter event.
23355 *
23356 * @element ANY
23357 * @priority 0
23358 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
23359 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
23360 *
23361 * @example
23362   <example>
23363     <file name="index.html">
23364      <button ng-mouseenter="count = count + 1" ng-init="count=0">
23365        Increment (when mouse enters)
23366      </button>
23367      count: {{count}}
23368     </file>
23369   </example>
23370 */
23371
23372
23373/**
23374 * @ngdoc directive
23375 * @name ngMouseleave
23376 *
23377 * @description
23378 * Specify custom behavior on mouseleave event.
23379 *
23380 * @element ANY
23381 * @priority 0
23382 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
23383 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
23384 *
23385 * @example
23386   <example>
23387     <file name="index.html">
23388      <button ng-mouseleave="count = count + 1" ng-init="count=0">
23389        Increment (when mouse leaves)
23390      </button>
23391      count: {{count}}
23392     </file>
23393   </example>
23394 */
23395
23396
23397/**
23398 * @ngdoc directive
23399 * @name ngMousemove
23400 *
23401 * @description
23402 * Specify custom behavior on mousemove event.
23403 *
23404 * @element ANY
23405 * @priority 0
23406 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
23407 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
23408 *
23409 * @example
23410   <example>
23411     <file name="index.html">
23412      <button ng-mousemove="count = count + 1" ng-init="count=0">
23413        Increment (when mouse moves)
23414      </button>
23415      count: {{count}}
23416     </file>
23417   </example>
23418 */
23419
23420
23421/**
23422 * @ngdoc directive
23423 * @name ngKeydown
23424 *
23425 * @description
23426 * Specify custom behavior on keydown event.
23427 *
23428 * @element ANY
23429 * @priority 0
23430 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
23431 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23432 *
23433 * @example
23434   <example>
23435     <file name="index.html">
23436      <input ng-keydown="count = count + 1" ng-init="count=0">
23437      key down count: {{count}}
23438     </file>
23439   </example>
23440 */
23441
23442
23443/**
23444 * @ngdoc directive
23445 * @name ngKeyup
23446 *
23447 * @description
23448 * Specify custom behavior on keyup event.
23449 *
23450 * @element ANY
23451 * @priority 0
23452 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
23453 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23454 *
23455 * @example
23456   <example>
23457     <file name="index.html">
23458       <p>Typing in the input box below updates the key count</p>
23459       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
23460
23461       <p>Typing in the input box below updates the keycode</p>
23462       <input ng-keyup="event=$event">
23463       <p>event keyCode: {{ event.keyCode }}</p>
23464       <p>event altKey: {{ event.altKey }}</p>
23465     </file>
23466   </example>
23467 */
23468
23469
23470/**
23471 * @ngdoc directive
23472 * @name ngKeypress
23473 *
23474 * @description
23475 * Specify custom behavior on keypress event.
23476 *
23477 * @element ANY
23478 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
23479 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
23480 * and can be interrogated for keyCode, altKey, etc.)
23481 *
23482 * @example
23483   <example>
23484     <file name="index.html">
23485      <input ng-keypress="count = count + 1" ng-init="count=0">
23486      key press count: {{count}}
23487     </file>
23488   </example>
23489 */
23490
23491
23492/**
23493 * @ngdoc directive
23494 * @name ngSubmit
23495 *
23496 * @description
23497 * Enables binding angular expressions to onsubmit events.
23498 *
23499 * Additionally it prevents the default action (which for form means 
23499sending the request to the
23500 * server and reloading the current page), but only if the form does not contain `action`,
23501 * `data-action`, or `x-action` attributes.
23502 *
23503 * <div class="alert alert-warning">
23504 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
23505 * `ngSubmit` handlers together. See the
23506 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
23507 * for a detailed discussion of when `ngSubmit` may be triggered.
23508 * </div>
23509 *
23510 * @element form
23511 * @priority 0
23512 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
23513 * ({@link guide/expression#-event- Event object is available as `$event`})
23514 *
23515 * @example
23516   <example module="submitExample">
23517     <file name="index.html">
23518      <script>
23519        angular.module('submitExample', [])
23520          .controller('ExampleController', ['$scope', function($scope) {
23521            $scope.list = [];
23522            $scope.text = 'hello';
23523            $scope.submit = function() {
23524              if ($scope.text) {
23525                $scope.list.push(this.text);
23526                $scope.text = '';
23527              }
23528            };
23529          }]);
23530      </script>
23531      <form ng-submit="submit()" ng-controller="ExampleController">
23532        Enter text and hit enter:
23533        <input type="text" ng-model="text" name="text" />
23534        <input type="submit" id="submit" value="Submit" />
23535        <pre>list={{list}}</pre>
23536      </form>
23537     </file>
23538     <file name="protractor.js" type="protractor">
23539       it('should check ng-submit', function() {
23540         expect(element(by.binding('list')).getText()).toBe('list=[]');
23541         element(by.css('#submit')).click();
23542         expect(element(by.binding('list')).getText()).toContain('hello');
23543         expect(element(by.model('text')).getAttribute('value')).toBe('');
23544       });
23545       it('should ignore empty strings', function() {
23546         expect(element(by.binding('list')).getText()).toBe('list=[]');
23547         element(by.css('#submit')).click();
23548         element(by.css('#submit')).click();
23549         expect(element(by.binding('list')).getText()).toContain('hello');
23550        });
23551     </file>
23552   </example>
23553 */
23554
23555/**
23556 * @ngdoc directive
23557 * @name ngFocus
23558 *
23559 * @description
23560 * Specify custom behavior on focus event.
23561 *
23562 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
23563 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
23564 * during an `$apply` to ensure a consistent state.
23565 *
23566 * @element window, input, select, textarea, a
23567 * @priority 0
23568 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
23569 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
23570 *
23571 * @example
23572 * See {@link ng.directive:ngClick ngClick}
23573 */
23574
23575/**
23576 * @ngdoc directive
23577 * @name ngBlur
23578 *
23579 * @description
23580 * Specify custom behavior on blur event.
23581 *
23582 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
23583 * an element has lost focus.
23584 *
23585 * Note: As the `blur` event is executed synchronously also during DOM manipulations
23586 * (e.g. removing a focussed input),
23587 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
23588 * during an `$apply` to ensure a consistent state.
23589 *
23590 * @element window, input, select, textarea, a
23591 * @priority 0
23592 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
23593 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
23594 *
23595 * @example
23596 * See {@link ng.directive:ngClick ngClick}
23597 */
23598
23599/**
23600 * @ngdoc directive
23601 * @name ngCopy
23602 *
23603 * @description
23604 * Specify custom behavior on copy event.
23605 *
23606 * @element window, input, select, textarea, a
23607 * @priority 0
23608 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
23609 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
23610 *
23611 * @example
23612   <example>
23613     <file name="index.html">
23614      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
23615      copied: {{copied}}
23616     </file>
23617   </example>
23618 */
23619
23620/**
23621 * @ngdoc directive
23622 * @name ngCut
23623 *
23624 * @description
23625 * Specify custom behavior on cut event.
23626 *
23627 * @element window, input, select, textarea, a
23628 * @priority 0
23629 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
23630 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
23631 *
23632 * @example
23633   <example>
23634     <file name="index.html">
23635      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
23636      cut: {{cut}}
23637     </file>
23638   </example>
23639 */
23640
23641/**
23642 * @ngdoc directive
23643 * @name ngPaste
23644 *
23645 * @description
23646 * Specify custom behavior on paste event.
23647 *
23648 * @element window, input, select, textarea, a
23649 * @priority 0
23650 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
23651 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
23652 *
23653 * @example
23654   <example>
23655     <file name="index.html">
23656      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
23657      pasted: {{paste}}
23658     </file>
23659   </example>
23660 */
23661
23662/**
23663 * @ngdoc directive
23664 * @name ngIf
23665 * @restrict A
23666 *
23667 * @description
23668 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
23669 * {expression}. If the expression assigned to `ngIf` evaluates to a false
23670 * value then the element is removed from the DOM, otherwise a clone of the
23671 * element is reinserted into the DOM.
23672 *
23673 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
23674 * element in the DOM rather than changing its visibility via the `display` css property.  A common
23675 * case when this difference is significant is when using css selectors that rely on an element's
23676 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classe
23676s.
23677 *
23678 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
23679 * is created when the element is restored.  The scope created within `ngIf` inherits from
23680 * its parent scope using
23681 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
23682 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
23683 * a javascript primitive defined in the parent scope. In this case any modifications made to the
23684 * variable within the child scope will override (hide) the value in the parent scope.
23685 *
23686 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
23687 * is if an element's class attribute is directly modified after it's compiled, using something like
23688 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
23689 * the added class will be lost because the original compiled state is used to regenerate the element.
23690 *
23691 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
23692 * and `leave` effects.
23693 *
23694 * @animations
23695 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
23696 * leave - happens just before the `ngIf` contents are removed from the DOM
23697 *
23698 * @element ANY
23699 * @scope
23700 * @priority 600
23701 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
23702 *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
23703 *     element is added to the DOM tree.
23704 *
23705 * @example
23706  <example module="ngAnimate" deps="angular-animate.js" animations="true">
23707    <file name="index.html">
23708      <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
23709      Show when checked:
23710      <span ng-if="checked" class="animate-if">
23711        This is removed when the checkbox is unchecked.
23712      </span>
23713    </file>
23714    <file name="animations.css">
23715      .animate-if {
23716        background:white;
23717        border:1px solid black;
23718        padding:10px;
23719      }
23720
23721      .animate-if.ng-enter, .animate-if.ng-leave {
23722        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23723        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23724      }
23725
23726      .animate-if.ng-enter,
23727      .animate-if.ng-leave.ng-leave-active {
23728        opacity:0;
23729      }
23730
23731      .animate-if.ng-leave,
23732      .animate-if.ng-enter.ng-enter-active {
23733        opacity:1;
23734      }
23735    </file>
23736  </example>
23737 */
23738var ngIfDirective = ['$animate', function($animate) {
23739  return {
23740    multiElement: true,
23741    transclude: 'element',
23742    priority: 600,
23743    terminal: true,
23744    restrict: 'A',
23745    $$tlb: true,
23746    link: function($scope, $element, $attr, ctrl, $transclude) {
23747        var block, childScope, previousElements;
23748        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
23749
23750          if (value) {
23751            if (!childScope) {
23752              $transclude(function(clone, newScope) {
23753                childScope = newScope;
23754                clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
23755                // Note: We only need the first/last node of the cloned nodes.
23756                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
23757                // by a directive with templateUrl when its template arrives.
23758                block = {
23759                  clone: clone
23760                };
23761                $animate.enter(clone, $element.parent(), $element);
23762              });
23763            }
23764          } else {
23765            if (previousElements) {
23766              previousElements.remove();
23767              previousElements = null;
23768            }
23769            if (childScope) {
23770              childScope.$destroy();
23771              childScope = null;
23772            }
23773            if (block) {
23774              previousElements = getBlockNodes(block.clone);
23775              $animate.leave(previousElements).then(function() {
23776                previousElements = null;
23777              });
23778              block = null;
23779            }
23780          }
23781        });
23782    }
23783  };
23784}];
23785
23786/**
23787 * @ngdoc directive
23788 * @name ngInclude
23789 * @restrict ECA
23790 *
23791 * @description
23792 * Fetches, compiles and includes an external HTML fragment.
23793 *
23794 * By default, the template URL is restricted to the same domain and protocol as the
23795 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
23796 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
23797 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
23798 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
23799 * ng.$sce Strict Contextual Escaping}.
23800 *
23801 * In addition, the browser's
23802 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
23803 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
23804 * policy may further restrict whether the template is successfully loaded.
23805 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
23806 * access on some browsers.
23807 *
23808 * @animations
23809 * enter - animation is used to bring new content into the browser.
23810 * leave - animation is used to animate existing content away.
23811 *
23812 * The enter and leave animation occur concurrently.
23813 *
23814 * @scope
23815 * @priority 400
23816 *
23817 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
23818 *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
23819 * @param {string=} onload Expression to evaluate when a new partial is loaded.
23820 *
23821 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
23822 *                  $anchorScroll} to scroll the viewport after the content is loaded.
23823 *
23824 *                  - If the attribute is not set, disable scrolling.
23825 *                  - If the attribute is set without value, enable scrolling.
23826 *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
23827 *
23828 * @example
23829  <example module="includeExample" deps="angular-animate.js" animations="true">
23830    <file name="index.html">
23831     <div ng-controller="ExampleController">
23832       <select ng-model="template" ng-options="t.name for t in templates">
23833        <option value="">(blank)</option>
23834       </select>
23835       url of the template: <code>{{template.url}}</code>
23836       <hr/>
23837       <div class="slide-animate-container">
23838         <div class="slide-animate" ng-include="template.url"></div>
23839       </div>
23840     </div>
23841    </file>
23842    <file name="script.js">
23843      angular.module('includeExample', ['ngAnimate'])
23844        .controller('ExampleController', ['$scope', function($scope) {
23845          $scope.templates =
23846            [ { name: 'template1.html', url: 'template1.html'},
23847              { name: 'template2.html', url: 'template2.html'} ];
23848          $scope.template = $scope.templates[0];
23849        }]);
23850     </file>
23851    <file name="template1.html">
23852      Content of template1.html
23853    </file>
23854    <file name="template2.html">
23855      Content of template2.html
23856    </file>
23857    <file name="animations.css">
23858      .slide-animate-container {
23859        position:relative;
23860        background:white;
23861        border:1px solid black;
23862        height:40px;
23863        overflow:hidden;
23864      }
23865
23866      .slide-animate {
23867        padding:10px;
23868      }
23869
23870      .slide-animate.ng-enter, .slide-animate.ng-leave {
23871        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23872        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23873
23874        position:absolute;
23875        top:0;
23876        left:0;
23877        right:0;
23878        bottom:0;
23879        display:block;
23880        padding:10px;
23881      }
23882
23883      .slide-animate.ng-enter {
23884        top:-50px;
23885      }
23886      .slide-animate.ng-enter.ng-enter-active {
23887        top:0;
23888      }
23889
23890      .slide-animate.ng-leave {
23891        top:0;
23892      }
23893      .slide-animate.ng-leave.ng-leave-active {
23894        top:50px;
23895      }
23896    </file>
23897    <file name="protractor.js" type="protractor">
23898      var templateSelect = element(by.model('template'));
23899      var includeElem = element(by.css('[ng-include]'));
23900
23901      it('should load template1.html', function() {
23902        expect(includeElem.getText()).toMatch(/Content of template1.html/);
23903      });
23904
23905      it('should load template2.html', function() {
23906        if (browser.params.browser == 'firefox') {
23907          // Firefox can't handle using selects
23908          // See https://github.com/angular/protractor/issues/480
23909          return;
23910        }
23911        templateSelect.click();
23912        templateSelect.all(by.css('option')).get(2).click();
23913        expect(includeElem.getText()).toMatch(/Content of template2.html/);
23914      });
23915
23916      it('should change to blank', function() {
23917        if (browser.params.browser == 'firefox') {
23918          // Firefox can't handle using selects
23919          return;
23920        }
23921        templateSelect.click();
23922        templateSelect.all(by.css('option')).get(0).click();
23923        expect(includeElem.isPresent()).toBe(false);
23924      });
23925    </file>
23926  </example>
23927 */
23928
23929
23930/**
23931 * @ngdoc event
23932 * @name ngInclude#$includeContentRequested
23933 * @eventType emit on the scope ngInclude was declared in
23934 * @description
23935 * Emitted every time the ngInclude content is requested.
23936 *
23937 * @param {Object} angularEvent Synthetic event object.
23938 * @param {String} src URL of content to load.
23939 */
23940
23941
23942/**
23943 * @ngdoc event
23944 * @name ngInclude#$includeContentLoaded
23945 * @eventType emit on the current ngInclude scope
23946 * @description
23947 * Emitted every time the ngInclude content is reloaded.
23948 *
23949 * @param {Object} angularEvent Synthetic event object.
23950 * @param {String} src URL of content to load.
23951 */
23952
23953
23954/**
23955 * @ngdoc event
23956 * @name ngInclude#$includeContentError
23957 * @eventType emit on the scope ngInclude was declared in
23958 * @description
23959 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
23960 *
23961 * @param {Object} angularEvent Synthetic event object.
23962 * @param {String} src URL of content to load.
23963 */
23964var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', '$sce',
23965                  function($templateRequest,   $anchorScroll,   $animate,   $sce) {
23966  return {
23967    restrict: 'ECA',
23968    priority: 400,
23969    terminal: true,
23970    transclude: 'element',
23971    controller: angular.noop,
23972    compile: function(element, attr) {
23973      var srcExp = attr.ngInclude || attr.src,
23974          onloadExp = attr.onload || '',
23975          autoScrollExp = attr.autoscroll;
23976
23977      return function(scope, $element, $attr, ctrl, $transclude) {
23978        var changeCounter = 0,
23979            currentScope,
23980            previousElement,
23981            currentElement;
23982
23983        var cleanupLastIncludeContent = function() {
23984          if (previousElement) {
23985            previousElement.remove();
23986            previousElement = null;
23987          }
23988          if (currentScope) {
23989            currentScope.$destroy();
23990            currentScope = null;
23991          }
23992          if (currentElement) {
23993            $animate.leave(currentElement).then(function() {
23994              previousElement = null;
23995            });
23996            previousElement = currentElement;
23997            currentElement = null;
23998          }
23999        };
24000
24001        scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
24002          var afterAnimation = function() {
24003            if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24004              $anchorScroll();
24005            }
24006          };
24007          var thisChangeId = ++changeCounter;
24008
24009          if (src) {
24010            //set the 2nd param to true to ignore the template request error so that the inner
24011            //contents and scope can be cleaned up.
24012            $templateRequest(src, true).then(function(response) {
24013              if (thisChangeId !== changeCounter) return;
24014              var newScope = scope.$new();
24015              ctrl.template = response;
24016
24017              // Note: This will also link all children of ng-include that were contained in the original
24018              // html. If that content contains controllers, ... they could pollute/change the scope.
24019              // However, using ng-include on an element with additional content does not make sense...
24020              // Note: We can't remove them in the cloneAttchFn of $transclude as that
24021              // function is called before linking the content, which would apply child
24022              // directives to non existing elements.
24023              var clone = $transclude(newScope, function(clone) {
24024                cleanupLastIncludeContent();
24025                $animate.enter(clone, null, $element).then(afterAnimation);
24026              });
24027
24028              currentScope = newScope;
24029              currentElement = clone;
24030
24031              currentScope.$emit('$includeContentLoaded', src);
24032              scope.$eval(onloadExp);
24033            }, function() {
24034              if (thisChangeId === changeCounter) {
24035                cleanupLastIncludeContent();
vendor: 2,651 bytes, lines 24036-24116
24036                scope.$emit('$includeContentError', src);
24037              }
24038            });
24039            scope.$emit('$includeContentRequested', src);
24040          } else {
24041            cleanupLastIncludeContent();
24042            ctrl.template = null;
24043          }
24044        });
24045      };
24046    }
24047  };
24048}];
24049
24050// This directive is called during the $transclude call of the first `ngInclude` directive.
24051// It will replace and compile the content of the element with the loaded template.
24052// We need this directive so that the element content is already filled when
24053// the link function of another directive on the same element as ngInclude
24054// is called.
24055var ngIncludeFillContentDirective = ['$compile',
24056  function($compile) {
24057    return {
24058      restrict: 'ECA',
24059      priority: -400,
24060      require: 'ngInclude',
24061      link: function(scope, $element, $attr, ctrl) {
24062        if (/SVG/.test($element[0].toString())) {
24063          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24064          // support innerHTML, so detect this here and try to generate the contents
24065          // specially.
24066          $element.empty();
24067          $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24068              function namespaceAdaptedClone(clone) {
24069            $element.append(clone);
24070          }, {futureParentElement: $element});
24071          return;
24072        }
24073
24074        $element.html(ctrl.template);
24075        $compile($element.contents())(scope);
24076      }
24077    };
24078  }];
24079
24080/**
24081 * @ngdoc directive
24082 * @name ngInit
24083 * @restrict AC
24084 *
24085 * @description
24086 * The `ngInit` directive allows you to evaluate an expression in the
24087 * current scope.
24088 *
24089 * <div class="alert alert-danger">
24090 * The only appropriate use of `ngInit` is for aliasing special properties of
24091 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
24092 * should use {@link guide/controller controllers} rather than `ngInit`
24093 * to initialize values on a scope.
24094 * </div>
24095 * <div class="alert alert-warning">
24096 * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make
24097 * sure you have parenthesis for correct precedence:
24098 * <pre class="prettyprint">
24099 * `<div ng-init="test1 = (data | orderBy:'name')"></div>`
24100 * </pre>
24101 * </div>
24102 *
24103 * @priority 450
24104 *
24105 * @element ANY
24106 * @param {expression} ngInit {@link guide/expression Expression} to eval.
24107 *
24108 * @example
24109   <example module="initExample">
24110     <file name="index.html">
24111   <script>
24112     angular.module('initExample', [])
24113       .controller('ExampleController', ['$scope', function($scope) {
24114         $scope.list = [['a', 'b'], ['c', 'd']];
24115       }]);
24116   </script>
24117   <div ng-controller="ExampleController">
24118     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24119       <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24120          <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24121       </div>
24122     </div>
24123   </div>
24124     </file>
24125     <file name="protractor.js" type="protractor">
24126       it('should alias index positions', function() {
24127         var elements = element.all(by.css('.example-init'));
24128         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24129         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24130         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24131         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24132       });
24133     </file>
24134   </example>
24135 */
24136var ngInitDirective = ngDirective({
24137  priority: 450,
24138  compile: function() {
24139    return {
24140      pre: function(scope, element, attrs) {
24141        scope.$eval(attrs.ngInit);
24142      }
24143    };
24144  }
24145});
24146
24147/**
24148 * @ngdoc directive
24149 * @name ngList
24150 *
24151 * @description
24152 * Text input that converts between a delimited string and an array of strings. The default
24153 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24154 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24155 *
24156 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
24157 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
24158 *   list item is respected. This implies that the user of the directive is responsible for
24159 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
24160 *   tab or newline character.
24161 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
24162 *   when joining the list items back together) and whitespace around each list item is stripped
24163 *   before it is added to the model.
24164 *
24165 * ### Example with Validation
24166 *
24167 * <example name="ngList-directive" module="listExample">
24168 *   <file name="app.js">
24169 *      angular.module('listExample', [])
24170 *        .controller('ExampleController', ['$scope', function($scope) {
24171 *          $scope.names = ['morpheus', 'neo', 'trinity'];
24172 *        }]);
24173 *   </file>
24174 *   <file name="index.html">
24175 *    <form name="myForm" ng-controller="ExampleController">
24176 *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
24177 *      <span role="alert">
24178 *        <span class="error" ng-show="myForm.namesInput.$error.required">
24179 *        Required!</span>
24180 *      </span>
24181 *      <br>
24182 *      <tt>names = {{names}}</tt><br/>
24183 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
24184 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
24185 *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24186 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24187 *     </form>
24188 *   </file>
24189 *   <file name="protractor.js" type="protractor">
24190 *     var listInput = element(by.model('names'));
24191 *     var names = element(by.exactBinding('names'));
24192 *     var valid = element(by.binding('myForm.namesInput.$valid'));
24193 *     var error = element(by.css('span.error'));
24194 *
24195 *     it('should initialize to model', function() {
24196 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
24197 *       expect(valid.getText()).toContain('true');
24198 *       expect(error.getCssValue('display')).toBe('none');
24199 *     });
24200 *
24201 *     it('should be invalid if empty', function() {
24202 *       listInput.clear();
24203 *       listInput.sendKeys('');
24204 *
24205 *       expect(names.getText()).toContain('');
24206 *       expect(valid.getText()).toContain('false');
24207 *       expect(error.getCssValue('display')).not.toBe('none');
24208 *     });
24209 *   </file>
24210 * </example>
24211 *
24212 * ### Example - splitting on whitespace
24213 * <example name="ngList-directive-newlines">
24214 *   <file name="index.html">
24215 *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
24216 *    <pre>{{ list | json }}</pre>
24217 *   </file>
24218 *   <file name="protractor.js" type="protractor">
24219 *     it("should split the text by newlines", function() {
24220 *       var listInput = element(by.model('list'));
24221 *       var output = element(by.binding('list | json'));
24222 *       listInput.sendKeys('abc\ndef\nghi');
24223 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
24224 *     });
24225 *   </file>
24226 * </example>
24227 *
24228 * @element input
24229 * @param {string=} ngList optional delimiter that should be used to split the value.
24230 */
24231var ngListDirective = function() {
24232  return {
24233    restrict: 'A',
24234    priority: 100,
24235    require: 'ngModel',
24236    link: function(scope, element, attr, ctrl) {
24237      // We want to control whitespace trimming so we use this convoluted approach
24238      // to access the ngList attribute, which doesn't pre-trim the attribute
24239      var ngList = element.attr(attr.$attr.ngList) || ', ';
24240      var trimValues = attr.ngTrim !== 'false';
24241      var separator = trimValues ? trim(ngList) : ngList;
24242
24243      var parse = function(viewValue) {
24244        // If the viewValue is invalid (say required but empty) it will be `undefined`
24245        if (isUndefined(viewValue)) return;
24246
24247        var list = [];
24248
24249        if (viewValue) {
24250          forEach(viewValue.split(separator), function(value) {
24251            if (value) list.push(trimValues ? trim(value) : value);
24252          });
24253        }
24254
24255        return list;
24256      };
24257
24258      ctrl.$parsers.push(parse);
24259      ctrl.$formatters.push(function(value) {
24260        if (isArray(value)) {
24261          return value.join(ngList);
24262        }
24263
24264        return undefined;
24265      });
24266
24267      // Override the standard $isEmpty because an empty array means the input is empty.
24268      ctrl.$isEmpty = function(value) {
24269        return !value || !value.length;
24270      };
24271    }
24272  };
24273};
24274
24275/* global VALID_CLASS: true,
24276  INVALID_CLASS: true,
24277  PRISTINE_CLASS: true,
24278  DIRTY_CLASS: true,
24279  UNTOUCHED_CLASS: true,
24280  TOUCHED_CLASS: true,
24281*/
24282
24283var VALID_CLASS = 'ng-valid',
24284    INVALID_CLASS = 'ng-invalid',
24285    PRISTINE_CLASS = 'ng-pristine',
24286    DIRTY_CLASS = 'ng-dirty',
24287    UNTOUCHED_CLASS = 'ng-untouched',
24288    TOUCHED_CLASS = 'ng-touched',
24289    PENDING_CLASS = 'ng-pending';
24290
24291
24292var $ngModelMinErr = new minErr('ngModel');
24293
24294/**
24295 * @ngdoc type
24296 * @name ngModel.NgModelController
24297 *
24298 * @property {string} $viewValue Actual string value in the view.
24299 * @property {*} $modelValue The value in the model that the control is bound to.
24300 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
24301       the control reads value from the DOM. The functions are called in array order, each passing
24302       its return value through to the next. The last return value is forwarded to the
24303       {@link ngModel.NgModelController#$validators `$validators`} collection.
24304
24305Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
24306`$viewValue`}.
24307
24308Returning `undefined` from a parser means a parse error occurred. In that case,
24309no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
24310will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
24311is set to `true`. The parse error is stored in `ngModel.$error.parse`.
24312
24313 *
24314 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
24315       the model value changes. The functions are called in reverse array order, each passing the value through to the
24316       next. The last return value is used as the actual DOM value.
24317       Used to format / convert values for display in the control.
24318 * ```js
24319 * function formatter(value) {
24320 *   if (value) {
24321 *     return value.toUpperCase();
24322 *   }
24323 * }
24324 * ngModel.$formatters.push(formatter);
24325 * ```
24326 *
24327 * @property {Object.<string, function>} $validators A collection of validators that are applied
24328 *      whenever the model value changes. The key value within the object refers to the name of the
24329 *      validator while the function refers to the validation operation. The validation operation is
24330 *      provided with the model value as an argument and must return a true or false value depending
24331 *      on the response of that validation.
24332 *
24333 * ```js
24334 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
24335 *   var value = modelValue || viewValue;
24336 *   return /[0-9]+/.test(value) &&
24337 *          /[a-z]+/.test(value) &&
24338 *          /[A-Z]+/.test(value) &&
24339 *          /\W+/.test(value);
24340 * };
24341 * ```
24342 *
24343 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
24344 *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
24345 *      is expected to return a promise when it is run during the model validation process. Once the promise
24346 *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
24347 *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
24348 *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
24349 *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
24350 *      will only run once all synchronous validators have passed.
24351 *
24352 * Please note that if $http is used then it is important that the server returns a success HTTP response code
24353 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
24354 *
24355 * ```js
24356 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
24357 *   var value = modelValue || viewValue;
24358 *
24359 *   // Lookup user by username
24360 *   return $http.get('/api/users/' + value).
24361 *      then(function resolved() {
24362 *        //username exists, this means validation fails
24363 *        return $q.reject('exists');
24364 *      }, function rejected() {
24365 *        //username does not exist, therefore this validation passes
24366 *        return true;
24367 *      });
24368 * };
24369 * ```
24370 *
24371 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
24372 *     view value has changed. It is called with no arguments, and its return value is ignored.
24373 *     This can be used in place of additional $watches against the model value.
24374 *
24375 * @property {Object} $error An object hash with all failing validator ids as keys.
24376 * @property {Object} $pending An object hash with all pending validator ids as keys.
24377 *
24378 * @property {boolean} $untouched True if control has not lost focus yet.
24379 * @property {boolean} $touched True if control has lost focus.
24380 * @property {boolean} $pristine True if user has not interacted with the control yet.
24381 * @property {boolean} $dirty True if user has already interacted with the control.
24382 * @property {boolean} $valid True if there is no error.
24383 * @property {boolean}
24383 $invalid True if at least one error on the control.
24384 * @property {string} $name The name attribute of the control.
24385 *
24386 * @description
24387 *
24388 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
24389 * The controller contains services for data-binding, validation, CSS updates, and value formatting
24390 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
24391 * listening to DOM events.
24392 * Such DOM related logic should be provided by other directives which make use of
24393 * `NgModelController` for data-binding to control elements.
24394 * Angular provides this DOM logic for most {@link input `input`} elements.
24395 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
24396 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
24397 *
24398 * @example
24399 * ### Custom Control Example
24400 * This example shows how to use `NgModelController` with a custom control to achieve
24401 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
24402 * collaborate together to achieve the desired result.
24403 *
24404 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
24405 * contents be edited in place by the user.
24406 *
24407 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
24408 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
24409 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
24410 * that content using the `$sce` service.
24411 *
24412 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
24413    <file name="style.css">
24414      [contenteditable] {
24415        border: 1px solid black;
24416        background-color: white;
24417        min-height: 20px;
24418      }
24419
24420      .ng-invalid {
24421        border: 1px solid red;
24422      }
24423
24424    </file>
24425    <file name="script.js">
24426      angular.module('customControl', ['ngSanitize']).
24427        directive('contenteditable', ['$sce', function($sce) {
24428          return {
24429            restrict: 'A', // only activate on element attribute
24430            require: '?ngModel', // get a hold of NgModelController
24431            link: function(scope, element, attrs, ngModel) {
24432              if (!ngModel) return; // do nothing if no ng-model
24433
24434              // Specify how UI should be updated
24435              ngModel.$render = function() {
24436                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
24437              };
24438
24439              // Listen for change events to enable binding
24440              element.on('blur keyup change', function() {
24441                scope.$evalAsync(read);
24442              });
24443              read(); // initialize
24444
24445              // Write data to the model
24446              function read() {
24447                var html = element.html();
24448                // When we clear the content editable the browser leaves a <br> behind
24449                // If strip-br attribute is provided then we strip this out
24450                if ( attrs.stripBr && html == '<br>' ) {
24451                  html = '';
24452                }
24453                ngModel.$setViewValue(html);
24454              }
24455            }
24456          };
24457        }]);
24458    </file>
24459    <file name="index.html">
24460      <form name="myForm">
24461       <div contenteditable
24462            name="myWidget" ng-model="userContent"
24463            strip-br="true"
24464            required>Change me!</div>
24465        <span ng-show="myForm.myWidget.$error.required">Required!</span>
24466       <hr>
24467       <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
24468      </form>
24469    </file>
24470    <file name="protractor.js" type="protractor">
24471    it('should data-bind and become invalid', function() {
24472      if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
24473        // SafariDriver can't handle contenteditable
24474        // and Firefox driver can't clear contenteditables very well
24475        return;
24476      }
24477      var contentEditable = element(by.css('[contenteditable]'));
24478      var content = 'Change me!';
24479
24480      expect(contentEditable.getText()).toEqual(content);
24481
24482      contentEditable.clear();
24483      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
24484      expect(contentEditable.getText()).toEqual('');
24485      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
24486    });
24487    </file>
24488 * </example>
24489 *
24490 *
24491 */
24492var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
24493    function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
24494  this.$viewValue = Number.NaN;
24495  this.$modelValue = Number.NaN;
24496  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
24497  this.$validators = {};
24498  this.$asyncValidators = {};
24499  this.$parsers = [];
24500  this.$formatters = [];
24501  this.$viewChangeListeners = [];
24502  this.$untouched = true;
24503  this.$touched = false;
24504  this.$pristine = true;
24505  this.$dirty = false;
24506  this.$valid = true;
24507  this.$invalid = false;
24508  this.$error = {}; // keep invalid keys here
24509  this.$$success = {}; // keep valid keys here
24510  this.$pending = undefined; // keep pending keys here
24511  this.$name = $interpolate($attr.name || '', false)($scope);
24512
24513
24514  var parsedNgModel = $parse($attr.ngModel),
24515      parsedNgModelAssign = parsedNgModel.assign,
24516      ngModelGet = parsedNgModel,
24517      ngModelSet = parsedNgModelAssign,
24518      pendingDebounce = null,
24519      parserValid,
24520      ctrl = this;
24521
24522  this.$$setOptions = function(options) {
24523    ctrl.$options = options;
24524    if (options && options.getterSetter) {
24525      var invokeModelGetter = $parse($attr.ngModel + '()'),
24526          invokeModelSetter = $parse($attr.ngModel + '($$$p)');
24527
24528      ngModelGet = function($scope) {
24529        var modelValue = parsedNgModel($scope);
24530        if (isFunction(modelValue)) {
24531          modelValue = invokeModelGetter($scope);
24532        }
24533        return modelValue;
24534      };
24535      ngModelSet = function($scope, newValue) {
24536        if (isFunction(parsedNgModel($scope))) {
24537          invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
24538        } else {
24539          parsedNgModelAssign($scope, ctrl.$modelValue);
24540        }
24541      };
24542    } else if (!parsedNgModel.assign) {
24543      throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
24544          $attr.ngModel, startingTag($element));
24545    }
24546  };
24547
24548  /**
24549   * @ngdoc method
24550   * @name ngModel.NgModelController#$render
24551   *
24552   * @description
24553   * Called when the view needs to be updated. It is expected that the user of the ng-model
24554   * directive will implement this method.
24555   *
24556   * The `$render()` method is invoked in the following situations:
24557   *
24558   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
24559   *   committed value then `$render()` is called to update the input control.
24560   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
24561   *   the `$viewValue` are different from last time.
24562   *
24563   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
24564   * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
24565   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
24566   * invoked if you only change a property on the objects.
24567   */
24568  this.$render = noop;
24569
24570  /**
24571   * @ngdoc method
24572   * @name ngModel.NgModelController#$isEmpty
24573   *
24574   * @description
24575   * This is called when we need to determine if the value of an input is empty.
24576   *
24577   * For instance, the required directive does this to work out if the input has data or not.
24578   *
24579   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
24580   *
24581   * You can override this for input directives whose concept of being empty is different from the
24582   * default. The `checkboxInputType` directive does this because in its case a value of `false`
24583   * implies empty.
24584   *
24585   * @param {*} value The value of the input to check for emptiness.
24586   * @returns {boolean} True if `value` is "empty".
24587   */
24588  this.$isEmpty = function(value) {
24589    return isUndefined(value) || value === '' || value === null || value !== value;
24590  };
24591
24592  var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
24593      currentValidationRunId = 0;
24594
24595  /**
24596   * @ngdoc method
24597   * @name ngModel.NgModelController#$setValidity
24598   *
24599   * @description
24600   * Change the validity state, and notify the form.
24601   *
24602   * This method can be called within $parsers/$formatters or a custom validation implementation.
24603   * However, in most cases it should be sufficient to use the `ngModel.$validators` and
24604   * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
24605   *
24606   * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
24607   *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
24608   *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
24609   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
24610   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
24611   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
24612   * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
24613   *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
24614   *                          Skipped is used by Angular when validators do not run because of parse errors and
24615   *                          when `$asyncValidators` do not run because any of the `$validators` failed.
24616   */
24617  addSetValidityMethod({
24618    ctrl: this,
24619    $element: $element,
24620    set: function(object, property) {
24621      object[property] = true;
24622    },
24623    unset: function(object, property) {
24624      delete object[property];
24625    },
24626    parentForm: parentForm,
24627    $animate: $animate
24628  });
24629
24630  /**
24631   * @ngdoc method
24632   * @name ngModel.NgModelController#$setPristine
24633   *
24634   * @description
24635   * Sets the control to its pristine state.
24636   *
24637   * This method can be called to remove the `ng-dirty` class and set the control to its pristine
24638   * state (`ng-pristine` class). A model is considered to be pristine when the control
24639   * has not been changed from when first compiled.
24640   */
24641  this.$setPristine = function() {
24642    ctrl.$dirty = false;
24643    ctrl.$pristine = true;
24644    $animate.removeClass($element, DIRTY_CLASS);
24645    $animate.addClass($element, PRISTINE_CLASS);
24646  };
24647
24648  /**
24649   * @ngdoc method
24650   * @name ngModel.NgModelController#$setDirty
24651   *
24652   * @description
24653   * Sets the control to its dirty state.
24654   *
24655   * This method can be called to remove the `ng-pristine` class and set the control to its dirty
24656   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
24657   * from when first compiled.
24658   */
24659  this.$setDirty = function() {
24660    ctrl.$dirty = true;
24661    ctrl.$pristine = false;
24662    $animate.removeClass($element, PRISTINE_CLASS);
24663    $animate.addClass($element, DIRTY_CLASS);
24664    parentForm.$setDirty();
24665  };
24666
24667  /**
24668   * @ngdoc method
24669   * @name ngModel.NgModelController#$setUntouched
24670   *
24671   * @description
24672   * Sets the control to its untouched state.
24673   *
24674   * This method can be called to remove the `ng-touched` class and set the control to its
24675   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
24676   * by default, however this function can be used to restore that state if the model has
24677   * already been touched by the user.
24678   */
24679  this.$setUntouched = function() {
24680    ctrl.$touched = false;
24681    ctrl.$untouched = true;
24682    $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
24683  };
24684
24685  /**
24686   * @ngdoc method
24687   * @name ngModel.NgModelController#$setTouched
24688   *
24689   * @description
24690   * Sets the control to its touched state.
24691   *
24692   * This method can be called to remove the `ng-untouched` class and set the control to its
24693   * touched state (`ng-touched` class). A model is considered to be touched when the user has
24694   * first focused the control element and then shifted focus away from the control (blur event).
24695   */
24696  this.$setTouched = function() {
24697    ctrl.$touched = true;
24698    ctrl.$untouched = false;
24699    $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
24700  };
24701
24702  /**
24703   * @ngdoc method
24704   * @name ngModel.NgModelController#$rollbackViewValue
24705   *
24706   * @description
24707   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
24708   * which may be caused by a pending debounced event or because the input is waiting for a some
24709   * future event.
24710   *
24711   * If you have an input that uses `ng-model-options` to set up debounced events or events such
24712   * as blur you can have a situation where there is a period when the `$viewValue`
24713   * is out of synch with the ngModel's `$modelValue`.
24714   *
24715   * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
24716   * programmatically before these debounced/future events have resolved/occurred, because Angular's
24717   * dirty checking mechanism is not able to tell whether the model has actually changed or not.
24718   *
24719   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
24720   * input which may have such events pending. This is important in order to make sure that the
24721   * input field will be updated with the new model value and any pending operations are cancelled.
24722   *
24723   * <example name="ng-model-cancel-update" module="cancel-update-example">
24724   *   <file name="app.js">
24725   *     angular.module('cancel-update-example', [])
24726   *
24727   *     .controller('CancelUpdateController', ['$scope', function($scope) {
24728   *       $scope.resetWithCancel = function(e) {
24729   *         if (e.keyCode == 27) {
24730   *           $scope.myForm.myInput1.$rollbackViewValue();
24731   *           $scope.myValue = '';
24732   *         }
24733   *       };
24734   *       $scope.resetWithoutCancel = function(e) {
24735   *         if (e.keyCode == 27) {
24736   *           $scope.myValue = '';
24737   *         }
24738   *       };
24739   *     }]);
24740   *   </file>
24741   *   <file name="index.html">
24742   *     <div ng-controller="CancelUpdateController">
24743   *       <p>Try typing something in each input.  See that the model only updates when you
24744   *          blur off the input.
24745   *        </p>
24746   *        <p>Now see what happens if you start typing then press the Escape key</p>
24747   *
24748   *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
24749   *         <p id="inputDescription1">With $rollbackViewValue()</p>
24750   *         <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
24751   *                ng-keydown="resetWithCancel($event)"><br/>
24752   *         myValue: "{{ myValue }}"
24753   *
24754   *         <p id="inputDescription2">Without $rollbackViewValue()</p>
24755   *         <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
24756   *                ng-keydown="resetWithoutCancel($event)"><br/>
24757   *         myValue: "{{ myValue }}"
24758   *       </form>
24759   *     </div>
24760   *   </file>
24761   * </example>
24762   */
24763  this.$rollbackViewValue = function() {
24764    $timeout.cancel(pendingDebounce);
24765    ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
24766    ctrl.$render();
24767  };
24768
24769  /**
24770   * @ngdoc method
24771   * @name ngModel.NgModelController#$validate
24772   *
24773   * @description
24774   * Runs each of the registered validators (first synchronous validators and then
24775   * asynchronous validators).
24776   * If the validity changes to invalid, the model will be set to `undefined`,
24777   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
24778   * If the validity changes to valid, it will set the model to the last available valid
24779   * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
24780   */
24781  this.$validate = function() {
24782    // ignore $validate before model is initialized
24783    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
24784      return;
24785    }
24786
24787    var viewValue = ctrl.$$lastCommittedViewValue;
24788    // Note: we use the $$rawModelValue as $modelValue might have been
24789    // set to undefined during a view -> model update that found validation
24790    // errors. We can't parse the view here, since that could change
24791    // the model although neither viewValue nor the model on the scope changed
24792    var modelValue = ctrl.$$rawModelValue;
24793
24794    var prevValid = ctrl.$valid;
24795    var prevModelValue = ctrl.$modelValue;
24796
24797    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
24798
24799    ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
24800      // If there was no change in validity, don't update the model
24801      // This prevents changing an invalid modelValue to undefined
24802      if (!allowInvalid && prevValid !== allValid) {
24803        // Note: Don't check ctrl.$valid here, as we could have
24804        // external validators (e.g. calculated on the server),
24805        // that just call $setValidity and need the model value
24806        // to calculate their validity.
24807        ctrl.$modelValue = allValid ? modelValue : undefined;
24808
24809        if (ctrl.$modelValue !== prevModelValue) {
24810          ctrl.$$writeModelToScope();
24811        }
24812      }
24813    });
24814
24815  };
24816
24817  this.$$runValidators = function(modelValue, viewValue, doneCallback) {
24818    currentValidationRunId++;
24819    var localValidationRunId = currentValidationRunId;
24820
24821    // check parser error
24822    if (!processParseErrors()) {
24823      validationDone(false);
24824      return;
24825    }
24826    if (!processSyncValidators()) {
24827      validationDone(false);
24828      return;
24829    }
24830    processAsyncValidators();
24831
24832    function processParseErrors() {
24833      var errorKey = ctrl.$$parserName || 'parse';
24834      if (parserValid === undefined) {
24835        setValidity(errorKey, null);
24836      } else {
24837        if (!parserValid) {
24838          forEach(ctrl.$validators, function(v, name) {
24839            setValidity(name, null);
24840          });
24841          forEach(ctrl.$asyncValidators, function(v, name) {
24842            setValidity(name, null);
24843          });
24844        }
24845        // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
24846        setValidity(errorKey, parserValid);
24847        return parserValid;
24848      }
24849      return true;
24850    }
24851
24852    function processSyncValidators() {
24853      var syncValidatorsValid = true;
24854      forEach(ctrl.$validators, function(validator, name) {
24855        var result = validator(modelValue, viewValue);
24856        syncValidatorsValid = syncValidatorsValid && result;
24857        setValidity(name, result);
24858      });
24859      if (!syncValidatorsValid) {
24860        forEach(ctrl.$asyncValidators, function(v, name) {
24861          setValidity(name, null);
24862        });
24863        return false;
24864      }
24865      return true;
24866    }
24867
24868    function processAsyncValidators() {
24869      var validatorPromises = [];
24870      var allValid = true;
24871      forEach(ctrl.$asyncValidators, function(validator, name) {
24872        var promise = validator(modelValue, viewValue);
24873        if (!isPromiseLike(promise)) {
24874          throw $ngModelMinErr("$asyncValidators",
24875            "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
24876        }
24877        setValidity(name, undefined);
24878        validatorPromises.push(promise.then(function() {
24879          setValidity(name, true);
24880        }, function(error) {
24881          allValid = false;
24882          setValidity(name, false);
24883        }));
24884      });
24885      if (!validatorPromises.length) {
24886        validationDone(true);
24887      } else {
24888        $q.all(validatorPromises).then(function() {
24889          validationDone(allValid);
24890        }, noop);
24891      }
24892    }
24893
24894    function setValidity(name, isValid) {
24895      if (localValidationRunId === currentValidationRunId) {
24896        ctrl.$setValidity(name, isValid);
24897      }
24898    }
24899
24900    function validationDone(allValid) {
24901      if (localValidationRunId === currentValidationRunId) {
24902
24903        doneCallback(allValid);
24904      }
24905    }
24906  };
24907
24908  /**
24909   * @ngdoc method
24910   * @name ngModel.NgModelController#$commitViewValue
24911   *
24912   * @description
24913   * Commit a pending update to the `$modelValue`.
24914   *
24915   * Updates may be pending by a debounced event or because the input is waiting for a some future
24916   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
24917   * usually handles calling this in response to input events.
24918   */
24919  this.$commitViewValue = function() {
24920    var viewValue = ctrl.$viewValue;
24921
24922    $timeout.cancel(pendingDebounce);
24923
24924    // If the view value has not changed then we should just exit, except in the case where there is
24925    // a native validator on the element. In this case the validation state may have changed even though
24926    // the viewValue has stayed empty.
24927    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
24928      return;
24929    }
24930    ctrl.$$lastCommittedViewValue = viewValue;
24931
24932    // change to dirty
24933    if (ctrl.$pristine) {
24934      this.$setDirty();
24935    }
24936    this.$$parseAndValidate();
24937  };
24938
24939  this.$$parseAndValidate = function() {
24940    var viewValue = ctrl.$$lastCommittedViewValue;
24941    var modelValue = viewValue;
24942    parserValid = isUndefined(modelValue) ? undefined : true;
24943
24944    if (parserValid) {
24945      for (var i = 0; i < ctrl.$parsers.length; i++) {
24946        modelValue = ctrl.$parsers[i](modelValue);
24947        if (isUndefined(modelValue)) {
24948          parserValid = false;
24949          break;
24950        }
24951      }
24952    }
24953    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
24954      // ctrl.$modelValue has not been touched yet...
24955      ctrl.$modelValue = ngModelGet($scope);
24956    }
24957    var prevModelValue = ctrl.$modelValue;
24958    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
24959    ctrl.$$rawModelValue = modelValue;
24960
24961    if (allowInvalid) {
24962      ctrl.$modelValue = modelValue;
24963      writeToModelIfNeeded();
24964    }
24965
24966    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
24967    // This can happen if e.g. $setViewValue is called from inside a parser
24968    ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
24969      if (!allowInvalid) {
24970        // Note: Don't check ctrl.$valid here, as we could have
24971        // external validators (e.g. calculated on the server),
24972        // that just call $setValidity and need the model value
24973        // to calculate their validity.
24974        ctrl.$modelValue = allValid ? modelValue : undefined;
24975        writeToModelIfNeeded();
24976      }
24977    });
24978
24979    function writeToModelIfNeeded() {
24980      if (ctrl.$modelValue !== prevModelValue) {
24981        ctrl.$$writeModelToScope();
24982      }
24983    }
24984  };
24985
24986  this.$$writeModelToScope = function() {
24987    ngModelSet($scope, ctrl.$modelValue);
24988    forEach(ctrl.$viewChangeListeners, function(listener) {
24989      try {
24990        listener();
24991      } catch (e) {
24992        $exceptionHandler(e);
24993      }
24994    });
24995  };
24996
24997  /**
24998   * @ngdoc method
24999   * @name ngModel.NgModelController#$setViewValue
25000   *
25001   * @description
25002   * Update the view value.
25003   *
25004   * This method should be called when an input directive want to change the view value; typically,
25005   * this is done from within a DOM event handler.
25006   *
25007   * For example {@link ng.directive:input input} calls it when the value of the input changes and
25008   * {@link ng.directive:select select} calls it when an option is selected.
25009   *
25010   * If the new `value` is an object (rather than a string or a number), we should make a copy of the
25011   * object before passing it to `$setViewValue`.  This is because `ngModel` does not perform a deep
25012   * watch of objects, it only looks for a change of identity. If you only change the property of
25013   * the object then ngModel will not realise that the object has changed and will not invoke the
25014   * `$parsers` and `$validators` pipelines.
25015   *
25016   * For this reason, you should not change properties of the copy once it has been passed to
25017   * `$setViewValue`. Otherwise you may cause the model value on the scope to change incorrectly.
25018   *
25019   * When this method is called, the new `value` will be staged for committing through the `$parsers`
25020   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25021   * value sent directly for processing, finally to be applied to `$modelValue` and then the
25022   * **expression** specified in the `ng-model` attribute.
25023   *
25024   * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
25025   *
25026   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25027   * and the `default` trigger is not listed, all those actions will remain pending until one of the
25028   * `updateOn` events is triggered on the DOM element.
25029   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25030   * directive is used with a custom debounce for this particular event.
25031   *
25032   * Note that calling this function does not trigger a `$digest`.
25033   *
25034   * @param {string} value Value from the view.
25035   * @param {string} trigger Event that triggered the update.
25036   */
25037  this.$setViewValue = function(value, trigger) {
25038    ctrl.$viewValue = value;
25039    if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25040      ctrl.$$debounceViewValueCommit(trigger);
25041    }
25042  };
25043
25044  this.$$debounceViewValueCommit = function(trigger) {
25045    var debounceDelay = 0,
25046        options = ctrl.$options,
25047        debounce;
25048
25049    if (options && isDefined(options.debounce)) {
25050      debounce = options.debounce;
25051      if (isNumber(debounce)) {
25052        debounceDelay = debounce;
25053      } else if (isNumber(debounce[trigger])) {
25054        debounceDelay = debounce[trigger];
25055      } else if (isNumber(debounce['default'])) {
25056        debounceDelay = debounce['default'];
25057      }
25058    }
25059
25060    $timeout.cancel(pendingDebounce);
25061    if (debounceDelay) {
25062      pendingDebounce = $timeout(function() {
25063        ctrl.$commitViewValue();
25064      }, debounceDelay);
25065    } else if ($rootScope.$$phase) {
25066      ctrl.$commitViewValue();
25067    } else {
25068      $scope.$apply(function() {
25069        ctrl.$commitViewValue();
25070      });
25071    }
25072  };
25073
25074  // model -> value
25075  // Note: we cannot use a normal scope.$watch as we want to detect the following:
25076  // 1. scope value is 'a'
25077  // 2. user enters 'b'
25078  // 3. ng-change kicks in and reverts scope value to 'a'
25079  //    -> scope value did not change since the last digest as
25080  //       ng-change executes in apply phase
25081  // 4. view should be changed back to 'a'
25082  $scope.$watch(function ngModelWatch() {
25083    var modelValue = ngModelGet($scope);
25084
25085    // if scope model value and ngModel value are out of sync
25086    // TODO(perf): why not move this to the action fn?
25087    if (modelValue !== ctrl.$modelValue &&
25088       // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25089       (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25090    ) {
25091      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25092      parserValid = undefined;
25093
25094      var formatters = ctrl.$formatters,
25095          idx = formatters.length;
25096
25097      var viewValue = modelValue;
25098      while (idx--) {
25099        viewValue = formatters[idx](viewValue);
25100      }
25101      if (ctrl.$viewValue !== viewValue) {
25102        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25103        ctrl.$render();
25104
25105        ctrl.$$runValidators(modelValue, viewValue, noop);
25106      }
25107    }
25108
25109    return modelValue;
25110  });
25111}];
25112
25113
25114/**
25115 * @ngdoc directive
25116 * @name ngModel
25117 *
25118 * @element input
25119 * @priority 1
25120 *
25121 * @description
25122 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
25123 * property on the scope using {@link ngModel.NgModelController NgModelController},
25124 * which is created and exposed by this directive.
25125 *
25126 * `ngModel` is responsible for:
25127 *
25128 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
25129 *   require.
25130 * - Providing validation behavior (i.e. required, number, email, url).
25131 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
25132 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
25133 * - Registering the control with its parent {@link ng.directive:form form}.
25134 *
25135 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
25136 * current scope. If the property doesn't already exist on this scope, it will be created
25137 * implicitly and added to the scope.
25138 *
25139 * For best practices on using `ngModel`, see:
25140 *
25141 *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
25142 *
25143 * For basic examples, how to use `ngModel`, see:
25144 *
25145 *  - {@link ng.directive:input input}
25146 *    - {@link input[text] text}
25147 *    - {@link input[checkbox] checkbox}
25148 *    - {@link input[radio] radio}
25149 *    - {@link input[number] number}
25150 *    - {@link input[email] email}
25151 *    - {@link input[url] url}
25152 *    - {@link input[date] date}
25153 *    - {@link input[datetime-local] datetime-local}
25154 *    - {@link input[time] time}
25155 *    - {@link input[month] month}
25156 *    - {@link input[week] week}
25157 *  - {@link ng.directive:select select}
25158 *  - {@link ng.directive:textarea textarea}
25159 *
25160 * # CSS classes
25161 * The following CSS classes are added and removed on the associated input/select/textarea element
25162 * depending on the validity of the model.
25163 *
25164 *  - `ng-valid`: the model is valid
25165 *  - `ng-invalid`: the model is invalid
25166 *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
25167 *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
25168 *  - `ng-pristine`: the control hasn't been interacted with yet
25169 *  - `ng-dirty`: the control has been interacted with
25170 *  - `ng-touched`: the control has been blurred
25171 *  - `ng-untouched`: the control hasn't been blurred
25172 *  - `ng-pending`: any `$asyncValidators` are unfulfilled
25173 *
25174 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
25175 *
25176 * ## Animation Hooks
25177 *
25178 * Animations within models are triggered when any of the associated CSS classes are added and removed
25179 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
25180 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
25181 * The animations that are triggered within ngModel are similar to how they work in ngClass and
25182 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
25183 *
25184 * The following example shows a simple way to utilize CSS transitions to style an input element
25185 * that has been rendered as invalid after it has been validated:
25186 *
25187 * <pre>
25188 * //be sure to include ngAnimate as a module to hook into more
25189 * //advanced animations
25190 * .my-input {
25191 *   transition:0.5s linear all;
25192 *   background: white;
25193 * }
25194 * .my-input.ng-invalid {
25195 *   background: red;
25196 *   color:white;
25197 * }
25198 * </pre>
25199 *
25200 * @example
25201 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
25202     <file name="index.html">
25203       <script>
25204        angular.module('inputExample', [])
25205          .controller('ExampleController', ['$scope', function($scope) {
25206            $scope.val = '1';
25207          }]);
25208       </script>
25209       <style>
25210         .my-input {
25211           -webkit-transition:all linear 0.5s;
25212           transition:all linear 0.5s;
25213           background: transparent;
25214         }
25215         .my-input.ng-invalid {
25216           color:white;
25217           background: red;
25218         }
25219       </style>
25220       <p id="inputDescription">
25221        Update input to see transitions when valid/invalid.
25222        Integer is a valid value.
25223       </p>
25224       <form name="testForm" ng-controller="ExampleController">
25225         <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
25226                aria-describedby="inputDescription" />
25227       </form>
25228     </file>
25229 * </example>
25230 *
25231 * ## Binding to a getter/setter
25232 *
25233 * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
25234 * function that returns a representation of the model when called with zero arguments, and sets
25235 * the internal state of a model when called with an argument. It's sometimes useful to use this
25236 * for models that have an internal representation that's different from what the model exposes
25237 * to the view.
25238 *
25239 * <div class="alert alert-success">
25240 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
25241 * frequently than other parts of your code.
25242 * </div>
25243 *
25244 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
25245 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
25246 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
25247 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
25248 *
25249 * The following example shows how to use `ngModel` with a getter/setter:
25250 *
25251 * @example
25252 * <example name="ngModel-getter-setter" module="getterSetterExample">
25253     <file name="index.html">
25254       <div ng-controller="ExampleController">
25255         <form name="userForm">
25256           <label>Name:
25257             <input type="text" name="userName"
25258                    ng-model="user.name"
25259                    ng-model-options="{ getterSetter: true }" />
25260           </label>
25261         </form>
25262         <pre>user.name = <span ng-bind="user.name()"></span></pre>
25263       </div>
25264     </file>
25265     <file name="app.js">
25266       angular.module('getterSetterExample', [])
25267         .controller('ExampleController', ['$scope', function($scope) {
25268           var _name = 'Brian';
25269           $scope.user = {
25270             name: function(newName) {
25271              // Note that newName can be undefined for two reasons:
25272              // 1. Because it is called as a getter and thus called with no arguments
25273              // 2. Because the property should actually be set to undefined. This happens e.g. if the
25274              //    input is invalid
25275              return arguments.length ? (_name = newName) : _name;
25276             }
25277           };
25278         }]);
25279     </file>
25280 * </example>
25281 */
25282var ngModelDirective = ['$rootScope', function($rootScope) {
25283  return {
25284    restrict: 'A',
25285    require: ['ngModel', '^?form', '^?ngModelOptions'],
25286    controller: NgModelController,
25287    // Prelink needs to run before any input directive
25288    // so that we can set the NgModelOptions in NgModelController
25289    // before anyone else uses it.
25290    priority: 1,
25291    compile: function ngModelCompile(element) {
25292      // Setup initial state of the control
25293      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
25294
25295      return {
25296        pre: function ngModelPreLink(scope, element, attr, ctrls) {
25297          var modelCtrl = ctrls[0],
25298              formCtrl = ctrls[1] || nullFormCtrl;
25299
25300          modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
25301
25302          // notify others, especially parent forms
25303          formCtrl.$addControl(modelCtrl);
25304
25305          attr.$observe('name', function(newValue) {
25306            if (modelCtrl.$name !== newValue) {
25307              formCtrl.$$renameControl(modelCtrl, newValue);
25308            }
25309          });
25310
25311          scope.$on('$destroy', function() {
25312            formCtrl.$removeControl(modelCtrl);
25313          });
25314        },
25315        post: function ngModelPostLink(scope, element, attr, ctrls) {
25316          var modelCtrl = ctrls[0];
25317          if (modelCtrl.$options && modelCtrl.$options.updateOn) {
25318            element.on(modelCtrl.$options.updateOn, function(ev) {
25319              modelCtrl.$$debounceViewValueCommit(ev && ev.type);
25320            });
25321          }
25322
25323          element.on('blur', function(ev) {
25324            if (modelCtrl.$touched) return;
25325
25326            if ($rootScope.$$phase) {
25327              scope.$evalAsync(modelCtrl.$setTouched);
25328            } else {
25329              scope.$apply(modelCtrl.$setTouched);
25330            }
25331          });
25332        }
25333      };
25334    }
25335  };
25336}];
25337
25338var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25339
25340/**
25341 * @ngdoc directive
25342 * @name ngModelOptions
25343 *
25344 * @description
25345 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
25346 * events that will trigger a model update and/or a debouncing delay so that the actual update only
25347 * takes place when a timer expires; this timer will be reset after another change takes place.
25348 *
25349 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
25350 * be different from the value in the actual model. This means that if you update the model you
25351 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
25352 * order to make sure it is synchronized with the model and that any debounced action is canceled.
25353 *
25354 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
25355 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
25356 * important because `form` controllers are published to the related scope under the name in their
25357 * `name` attribute.
25358 *
25359 * Any pending changes will take place immediately when an enclosing form is submitted via the
25360 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
25361 * to have access to the updated model.
25362 *
25363 * `ngModelOptions` has an effect on the element it's declared on and its desce
25363ndants.
25364 *
25365 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
25366 *   - `updateOn`: string specifying which event should the input be bound to. You can set several
25367 *     events using an space delimited list. There is a special event called `default` that
25368 *     matches the default events belonging of the control.
25369 *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
25370 *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
25371 *     custom value for each event. For example:
25372 *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
25373 *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
25374 *     not validate correctly instead of the default behavior of setting the model to undefined.
25375 *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
25376       `ngModel` as getters/setters.
25377 *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
25378 *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
25379 *     continental US time zone abbreviations, but for general use, use a time zone offset, for
25380 *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
25381 *     If not specified, the timezone of the browser will be used.
25382 *
25383 * @example
25384
25385  The following example shows how to override immediate updates. Changes on the inputs within the
25386  form will update the model only when the control loses focus (blur event). If `escape` key is
25387  pressed while the input field is focused, the value is reset to the value in the current model.
25388
25389  <example name="ngModelOptions-directive-blur" module="optionsExample">
25390    <file name="index.html">
25391      <div ng-controller="ExampleController">
25392        <form name="userForm">
25393          <label>Name:
25394            <input type="text" name="userName"
25395                   ng-model="user.name"
25396                   ng-model-options="{ updateOn: 'blur' }"
25397                   ng-keyup="cancel($event)" />
25398          </label><br />
25399          <label>Other data:
25400            <input type="text" ng-model="user.data" />
25401          </label><br />
25402        </form>
25403        <pre>user.name = <span ng-bind="user.name"></span></pre>
25404      </div>
25405    </file>
25406    <file name="app.js">
25407      angular.module('optionsExample', [])
25408        .controller('ExampleController', ['$scope', function($scope) {
25409          $scope.user = { name: 'say', data: '' };
25410
25411          $scope.cancel = function(e) {
25412            if (e.keyCode == 27) {
25413              $scope.userForm.userName.$rollbackViewValue();
25414            }
25415          };
25416        }]);
25417    </file>
25418    <file name="protractor.js" type="protractor">
25419      var model = element(by.binding('user.name'));
25420      var input = element(by.model('user.name'));
25421      var other = element(by.model('user.data'));
25422
25423      it('should allow custom events', function() {
25424        input.sendKeys(' hello');
25425        input.click();
25426        expect(model.getText()).toEqual('say');
25427        other.click();
25428        expect(model.getText()).toEqual('say hello');
25429      });
25430
25431      it('should $rollbackViewValue when model changes', function() {
25432        input.sendKeys(' hello');
25433        expect(input.getAttribute('value')).toEqual('say hello');
25434        input.sendKeys(protractor.Key.ESCAPE);
25435        expect(input.getAttribute('value')).toEqual('say');
25436        other.click();
25437        expect(model.getText()).toEqual('say');
25438      });
25439    </file>
25440  </example>
25441
25442  This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
25443  If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
25444
25445  <example name="ngModelOptions-directive-debounce" module="optionsExample">
25446    <file name="index.html">
25447      <div ng-controller="ExampleController">
25448        <form name="userForm">
25449          <label>Name:
25450            <input type="text" name="userName"
25451                   ng-model="user.name"
25452                   ng-model-options="{ debounce: 1000 }" />
25453          </label>
25454          <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
25455          <br />
25456        </form>
25457        <pre>user.name = <span ng-bind="user.name"></span></pre>
25458      </div>
25459    </file>
25460    <file name="app.js">
25461      angular.module('optionsExample', [])
25462        .controller('ExampleController', ['$scope', function($scope) {
25463          $scope.user = { name: 'say' };
25464        }]);
25465    </file>
25466  </example>
25467
25468  This one shows how to bind to getter/setters:
25469
25470  <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
25471    <file name="index.html">
25472      <div ng-controller="ExampleController">
25473        <form name="userForm">
25474          <label>Name:
25475            <input type="text" name="userName"
25476                   ng-model="user.name"
25477                   ng-model-options="{ getterSetter: true }" />
25478          </label>
25479        </form>
25480        <pre>user.name = <span ng-bind="user.name()"></span></pre>
25481      </div>
25482    </file>
25483    <file name="app.js">
25484      angular.module('getterSetterExample', [])
25485        .controller('ExampleController', ['$scope', function($scope) {
25486          var _name = 'Brian';
25487          $scope.user = {
25488            name: function(newName) {
25489              // Note that newName can be undefined for two reasons:
25490              // 1. Because it is called as a getter and thus called with no arguments
25491              // 2. Because the property should actually be set to undefined. This happens e.g. if the
25492              //    input is invalid
25493              return arguments.length ? (_name = newName) : _name;
25494            }
25495          };
25496        }]);
25497    </file>
25498  </example>
25499 */
25500var ngModelOptionsDirective = function() {
25501  return {
25502    restrict: 'A',
25503    controller: ['$scope', '$attrs', function($scope, $attrs) {
25504      var that = this;
25505      this.$options = copy($scope.$eval($attrs.ngModelOptions));
25506      // Allow adding/overriding bound events
25507      if (this.$options.updateOn !== undefined) {
25508        this.$options.updateOnDefault = false;
25509        // extract "default" pseudo-event from list of events that can trigger a model update
25510        this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
25511          that.$options.updateOnDefault = true;
25512          return ' ';
25513        }));
25514      } else {
25515        this.$options.updateOnDefault = true;
25516      }
25517    }]
25518  };
25519};
25520
25521
25522
25523// helper methods
25524function addSetValidityMethod(context) {
25525  var ctrl = context.ctrl,
25526      $element = context.$element,
25527      classCache = {},
25528      set = context.set,
25529      unset = context.unset,
25530      parentForm = context.parentForm,
25531      $animate = context.$animate;
25532
25533  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
25534
25535  ctrl.$setValidity = setValidity;
25536
25537  function setValidity(validationErrorKey, state, controller) {
25538    if (state === undefined) {
25539      createAndSet('$pending', validationErrorKey, controller);
25540    } else {
25541      unsetAndCleanup('$pending', validationErrorKey, controller);
25542    }
25543    if (!isBoolean(state)) {
25544      unset(ctrl.$error, validationErrorKey, controller);
25545      unset(ctrl.$$success, validationErrorKey, controller);
25546    } else {
25547      if (state) {
25548        unset(ctrl.$error, validationErrorKey, controller);
25549        set(ctrl.$$success, validationErrorKey, controller);
25550      } else {
25551        set(ctrl.$error, validationErrorKey, controller);
25552        unset(ctrl.$$success, validationErrorKey, controller);
25553      }
25554    }
25555    if (ctrl.$pending) {
25556      cachedToggleClass(PENDING_CLASS, true);
25557      ctrl.$valid = ctrl.$invalid = undefined;
25558      toggleValidationCss('', null);
25559    } else {
25560      cachedToggleClass(PENDING_CLASS, false);
25561      ctrl.$valid = isObjectEmpty(ctrl.$error);
25562      ctrl.$invalid = !ctrl.$valid;
25563      toggleValidationCss('', ctrl.$valid);
25564    }
25565
25566    // re-read the state as the set/unset methods could have
25567    // combined state in ctrl.$error[validationError] (used for forms),
25568    // where setting/unsetting only increments/decrements the value,
25569    // and does not replace it.
25570    var combinedState;
25571    if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
25572      combinedState = undefined;
25573    } else if (ctrl.$error[validationErrorKey]) {
25574      combinedState = false;
25575    } else if (ctrl.$$success[validationErrorKey]) {
25576      combinedState = true;
25577    } else {
25578      combinedState = null;
25579    }
25580
25581    toggleValidationCss(validationErrorKey, combinedState);
25582    parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
25583  }
25584
25585  function createAndSet(name, value, controller) {
25586    if (!ctrl[name]) {
25587      ctrl[name] = {};
25588    }
25589    set(ctrl[name], value, controller);
25590  }
25591
25592  function unsetAndCleanup(name, value, controller) {
25593    if (ctrl[name]) {
25594      unset(ctrl[name], value, controller);
25595    }
25596    if (isObjectEmpty(ctrl[name])) {
25597      ctrl[name] = undefined;
25598    }
25599  }
25600
25601  function cachedToggleClass(className, switchValue) {
25602    if (switchValue && !classCache[className]) {
25603      $animate.addClass($element, className);
25604      classCache[className] = true;
25605    } else if (!switchValue && classCache[className]) {
25606      $animate.removeClass($element, className);
25607      classCache[className] = false;
25608    }
25609  }
25610
25611  function toggleValidationCss(validationErrorKey, isValid) {
25612    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
25613
25614    cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
25615    cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
25616  }
25617}
25618
25619function isObjectEmpty(obj) {
25620  if (obj) {
25621    for (var prop in obj) {
25622      if (obj.hasOwnProperty(prop)) {
25623        return false;
25624      }
25625    }
25626  }
25627  return true;
25628}
25629
25630/**
25631 * @ngdoc directive
25632 * @name ngNonBindable
25633 * @restrict AC
25634 * @priority 1000
25635 *
25636 * @description
25637 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
25638 * DOM element. This is useful if the element contains what appears to be Angular directives and
25639 * bindings but which should be ignored by Angular. This could be the case if you have a site that
25640 * displays snippets of code, for instance.
25641 *
25642 * @element ANY
25643 *
25644 * @example
25645 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
25646 * but the one wrapped in `ngNonBindable` is left alone.
25647 *
25648 * @example
25649    <example>
25650      <file name="index.html">
25651        <div>Normal: {{1 + 2}}</div>
25652        <div ng-non-bindable>Ignored: {{1 + 2}}</div>
25653      </file>
25654      <file name="protractor.js" type="protractor">
25655       it('should check ng-non-bindable', function() {
25656         expect(element(by.binding('1 + 2')).getText()).toContain('3');
25657         expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
25658       });
25659      </file>
25660    </example>
25661 */
25662var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
25663
25664/* global jqLiteRemove */
25665
25666var ngOptionsMinErr = minErr('ngOptions');
25667
25668/**
25669 * @ngdoc directive
25670 * @name ngOptions
25671 * @restrict A
25672 *
25673 * @description
25674 *
25675 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
25676 * elements for the `<select>` element using the array or object obtained by evaluating the
25677 * `ngOptions` comprehension expression.
25678 *
25679 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
25680 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
25681 * increasing speed by not creating a new scope for each repeated instance, as well as providing
25682 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
25683 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
25684 *  to a non-string value. This is because an option element can only be bound to string values at
25685 * present.
25686 *
25687 * When an item in the `<select>` menu is selected, the array element or object property
25688 * represented by the selected option will be bound to the model identified by the `ngModel`
25689 * directive.
25690 *
25691 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
25692 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
25693 * option. See example below for demonstration.
25694 *
25695 * ## Complex Models (objects or collections)
25696 *
25697 * **Note:** By default, `ngModel` watches the model by reference, not value. This is important when
25698 * binding any input directive to a model that is an object or a collection.
25699 *
25700 * Since this is a common situation for `ngOptions` the directive additionally watches the model using
25701 * `$watchCollection` when the select has the `multiple` attribute or when there is a `track by` clause in
25702 * the options expression. This allows ngOptions to trigger a re-rendering of the options even if the actual
25703 * object/collection has not changed identity but only a property on the object or an item in the collection
25704 * changes.
25705 *
25706 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
25707 * if the model is an array). This means that changing a property deeper inside the object/collection that the
25708 * first level will not trigger a re-rendering.
25709 *
25710 *
25711 * ## `select` **`as`**
25712 *
25713 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
25714 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
25715 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
25716 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
25717 *
25718 *
25719 * ### `select` **`as`** and **`track by`**
25720 *
25721 * <div class="alert alert-warning">
25722 * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
25723 * </div>
25724 *
25725 * Consider the following example:
25726 *
25727 * ```html
25728 * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">
25729 * ```
25730 *
25731 * ```js
25732 * $scope.values = [{
25733 *   id: 1,
25734 *   label: 'aLabel',
25735 *   subItem: { name: 'aSubItem' }
25736 * }, {
25737 *   id: 2,
25738 *   label: 'bLabel',
25739 *   subItem: { name: 'bSubItem' }
25740 * }];
25741 *
25742 * $scope.selected = { name: 'aSubItem' };
25743 * ```
25744 *
25745 * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
25746 * of the data source (to `item` in this example). To calculate whether an element is selected, we do the
25747 * following:
25748 *
25749 * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
25750 * 2. Apply **`track by`** to the already selected value in `ngModel`.
25751 *    In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
25752 *    value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
25753 *    a wrong object, the selected element can't be found, `<select>` is always reset to the "not
25754 *    selected" option.
25755 *
25756 *
25757 * @param {string} ngModel Assignable angular expression to data-bind to.
25758 * @param {string=} name Property name of the form under which the control is published.
25759 * @param {string=} required The control is considered valid only if value is entered.
25760 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25761 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25762 *    `required` when you want to data-bind to the `required` attribute.
25763 * @param {comprehension_expression=} ngOptions in one of the following forms:
25764 *
25765 *   * for array data sources:
25766 *     * `label` **`for`** `value` **`in`** `array`
25767 *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
25768 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
25769 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
25770 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
25771 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
25772 *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
25773 *        (for including a filter with `track by`)
25774 *   * for object data sources:
25775 *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25776 *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25777 *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
25778 *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
25779 *     * `select` **`as`** `label` **`group by`** `group`
25780 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
25781 *     * `select` **`as`** `label` **`disable when`** `disable`
25782 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
25783 *
25784 * Where:
25785 *
25786 *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
25787 *   * `value`: local variable which will refer to each item in the `array` or each property value
25788 *      of `object` during iteration.
25789 *   * `key`: local variable which will refer to a property name in `object` during iteration.
25790 *   * `label`: The result of this expression will be the label for `<option>` element. The
25791 *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
25792 *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
25793 *      element. If not specified, `select` expression will default to `value`.
25794 *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
25795 *      DOM element.
25796 *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
25797 *      element. Return `true` to disable.
25798 *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
25799 *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
25800 *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
25801 *      even when the options are recreated (e.g. reloaded from the server).
25802 *
25803 * @example
25804    <example module="selectExample">
25805      <file name="index.html">
25806        <script>
25807        angular.module('selectExample', [])
25808          .controller('ExampleController', ['$scope', function($scope) {
25809            $scope.colors = [
25810              {name:'black', shade:'dark'},
25811              {name:'white', shade:'light', notAnOption: true},
25812              {name:'red', shade:'dark'},
25813              {name:'blue', shade:'dark', notAnOption: true},
25814              {name:'yellow', shade:'light', notAnOption: false}
25815            ];
25816            $scope.myColor = $scope.colors[2]; // red
25817          }]);
25818        </script>
25819        <div ng-controller="ExampleController">
25820          <ul>
25821            <li ng-repeat="color in colors">
25822              <label>Name: <input ng-model="color.name"></label>
25823              <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
25824              <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
25825            </li>
25826            <li>
25827              <button ng-click="colors.push({})">add</button>
25828            </li>
25829          </ul>
25830          <hr/>
25831          <label>Color (null not allowed):
25832            <select ng-model="myColor" ng-options="color.name for color in colors"></select>
25833          </label><br/>
25834          <label>Color (null allowed):
25835          <span  class="nullable">
25836            <select ng-model="myColor" ng-options="color.name for color in colors">
25837              <option value="">-- choose color --</option>
25838            </select>
25839          </span></label><br/>
25840
25841          <label>Color grouped by shade:
25842            <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
25843            </select>
25844          </label><br/>
25845
25846          <label>Color grouped by shade, with some disabled:
25847            <select ng-model="myColor"
25848                  ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
25849            </select>
25850          </label><br/>
25851
25852
25853
25854          Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
25855          <br/>
25856          <hr/>
25857          Currently selected: {{ {selected_color:myColor} }}
25858          <div style="border:solid 1px black; height:20px"
25859               ng-style="{'background-color':myColor.name}">
25860          </div>
25861        </div>
25862      </file>
25863      <file name="protractor.js" type="protractor">
25864         it('should check ng-options', function() {
25865           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
25866           element.all(by.model('myColor')).first().click();
25867           element.all(by.css('select[ng-model="myColor"] option')).first().click();
25868           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
25869           element(by.css('.nullable select[ng-model="myColor"]')).click();
25870           element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
25871           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
25872         });
25873      </file>
25874    </example>
25875 */
25876
25877// jshint maxlen: false
25878//                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
25879var 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]+?))?$/;
25880                        // 1: value expression (valueFn)
25881                        // 2: label expression (displayFn)
25882                        // 3: group by expression (groupByFn)
25883                        // 4: disable when expression (disableWhenFn)
25884                        // 5: array item variable name
25885                        // 6: object item key variable name
25886                        // 7: object item value variable name
25887                        // 8: collection expression
25888                        // 9: track by expression
25889// jshint maxlen: 100
25890
25891
25892var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
25893
25894  function parseOptionsExpression(optionsExp, selectElement, scope) {
25895
25896    var match = optionsExp.match(NG_OPTIONS_REGEXP);
25897    if (!(match)) {
25898      throw ngOptionsMinErr('iexp',
25899        "Expected expression in form of " +
25900        "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
25901        " but got '{0}'. Element: {1}",
25902        optionsExp, startingTag(selectElement));
25903    }
25904
25905    // Extract the parts from the ngOptions expression
25906
25907    // The variable name for the value of the item in the collection
25908    var valueName = match[5] || match[7];
25909    // The variable name for the key of the item in the collection
25910    var keyName = match[6];
25911
25912    // An expression that generates the viewValue for an option if there is a label expression
25913    var selectAs = / as /.test(match[0]) && match[1];
25914    // An expression that is used to track the id of each object in the options collection
25915    var trackBy = match[9];
25916    // An expression that generates the viewValue for an option if there is no label expression
25917    var valueFn = $parse(match[2] ? match[1] : valueName);
25918    var selectAsFn = selectAs && $parse(selectAs);
25919    var viewValueFn = selectAsFn || valueFn;
25920    var trackByFn = trackBy && $parse(trackBy);
25921
25922    // Get the value by which we are going to track the option
25923    // if we have a trackFn then use that (passing scope and locals)
25924    // otherwise just hash the given viewValue
25925    var getTrackByValueFn = trackBy ?
25926                              function(value, locals) { return trackByFn(scope, locals); } :
25927                              function getHashOfValue(value) { return hashKey(value); };
25928    var getTrackByValue = function(value, key) {
25929      return getTrackByValueFn(value, getLocals(value, key));
25930    };
25931
25932    var displayFn = $parse(match[2] || match[1]);
25933    var groupByFn = $parse(match[3] || '');
25934    var disableWhenFn = $parse(match[4] || '');
25935    var valuesFn = $parse(match[8]);
25936
25937    var locals = {};
25938    var getLocals = keyName ? function(value, key) {
25939      locals[keyName] = key;
25940      locals[valueName] = value;
25941      return locals;
25942    } : function(value) {
25943      locals[valueName] = value;
25944      return locals;
25945    };
25946
25947
25948    function Option(selectValue, viewValue, label, group, disabled) {
25949      this.selectValue = selectValue;
25950      this.viewValue = viewValue;
25951      this.label = label;
25952      this.group = group;
25953      this.disabled = disabled;
25954    }
25955
25956    return {
25957      trackBy: trackBy,
25958      getTrackByValue: getTrackByValue,
25959      getWatchables: $parse(valuesFn, function(values) {
25960        // Create a collection of things that we would like to watch (watchedArray)
25961        // so that they can all be watched using a single $watchCollection
25962        // that only runs the handler once if anything changes
25963        var watchedArray = [];
25964        values = values || [];
25965
25966        Object.keys(values).forEach(function getWatchable(key) {
25967          if (key.charAt(0) === '$') return;
25968          var locals = getLocals(values[key], key);
25969          var selectValue = getTrackByValueFn(values[key], locals);
25970          watchedArray.push(selectValue);
25971
25972          // Only need to watch the displayFn if there is a specific label expression
25973          if (match[2] || match[1]) {
25974            var label = displayFn(scope, locals);
25975            watchedArray.push(label);
25976          }
25977
25978          // Only need to watch the disableWhenFn if there is a specific disable expression
25979          if (match[4]) {
25980            var disableWhen = disableWhenFn(scope, locals);
25981            watchedArray.push(disableWhen);
25982          }
25983        });
25984        return watchedArray;
25985      }),
25986
25987      getOptions: function() {
25988
25989        var optionItems = [];
25990        var selectValueMap = {};
25991
25992        // The option values were already computed in the `getWatchables` fn,
25993        // which must have been called to trigger `getOptions`
25994        var optionValues = valuesFn(scope) || [];
25995        var optionValuesKeys;
25996
25997
25998        if (!keyName && isArrayLike(optionValues)) {
25999          optionValuesKeys = optionValues;
26000        } else {
26001          // if object, extract keys, in enumeration order, unsorted
26002          optionValuesKeys = [];
26003          for (var itemKey in optionValues) {
26004            if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26005              optionValuesKeys.push(itemKey);
26006            }
26007          }
26008        }
26009
26010        var optionValuesLength = optionValuesKeys.length;
26011
26012        for (var index = 0; index < optionValuesLength; index++) {
26013          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26014          var value = optionValues[key];
26015          var locals = getLocals(value, key);
26016          var viewValue = viewValueFn(scope, locals);
26017          var selectValue = getTrackByValueFn(viewValue, locals);
26018          var label = displayFn(scope, locals);
26019          var group = groupByFn(scope, locals);
26020          var disabled = disableWhenFn(scope, locals);
26021          var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26022
26023          optionItems.push(optionItem);
26024          selectValueMap[selectValue] = optionItem;
26025        }
26026
26027        return {
26028          items: optionItems,
26029          selectValueMap: selectValueMap,
26030          getOptionFromViewValue: function(value) {
26031            return selectValueMap[getTrackByValue(value)];
26032          },
26033          getViewValueFromOption: function(option) {
26034            // If the viewValue could be an object that may be mutated by the application,
26035            // we need to make a copy and not return the reference to the value on the option.
26036            return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26037          }
26038        };
26039      }
26040    };
26041  }
26042
26043
26044  // we can't just jqLite('<option>') since jqLite is not smart enough
26045  // to create it in <select> and IE barfs otherwise.
26046  var optionTemplate = document.createElement('option'),
26047      optGroupTemplate = document.createElement('optgroup');
26048
26049  return {
26050    restrict: 'A',
26051    terminal: true,
26052    require: ['select', '?ngModel'],
26053    link: function(scope, selectElement, attr, ctrls) {
26054
26055      // if ngModel is not defined, we don't need to do anything
26056      var ngModelCtrl = ctrls[1];
26057      if (!ngModelCtrl) return;
26058
26059      var selectCtrl = ctrls[0];
26060      var multiple = attr.multiple;
26061
26062      // The emptyOption allows the application developer to provide their own custom "empty"
26063      // option when the viewValue does not match any of the option values.
26064      var emptyOption;
26065      for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26066        if (children[i].value === '') {
26067          emptyOption = children.eq(i);
26068          break;
26069        }
26070      }
26071
26072      var providedEmptyOption = !!emptyOption;
26073
26074      var unknownOption = jqLite(optionTemplate.cloneNode(false));
26075      unknownOption.val('?');
26076
26077      var options;
26078      var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26079
26080
26081      var renderEmptyOption = function() {
26082        if (!providedEmptyOption) {
26083          selectElement.prepend(emptyOption);
26084        }
26085        selectElement.val('');
26086        emptyOption.prop('selected', true); // needed for IE
26087        emptyOption.attr('selected', true);
26088      };
26089
26090      var removeEmptyOption = function() {
26091        if (!providedEmptyOption) {
26092          emptyOption.remove();
26093        }
26094      };
26095
26096
26097      var renderUnknownOption = function() {
26098        selectElement.prepend(unknownOption);
26099        selectElement.val('?');
26100        unknownOption.prop('selected', true); // needed for IE
26101        unknownOption.attr('selected', true);
26102      };
26103
26104      var removeUnknownOption = function() {
26105        unknownOption.remove();
26106      };
26107
26108
26109      // Update the controller methods for multiple selectable options
26110      if (!multiple) {
26111
26112        selectCtrl.writeValue = function writeNgOptionsValue(value) {
26113          var option = options.getOptionFromViewValue(value);
26114
26115          if (option && !option.disabled) {
26116            if (selectElement[0].value !== option.selectValue) {
26117              removeUnknownOption();
26118              removeEmptyOption();
26119
26120              selectElement[0].value = option.selectValue;
26121              option.element.selected = true;
26122              option.element.setAttribute('selected', 'selected');
26123            }
26124          } else {
26125            if (value === null || providedEmptyOption) {
26126              removeUnknownOption();
26127              renderEmptyOption();
26128            } else {
26129              removeEmptyOption();
26130              renderUnknownOption();
26131            }
26132          }
26133        };
26134
26135        selectCtrl.readValue = function readNgOptionsValue() {
26136
26137          var selectedOption = options.selectValueMap[selectElement.val()];
26138
26139          if (selectedOption && !selectedOption.disabled) {
26140            removeEmptyOption();
26141            removeUnknownOption();
26142            return options.getViewValueFromOption(selectedOption);
26143          }
26144          return null;
26145        };
26146
26147        // If we are using `track by` then we must watch the tracked value on the model
26148        // since ngModel only watches for object identity change
26149        if (ngOptions.trackBy) {
26150          scope.$watch(
26151            function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
26152            function() { ngModelCtrl.$render(); }
26153          );
26154        }
26155
26156      } else {
26157
26158        ngModelCtrl.$isEmpty = function(value) {
26159          return !value || value.length === 0;
26160        };
26161
26162
26163        selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
26164          options.items.forEach(function(option) {
26165            option.element.selected = false;
26166          });
26167
26168          if (value) {
26169            value.forEach(function(item) {
26170              var option = options.getOptionFromViewValue(item);
26171              if (option && !option.disabled) option.element.selected = true;
26172            });
26173          }
26174        };
26175
26176
26177        selectCtrl.readValue = function readNgOptionsMultiple() {
26178          var selectedValues = selectElement.val() || [],
26179              selections = [];
26180
26181          forEach(selectedValues, function(value) {
26182            var option = options.selectValueMap[value];
26183            if (!option.disabled) selections.push(options.getViewValueFromOption(option));
26184          });
26185
26186          return selections;
26187        };
26188
26189        // If we are using `track by` then we must watch these tracked values on the model
26190        // since ngModel only watches for object identity change
26191        if (ngOptions.trackBy) {
26192
26193          scope.$watchCollection(function() {
26194            if (isArray(ngModelCtrl.$viewValue)) {
26195              return ngModelCtrl.$viewValue.map(function(value) {
26196                return ngOptions.getTrackByValue(value);
26197              });
26198            }
26199          }, function() {
26200            ngModelCtrl.$render();
26201          });
26202
26203        }
26204      }
26205
26206
26207      if (providedEmptyOption) {
26208
26209        // we need to remove it before calling selectElement.empty() because otherwise IE will
26210        // remove the label from the element. wtf?
26211        emptyOption.remove();
26212
26213        // compile the element since there might be bindings in it
26214        $compile(emptyOption)(scope);
26215
26216        // remove the class, which is added automatically because we recompile the element and it
26217        // becomes the compilation root
26218        emptyOption.removeClass('ng-scope');
26219      } else {
26220        emptyOption = jqLite(optionTemplate.cloneNode(false));
26221      }
26222
26223      // We need to do this here to ensure that the options object is defined
26224      // when we first hit it in writeNgOptionsValue
26225      updateOptions();
26226
26227      // We will re-render the option elements if the option values or labels change
26228      scope.$watchCollection(ngOptions.getWatchables, updateOptions);
26229
26230      // ------------------------------------------------------------------ //
26231
26232
26233      function updateOptionElement(option, element) {
26234        option.element = element;
26235        element.disabled = option.disabled;
26236        if (option.value !== element.value) element.value = option.selectValue;
26237        if (option.label !== element.label) {
26238          element.label = option.label;
26239          element.textContent = option.label;
26240        }
26241      }
26242
26243      function addOrReuseElement(parent, current, type, templateElement) {
26244        var element;
26245        // Check whether we can reuse the next element
26246        if (current && lowercase(current.nodeName) === type) {
26247          // The next element is the right type so reuse it
26248          element = current;
26249        } else {
26250          // The next element is not the right type so create a new one
26251          element = templateElement.cloneNode(false);
26252          if (!current) {
26253            // There are no more elements so just append it to the select
26254            parent.appendChild(element);
26255          } else {
26256            // The next element is not a group so insert the new one
26257            parent.insertBefore(element, current);
26258          }
26259        }
26260        return element;
26261      }
26262
26263
26264      function removeExcessElements(current) {
26265        var next;
26266        while (current) {
26267          next = current.nextSibling;
26268          jqLiteRemove(current);
26269          current = next;
26270        }
26271      }
26272
26273
26274      function skipEmptyAndUnknownOptions(current) {
26275        var emptyOption_ = emptyOption && emptyOption[0];
26276        var unknownOption_ = unknownOption && unknownOption[0];
26277
26278        if (emptyOption_ || unknownOption_) {
26279          while (current &&
26280                (current === emptyOption_ ||
26281                current === unknownOption_)) {
26282            current = current.nextSibling;
26283          }
26284        }
26285        return current;
26286      }
26287
26288
26289      function updateOptions() {
26290
26291        var previousValue = options && selectCtrl.readValue();
26292
26293        options = ngOptions.getOptions();
26294
26295        var groupMap = {};
26296        var currentElement = selectElement[0].firstChild;
26297
26298        // Ensure that the empty option is always there if it was explicitly provided
26299        if (providedEmptyOption) {
26300          selectElement.prepend(emptyOption);
26301        }
26302
26303        currentElement = skipEmptyAndUnknownOptions(currentElement);
26304
26305        options.items.forEach(function updateOption(option) {
26306          var group;
26307          var groupElement;
26308          var optionElement;
26309
26310          if (option.group) {
26311
26312            // This option is to live in a group
26313            // See if we have already created this group
26314            group = groupMap[option.group];
26315
26316            if (!group) {
26317
26318              // We have not already created this group
26319              groupElement = addOrReuseElement(selectElement[0],
26320                                               currentElement,
26321                                               'optgroup',
26322                                               optGroupTemplate);
26323              // Move to the next element
26324              currentElement = groupElement.nextSibling;
26325
26326              // Update the label on the group element
26327              groupElement.label = option.group;
26328
26329              // Store it for use later
26330              group = groupMap[option.group] = {
26331                groupElement: groupElement,
26332                currentOptionElement: groupElement.firstChild
26333              };
26334
26335            }
26336
26337            // So now we have a group for this option we add the option to the group
26338            optionElement = addOrReuseElement(group.groupElement,
26339                                              group.currentOptionElement,
26340                                              'option',
26341                                              optionTemplate);
26342            updateOptionElement(option, optionElement);
26343            // Move to the next element
26344            group.currentOptionElement = optionElement.nextSibling;
26345
26346          } else {
26347
26348            // This option is not in a group
26349            optionElement = addOrReuseElement(selectElement[0],
26350                                              currentElement,
26351                                              'option',
26352                                              optionTemplate);
26353            updateOptionElement(option, optionElement);
26354            // Move to the next element
26355            currentElement = optionElement.nextSibling;
26356          }
26357        });
26358
26359
26360        // Now remove all excess options and group
26361        Object.keys(groupMap).forEach(function(key) {
26362          removeExcessElements(groupMap[key].currentOptionElement);
26363        });
26364        removeExcessElements(currentElement);
26365
26366        ngModelCtrl.$render();
26367
26368        // Check to see if the value has changed due to the update to the options
26369        if (!ngModelCtrl.$isEmpty(previousValue)) {
26370          var nextValue = selectCtrl.readValue();
26371          if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
26372            ngModelCtrl.$setViewValue(nextValue);
26373            ngModelCtrl.$render();
26374          }
26375        }
26376
26377      }
26378
26379    }
26380  };
26381}];
26382
26383/**
26384 * @ngdoc directive
26385 * @name ngPluralize
26386 * @restrict EA
26387 *
26388 * @description
26389 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
26390 * These rules are bundled with angular.js, but can be overridden
26391 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
26392 * by specifying the mappings between
26393 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26394 * and the strings to be displayed.
26395 *
26396 * # Plural categories and explicit number rules
26397 * There are two
26398 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26399 * in Angular's default en-US locale: "one" and "other".
26400 *
26401 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
26402 * any number that is not 1), an explicit number rule can only match one number. For example, the
26403 * explicit number rule for "3" matches the number 3. There are examples of plural categories
26404 * and explicit number rules throughout the rest of this documentation.
26405 *
26406 * # Configuring ngPluralize
26407 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
26408 * You can also provide an optional attribute, `offset`.
26409 *
26410 * The value of the `count` attribute can be either a string or an {@link guide/expression
26411 * Angular expression}; these are evaluated on the current scope for its bound value.
26412 *
26413 * The `when` attribute specifies the mappings between plural categories and the actual
26414 * string to be displayed. The value of the attribute should be a JSON object.
26415 *
26416 * The following example shows how to configure ngPluralize:
26417 *
26418 * ```html
26419 * <ng-pluralize count="personCount"
26420                 when="{'0': 'Nobody is viewing.',
26421 *                      'one': '1 person is viewing.',
26422 *                      'other': '{} people are viewing.'}">
26423 * </ng-pluralize>
26424 *```
26425 *
26426 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
26427 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
26428 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
26429 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
26430 * show "a dozen people are viewing".
26431 *
26432 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
26433 * into pluralized strings. In the previous example, Angular will replace `{}` with
26434 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
26435 * for <span ng-non-bindable>{{numberExpression}}</span>.
26436 *
26437 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
26438 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
26439 *
26440 * # Configuring ngPluralize with offset
26441 * The `offset` attribute allows further customization of pluralized text, which can result in
26442 * a better user experience. For example, instead of the message "4 people are viewing this document",
26443 * you might display "John, Kate and 2 others are viewing this document".
26444 * The offset attribute allows you to offset a number by any desired value.
26445 * Let's take a look at an example:
26446 *
26447 * ```html
26448 * <ng-pluralize count="personCount" offset=2
26449 *               when="{'0': 'Nobody is viewing.',
26450 *                      '1': '{{person1}} is viewing.',
26451 *                      '2': '{{person1}} and {{person2}} are viewing.',
26452 *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
26453 *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
26454 * </ng-pluralize>
26455 * ```
26456 *
26457 * Notice that we are still using two plural categories(one, other), but we added
26458 * three explicit number rules 0, 1 and 2.
26459 * When one person, perhaps John, views the document, "John is viewing" will be shown.
26460 * When three people view the document, no explicit number rule is found, so
26461 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
26462 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
26463 * is shown.
26464 *
26465 * Note that when you specify offsets, you must provide explicit number rules for
26466 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
26467 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
26468 * plural categories "one" and "other".
26469 *
26470 * @param {string|expression} count The variable to be bound to.
26471 * @param {string} when The mapping between plural category to its corresponding strings.
26472 * @param {number=} offset Offset to deduct from the total number.
26473 *
26474 * @example
26475    <example module="pluralizeExampl
vendor: 4,913 bytes, lines 26475-26589
26475e">
26476      <file name="index.html">
26477        <script>
26478          angular.module('pluralizeExample', [])
26479            .controller('ExampleController', ['$scope', function($scope) {
26480              $scope.person1 = 'Igor';
26481              $scope.person2 = 'Misko';
26482              $scope.personCount = 1;
26483            }]);
26484        </script>
26485        <div ng-controller="ExampleController">
26486          <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
26487          <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
26488          <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
26489
26490          <!--- Example with simple pluralization rules for en locale --->
26491          Without Offset:
26492          <ng-pluralize count="personCount"
26493                        when="{'0': 'Nobody is viewing.',
26494                               'one': '1 person is viewing.',
26495                               'other': '{} people are viewing.'}">
26496          </ng-pluralize><br>
26497
26498          <!--- Example with offset --->
26499          With Offset(2):
26500          <ng-pluralize count="personCount" offset=2
26501                        when="{'0': 'Nobody is viewing.',
26502                               '1': '{{person1}} is viewing.',
26503                               '2': '{{person1}} and {{person2}} are viewing.',
26504                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
26505                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
26506          </ng-pluralize>
26507        </div>
26508      </file>
26509      <file name="protractor.js" type="protractor">
26510        it('should show correct pluralized string', function() {
26511          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
26512          var withOffset = element.all(by.css('ng-pluralize')).get(1);
26513          var countInput = element(by.model('personCount'));
26514
26515          expect(withoutOffset.getText()).toEqual('1 person is viewing.');
26516          expect(withOffset.getText()).toEqual('Igor is viewing.');
26517
26518          countInput.clear();
26519          countInput.sendKeys('0');
26520
26521          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
26522          expect(withOffset.getText()).toEqual('Nobody is viewing.');
26523
26524          countInput.clear();
26525          countInput.sendKeys('2');
26526
26527          expect(withoutOffset.getText()).toEqual('2 people are viewing.');
26528          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
26529
26530          countInput.clear();
26531          countInput.sendKeys('3');
26532
26533          expect(withoutOffset.getText()).toEqual('3 people are viewing.');
26534          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
26535
26536          countInput.clear();
26537          countInput.sendKeys('4');
26538
26539          expect(withoutOffset.getText()).toEqual('4 people are viewing.');
26540          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
26541        });
26542        it('should show data-bound names', function() {
26543          var withOffset = element.all(by.css('ng-pluralize')).get(1);
26544          var personCount = element(by.model('personCount'));
26545          var person1 = element(by.model('person1'));
26546          var person2 = element(by.model('person2'));
26547          personCount.clear();
26548          personCount.sendKeys('4');
26549          person1.clear();
26550          person1.sendKeys('Di');
26551          person2.clear();
26552          person2.sendKeys('Vojta');
26553          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
26554        });
26555      </file>
26556    </example>
26557 */
26558var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
26559  var BRACE = /{}/g,
26560      IS_WHEN = /^when(Minus)?(.+)$/;
26561
26562  return {
26563    link: function(scope, element, attr) {
26564      var numberExp = attr.count,
26565          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
26566          offset = attr.offset || 0,
26567          whens = scope.$eval(whenExp) || {},
26568          whensExpFns = {},
26569          startSymbol = $interpolate.startSymbol(),
26570          endSymbol = $interpolate.endSymbol(),
26571          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
26572          watchRemover = angular.noop,
26573          lastCount;
26574
26575      forEach(attr, function(expression, attributeName) {
26576        var tmpMatch = IS_WHEN.exec(attributeName);
26577        if (tmpMatch) {
26578          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
26579          whens[whenKey] = element.attr(attr.$attr[attributeName]);
26580        }
26581      });
26582      forEach(whens, function(expression, key) {
26583        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
26584
26585      });
26586
26587      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
26588        var count = parseFloat(newVal);
26589        var countIsNaN = isN
vendor: 10,711 bytes, lines 26589-26825
26589aN(count);
26590
26591        if (!countIsNaN && !(count in whens)) {
26592          // If an explicit number rule such as 1, 2, 3... is defined, just use it.
26593          // Otherwise, check it against pluralization rules in $locale service.
26594          count = $locale.pluralCat(count - offset);
26595        }
26596
26597        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
26598        // In JS `NaN !== NaN`, so we have to exlicitly check.
26599        if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
26600          watchRemover();
26601          var whenExpFn = whensExpFns[count];
26602          if (isUndefined(whenExpFn)) {
26603            if (newVal != null) {
26604              $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
26605            }
26606            watchRemover = noop;
26607            updateElementText();
26608          } else {
26609            watchRemover = scope.$watch(whenExpFn, updateElementText);
26610          }
26611          lastCount = count;
26612        }
26613      });
26614
26615      function updateElementText(newText) {
26616        element.text(newText || '');
26617      }
26618    }
26619  };
26620}];
26621
26622/**
26623 * @ngdoc directive
26624 * @name ngRepeat
26625 *
26626 * @description
26627 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
26628 * instance gets its own scope, where the given loop variable is set to the current collection item,
26629 * and `$index` is set to the item index or key.
26630 *
26631 * Special properties are exposed on the local scope of each template instance, including:
26632 *
26633 * | Variable  | Type            | Details                                                                     |
26634 * |-----------|-----------------|-----------------------------------------------------------------------------|
26635 * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
26636 * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
26637 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
26638 * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
26639 * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
26640 * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
26641 *
26642 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
26643 * This may be useful when, for instance, nesting ngRepeats.
26644 *
26645 *
26646 * # Iterating over object properties
26647 *
26648 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
26649 * syntax:
26650 *
26651 * ```js
26652 * <div ng-repeat="(key, value) in myObj"> ... </div>
26653 * ```
26654 *
26655 * You need to be aware that the JavaScript specification does not define the order of keys
26656 * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
26657 * used to sort the keys alphabetically.)
26658 *
26659 * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
26660 * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
26661 * keys in the order in which they were defined, although there are exceptions when keys are deleted
26662 * and reinstated. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_issues
26663 *
26664 * If this is not desired, the recommended workaround is to convert your object into an array
26665 * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
26666 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
26667 * or implement a `$watch` on the object yourself.
26668 *
26669 *
26670 * # Tracking and Duplicates
26671 *
26672 * When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM:
26673 *
26674 * * When an item is added, a new instance of the template is added to the DOM.
26675 * * When an item is removed, its template instance is removed from the DOM.
26676 * * When items are reordered, their respective templates are reordered in the DOM.
26677 *
26678 * By default, `ngRepeat` does not allow duplicate items in arrays. This is because when
26679 * there are duplicates, it is not possible to maintain a one-to-one mapping between collection
26680 * items and DOM elements.
26681 *
26682 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
26683 * with your own using the `track by` expression.
26684 *
26685 * For example, you may track items by the index of each item in the collection, using the
26686 * special scope property `$index`:
26687 * ```html
26688 *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
26689 *      {{n}}
26690 *    </div>
26691 * ```
26692 *
26693 * You may use arbitrary expressions in `track by`, including references to custom functions
26694 * on the scope:
26695 * ```html
26696 *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
26697 *      {{n}}
26698 *    </div>
26699 * ```
26700 *
26701 * If you are working with objects that have an identifier property, you can track
26702 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
26703 * will not have to rebuild the DOM elements for items it has already rendered, even if the
26704 * JavaScript objects in the collection have been substituted for new ones:
26705 * ```html
26706 *    <div ng-repeat="model in collection track by model.id">
26707 *      {{model.name}}
26708 *    </div>
26709 * ```
26710 *
26711 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
26712 * `$id` function, which tracks items by their identity:
26713 * ```html
26714 *    <div ng-repeat="obj in collection track by $id(obj)">
26715 *      {{obj.prop}}
26716 *    </div>
26717 * ```
26718 *
26719 * <div class="alert alert-warning">
26720 * **Note:** `track by` must always be the last expression:
26721 * </div>
26722 * ```
26723 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
26724 *     {{model.name}}
26725 * </div>
26726 * ```
26727 *
26728 * # Special repeat start and end points
26729 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
26730 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
26731 * 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)
26732 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
26733 *
26734 * The example below makes use of this feature:
26735 * ```html
26736 *   <header ng-repeat-start="item in items">
26737 *     Header {{ item }}
26738 *   </header>
26739 *   <div class="body">
26740 *     Body {{ item }}
26741 *   </div>
26742 *   <footer ng-repeat-end>
26743 *     Footer {{ item }}
26744 *   </footer>
26745 * ```
26746 *
26747 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
26748 * ```html
26749 *   <header>
26750 *     Header A
26751 *   </header>
26752 *   <div class="body">
26753 *     Body A
26754 *   </div>
26755 *   <footer>
26756 *     Footer A
26757 *   </footer>
26758 *   <header>
26759 *     Header B
26760 *   </header>
26761 *   <div class="body">
26762 *     Body B
26763 *   </div>
26764 *   <footer>
26765 *     Footer B
26766 *   </footer>
26767 * ```
26768 *
26769 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
26770 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
26771 *
26772 * @animations
26773 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
26774 *
26775 * **.leave** - when an item is removed from the list or when an item is filtered out
26776 *
26777 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
26778 *
26779 * @element ANY
26780 * @scope
26781 * @priority 1000
26782 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
26783 *   formats are currently supported:
26784 *
26785 *   * `variable in expression` – where variable is the user defined loop variable and `expression`
26786 *     is a scope expression giving the collection to enumerate.
26787 *
26788 *     For example: `album in artist.albums`.
26789 *
26790 *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
26791 *     and `expression` is the scope expression giving the collection to enumerate.
26792 *
26793 *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
26794 *
26795 *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
26796 *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
26797 *     is specified, ng-repeat associates elements by identity. It is an error to have
26798 *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
26799 *     mapped to the same DOM element, which is not possible.)
26800 *
26801 *     Note that the tracking expression must come last, after any filters, and the alias expression.
26802 *
26803 *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
26804 *     will be associated by item identity in the array.
26805 *
26806 *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
26807 *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
26808 *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
26809 *     element in the same way in the DOM.
26810 *
26811 *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
26812 *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
26813 *     property is same.
26814 *
26815 *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
26816 *     to items in conjunction with a tracking expression.
26817 *
26818 *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
26819 *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
26820 *     when a filter is active on the repeater, but the filtered result set is empty.
26821 *
26822 *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
26823 *     the items have been processed through the filter.
26824 *
26825 *     Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax s
26825o it can be used only at the end
26826 *     (and not as operator, inside an expression).
26827 *
26828 *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
26829 *
26830 * @example
26831 * This example initializes the scope to a list of names and
26832 * then uses `ngRepeat` to display every person:
26833  <example module="ngAnimate" deps="angular-animate.js" animations="true">
26834    <file name="index.html">
26835      <div ng-init="friends = [
26836        {name:'John', age:25, gender:'boy'},
26837        {name:'Jessie', age:30, gender:'girl'},
26838        {name:'Johanna', age:28, gender:'girl'},
26839        {name:'Joy', age:15, gender:'girl'},
26840        {name:'Mary', age:28, gender:'girl'},
26841        {name:'Peter', age:95, gender:'boy'},
26842        {name:'Sebastian', age:50, gender:'boy'},
26843        {name:'Erika', age:27, gender:'girl'},
26844        {name:'Patrick', age:40, gender:'boy'},
26845        {name:'Samantha', age:60, gender:'girl'}
26846      ]">
26847        I have {{friends.length}} friends. They are:
26848        <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
26849        <ul class="example-animate-container">
26850          <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
26851            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
26852          </li>
26853          <li class="animate-repeat" ng-if="results.length == 0">
26854            <strong>No results found...</strong>
26855          </li>
26856        </ul>
26857      </div>
26858    </file>
26859    <file name="animations.css">
26860      .example-animate-container {
26861        background:white;
26862        border:1px solid black;
26863        list-style:none;
26864        margin:0;
26865        padding:0 10px;
26866      }
26867
26868      .animate-repeat {
26869        line-height:40px;
26870        list-style:none;
26871        box-sizing:border-box;
26872      }
26873
26874      .animate-repeat.ng-move,
26875      .animate-repeat.ng-enter,
26876      .animate-repeat.ng-leave {
26877        -webkit-transition:all linear 0.5s;
26878        transition:all linear 0.5s;
26879      }
26880
26881      .animate-repeat.ng-leave.ng-leave-active,
26882      .animate-repeat.ng-move,
26883      .animate-repeat.ng-enter {
26884        opacity:0;
26885        max-height:0;
26886      }
26887
26888      .animate-repeat.ng-leave,
26889      .animate-repeat.ng-move.ng-move-active,
26890      .animate-repeat.ng-enter.ng-enter-active {
26891        opacity:1;
26892        max-height:40px;
26893      }
26894    </file>
26895    <file name="protractor.js" type="protractor">
26896      var friends = element.all(by.repeater('friend in friends'));
26897
26898      it('should render initial data set', function() {
26899        expect(friends.count()).toBe(10);
26900        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
26901        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
26902        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
26903        expect(element(by.binding('friends.length')).getText())
26904            .toMatch("I have 10 friends. They are:");
26905      });
26906
26907       it('should update repeater when filter predicate changes', function() {
26908         expect(friends.count()).toBe(10);
26909
26910         element(by.model('q')).sendKeys('ma');
26911
26912         expect(friends.count()).toBe(2);
26913         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
26914         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
26915       });
26916      </file>
26917    </example>
26918 */
26919var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
26920  var NG_REMOVED = '$$NG_REMOVED';
26921  var ngRepeatMinErr = minErr('ngRepeat');
26922
26923  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
26924    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
26925    scope[valueIdentifier] = value;
26926    if (keyIdentifier) scope[keyIdentifier] = key;
26927    scope.$index = index;
26928    scope.$first = (index === 0);
26929    scope.$last = (index === (arrayLength - 1));
26930    scope.$middle = !(scope.$first || scope.$last);
26931    // jshint bitwise: false
26932    scope.$odd = !(scope.$even = (index&1) === 0);
26933    // jshint bitwise: true
26934  };
26935
26936  var getBlockStart = function(block) {
26937    return block.clone[0];
26938  };
26939
26940  var getBlockEnd = function(block) {
26941    return block.clone[block.clone.length - 1];
26942  };
26943
26944
26945  return {
26946    restrict: 'A',
26947    multiElement: true,
26948    transclude: 'element',
26949    priority: 1000,
26950    terminal: true,
26951    $$tlb: true,
26952    compile: function ngRepeatCompile($element, $attr) {
26953      var expression = $attr.ngRepeat;
26954      var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
26955
26956      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*$/);
26957
26958      if (!match) {
26959        throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
26960            expression);
26961      }
26962
26963      var lhs = match[1];
26964      var rhs = match[2];
26965      var aliasAs = match[3];
26966      var trackByExp = match[4];
26967
26968      match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
26969
26970      if (!match) {
26971        throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
26972            lhs);
26973      }
26974      var valueIdentifier = match[3] || match[1];
26975      var keyIdentifier = match[2];
26976
26977      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
26978          /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
26979        throw ngRepeatMinErr('badident', "alias '{0}
vendor: 11,910 bytes, lines 26979-27250
26979' is invalid --- must be a valid JS identifier which is not a reserved name.",
26980          aliasAs);
26981      }
26982
26983      var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
26984      var hashFnLocals = {$id: hashKey};
26985
26986      if (trackByExp) {
26987        trackByExpGetter = $parse(trackByExp);
26988      } else {
26989        trackByIdArrayFn = function(key, value) {
26990          return hashKey(value);
26991        };
26992        trackByIdObjFn = function(key) {
26993          return key;
26994        };
26995      }
26996
26997      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
26998
26999        if (trackByExpGetter) {
27000          trackByIdExpFn = function(key, value, index) {
27001            // assign key, value, and $index to the locals so that they can be used in hash functions
27002            if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27003            hashFnLocals[valueIdentifier] = value;
27004            hashFnLocals.$index = index;
27005            return trackByExpGetter($scope, hashFnLocals);
27006          };
27007        }
27008
27009        // Store a list of elements from previous run. This is a hash where key is the item from the
27010        // iterator, and the value is objects with following properties.
27011        //   - scope: bound scope
27012        //   - element: previous element.
27013        //   - index: position
27014        //
27015        // We are using no-proto object so that we don't need to guard against inherited props via
27016        // hasOwnProperty.
27017        var lastBlockMap = createMap();
27018
27019        //watch props
27020        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27021          var index, length,
27022              previousNode = $element[0],     // node that cloned nodes should be inserted after
27023                                              // initialized to the comment node anchor
27024              nextNode,
27025              // Same as lastBlockMap but it has the current state. It will become the
27026              // lastBlockMap on the next iteration.
27027              nextBlockMap = createMap(),
27028              collectionLength,
27029              key, value, // key/value of iteration
27030              trackById,
27031              trackByIdFn,
27032              collectionKeys,
27033              block,       // last object information {scope, element, id}
27034              nextBlockOrder,
27035              elementsToRemove;
27036
27037          if (aliasAs) {
27038            $scope[aliasAs] = collection;
27039          }
27040
27041          if (isArrayLike(collection)) {
27042            collectionKeys = collection;
27043            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
27044          } else {
27045            trackByIdFn = trackByIdExpFn || trackByIdObjFn;
27046            // if object, extract keys, in enumeration order, unsorted
27047            collectionKeys = [];
27048            for (var itemKey in collection) {
27049              if (collection.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
27050                collectionKeys.push(itemKey);
27051              }
27052            }
27053          }
27054
27055          collectionLength = collectionKeys.length;
27056          nextBlockOrder = new Array(collectionLength);
27057
27058          // locate existing items
27059          for (index = 0; index < collectionLength; index++) {
27060            key = (collection === collectionKeys) ? index : collectionKeys[index];
27061            value = collection[key];
27062            trackById = trackByIdFn(key, value, index);
27063            if (lastBlockMap[trackById]) {
27064              // found previously seen block
27065              block = lastBlockMap[trackById];
27066              delete lastBlockMap[trackById];
27067              nextBlockMap[trackById] = block;
27068              nextBlockOrder[index] = block;
27069            } else if (nextBlockMap[trackById]) {
27070              // if collision detected. restore lastBlockMap and throw an error
27071              forEach(nextBlockOrder, function(block) {
27072                if (block && block.scope) lastBlockMap[block.id] = block;
27073              });
27074              throw ngRepeatMinErr('dupes',
27075                  "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
27076                  expression, trackById, value);
27077            } else {
27078              // new never before seen block
27079              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
27080              nextBlockMap[trackById] = true;
27081            }
27082          }
27083
27084          // remove leftover items
27085          for (var blockKey in lastBlockMap) {
27086            block = lastBlockMap[blockKey];
27087            elementsToRemove = getBlockNodes(block.clone);
27088            $animate.leave(elementsToRemove);
27089            if (elementsToRemove[0].parentNode) {
27090              // if the element was not removed yet because of pending animation, mark it as deleted
27091              // so that we can ignore it later
27092              for (index = 0, length = elementsToRemove.length; index < length; index++) {
27093                elementsToRemove[index][NG_REMOVED] = true;
27094              }
27095            }
27096            block.scope.$destroy();
27097          }
27098
27099          // we are not using forEach for perf reasons (trying to avoid #call)
27100          for (index = 0; index < collectionLength; index++) {
27101            key = (collection === collectionKeys) ? index : collectionKeys[index];
27102            value = collection[key];
27103            block = nextBlockOrder[index];
27104
27105            if (block.scope) {
27106              // if we have already seen this object, then we need to reuse the
27107              // associated scope/element
27108
27109              nextNode = previousNode;
27110
27111              // skip nodes that are already pending removal via leave animation
27112              do {
27113                nextNode = nextNode.nextSibling;
27114              } while (nextNode && nextNode[NG_REMOVED]);
27115
27116              if (getBlockStart(block) != nextNode) {
27117                // existing item which got moved
27118                $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
27119              }
27120              previousNode = getBlockEnd(block);
27121              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27122            } else {
27123              // new item which we don't know about
27124              $transclude(function ngRepeatTransclude(clone, scope) {
27125                block.scope = scope;
27126                // http://jsperf.com/clone-vs-createcomment
27127                var endNode = ngRepeatEndComment.cloneNode(false);
27128                clone[clone.length++] = endNode;
27129
27130                // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
27131                $animate.enter(clone, null, jqLite(previousNode));
27132                previousNode = endNode;
27133                // Note: We only need the first/last node of the cloned nodes.
27134                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27135                // by a directive with templateUrl when its template arrives.
27136                block.clone = clone;
27137                nextBlockMap[block.id] = block;
27138                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27139              });
27140            }
27141          }
27142          lastBlockMap = nextBlockMap;
27143        });
27144      };
27145    }
27146  };
27147}];
27148
27149var NG_HIDE_CLASS = 'ng-hide';
27150var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
27151/**
27152 * @ngdoc directive
27153 * @name ngShow
27154 *
27155 * @description
27156 * The `ngShow` directive shows or hides the given HTML element based on the expression
27157 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
27158 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27159 * in AngularJS and sets the display style to none (using an !important flag).
27160 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27161 *
27162 * ```html
27163 * <!-- when $scope.myValue is truthy (element is visible) -->
27164 * <div ng-show="myValue"></div>
27165 *
27166 * <!-- when $scope.myValue is falsy (element is hidden) -->
27167 * <div ng-show="myValue" class="ng-hide"></div>
27168 * ```
27169 *
27170 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
27171 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
27172 * from the element causing the element not to appear hidden.
27173 *
27174 * ## Why is !important used?
27175 *
27176 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27177 * can be easily overridden by heavier selectors. For example, something as simple
27178 * as changing the display style on a HTML list item would make hidden elements appear visible.
27179 * This also becomes a bigger issue when dealing with CSS frameworks.
27180 *
27181 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27182 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27183 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27184 *
27185 * ### Overriding `.ng-hide`
27186 *
27187 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27188 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27189 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
27190 * with extra animation classes that can be added.
27191 *
27192 * ```css
27193 * .ng-hide:not(.ng-hide-animate) {
27194 *   /&#42; this is just another form of hiding an element &#42;/
27195 *   display: block!important;
27196 *   position: absolute;
27197 *   top: -9999px;
27198 *   left: -9999px;
27199 * }
27200 * ```
27201 *
27202 * By default you don't need to override in CSS anything and the animations will work around the display style.
27203 *
27204 * ## A note about animations with `ngShow`
27205 *
27206 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27207 * is true and false. This system works like the animation system present with ngClass except that
27208 * you must also include the !important flag to override the display property
27209 * so that you can perform an animation when the element is hidden during the time of the animation.
27210 *
27211 * ```css
27212 * //
27213 * //a working example can be found at the bottom of this page
27214 * //
27215 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27216 *   /&#42; this is required as of 1.3x to properly
27217 *      apply all styling in a show/hide animation &#42;/
27218 *   transition: 0s linear all;
27219 * }
27220 *
27221 * .my-element.ng-hide-add-active,
27222 * .my-element.ng-hide-remove-active {
27223 *   /&#42; the transition is defined in the active class &#42;/
27224 *   transition: 1s linear all;
27225 * }
27226 *
27227 * .my-element.ng-hide-add { ... }
27228 * .my-element.ng-hide-add.ng-hide-add-active { ... }
27229 * .my-element.ng-hide-remove { ... }
27230 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27231 * ```
27232 *
27233 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27234 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27235 *
27236 * @animations
27237 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
27238 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
27239 *
27240 * @element ANY
27241 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
27242 *     then the element is shown or hidden respectively.
27243 *
27244 * @example
27245  <example module="ngAnimate" deps="angular-animate.js" animations="true">
27246    <file name="index.html">
27247      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
27248      <div>
27249        Show:
27250        <div class="check-element animate-show" ng-show="checked">
27251          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27252        </div>
27253      </div>
27254      <div>
27255        Hide:
27256        <div class="check-element animate-show" ng-hide="checked">
27257          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27258        </div>
27259      </div>
27260    </file>
27261    <file name="glyphicons.css">
27262      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27263    </file>
27264    <file name="animations.css">
27265      .animate-show {
27266        line-height: 20px;
27267        opacity: 1;
27268        padding: 10px;
27269        border: 1px solid black;
27270        background: white;
27271      }
27272
27273      .animate-show.ng-hide-add.ng-hide-add-active,
27274      .animate-show.ng-hide-remove.ng-hide-remove-active {
27275        -webkit-transition: all linear 0.5s;
27276        transition: all linear 0.5s;
27277      }
27278
27279      .animate-show.ng-hide {
27280        line-height: 0;
27281        opacity: 0;
27282        padding: 0 10px;
27283      }
27284
27285      .check-element {
27286        padding: 10px;
27287        border: 1px solid black;
27288        background: white;
27289      }
27290    </file>
27291    <file name="protractor.js" type="protractor">
27292      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27293      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27294
27295      it('should check ng-show / ng-hide', function() {
27296        expect(thumbsUp.isDisplayed()).toBeFalsy();
27297        expect(thumbsDown.isDisplayed()).toBeTruthy();
27298
27299        element(by.model('checked')).click();
27300
27301        expect(thumbsUp.isDisplayed()).toBeTruthy();
27302        expect(thumbsDown.isDisplayed()).toBeFalsy();
27303      });
27304    </file>
27305  </example>
27306 */
27307var ngShowDirective = ['$animate', function($animate) {
27308  return {
27309    restrict: 'A',
27310    multiElement: true,
27311    link: function(scope, element, attr) {
27312      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
27313        // we're adding a temporary, animation-specific class for ng-hide since this way
27314        // we can control when the element is actually displayed on screen without having
27315        // to have a global/greedy CSS selector that breaks when other animations are run.
27316        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
27317        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
27318          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27319        });
27320      });
27321    }
27322  };
27323}];
27324
27325
27326/**
27327 * @ngdoc directive
27328 * @name ngHide
27329 *
27330 * @description
27331 * The `ngHide` directive shows or hides the given HTML element based on the expression
27332 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
27333 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27334 * in AngularJS and sets the display style to none (using an !important flag).
27335 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27336 *
27337 * ```html
27338 * <!-- when $scope.myValue is truthy (element is hidden) -->
27339 * <div ng-hide="myValue" class="ng-hide"></div>
27340 *
27341 * <!-- when $scope.myValue is falsy (element is visible) -->
27342 * <div ng-hide="myValue"></div>
27343 * ```
27344 *
27345 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
27346 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
27347 * from the element causing the element not to appear hidden.
27348 *
27349 * ## Why is !important used?
27350 *
27351 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27352 * can be easily overridden by heavier selectors. For example, something as simple
27353 * as changing the display style on a HTML list item would make hidden elements appear visible.
27354 * This also becomes a bigger issue when dealing with CSS frameworks.
27355 *
27356 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27357 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27358 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27359 *
27360 * ### Overriding `.ng-hide`
27361 *
27362 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27363 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27364 * class in CSS:
27365 *
27366 * ```css
27367 * .ng-hide {
27368 *   /&#42; this is just another form of hiding an element &#42;/
27369 *   display: block!important;
27370 *   position: absolute;
27371 *   top: -9999px;
27372 *   left: -9999px;
27373 * }
27374 * ```
27375 *
27376 * By default you don't need to override in CSS anything and the animations will work around the display style.
27377 *
27378 * ## A note about animations with `ngHide`
27379 *
27380 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27381 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
27382 * CSS class is added and removed for you instead of your own CSS class.
27383 *
27384 * ```css
27385 * //
27386 * //a working example can be found at the bottom of this page
27387 * //
27388 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27389 *   transition: 0.5s linear all;
27390 * }
27391 *
27392 * .my-element.ng-hide-add { ... }
27393 * .my-element.ng-hide-add.ng-hide-add-active { ... }
27394 * .my-element.ng-hide-remove { ... }
27395 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27396 * ```
27397 *
27398 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27399 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27400 *
27401 * @animations
27402 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
27403 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
27404 *
27405 * @element ANY
27406 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
27407 *     the element is shown or hidden respectively.
27408 *
27409 * @example
27410  <example module="ngAnimate" deps="angular-animate.js" animations="true">
27411    <file name="index.html">
27412      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
27413      <div>
27414        Show:
27415        <div class="check-element animate-hide" ng-show="checked">
27416          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27417        </div>
27418      </div>
27419      <div>
27420        Hide:
27421        <div class="check-element animate-hide" ng-hide="checked">
27422          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27423        </div>
27424      </div>
27425    </file>
27426    <file name="glyphicons.css">
27427      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27428    </file>
27429    <file name="animations.css">
27430      .animate-hide {
27431        -webkit-transition: all linear 0.5s;
27432        transition: all linear 0.5s;
27433        line-height: 20px;
27434        opacity: 1;
27435        padding: 10px;
27436        border: 1px solid black;
27437        background: white;
27438      }
27439
27440      .animate-hide.ng-hide {
27441        line-height: 0;
27442        opacity: 0;
27443        padding: 0 10px;
27444      }
27445
27446      .check-element {
27447        padding: 10px;
27448        border: 1px solid black;
27449        background: white;
27450      }
27451    </file>
27452    <file name="protractor.js" type="protractor">
27453      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27454      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27455
27456      it('should check ng-show / ng-hide', function() {
27457        expect(thumbsUp.isDisplayed()).toBeFalsy();
27458        expect(thumbsDown.isDisplayed()).toBeTruthy();
27459
27460        element(by.model('checked')).click();
27461
27462        expect(thumbsUp.isDisplayed()).toBeTruthy();
27463        expect(thumbsDown.isDisplayed()).toBeFalsy();
27464      });
27465    </file>
27466  </example>
27467 */
27468var ngHideDirective = ['$animate', function($animate) {
27469  return {
27470    restrict: 'A',
27471    multiElement: true,
27472    link: function(scope, element, attr) {
27473      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
27474        // The comment inside of the ngShowDirective explains why we add and
27475        // remove a temporary class for the show/hide animation
27476        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
27477          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27478        });
27479      });
27480    }
27481  };
27482}];
27483
27484/**
27485 * @ngdoc directive
27486 * @name ngStyle
27487 * @restrict AC
27488 *
27489 * @description
27490 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
27491 *
27492 * @element ANY
27493 * @param {expression} ngStyle
27494 *
27495 * {@link guide/expression Expression} which evals to an
27496 * object whose keys are CSS style names and values are corresponding values for those CSS
27497 * keys.
27498 *
27499 * Since some CSS style names are not valid keys for an object, they must be quoted.
27500 * See the 'background-color' style in the example below.
27501 *
27502 * @example
27503   <example>
27504     <file name="index.html">
27505        <input type="button" value="set color" ng-click="myStyle={color:'red'}">
27506        <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
27507        <input type="button" value="clear" ng-click="myStyle={}">
27508        <br/>
27509        <span ng-style="myStyle">Sample Text</span>
27510        <pre>myStyle={{myStyle}}</pre>
27511     </file>
27512     <file name="style.css">
27513       span {
27514         color: black;
27515       }
27516     </file>
27517     <file name="protractor.js" type="protractor">
27518       var colorSpan = element(by.css('span'));
27519
27520       it('should check ng-style', function() {
27521         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
27522         element(by.css('input[value=\'set color\']')).click();
27523         expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
27524         element(by.css('input[value=clear]')).click();
27525         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
27526       });
27527     </file>
27528   </example>
27529 */
27530var ngStyleDirective = ngDirective(function(scope, element, attr) {
27531  scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
27532    if (oldStyles && (newStyles !== oldStyles)) {
27533      forEach(oldStyles, function(val, style) { element.css(style, '');});
27534    }
27535    if (newStyles) element.css(newStyles);
27536  }, true);
27537});
27538
27539/**
27540 * @ngdoc directive
27541 * @name ngSwitch
27542 * @restrict EA
27543 *
27544 * @description
27545 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
27546 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
27547 * as specified in the template.
27548 *
27549 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
27550 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
27551 * matches the value obtained from the evaluated expression. In other words, you define a container element
27552 * (where you place the directive), place an expression on the **`on="..."` attribute**
27553 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
27554 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
27555 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
27556 * attribute is displayed.
27557 *
27558 * <div class="alert alert-info">
27559 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
27560 * as literal string values to match against.
27561 * For example, **`ng-switch-when="someVal"`** will match against the string `"s
27561omeVal"` not against the
27562 * value of the expression `$scope.someVal`.
27563 * </div>
27564
27565 * @animations
27566 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
27567 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
27568 *
27569 * @usage
27570 *
27571 * ```
27572 * <ANY ng-switch="expression">
27573 *   <ANY ng-switch-when="matchValue1">...</ANY>
27574 *   <ANY ng-switch-when="matchValue2">...</ANY>
27575 *   <ANY ng-switch-default>...</ANY>
27576 * </ANY>
27577 * ```
27578 *
27579 *
27580 * @scope
27581 * @priority 1200
27582 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
27583 * On child elements add:
27584 *
27585 * * `ngSwitchWhen`: the case statement to match against. If match then this
27586 *   case will be displayed. If the same match appears multiple times, all the
27587 *   elements will be displayed.
27588 * * `ngSwitchDefault`: the default case when no other case match. If there
27589 *   are multiple default cases, all of them will be displayed when no other
27590 *   case match.
27591 *
27592 *
27593 * @example
27594  <example module="switchExample" deps="angular-animate.js" animations="true">
27595    <file name="index.html">
27596      <div ng-controller="ExampleController">
27597        <select ng-model="selection" ng-options="item for item in items">
27598        </select>
27599        <code>selection={{selection}}</code>
27600        <hr/>
27601        <div class="animate-switch-container"
27602          ng-switch on="selection">
27603            <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
27604            <div class="animate-switch" ng-switch-when="home">Home Span</div>
27605            <div class="animate-switch" ng-switch-default>default</div>
27606        </div>
27607      </div>
27608    </file>
27609    <file name="script.js">
27610      angular.module('switchExample', ['ngAnimate'])
27611        .controller('ExampleController', ['$scope', function($scope) {
27612          $scope.items = ['settings', 'home', 'other'];
27613          $scope.selection = $scope.items[0];
27614        }]);
27615    </file>
27616    <file name="animations.css">
27617      .animate-switch-container {
27618        position:relative;
27619        background:white;
27620        border:1px solid black;
27621        height:40px;
27622        overflow:hidden;
27623      }
27624
27625      .animate-switch {
27626        padding:10px;
27627      }
27628
27629      .animate-switch.ng-animate {
27630        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
27631        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
27632
27633        position:absolute;
27634        top:0;
27635        left:0;
27636        right:0;
27637        bottom:0;
27638      }
27639
27640      .animate-switch.ng-leave.ng-leave-active,
27641      .animate-switch.ng-enter {
27642        top:-50px;
27643      }
27644      .animate-switch.ng-leave,
27645      .animate-switch.ng-enter.ng-enter-active {
27646        top:0;
27647      }
27648    </file>
27649    <file name="protractor.js" type="protractor">
27650      var switchElem = element(by.css('[ng-switch]'));
27651      var select = element(by.model('selection'));
27652
27653      it('should start in settings', function() {
27654        expect(switchElem.getText()).toMatch(/Settings Div/);
27655      });
27656      it('should change to home', function() {
27657        select.all(by.css('option')).get(1).click();
27658        expect(switchElem.getText()).toMatch(/Home Span/);
27659      });
27660      it('should select default', function() {
27661        select.all(by.css('option')).get(2).click();
27662        expect(switchElem.getText()).toMatch(/default/);
27663      });
27664    </file>
27665  </example>
27666 */
27667var ngSwitchDirective = ['$animate', function($animate) {
27668  return {
27669    require: 'ngSwitch',
27670
27671    // asks for $scope to fool the BC controller module
27672    controller: ['$scope', function ngSwitchController() {
27673     this.cases = {};
27674    }],
27675    link: function(scope, element, attr, ngSwitchController) {
27676      var watchExpr = attr.ngSwitch || attr.on,
27677          selectedTranscludes = [],
27678          selectedElements = [],
27679          previousLeaveAnimations = [],
27680          selectedScopes = [];
27681
27682      var spliceFactory = function(array, index) {
27683          return function() { array.splice(index, 1); };
27684      };
27685
27686      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
27687        var i, ii;
27688        for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
27689          $animate.cancel(previousLeaveAnimations[i]);
27690        }
27691        previousLeaveAnimations.length = 0;
27692
27693        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
27694          var selected = getBlockNodes(selectedElements[i].clone);
27695          selectedScopes[i].$destroy();
27696          var promise = previousLeaveAnimations[i] = $animate.leave(selected);
27697          promise.then(spliceFactory(previousLeaveAnimations, i));
27698        }
27699
27700        selectedElements.length = 0;
27701        selectedScopes.length = 0;
27702
27703        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
27704          forEach(selectedTranscludes, function(selectedTransclude) {
27705            selectedTransclude.transclude(function(caseElement, selectedScope) {
27706              selectedScopes.push(selectedScope);
27707              var anchor = selectedTransclude.element;
27708              caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
27709              var block = { clone: caseElement };
27710
27711              selectedElements.push(block);
27712              $animate.enter(caseElement, anchor.parent(), anchor);
27713            });
27714          });
27715        }
27716      });
27717    }
27718  };
27719}];
27720
27721var ngSwitchWhenDirective = ngDirective({
27722  transclude: 'element',
27723  priority: 1200,
27724  require: '^ngSwitch',
27725  multiElement: true,
27726  link: function(scope, element, attrs, ctrl, $transclude) {
27727    ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
27728    ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
27729  }
27730});
27731
27732var ngSwitchDefaultDirective = ngDirective({
27733  transclude: 'element',
27734  priority: 1200,
27735  require: '^ngSwitch',
27736  multiElement: true,
27737  link: function(scope, element, attr, ctrl, $transclude) {
27738    ctrl.cases['?'] = (ctrl.cases['?'] || []);
27739    ctrl.cases['?'].push({ transclude: $transclude, element: element });
27740   }
27741});
27742
27743/**
27744 * @ngdoc directive
27745 * @name ngTransclude
27746 * @restrict EAC
27747 *
27748 * @description
27749 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
27750 *
27751 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
27752 *
27753 * @element ANY
27754 *
27755 * @example
27756   <example module="transcludeExample">
27757     <file name="index.html">
27758       <script>
27759         angular.module('transcludeExample', [])
27760          .directive('pane', function(){
27761             return {
27762               restrict: 'E',
27763               transclude: true,
27764               scope: { title:'@' },
27765               template: '<div style="border: 1px solid black;">' +
27766                           '<div style="background-color: gray">{{title}}</div>' +
27767                           '<ng-transclude></ng-transclude>' +
27768                         '</div>'
27769             };
27770         })
27771         .controller('ExampleController', ['$scope', function($scope) {
27772           $scope.title = 'Lorem Ipsum';
27773           $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
27774         }]);
27775       </script>
27776       <div ng-controller="ExampleController">
27777         <input ng-model="title" aria-label="title"> <br/>
27778         <textarea ng-model="text" aria-label="text"></textarea> <br/>
27779         <pane title="{{title}}">{{text}}</pane>
27780       </div>
27781     </file>
27782     <file name="protractor.js" type="protractor">
27783        it('should have transcluded', function() {
27784          var titleElement = element(by.model('title'));
27785          titleElement.clear();
27786          titleElement.sendKeys('TITLE');
27787          var textElement = element(by.model('text'));
27788          textElement.clear();
27789          textElement.sendKeys('TEXT');
27790          expect(element(by.binding('title')).getText()).toEqual('TITLE');
27791          expect(element(by.binding('text')).getText()).toEqual('TEXT');
27792        });
27793     </file>
27794   </example>
27795 *
27796 */
27797var ngTranscludeDirective = ngDirective({
27798  restrict: 'EAC',
27799  link: function($scope, $element, $attrs, controller, $transclude) {
27800    if (!$transclude) {
27801      throw minErr('ngTransclude')('orphan',
27802       'Illegal use of ngTransclude directive in the template! ' +
27803       'No parent directive that requires a transclusion found. ' +
27804       'Element: {0}',
27805       startingTag($element));
27806    }
27807
27808    $transclude(function(clone) {
27809      $element.empty();
27810      $element.append(clone);
27811    });
27812  }
27813});
27814
27815/**
27816 * @ngdoc directive
27817 * @name script
27818 * @restrict E
27819 *
27820 * @description
27821 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
27822 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
27823 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
27824 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
27825 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
27826 *
27827 * @param {string} type Must be set to `'text/ng-template'`.
27828 * @param {string} id Cache name of the template.
27829 *
27830 * @example
27831  <example>
27832    <file name="index.html">
27833      <script type="text/ng-template" id="/tpl.html">
27834        Content of the template.
27835      </script>
27836
27837      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
27838      <div id="tpl-content" ng-include src="currentTpl"></div>
27839    </file>
27840    <file name="protractor.js" type="protractor">
27841      it('should load template defined inside script tag', function() {
27842        element(by.css('#tpl-link')).click();
27843        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
27844      });
27845    </file>
27846  </example>
27847 */
27848var scriptDirective = ['$templateCache', function($templateCache) {
27849  return {
27850    restrict: 'E',
27851    terminal: true,
27852    compile: function(element, attr) {
27853      if (attr.type == 'text/ng-template') {
27854        var templateUrl = attr.id,
27855            text = element[0].text;
27856
27857        $templateCache.put(templateUrl, text);
27858      }
27859    }
27860  };
27861}];
27862
27863var noopNgModelController = { $setViewValue: noop, $render: noop };
27864
27865/**
27866 * @ngdoc type
27867 * @name  select.SelectController
27868 * @description
27869 * The controller for the `<select>` directive. This provides support for reading
27870 * and writing the selected value(s) of the control and also coordinates dynamically
27871 * added `<option>` elements, perhaps by an `ngRepeat` directive.
27872 */
27873var SelectController =
27874        ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
27875
27876  var self = this,
27877      optionsMap = new HashMap();
27878
27879  // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
27880  self.ngModelCtrl = noopNgModelController;
27881
27882  // The "unknown" option is one that is prepended to the list if the viewValue
27883  // does not match any of the options. When it is rendered the value of the unknown
27884  // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
27885  //
27886  // We can't just jqLite('<option>') since jqLite is not smart enough
27887  // to create it in <select> and IE barfs otherwise.
27888  self.unknownOption = jqLite(document.createElement('option'));
27889  self.renderUnknownOption = function(val) {
27890    var unknownVal = '? ' + hashKey(val) + ' ?';
27891    self.unknownOption.val(unknownVal);
27892    $element.prepend(self.unknownOption);
27893    $element.val(unknownVal);
27894  };
27895
27896  $scope.$on('$destroy', function() {
27897    // disable unknown option so that we don't do work when the whole select is being destroyed
27898    self.renderUnknownOption = noop;
27899  });
27900
27901  self.removeUnknownOption = function() {
27902    if (self.unknownOption.parent()) self.unknownOption.remove();
27903  };
27904
27905
27906  // Read the value of the select control, the implementation of this changes depending
27907  // upon whether the select can have multiple values and whether ngOptions is at work.
27908  self.readValue = function readSingleValue() {
27909    self.removeUnknownOption();
27910    return $element.val();
27911  };
27912
27913
27914  // Write the value to the select control, the implementation of this changes depending
27915  // upon whether the select can have multiple values and whether ngOptions is at work.
27916  self.writeValue = function writeSingleValue(value) {
27917    if (self.hasOption(value)) {
27918      self.removeUnknownOption();
27919      $element.val(value);
27920      if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
27921    } else {
27922      if (value == null && self.emptyOption) {
27923        self.removeUnknownOption();
27924        $element.val('');
27925      } else {
27926        self.renderUnknownOption(value);
27927      }
27928    }
27929  };
27930
27931
27932  // Tell the select control that an option, with the given value, has been added
27933  self.addOption = function(value, element) {
27934    assertNotHasOwnProperty(value, '"option value"');
27935    if (value === '') {
27936      self.emptyOption = element;
27937    }
27938    var count = optionsMap.get(value) || 0;
27939    optionsMap.put(value, count + 1);
27940  };
27941
27942  // Tell the select control that an option, with the given value, has been removed
27943  self.removeOption = function(value) {
27944    var count = optionsMap.get(value);
27945    if (count) {
27946      if (count === 1) {
27947        optionsMap.remove(value);
27948        if (value === '') {
27949          self.emptyOption = undefined;
27950        }
27951      } else {
27952        optionsMap.put(value, count - 1);
27953      }
27954    }
27955  };
27956
27957  // Check whether the select control has an option matching the given value
27958  self.hasOption = function(value) {
27959    return !!optionsMap.get(value);
27960  };
27961}];
27962
27963/**
27964 * @ngdoc directive
27965 * @name select
27966 * @restrict E
27967 *
27968 * @description
27969 * HTML `SELECT` element with angular data-binding.
27970 *
27971 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
27972 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits such as reducing
27973 * memory and increasing speed by not creating a new scope for each repeated instance, as well as providing
27974 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
27975 * comprehension expression.
27976 *
27977 * When an item in the `<select>` menu is selected, the array element or object property
27978 * represented by the selected option will be bound to the model identified by the `ngModel`
27979 * directive.
27980 *
27981 * If the viewValue contains a value that doesn't match any of the options then the control
27982 * will automatically add an "unknown" option, which it then removes when this is resolved.
27983 *
27984 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
27985 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
27986 * option. See example below for demonstration.
27987 *
27988 * <div class="alert alert-info">
27989 * The value of a `select` directive used without `ngOptions` is always a string.
27990 * When the model needs to be bound to a non-string value, you must either explictly convert it
27991 * using a directive (see example below) or use `ngOptions` to specify the set of options.
27992 * This is because an option element can only be bound to string values at present.
27993 * </div>
27994 *
27995 * ### Example (binding `select` to a non-string value)
27996 *
27997 * <example name="select-with-non-string-options" module="nonStringSelect">
27998 *   <file name="index.html">
27999 *     <select ng-model="model.id" convert-to-number>
28000 *       <option value="0">Zero</option>
28001 *       <option value="1">One</option>
28002 *       <option value="2">Two</option>
28003 *     </select>
28004 *     {{ model }}
28005 *   </file>
28006 *   <file name="app.js">
28007 *     angular.module('nonStringSelect', [])
28008 *       .run(function($rootScope) {
28009 *         $rootScope.model = { id: 2 };
28010 *       })
28011 *       .directive('convertToNumber', function() {
28012 *         return {
28013 *           require: 'ngModel',
28014 *           link: function(scope, element, attrs, ngModel) {
28015 *             ngModel.$parsers.push(function(val) {
28016 *               return parseInt(val, 10);
28017 *             });
28018 *             ngModel.$formatters.push(function(val) {
28019 *               return '' + val;
28020 *             });
28021 *           }
28022 *         };
28023 *       });
28024 *   </file>
28025 *   <file name="protractor.js" type="protractor">
28026 *     it('should initialize to model', function() {
28027 *       var select = element(by.css('select'));
28028 *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
28029 *     });
28030 *   </file>
28031 * </example>
28032 *
28033 */
28034var selectDirective = function() {
28035
28036  return {
28037    restrict: 'E',
28038    require: ['select', '?ngModel'],
28039    controller: SelectController,
28040    link: function(scope, element, attr, ctrls) {
28041
28042      // if ngModel is not defined, we don't need to do anything
28043      var ngModelCtrl = ctrls[1];
28044      if (!ngModelCtrl) return;
28045
28046      var selectCtrl = ctrls[0];
28047
28048      selectCtrl.ngModelCtrl = ngModelCtrl;
28049
28050      // We delegate rendering to the `writeValue` method, which can be changed
28051      // if the select can have multiple selected values or if the options are being
28052      // generated by `ngOptions`
28053      ngModelCtrl.$render = function() {
28054        selectCtrl.writeValue(ngModelCtrl.$viewValue);
28055      };
28056
28057      // When the selected item(s) changes we delegate getting the value of the select control
28058      // to the `readValue` method, which can be changed if the select can have multiple
28059      // selected values or if the options are being generated by `ngOptions`
28060      element.on('change', function() {
28061        scope.$apply(function() {
28062          ngModelCtrl.$setViewValue(selectCtrl.readValue());
28063        });
28064      });
28065
28066      // If the select allows multiple values then we need to modify how we read and write
28067      // values from and to the control; also what it means for the value to be empty and
28068      // we have to add an extra watch since ngModel doesn't work well with arrays - it
28069      // doesn't trigger rendering if only an item in the array changes.
28070      if (attr.multiple) {
28071
28072        // Read value now needs to check each option to see if it is selected
28073        selectCtrl.readValue = function readMultipleValue() {
28074          var array = [];
28075          forEach(element.find('option'), function(option) {
28076            if (option.selected) {
28077              array.push(option.value);
28078            }
28079          });
28080          return array;
28081        };
28082
28083        // Write value now needs to set the selected property of each matching option
28084        selectCtrl.writeValue = function writeMultipleValue(value) {
28085          var items = new HashMap(value);
28086          forEach(element.find('option'), function(option) {
28087            option.selected = isDefined(items.get(option.value));
28088          });
28089        };
28090
28091        // we have to do it on each watch since ngModel watches reference, but
28092        // we need to work of an array, so we need to see if anything was inserted/removed
28093        var lastView, lastViewRef = NaN;
28094        scope.$watch(function selectMultipleWatch() {
28095          if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
28096            lastView = shallowCopy(ngModelCtrl.$viewValue);
28097            ngModelCtrl.$render();
28098          }
28099          lastViewRef = ngModelCtrl.$viewValue;
28100        });
28101
28102        // If we are a multiple select then value is now a collection
28103        // so the meaning of $isEmpty changes
28104        ngModelCtrl.$isEmpty = function(value) {
28105          return !value || value.length === 0;
28106        };
28107
28108      }
28109    }
28110  };
28111};
28112
28113
28114// The option directive is purely designed to communicate the existence (or lack of)
28115// of dynamically created (and destroyed) option elements to their containing select
28116// directive via its controller.
28117var optionDirective = ['$interpolate', function($interpolate) {
28118
28119  function chromeHack(optionElement) {
28120    // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28121    // Adding an <option selected="selected"> element to a <select required="required"> should
28122    // automatically select the new element
28123    if (optionElement[0].hasAttribute('selected')) {
28124      optionElement[0].selected = true;
28125    }
28126  }
28127
28128  return {
28129    restrict: 'E',
28130    priority: 100,
28131    compile: function(element, attr) {
28132
28133      // If the value attribute is not defined then we fall back to the
28134      // text content of the option element, which may be interpolated
28135      if (isUndefined(attr.value)) {
28136        var interpolateFn = $interpolate(element.text(), true);
28137        if (!interpolateFn) {
28138          attr.$set('value', element.text());
28139        }
28140      }
28141
28142      return function(scope, element, attr) {
28143
28144        // This is an optimization over using ^^ since we don't want to have to search
28145        // all the way to the root of the DOM for every single option element
28146        var selectCtrlName = '$selectController',
28147            parent = element.parent(),
28148            selectCtrl = parent.data(selectCtrlName) ||
28149              parent.parent().data(selectCtrlName); // in case we are in optgroup
28150
28151        // Only update trigger option updates if this is an option within a `select`
28152        // that also has `ngModel` attached
28153        if (selectCtrl && selectCtrl.ngModelCtrl) {
28154
28155          if (interpolateFn) {
28156            scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
28157              attr.$set('value', newVal);
28158              if (oldVal !== newVal) {
28159                selectCtrl.removeOption(oldVal);
28160              }
28161              selectCtrl.addOption(newVal, element);
28162              selectCtrl.ngModelCtrl.$render();
28163              chromeHack(element);
28164            });
28165          } else {
28166            selectCtrl.addOption(attr.value, element);
28167            selectCtrl.ngModelCtrl.$render();
28168            chromeHack(element);
28169          }
28170
28171          element.on('$destroy', function() {
28172            selectCtrl.removeOption(attr.value);
28173            selectCtrl.ngModelCtrl.$render();
28174          });
28175        }
28176      };
28177    }
28178  };
28179}];
28180
28181var styleDirective = valueFn({
28182  restrict: 'E',
28183  terminal: false
28184});
28185
28186var requiredDirective = function() {
28187  return {
28188    restrict: 'A',
28189    require: '?ngModel',
28190    link: function(scope, elm, attr, ctrl) {
28191      if (!ctrl) return;
28192      attr.required = true; // force truthy in case we are on non input element
28193
28194      ctrl.$validators.required = function(modelValue, viewValue) {
28195        return !attr.required || !ctrl.$isEmpty(viewValue);
28196      };
28197
28198      attr.$observe('required', function() {
28199        ctrl.$validate();
28200      });
28201    }
28202  };
28203};
28204
28205
28206var patternDirective = function() {
28207  return {
28208    restrict: 'A',
28209    require: '?ngModel',
28210    link: function(scope, elm, attr, ctrl) {
28211      if (!ctrl) return;
28212
28213      var regexp, patternExp = attr.ngPattern || attr.pattern;
28214      attr.$observe('pattern', function(regex) {
28215        if (isString(regex) && regex.length > 0) {
28216          regex = new RegExp('^' + regex + '$');
28217        }
28218
28219        if (regex && !regex.test) {
28220          throw minErr('ngPattern')('noregexp',
28221            'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
28222            regex, startingTag(elm));
28223        }
28224
28225        regexp = regex || undefined;
28226        ctrl.$validate();
28227      });
28228
28229      ctrl.$validators.pattern = function(value) {
28230        return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
28231      };
28232    }
28233  };
28234};
28235
28236
28237var maxlengthDirective = function() {
28238  return {
28239    restrict: 'A',
28240    require: '?ngModel',
28241    link: function(scope, elm, attr, ctrl) {
28242      if (!ctrl) return;
28243
28244      var maxlength = -1;
28245      attr.$observe('maxlength', function(value) {
28246        var intVal = toInt(value);
28247        maxlength = isNaN(intVal) ? -1 : intVal;
28248        ctrl.$validate();
28249      });
28250      ctrl.$validators.maxlength = function(modelValue, viewValue) {
28251        return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
28252      };
28253    }
28254  };
28255};
28256
28257var minlengthDirective = function() {
28258  return {
28259    restrict: 'A',
28260    require: '?ngModel',
28261    link: function(scope, elm, attr, ctrl) {
28262      if (!ctrl) return;
28263
28264      var minlength = 0;
28265      attr.$observe('minlength', function(value) {
28266        minlength = toInt(value) || 0;
28267        ctrl.$validate();
28268      });
28269      ctrl.$validators.minlength = function(modelValue, viewValue) {
28270        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
28271      };
28272    }
28273  };
28274};
28275
28276  if (window.angular.bootstrap) {
28277    //AngularJS is already loaded, so we can return here...
28278    console.log('WARNING: Tried to load angular more than once.');
28279    return;
28280  }
28281
28282  //try to bind to jquery now so that one can write jqLite(document).ready()
28283  //but we will rebind on bootstrap again.
28284  bindJQuery();
28285
28286  publishExternalAPI(angular);
28287
28288  jqLite(document).ready(function() {
28289    angularInit(document, bootstrap);
28290  });
28291
28292})(window, document);
28293
28294!window.angular.$$csp() && window.angular.element(document).find('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.