PageSourceSearch

https://www.rittal.com/us_en/apps/drawing/min/js/production.js?ver=1.10

js rittal.com collected 2026-10-09 22:40:04 UTC 1,349,942 bytes, 36,564 lines download raw bytes

1/**
2 * @license AngularJS v1.4.9
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.9/' +
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
271  // `null`, `undefined` and `window` are not array-like
272  if (obj == null || isWindow(obj)) return false;
273
274  // arrays, strings and jQuery/jqLite objects are array like
275  // * jqLite is either the jQuery or jqLite constructor function
276  // * we have to check the existance of jqLite first as this method is called
277  //   via the forEach method when constructing the jqLite object in the first place
278  if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
279
280  // Support: iOS 8.2 (not reproducible in simulator)
281  // "length" in obj used to prevent JIT error (gh-11508)
282  var length = "length" in Object(obj) && obj.length;
283
284  // NodeList objects (with `item` method) and
285  // other objects with suitable length characteristics are array-like
286  return isNumber(length) &&
287    (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
288
289}
290
291/**
292 * @ngdoc function
293 * @name angular.forEach
294 * @module ng
295 * @kind function
296 *
297 * @description
298 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
299 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
300 * is the value of an object property or an array element, `key` is the object property key or
301 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
302 *
303 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
304 * using the `hasOwnProperty` method.
305 *
306 * Unlike ES262's
307 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
308 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
309 * return the value provided.
310 *
311   ```js
312     var values = {name: 'misko', gender: 'male'};
313     var log = [];
314     angular.forEach(values, function(value, key) {
315       this.push(key + ': ' + value);
316     }, log);
317     expect(log).toEqual(['name: misko', 'gender: male']);
318   ```
319 *
320 * @param {Object|Array} obj Object to iterate over.
321 * @param {Function} iterator Iterator function.
322 * @param {Object=} context Object to become context (`this`) for the iterator function.
323 * @returns {Object|Array} Reference to `obj`.
324 */
325
326function forEach(obj, iterator, context) {
327  var key, length;
328  if (obj) {
329    if (isFunction(obj)) {
330      for (key in obj) {
331        // Need to check if hasOwnProperty exists,
332        // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
333        if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
334          iterator.call(context, obj[key], key, obj);
335        }
336      }
337    } else if (isArray(obj) || isArrayLike(obj)) {
338      var isPrimitive = typeof obj !== 'object';
339      for (key = 0, length = obj.length; key < length; key++) {
340        if (isPrimitive || key in obj) {
341          iterator.call(context, obj[key], key, obj);
342        }
343      }
344    }
vendor: 11,524 bytes, lines 344-824
344 else if (obj.forEach && obj.forEach !== forEach) {
345        obj.forEach(iterator, context, obj);
346    } else if (isBlankObject(obj)) {
347      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
348      for (key in obj) {
349        iterator.call(context, obj[key], key, obj);
350      }
351    } else if (typeof obj.hasOwnProperty === 'function') {
352      // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
353      for (key in obj) {
354        if (obj.hasOwnProperty(key)) {
355          iterator.call(context, obj[key], key, obj);
356        }
357      }
358    } else {
359      // Slow path for objects which do not have a method `hasOwnProperty`
360      for (key in obj) {
361        if (hasOwnProperty.call(obj, key)) {
362          iterator.call(context, obj[key], key, obj);
363        }
364      }
365    }
366  }
367  return obj;
368}
369
370function forEachSorted(obj, iterator, context) {
371  var keys = Object.keys(obj).sort();
372  for (var i = 0; i < keys.length; i++) {
373    iterator.call(context, obj[keys[i]], keys[i]);
374  }
375  return keys;
376}
377
378
379/**
380 * when using forEach the params are value, key, but it is often useful to have key, value.
381 * @param {function(string, *)} iteratorFn
382 * @returns {function(*, string)}
383 */
384function reverseParams(iteratorFn) {
385  return function(value, key) { iteratorFn(key, value); };
386}
387
388/**
389 * A consistent way of creating unique IDs in angular.
390 *
391 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
392 * we hit number precision issues in JavaScript.
393 *
394 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
395 *
396 * @returns {number} an unique alpha-numeric string
397 */
398function nextUid() {
399  return ++uid;
400}
401
402
403/**
404 * Set or clear the hashkey for an object.
405 * @param obj object
406 * @param h the hashkey (!truthy to delete the hashkey)
407 */
408function setHashKey(obj, h) {
409  if (h) {
410    obj.$$hashKey = h;
411  } else {
412    delete obj.$$hashKey;
413  }
414}
415
416
417function baseExtend(dst, objs, deep) {
418  var h = dst.$$hashKey;
419
420  for (var i = 0, ii = objs.length; i < ii; ++i) {
421    var obj = objs[i];
422    if (!isObject(obj) && !isFunction(obj)) continue;
423    var keys = Object.keys(obj);
424    for (var j = 0, jj = keys.length; j < jj; j++) {
425      var key = keys[j];
426      var src = obj[key];
427
428      if (deep && isObject(src)) {
429        if (isDate(src)) {
430          dst[key] = new Date(src.valueOf());
431        } else if (isRegExp(src)) {
432          dst[key] = new RegExp(src);
433        } else if (src.nodeName) {
434          dst[key] = src.cloneNode(true);
435        } else if (isElement(src)) {
436          dst[key] = src.clone();
437        } else {
438          if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
439          baseExtend(dst[key], [src], true);
440        }
441      } else {
442        dst[key] = src;
443      }
444    }
445  }
446
447  setHashKey(dst, h);
448  return dst;
449}
450
451/**
452 * @ngdoc function
453 * @name angular.extend
454 * @module ng
455 * @kind function
456 *
457 * @description
458 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
459 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
460 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
461 *
462 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
463 * {@link angular.merge} for this.
464 *
465 * @param {Object} dst Destination object.
466 * @param {...Object} src Source object(s).
467 * @returns {Object} Reference to `dst`.
468 */
469function extend(dst) {
470  return baseExtend(dst, slice.call(arguments, 1), false);
471}
472
473
474/**
475* @ngdoc function
476* @name angular.merge
477* @module ng
478* @kind function
479*
480* @description
481* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
482* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
483* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
484*
485* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
486* objects, performing a deep copy.
487*
488* @param {Object} dst Destination object.
489* @param {...Object} src Source object(s).
490* @returns {Object} Reference to `dst`.
491*/
492function merge(dst) {
493  return baseExtend(dst, slice.call(arguments, 1), true);
494}
495
496
497
498function toInt(str) {
499  return parseInt(str, 10);
500}
501
502
503function inherit(parent, extra) {
504  return extend(Object.create(parent), extra);
505}
506
507/**
508 * @ngdoc function
509 * @name angular.noop
510 * @module ng
511 * @kind function
512 *
513 * @description
514 * A function that performs no operations. This function can be useful when writing code in the
515 * functional style.
516   ```js
517     function foo(callback) {
518       var result = calculateResult();
519       (callback || angular.noop)(result);
520     }
521   ```
522 */
523function noop() {}
524noop.$inject = [];
525
526
527/**
528 * @ngdoc function
529 * @name angular.identity
530 * @module ng
531 * @kind function
532 *
533 * @description
534 * A function that returns its first argument. This function is useful when writing code in the
535 * functional style.
536 *
537   ```js
538     function transformer(transformationFn, value) {
539       return (transformationFn || angular.identity)(value);
540     };
541   ```
542  * @param {*} value to be returned.
543  * @returns {*} the value passed in.
544 */
545function identity($) {return $;}
546identity.$inject = [];
547
548
549function valueFn(value) {return function() {return value;};}
550
551function hasCustomToString(obj) {
552  return isFunction(obj.toString) && obj.toString !== toString;
553}
554
555
556/**
557 * @ngdoc function
558 * @name angular.isUndefined
559 * @module ng
560 * @kind function
561 *
562 * @description
563 * Determines if a reference is undefined.
564 *
565 * @param {*} value Reference to check.
566 * @returns {boolean} True if `value` is undefined.
567 */
568function isUndefined(value) {return typeof value === 'undefined';}
569
570
571/**
572 * @ngdoc function
573 * @name angular.isDefined
574 * @module ng
575 * @kind function
576 *
577 * @description
578 * Determines if a reference is defined.
579 *
580 * @param {*} value Reference to check.
581 * @returns {boolean} True if `value` is defined.
582 */
583function isDefined(value) {return typeof value !== 'undefined';}
584
585
586/**
587 * @ngdoc function
588 * @name angular.isObject
589 * @module ng
590 * @kind function
591 *
592 * @description
593 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
594 * considered to be objects. Note that JavaScript arrays are objects.
595 *
596 * @param {*} value Reference to check.
597 * @returns {boolean} True if `value` is an `Object` but not `null`.
598 */
599function isObject(value) {
600  // http://jsperf.com/isobject4
601  return value !== null && typeof value === 'object';
602}
603
604
605/**
606 * Determine if a value is an object with a null prototype
607 *
608 * @returns {boolean} True if `value` is an `Object` with a null prototype
609 */
610function isBlankObject(value) {
611  return value !== null && typeof value === 'object' && !getPrototypeOf(value);
612}
613
614
615/**
616 * @ngdoc function
617 * @name angular.isString
618 * @module ng
619 * @kind function
620 *
621 * @description
622 * Determines if a reference is a `String`.
623 *
624 * @param {*} value Reference to check.
625 * @returns {boolean} True if `value` is a `String`.
626 */
627function isString(value) {return typeof value === 'string';}
628
629
630/**
631 * @ngdoc function
632 * @name angular.isNumber
633 * @module ng
634 * @kind function
635 *
636 * @description
637 * Determines if a reference is a `Number`.
638 *
639 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
640 *
641 * If you wish to exclude these then you can use the native
642 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
643 * method.
644 *
645 * @param {*} value Reference to check.
646 * @returns {boolean} True if `value` is a `Number`.
647 */
648function isNumber(value) {return typeof value === 'number';}
649
650
651/**
652 * @ngdoc function
653 * @name angular.isDate
654 * @module ng
655 * @kind function
656 *
657 * @description
658 * Determines if a value is a date.
659 *
660 * @param {*} value Reference to check.
661 * @returns {boolean} True if `value` is a `Date`.
662 */
663function isDate(value) {
664  return toString.call(value) === '[object Date]';
665}
666
667
668/**
669 * @ngdoc function
670 * @name angular.isArray
671 * @module ng
672 * @kind function
673 *
674 * @description
675 * Determines if a reference is an `Array`.
676 *
677 * @param {*} value Reference to check.
678 * @returns {boolean} True if `value` is an `Array`.
679 */
680var isArray = Array.isArray;
681
682/**
683 * @ngdoc function
684 * @name angular.isFunction
685 * @module ng
686 * @kind function
687 *
688 * @description
689 * Determines if a reference is a `Function`.
690 *
691 * @param {*} value Reference to check.
692 * @returns {boolean} True if `value` is a `Function`.
693 */
694function isFunction(value) {return typeof value === 'function';}
695
696
697/**
698 * Determines if a value is a regular expression object.
699 *
700 * @private
701 * @param {*} value Reference to check.
702 * @returns {boolean} True if `value` is a `RegExp`.
703 */
704function isRegExp(value) {
705  return toString.call(value) === '[object RegExp]';
706}
707
708
709/**
710 * Checks if `obj` is a window object.
711 *
712 * @private
713 * @param {*} obj Object to check
714 * @returns {boolean} True if `obj` is a window obj.
715 */
716function isWindow(obj) {
717  return obj && obj.window === obj;
718}
719
720
721function isScope(obj) {
722  return obj && obj.$evalAsync && obj.$watch;
723}
724
725
726function isFile(obj) {
727  return toString.call(obj) === '[object File]';
728}
729
730
731function isFormData(obj) {
732  return toString.call(obj) === '[object FormData]';
733}
734
735
736function isBlob(obj) {
737  return toString.call(obj) === '[object Blob]';
738}
739
740
741function isBoolean(value) {
742  return typeof value === 'boolean';
743}
744
745
746function isPromiseLike(obj) {
747  return obj && isFunction(obj.then);
748}
749
750
751var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
752function isTypedArray(value) {
753  return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
754}
755
756
757var trim = function(value) {
758  return isString(value) ? value.trim() : value;
759};
760
761// Copied from:
762// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
763// Prereq: s is a string.
764var escapeForRegexp = function(s) {
765  return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
766           replace(/\x08/g, '\\x08');
767};
768
769
770/**
771 * @ngdoc function
772 * @name angular.isElement
773 * @module ng
774 * @kind function
775 *
776 * @description
777 * Determines if a reference is a DOM element (or wrapped jQuery element).
778 *
779 * @param {*} value Reference to check.
780 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
781 */
782function isElement(node) {
783  return !!(node &&
784    (node.nodeName  // we are a direct element
785    || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
786}
787
788/**
789 * @param str 'key1,key2,...'
790 * @returns {object} in the form of {key1:true, key2:true, ...}
791 */
792function makeMap(str) {
793  var obj = {}, items = str.split(","), i;
794  for (i = 0; i < items.length; i++) {
795    obj[items[i]] = true;
796  }
797  return obj;
798}
799
800
801function nodeName_(element) {
802  return lowercase(element.nodeName || (element[0] && element[0].nodeName));
803}
804
805function includes(array, obj) {
806  return Array.prototype.indexOf.call(array, obj) != -1;
807}
808
809function arrayRemove(array, value) {
810  var index = array.indexOf(value);
811  if (index >= 0) {
812    array.splice(index, 1);
813  }
814  return index;
815}
816
817/**
818 * @ngdoc function
819 * @name angular.copy
820 * @module ng
821 * @kind function
822 *
823 * @description
824 * Creates a deep copy of `source`, which should be an object or an array.
825 *
826 * * If no destination is supplied, a copy of the object or array is created.
827 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
828 *   are deleted and then all elements/properties from the source are copied to it.
829 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
830 * * If `source` is identical to 'destination' an exception will be thrown.
831 *
832 * @param {*} source The source that will be used to make a copy.
833 *                   Can be any type, including primitives, `null`, and `undefined`.
834 * @param {(Object|Array)=} destination Destination into which the source is copied. If
835 *     provided, must be of the same type as `source`.
836 * @returns {*} The copy or updated `destination`, if `destination` was specified.
837 *
838 * @example
839 <example module="copyExample">
840 <file name="index.html">
841 <div ng-controller="ExampleController">
842 <form novalidate class="simple-form">
843 Name: <input type="text" ng-model="user.name" /><br />
844 E-mail: <input type="email" ng-model="user.email" /><br />
845 Gender: <input type="radio" ng-model="user.gender" value="male" />male
846 <input type="radio" ng-model="user.gender" value="female" />female<br />
847 <button ng-click="reset()">RESET</button>
848 <button ng-click="update(user)">SAVE</button>
849 </form>
850 <pre>form = {{user | json}}</pre>
851 <pre>master = {{master | json}}</pre>
852 </div>
853
854 <script>
855  angular.module('copyExample', [])
856    .controller('ExampleController', ['$scope', function($scope) {
857      $scope.master= {};
858
859      $scope.update = function(user) {
860        // Example with 1 argument
861        $scope.master= angular.copy(user);
862      };
863
864      $scope.reset = function() {
865        // Example with 2 arguments
866        angular.copy($scope.master, $scope.user);
867      };
868
869      $scope.reset();
870    }]);
871 </script>
872 </file>
873 </example>
874 */
875function copy(source, destination) {
876  var stackSource = [];
877  var stackDest = [];
878
879  if (destination) {
880    if (isTypedArray(destination)) {
881      throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
882    }
883    if (source === destination) {
884      throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
885    }
886
887    // Empty the destination object
888    if (isArray(destination)) {
889      destination.length = 0;
890    } else {
891      forEach(destination, function(value, key) {
892        if (key !== '$$hashKey') {
893          delete destination[key];
894        }
895      });
896    }
897
898    stackSource.push(source);
899    stackDest.push(destination);
900    return copyRecurse(source, destination);
901  }
902
903  return copyElement(source);
904
905  function copyRecurse(source, destination) {
906    var h = destination.$$hashKey;
907    var result, key;
908    if (isArray(source)) {
909      for (var i = 0, ii = source.length; i < ii; i++) {
910        destination.push(copyElement(source[i]));
911      }
912    } else if (isBlankObject(source)) {
913      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
914      for (key in source) {
915        destination[key] = copyElement(source[key]);
916      }
917    } else if (source && typeof source.hasOwnProperty === 'function') {
918      // Slow path, which must rely on hasOwnProperty
919      for (key in source) {
920        if (source.hasOwnProperty(key)) {
921          destination[key] = copyElement(source[key]);
922        }
923      }
924    } else {
925      // Slowest path --- hasOwnProperty can't be called as a method
926      for (key in source) {
927        if (hasOwnProperty.call(source, key)) {
928          destination[key] = copyElement(source[key]);
929        }
930      }
931    }
932    setHashKey(destination, h);
933    return destination;
934  }
935
936  function copyElement(source) {
937    // Simple values
938    if (!isObject(source)) {
939      return source;
940    }
941
942    // Already copied values
943    var index = stackSource.indexOf(source);
944    if (index !== -1) {
945      return stackDest[index];
946    }
947
948    if (isWindow(source) || isScope(source)) {
949      throw ngMinErr('cpws',
950        "Can't copy! Making copies of Window or Scope instances is not supported.");
951    }
952
953    var needsRecurse = false;
954    var destination;
955
956    if (isArray(source)) {
957      destination = [];
958      needsRecurse = true;
959    } else if (isTypedArray(source)) {
960      destination = new source.constructor(source);
961    } else if (isDate(source)) {
962      destination = new Date(source.getTime());
963    } else if (isRegExp(source)) {
964      destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
965      destination.lastIndex = source.lastIndex;
966    } else if (isFunction(source.cloneNode)) {
967        destination = source.cloneNode(true);
968    } else {
969      destination = Object.create(getPrototypeOf(source));
970      needsRecurse = true;
971    }
972
973    stackSource.push(source);
974    stackDest.push(destination);
975
976    return needsRecurse
977      ? copyRecurse(source, destination)
978      : destination;
979  }
980}
981
982/**
983 * Creates a shallow copy of an object, an array or a primitive.
984 *
985 * Assumes that there are no proto properties for objects.
986 */
987function shallowCopy(src, dst) {
988  if (isArray(src)) {
989    dst = dst || [];
990
991    for (var i = 0, ii = src.length; i < ii; i++) {
992      dst[i] = src[i];
993    }
994  } else if (isObject(src)) {
995    dst = dst || {};
996
997    for (var key in src) {
998      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
999        dst[key] = src[key];
1000      }
1001    }
1002  }
1003
1004  return dst || src;
1005}
1006
1007
1008/**
1009 * @ngdoc function
1010 * @name angular.equals
1011 * @module ng
1012 * @kind function
1013 *
1014 * @description
1015 * Determines if two objects or two values are equivalent. Supports value types, regular
1016 * expressions, arrays and objects.
1017 *
1018 * Two objects or values are considered equivalent if at least one of the following is true:
1019 *
1020 * * Both objects or values pass `===` comparison.
1021 * * Both objects or values are of the same type and all of their properties are equal by
1022 *   comparing them with `angular.equals`.
1023 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1024 * * Both values represent the same regular expression (In JavaScript,
1025 *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1026 *   representation matches).
1027 *
1028 * During a property comparison, properties of `function` type and properties with names
1029 * that begin with `$` are ignored.
1030 *
1031 * Scope and DOMWindow objects are being compared only by identify (`===`).
1032 *
1033 * @param {*} o1 Object or value to compare.
1034 * @param {*} o2 Object or value to compare.
1035 * @returns {boolean} True if arguments are equal.
1036 */
1037function equals(o1, o2) {
1038  if (o1 === o2) return true;
1039  if (o1 === null || o2 === null) return false;
1040  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1041  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1042  if (t1 == t2) {
1043    if (t1 == 'object') {
1044      if (isArray(o1)) {
1045        if (!isArray(o2)) return false;
1046        if ((length = o1.length) == o2.length) {
1047          for (key = 0; key < length; key++) {
1048            if (!equals(o1[key], o2[key])) return false;
1049          }
1050          return true;
1051        }
1052      } else if (isDate(o1)) {
1053        if (!isDate(o2)) return false;
1054        return equals(o1.getTime(), o2.getTime());
1055      } else if (isRegExp(o1)) {
1056        return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1057      } else {
1058        if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1059          isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1060        keySet = createMap();
1061        for (key in o1) {
1062          if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1063          if (!equals(o1[key], o2[key])) return false;
1064          keySet[key] = true;
1065        }
1066        for (key in o2) {
1067          if (!(key in keySet) &&
1068              key.charAt(0) !== '$' &&
1069              isDefined(o2[key]) &&
1070              !isFunction(o2[key])) return false;
1071        }
1072        return true;
1073      }
1074    }
1075  }
1076  return false;
1077}
1078
1079var csp = function() {
1080  if (!isDefined(csp.rules)) {
1081
1082
1083    var ngCspElement = (document.querySelector('[ng-csp]') ||
1084                    document.querySelector('[data-ng-csp]'));
1085
1086    if (ngCspElement) {
1087      var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1088                    ngCspElement.getAttribute('data-ng-csp');
1089      csp.rules = {
1090        noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1091        noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1092      };
1093    } else {
1094      csp.rules = {
1095        noUnsafeEval: noUnsafeEval(),
1096        noInlineStyle: false
1097      };
1098    }
1099  }
1100
1101  return csp.rules;
1102
1103  function noUnsafeEval() {
1104    try {
1105      /* jshint -W031, -W054 */
1106      new Function('');
1107      /* jshint +W031, +W054 */
1108      return false;
vendor: 10,174 bytes, lines 1109-1450
1109    } catch (e) {
1110      return true;
1111    }
1112  }
1113};
1114
1115/**
1116 * @ngdoc directive
1117 * @module ng
1118 * @name ngJq
1119 *
1120 * @element ANY
1121 * @param {string=} ngJq the name of the library available under `window`
1122 * to be used for angular.element
1123 * @description
1124 * Use this directive to force the angular.element library.  This should be
1125 * used to force either jqLite by leaving ng-jq blank or setting the name of
1126 * the jquery variable under window (eg. jQuery).
1127 *
1128 * Since angular looks for this directive when it is loaded (doesn't wait for the
1129 * DOMContentLoaded event), it must be placed on an element that comes before the script
1130 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1131 * others ignored.
1132 *
1133 * @example
1134 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1135 ```html
1136 <!doctype html>
1137 <html ng-app ng-jq>
1138 ...
1139 ...
1140 </html>
1141 ```
1142 * @example
1143 * This example shows how to use a jQuery based library of a different name.
1144 * The library name must be available at the top most 'window'.
1145 ```html
1146 <!doctype html>
1147 <html ng-app ng-jq="jQueryLib">
1148 ...
1149 ...
1150 </html>
1151 ```
1152 */
1153var jq = function() {
1154  if (isDefined(jq.name_)) return jq.name_;
1155  var el;
1156  var i, ii = ngAttrPrefixes.length, prefix, name;
1157  for (i = 0; i < ii; ++i) {
1158    prefix = ngAttrPrefixes[i];
1159    if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1160      name = el.getAttribute(prefix + 'jq');
1161      break;
1162    }
1163  }
1164
1165  return (jq.name_ = name);
1166};
1167
1168function concat(array1, array2, index) {
1169  return array1.concat(slice.call(array2, index));
1170}
1171
1172function sliceArgs(args, startIndex) {
1173  return slice.call(args, startIndex || 0);
1174}
1175
1176
1177/* jshint -W101 */
1178/**
1179 * @ngdoc function
1180 * @name angular.bind
1181 * @module ng
1182 * @kind function
1183 *
1184 * @description
1185 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1186 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1187 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1188 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1189 *
1190 * @param {Object} self Context which `fn` should be evaluated in.
1191 * @param {function()} fn Function to be bound.
1192 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1193 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1194 */
1195/* jshint +W101 */
1196function bind(self, fn) {
1197  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1198  if (isFunction(fn) && !(fn instanceof RegExp)) {
1199    return curryArgs.length
1200      ? function() {
1201          return arguments.length
1202            ? fn.apply(self, concat(curryArgs, arguments, 0))
1203            : fn.apply(self, curryArgs);
1204        }
1205      : function() {
1206          return arguments.length
1207            ? fn.apply(self, arguments)
1208            : fn.call(self);
1209        };
1210  } else {
1211    // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1212    return fn;
1213  }
1214}
1215
1216
1217function toJsonReplacer(key, value) {
1218  var val = value;
1219
1220  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1221    val = undefined;
1222  } else if (isWindow(value)) {
1223    val = '$WINDOW';
1224  } else if (value &&  document === value) {
1225    val = '$DOCUMENT';
1226  } else if (isScope(value)) {
1227    val = '$SCOPE';
1228  }
1229
1230  return val;
1231}
1232
1233
1234/**
1235 * @ngdoc function
1236 * @name angular.toJson
1237 * @module ng
1238 * @kind function
1239 *
1240 * @description
1241 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1242 * stripped since angular uses this notation internally.
1243 *
1244 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1245 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1246 *    If set to an integer, the JSON output will contain that many spaces per indentation.
1247 * @returns {string|undefined} JSON-ified string representing `obj`.
1248 */
1249function toJson(obj, pretty) {
1250  if (typeof obj === 'undefined') return undefined;
1251  if (!isNumber(pretty)) {
1252    pretty = pretty ? 2 : null;
1253  }
1254  return JSON.stringify(obj, toJsonReplacer, pretty);
1255}
1256
1257
1258/**
1259 * @ngdoc function
1260 * @name angular.fromJson
1261 * @module ng
1262 * @kind function
1263 *
1264 * @description
1265 * Deserializes a JSON string.
1266 *
1267 * @param {string} json JSON string to deserialize.
1268 * @returns {Object|Array|string|number} Deserialized JSON string.
1269 */
1270function fromJson(json) {
1271  return isString(json)
1272      ? JSON.parse(json)
1273      : json;
1274}
1275
1276
1277function timezoneToOffset(timezone, fallback) {
1278  var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1279  return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1280}
1281
1282
1283function addDateMinutes(date, minutes) {
1284  date = new Date(date.getTime());
1285  date.setMinutes(date.getMinutes() + minutes);
1286  return date;
1287}
1288
1289
1290function convertTimezoneToLocal(date, timezone, reverse) {
1291  reverse = reverse ? -1 : 1;
1292  var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1293  return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1294}
1295
1296
1297/**
1298 * @returns {string} Returns the string representation of the element.
1299 */
1300function startingTag(element) {
1301  element = jqLite(element).clone();
1302  try {
1303    // turns out IE does not let you set .html() on elements which
1304    // are not allowed to have children. So we just ignore it.
1305    element.empty();
1306  } catch (e) {}
1307  var elemHtml = jqLite('<div>').append(element).html();
1308  try {
1309    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1310        elemHtml.
1311          match(/^(<[^>]+>)/)[1].
1312          replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1313  } catch (e) {
1314    return lowercase(elemHtml);
1315  }
1316
1317}
1318
1319
1320/////////////////////////////////////////////////
1321
1322/**
1323 * Tries to decode the URI component without throwing an exception.
1324 *
1325 * @private
1326 * @param str value potential URI component to check.
1327 * @returns {boolean} True if `value` can be decoded
1328 * with the decodeURIComponent function.
1329 */
1330function tryDecodeURIComponent(value) {
1331  try {
1332    return decodeURIComponent(value);
1333  } catch (e) {
1334    // Ignore any invalid uri component
1335  }
1336}
1337
1338
1339/**
1340 * Parses an escaped url query string into key-value pairs.
1341 * @returns {Object.<string,boolean|Array>}
1342 */
1343function parseKeyValue(/**string*/keyValue) {
1344  var obj = {};
1345  forEach((keyValue || "").split('&'), function(keyValue) {
1346    var splitPoint, key, val;
1347    if (keyValue) {
1348      key = keyValue = keyValue.replace(/\+/g,'%20');
1349      splitPoint = keyValue.indexOf('=');
1350      if (splitPoint !== -1) {
1351        key = keyValue.substring(0, splitPoint);
1352        val = keyValue.substring(splitPoint + 1);
1353      }
1354      key = tryDecodeURIComponent(key);
1355      if (isDefined(key)) {
1356        val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1357        if (!hasOwnProperty.call(obj, key)) {
1358          obj[key] = val;
1359        } else if (isArray(obj[key])) {
1360          obj[key].push(val);
1361        } else {
1362          obj[key] = [obj[key],val];
1363        }
1364      }
1365    }
1366  });
1367  return obj;
1368}
1369
1370function toKeyValue(obj) {
1371  var parts = [];
1372  forEach(obj, function(value, key) {
1373    if (isArray(value)) {
1374      forEach(value, function(arrayValue) {
1375        parts.push(encodeUriQuery(key, true) +
1376                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1377      });
1378    } else {
1379    parts.push(encodeUriQuery(key, true) +
1380               (value === true ? '' : '=' + encodeUriQuery(value, true)));
1381    }
1382  });
1383  return parts.length ? parts.join('&') : '';
1384}
1385
1386
1387/**
1388 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1389 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1390 * segments:
1391 *    segment       = *pchar
1392 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1393 *    pct-encoded   = "%" HEXDIG HEXDIG
1394 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1395 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1396 *                     / "*" / "+" / "," / ";" / "="
1397 */
1398function encodeUriSegment(val) {
1399  return encodeUriQuery(val, true).
1400             replace(/%26/gi, '&').
1401             replace(/%3D/gi, '=').
1402             replace(/%2B/gi, '+');
1403}
1404
1405
1406/**
1407 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1408 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1409 * encoded per http://tools.ietf.org/html/rfc3986:
1410 *    query       = *( pchar / "/" / "?" )
1411 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1412 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1413 *    pct-encoded   = "%" HEXDIG HEXDIG
1414 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1415 *                     / "*" / "+" / "," / ";" / "="
1416 */
1417function encodeUriQuery(val, pctEncodeSpaces) {
1418  return encodeURIComponent(val).
1419             replace(/%40/gi, '@').
1420             replace(/%3A/gi, ':').
1421             replace(/%24/g, '$').
1422             replace(/%2C/gi, ',').
1423             replace(/%3B/gi, ';').
1424             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1425}
1426
1427var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1428
1429function getNgAttribute(element, ngAttr) {
1430  var attr, i, ii = ngAttrPrefixes.length;
1431  for (i = 0; i < ii; ++i) {
1432    attr = ngAttrPrefixes[i] + ngAttr;
1433    if (isString(attr = element.getAttribute(attr))) {
1434      return attr;
1435    }
1436  }
1437  return null;
1438}
1439
1440/**
1441 * @ngdoc directive
1442 * @name ngApp
1443 * @module ng
1444 *
1445 * @element ANY
1446 * @param {angular.Module} ngApp an optional application
1447 *   {@link angular.module module} name to load.
1448 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1449 *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1450 *   do not use explicit function annotation (and are thus unsuitable for minification), as de
1450scribed
1451 *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1452 *   tracking down the root of these bugs.
1453 *
1454 * @description
1455 *
1456 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1457 * designates the **root element** of the application and is typically placed near the root element
1458 * of the page - e.g. on the `<body>` or `<html>` tags.
1459 *
1460 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1461 * found in the document will be used to define the root element to auto-bootstrap as an
1462 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1463 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1464 *
1465 * You can specify an **AngularJS module** to be used as the root module for the application.  This
1466 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1467 * should contain the application code needed or have dependencies on other modules that will
1468 * contain the code. See {@link angular.module} for more information.
1469 *
1470 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1471 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1472 * would not be resolved to `3`.
1473 *
1474 * `ngApp` is the easiest, and most common way to bootstrap an application.
1475 *
1476 <example module="ngAppDemo">
1477   <file name="index.html">
1478   <div ng-controller="ngAppDemoController">
1479     I can add: {{a}} + {{b}} =  {{ a+b }}
1480   </div>
1481   </file>
1482   <file name="script.js">
1483   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1484     $scope.a = 1;
1485     $scope.b = 2;
1486   });
1487   </file>
1488 </example>
1489 *
1490 * Using `ngStrictDi`, you would see something like this:
1491 *
1492 <example ng-app-included="true">
1493   <file name="index.html">
1494   <div ng-app="ngAppStrictDemo" ng-strict-di>
1495       <div ng-controller="GoodController1">
1496           I can add: {{a}} + {{b}} =  {{ a+b }}
1497
1498           <p>This renders because the controller does not fail to
1499              instantiate, by using explicit annotation style (see
1500              script.js for details)
1501           </p>
1502       </div>
1503
1504       <div ng-controller="GoodController2">
1505           Name: <input ng-model="name"><br />
1506           Hello, {{name}}!
1507
1508           <p>This renders because the controller does not fail to
1509              instantiate, by using explicit annotation style
1510              (see script.js for details)
1511           </p>
1512       </div>
1513
1514       <div ng-controller="BadController">
1515           I can add: {{a}} + {{b}} =  {{ a+b }}
1516
1517           <p>The controller could not be instantiated, due to relying
1518              on automatic function annotations (which are disabled in
1519              strict mode). As such, the content of this section is not
1520              interpolated, and there should be an error in your web console.
1521           </p>
1522       </div>
1523   </div>
1524   </file>
1525   <file name="script.js">
1526   angular.module('ngAppStrictDemo', [])
1527     // BadController will fail to instantiate, due to relying on automatic function annotation,
1528     // rather than an explicit annotation
1529     .controller('BadController', function($scope) {
1530       $scope.a = 1;
1531       $scope.b = 2;
1532     })
1533     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1534     // due to using explicit annotations using the array style and $inject property, respectively.
1535     .controller('GoodController1', ['$scope', function($scope) {
1536       $scope.a = 1;
1537       $scope.b = 2;
1538     }])
1539     .controller('GoodController2', GoodController2);
1540     function GoodController2($scope) {
1541       $scope.name = "World";
1542     }
1543     GoodController2.$inject = ['$scope'];
1544   </file>
1545   <file name="style.css">
1546   div[ng-controller] {
1547       margin-bottom: 1em;
1548       -webkit-border-radius: 4px;
1549       border-radius: 4px;
1550       border: 1px solid;
1551       padding: .5em;
1552   }
1553   div[ng-controller^=Good] {
1554       border-color: #d6e9c6;
1555       background-color: #dff0d8;
1556       color: #3c763d;
1557   }
1558   div[ng-controller^=Bad] {
1559       border-color: #ebccd1;
1560       background-color: #f2dede;
1561       color: #a94442;
1562       margin-bottom: 0;
1563   }
1564   </file>
1565 </example>
1566 */
1567function angularInit(element, bootstrap) {
1568  var appElement,
1569      module,
1570      config = {};
1571
1572  // The element `element` has priority over any other element
1573  forEach(ngAttrPrefixes, function(prefix) {
1574    var name = prefix + 'app';
1575
1576    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1577      appElement = element;
1578      module = element.getAttribute(name);
1579    }
1580  });
1581  forEach(ngAttrPrefixes, function(prefix) {
1582    var name = prefix + 'app';
1583    var candidate;
1584
1585    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1586      appElement = candidate;
1587      module = candidate.getAttribute(name);
1588    }
1589  });
1590  if (appElement) {
1591    config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1592    bootstrap(appElement, module ? [module] : [], config);
1593  }
1594}
1595
1596/**
1597 * @ngdoc function
1598 * @name angular.bootstrap
1599 * @module ng
1600 * @description
1601 * Use this function to manually start up angular application.
1602 *
1603 * See: {@link guide/bootstrap Bootstrap}
1604 *
1605 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1606 * They must use {@link ng.directive:ngApp ngApp}.
1607 *
1608 * Angular will detect if it has been loaded into the browser more than once and only allow the
1609 * first loaded script to be bootstrapped and will report a warning to the browser console for
1610 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1611 * multiple instances of Angular try to work on the DOM.
1612 *
1613 * ```html
1614 * <!doctype html>
1615 * <html>
1616 * <body>
1617 * <div ng-controller="WelcomeController">
1618 *   {{greeting}}
1619 * </div>
1620 *
1621 * <script src="angular.js"></script>
1622 * <script>
1623 *   var app = angular.module('demo', [])
1624 *   .controller('WelcomeController', function($scope) {
1625 *       $scope.greeting = 'Welcome!';
1626 *   });
1627 *   angular.bootstrap(document, ['demo']);
1628 * </script>
1629 * </body>
1630 * </html>
1631 * ```
1632 *
1633 * @param {DOMElement} element DOM element which is the root of angular application.
1634 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1635 *     Each item in the array should be the name of a predefined module or a (DI annotated)
1636 *     function that will be invoked by the injector as a `config` block.
1637 *     See: {@link angular.module modules}
1638 * @param {Object=} config an object for defining configuration options for the application. The
1639 *     following keys are supported:
1640 *
1641 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1642 *   assist in finding bugs which break minified code. Defaults to `false`.
1643 *
1644 * @returns {auto.$injector} Returns the newly created injector for this app.
1645 */
1646function bootstrap(element, modules, config) {
1647  if (!isObject(config)) config = {};
1648  var defaultConfig = {
1649    strictDi: false
1650  };
1651  config = extend(defaultConfig, config);
1652  var doBootstrap = function() {
1653    element = jqLite(element);
1654
1655    if (element.injector()) {
1656      var tag = (element[0] === document) ? 'document' : startingTag(element);
1657      //Encode angle brackets to prevent input from being sanitized to empty string #8683
1658      throw ngMinErr(
1659          'btstrpd',
1660          "App Already Bootstrapped with this Element '{0}'",
1661          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1662    }
1663
1664    modules = modules || [];
1665    modules.unshift(['$provide', function($provide) {
1666      $provide.value('$rootElement', element);
1667    }]);
1668
1669    if (config.debugInfoEnabled) {
1670      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1671      modules.push(['$compileProvider', function($compileProvider) {
1672        $compileProvider.debugInfoEnabled(true);
1673      }]);
1674    }
1675
1676    modules.unshift('ng');
1677    var injector = createInjector(modules, config.strictDi);
1678    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1679       function bootstrapApply(scope, element, compile, injector) {
1680        scope.$apply(function() {
1681          element.data('$injector', injector);
1682          compile(element)(scope);
1683        });
1684      }]
1685    );
1686    return injector;
1687  };
1688
1689  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1690  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1691
1692  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1693    config.debugInfoEnabled = true;
1694    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1695  }
1696
1697  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1698    return doBootstrap();
1699  }
1700
1701  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1702  angular.resumeBootstrap = function(extraModules) {
1703    forEach(extraModules, function(module) {
1704      modules.push(module);
1705    });
1706    return doBootstrap();
1707  };
1708
1709  if (isFunction(angular.resumeDeferredBootstrap)) {
1710    angular.resumeDeferredBootstrap();
1711  }
1712}
1713
1714/**
1715 * @ngdoc function
1716 * @name angular.reloadWithDebugInfo
1717 * @module ng
1718 * @description
1719 * Use this function to reload the current application with debug information turned on.
1720 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1721 *
1722 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1723 */
1724function reloadWithDebugInfo() {
1725  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1726  window.location.reload();
1727}
1728
1729/**
1730 * @name angular.getTestability
1731 * @module ng
1732 * @description
1733 * Get the testability service for the instance of Angular on the given
1734 * element.
1735 * @param {DOMElement} element DOM element which is the root of angular application.
1736 */
1737function getTestability(rootElement) {
1738  var injector = angular.element(rootElement).injector();
1739  if (!injector) {
1740    throw ngMinErr('test',
1741      'no injector found for element argument to getTestability');
1742  }
1743  return injector.get('$$testability');
1744}
1745
1746var SNAKE_CASE_REGEXP = /[A-Z]/g;
1747function snake_case(name, separator) {
1748  separator = separator || '_';
1749  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1750    return (pos ? separator : '') + letter.toLowerCase();
1751  });
1752}
1753
1754var bindJQueryFired = false;
vendor: 9,107 bytes, lines 1755-2044
1755var skipDestroyOnNextJQueryCleanData;
1756function bindJQuery() {
1757  var originalCleanData;
1758
1759  if (bindJQueryFired) {
1760    return;
1761  }
1762
1763  // bind to jQuery if present;
1764  var jqName = jq();
1765  jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
1766           !jqName             ? undefined     :   // use jqLite
1767                                 window[jqName];   // use jQuery specified by `ngJq`
1768
1769  // Use jQuery if it exists with proper functionality, otherwise default to us.
1770  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1771  // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1772  // versions. It will not work for sure with jQuery <1.7, though.
1773  if (jQuery && jQuery.fn.on) {
1774    jqLite = jQuery;
1775    extend(jQuery.fn, {
1776      scope: JQLitePrototype.scope,
1777      isolateScope: JQLitePrototype.isolateScope,
1778      controller: JQLitePrototype.controller,
1779      injector: JQLitePrototype.injector,
1780      inheritedData: JQLitePrototype.inheritedData
1781    });
1782
1783    // All nodes removed from the DOM via various jQuery APIs like .remove()
1784    // are passed through jQuery.cleanData. Monkey-patch this method to fire
1785    // the $destroy event on all removed nodes.
1786    originalCleanData = jQuery.cleanData;
1787    jQuery.cleanData = function(elems) {
1788      var events;
1789      if (!skipDestroyOnNextJQueryCleanData) {
1790        for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1791          events = jQuery._data(elem, "events");
1792          if (events && events.$destroy) {
1793            jQuery(elem).triggerHandler('$destroy');
1794          }
1795        }
1796      } else {
1797        skipDestroyOnNextJQueryCleanData = false;
1798      }
1799      originalCleanData(elems);
1800    };
1801  } else {
1802    jqLite = JQLite;
1803  }
1804
1805  angular.element = jqLite;
1806
1807  // Prevent double-proxying.
1808  bindJQueryFired = true;
1809}
1810
1811/**
1812 * throw error if the argument is falsy.
1813 */
1814function assertArg(arg, name, reason) {
1815  if (!arg) {
1816    throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1817  }
1818  return arg;
1819}
1820
1821function assertArgFn(arg, name, acceptArrayAnnotation) {
1822  if (acceptArrayAnnotation && isArray(arg)) {
1823      arg = arg[arg.length - 1];
1824  }
1825
1826  assertArg(isFunction(arg), name, 'not a function, got ' +
1827      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1828  return arg;
1829}
1830
1831/**
1832 * throw error if the name given is hasOwnProperty
1833 * @param  {String} name    the name to test
1834 * @param  {String} context the context in which the name is used, such as module or directive
1835 */
1836function assertNotHasOwnProperty(name, context) {
1837  if (name === 'hasOwnProperty') {
1838    throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1839  }
1840}
1841
1842/**
1843 * Return the value accessible from the object by path. Any undefined traversals are ignored
1844 * @param {Object} obj starting object
1845 * @param {String} path path to traverse
1846 * @param {boolean} [bindFnToScope=true]
1847 * @returns {Object} value as accessible by path
1848 */
1849//TODO(misko): this function needs to be removed
1850function getter(obj, path, bindFnToScope) {
1851  if (!path) return obj;
1852  var keys = path.split('.');
1853  var key;
1854  var lastInstance = obj;
1855  var len = keys.length;
1856
1857  for (var i = 0; i < len; i++) {
1858    key = keys[i];
1859    if (obj) {
1860      obj = (lastInstance = obj)[key];
1861    }
1862  }
1863  if (!bindFnToScope && isFunction(obj)) {
1864    return bind(lastInstance, obj);
1865  }
1866  return obj;
1867}
1868
1869/**
1870 * Return the DOM siblings between the first and last node in the given array.
1871 * @param {Array} array like object
1872 * @returns {Array} the inputted object or a jqLite collection containing the nodes
1873 */
1874function getBlockNodes(nodes) {
1875  // TODO(perf): update `nodes` instead of creating a new object?
1876  var node = nodes[0];
1877  var endNode = nodes[nodes.length - 1];
1878  var blockNodes;
1879
1880  for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1881    if (blockNodes || nodes[i] !== node) {
1882      if (!blockNodes) {
1883        blockNodes = jqLite(slice.call(nodes, 0, i));
1884      }
1885      blockNodes.push(node);
1886    }
1887  }
1888
1889  return blockNodes || nodes;
1890}
1891
1892
1893/**
1894 * Creates a new object without a prototype. This object is useful for lookup without having to
1895 * guard against prototypically inherited properties via hasOwnProperty.
1896 *
1897 * Related micro-benchmarks:
1898 * - http://jsperf.com/object-create2
1899 * - http://jsperf.com/proto-map-lookup/2
1900 * - http://jsperf.com/for-in-vs-object-keys2
1901 *
1902 * @returns {Object}
1903 */
1904function createMap() {
1905  return Object.create(null);
1906}
1907
1908var NODE_TYPE_ELEMENT = 1;
1909var NODE_TYPE_ATTRIBUTE = 2;
1910var NODE_TYPE_TEXT = 3;
1911var NODE_TYPE_COMMENT = 8;
1912var NODE_TYPE_DOCUMENT = 9;
1913var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1914
1915/**
1916 * @ngdoc type
1917 * @name angular.Module
1918 * @module ng
1919 * @description
1920 *
1921 * Interface for configuring angular {@link angular.module modules}.
1922 */
1923
1924function setupModuleLoader(window) {
1925
1926  var $injectorMinErr = minErr('$injector');
1927  var ngMinErr = minErr('ng');
1928
1929  function ensure(obj, name, factory) {
1930    return obj[name] || (obj[name] = factory());
1931  }
1932
1933  var angular = ensure(window, 'angular', Object);
1934
1935  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1936  angular.$$minErr = angular.$$minErr || minErr;
1937
1938  return ensure(angular, 'module', function() {
1939    /** @type {Object.<string, angular.Module>} */
1940    var modules = {};
1941
1942    /**
1943     * @ngdoc function
1944     * @name angular.module
1945     * @module ng
1946     * @description
1947     *
1948     * The `angular.module` is a global place for creating, registering and retrieving Angular
1949     * modules.
1950     * All modules (angular core or 3rd party) that should be available to an application must be
1951     * registered using this mechanism.
1952     *
1953     * Passing one argument retrieves an existing {@link angular.Module},
1954     * whereas passing more than one argument creates a new {@link angular.Module}
1955     *
1956     *
1957     * # Module
1958     *
1959     * A module is a collection of services, directives, controllers, filters, and configuration information.
1960     * `angular.module` is used to configure the {@link auto.$injector $injector}.
1961     *
1962     * ```js
1963     * // Create a new module
1964     * var myModule = angular.module('myModule', []);
1965     *
1966     * // register a new service
1967     * myModule.value('appName', 'MyCoolApp');
1968     *
1969     * // configure existing services inside initialization blocks.
1970     * myModule.config(['$locationProvider', function($locationProvider) {
1971     *   // Configure existing providers
1972     *   $locationProvider.hashPrefix('!');
1973     * }]);
1974     * ```
1975     *
1976     * Then you can create an injector and load your modules like this:
1977     *
1978     * ```js
1979     * var injector = angular.injector(['ng', 'myModule'])
1980     * ```
1981     *
1982     * However it's more likely that you'll just use
1983     * {@link ng.directive:ngApp ngApp} or
1984     * {@link angular.bootstrap} to simplify this process for you.
1985     *
1986     * @param {!string} name The name of the module to create or retrieve.
1987     * @param {!Array.<string>=} requires If specified then new module is being created. If
1988     *        unspecified then the module is being retrieved for further configuration.
1989     * @param {Function=} configFn Optional configuration function for the module. Same as
1990     *        {@link angular.Module#config Module#config()}.
1991     * @returns {angular.Module} new module with the {@link angular.Module} api.
1992     */
1993    return function module(name, requires, configFn) {
1994      var assertNotHasOwnProperty = function(name, context) {
1995        if (name === 'hasOwnProperty') {
1996          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
1997        }
1998      };
1999
2000      assertNotHasOwnProperty(name, 'module');
2001      if (requires && modules.hasOwnProperty(name)) {
2002        modules[name] = null;
2003      }
2004      return ensure(modules, name, function() {
2005        if (!requires) {
2006          throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2007             "the module name or forgot to load it. If registering a module ensure that you " +
2008             "specify the dependencies as the second argument.", name);
2009        }
2010
2011        /** @type {!Array.<Array.<*>>} */
2012        var invokeQueue = [];
2013
2014        /** @type {!Array.<Function>} */
2015        var configBlocks = [];
2016
2017        /** @type {!Array.<Function>} */
2018        var runBlocks = [];
2019
2020        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2021
2022        /** @type {angular.Module} */
2023        var moduleInstance = {
2024          // Private state
2025          _invokeQueue: invokeQueue,
2026          _configBlocks: configBlocks,
2027          _runBlocks: runBlocks,
2028
2029          /**
2030           * @ngdoc property
2031           * @name angular.Module#requires
2032           * @module ng
2033           *
2034           * @description
2035           * Holds the list of modules which the injector will load before the current module is
2036           * loaded.
2037           */
2038          requires: requires,
2039
2040          /**
2041           * @ngdoc property
2042           * @name angular.Module#name
2043           * @module ng
2044           *
2045           * @description
2046           * Name of the module.
2047           */
2048          name: name,
2049
2050
2051          /**
2052           * @ngdoc method
2053           * @name angular.Module#provider
2054           * @module ng
2055           * @param {string} name service name
2056           * @param {Function} providerType Construction function for creating new instance of the
2057           *                                service.
2058           * @description
2059           * See {@link auto.$provide#provider $provide.provider()}.
2060           */
2061          provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2062
2063          /**
2064           * @ngdoc method
2065           * @name angular.Module#factory
2066           * @module ng
2067           * @param {string} name service name
2068           * @param {Function} providerFunction Function for creating new instance of the service.
2069           * @description
2070           * See {@link auto.$provide#factory $provide.factory()}.
2071           */
2072          factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2073
2074          /**
2075           * @ngdoc method
2076           * @name angular.Module#service
2077           * @module ng
2078           * @param {string} name service name
2079           * @param {Function} constructor A constructor function that will be instantiated.
2080           * @description
2081           * See {@link auto.$provide#service $provide.service()}.
2082           */
2083          service: invokeLaterAndSetModuleName('$provide', 'service'),
2084
2085          /**
2086           * @ngdoc method
2087           * @name angular.Module#value
2088           * @module ng
2089           * @param {string} name service name
2090           * @param {*} object Service instance object.
2091           * @description
2092           * See {@link auto.$provide#value $provide.value()}.
2093           */
2094          value: invokeLater('$provide', 'value'),
2095
2096          /**
2097           * @ngdoc method
2098           * @name angular.Module#constant
2099           * @module ng
2100           * @param {string} name constant name
2101           * @param {*} object Constant value.
2102           * @description
2103           * Because the constants are fixed, they get applied before other provide methods.
2104           * See {@link auto.$provide#constant $provide.constant()}.
2105           */
2106          constant: invokeLater('$provide', 'constant', 'unshift'),
2107
2108           /**
2109           * @ngdoc method
2110           * @name angular.Module#decorator
2111           * @module ng
2112           * @param {string} The name of the service to decorate.
2113           * @param {Function} This function will be invoked when the service needs to be
2114           *                                    instantiated and should return the decorated service instance.
2115           * @description
2116           * See {@link auto.$provide#decorator $provide.decorator()}.
2117           */
2118          decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2119
2120          /**
2121           * @ngdoc method
2122           * @name angular.Module#animation
2123           * @module ng
2124           * @param {string} name animation name
2125           * @param {Function} animationFactory Factory function for creating new instance of an
2126           *                                    animation.
2127           * @description
2128           *
2129           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2130           *
2131           *
2132           * Defines an animation hook that can be later used with
2133           * {@link $animate $animate} service and directives that use this service.
2134           *
2135           * ```js
2136           * module.animation('.animation-name', function($inject1, $inject2) {
2137           *   return {
2138           *     eventName : function(element, done) {
2139           *       //code to run the animation
2140           *       //once complete, then run done()
2141           *       return function cancellationFunction(element) {
2142           *         //code to cancel the animation
2143           *       }
2144           *     }
2145           *   }
2146           * })
2147           * ```
2148           *
2149           * See {@link ng.$animateProvider#register $animateProvider.register()} and
2150           * {@link ngAnimate ngAnimate module} for more information.
2151           */
2152          animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2153
2154          /**
2155           * @ngdoc method
2156           * @name angular.Module#filter
2157           * @module ng
2158           * @param {string} name Filter name - this must be a valid angular expression identifier
2159           * @param {Function} filterFactory Factory function for creating new instance of filter.
2160           * @description
2161           * See {@link ng.$filterProvider#register $filterProvider.register()}.
2162           *
2163           * <div class="alert alert-warning">
2164           * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2165           * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2166           * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2167           * (`myapp_subsection_filterx`).
2168           * </div>
2169           */
2170          filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2171
2172          /**
2173           * @ngdoc method
2174           * @name angular.Module#controller
2175           * @module ng
2176           * @param {string|Object} name Controller name, or an object map of controllers where the
2177           *    keys are the names and the values are the constructors.
2178           * @param {Function} constructor Controller constructor function.
2179           * @description
2180           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2181           */
2182          controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2183
2184          /**
2185           * @ngdoc method
2186           * @name angular.Module#directive
2187           * @module ng
2188           * @param {string|Object} name Directive name, or an object map of directives where the
2189           *    keys are the names and the values are the factories.
2190           * @param {Function} directiveFactory Factory function for creating new instance of
2191           * directives.
2192           * @description
2193           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2194           */
2195          directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2196
2197          /**
2198           * @ngdoc method
2199           * @name angular.Module#config
2200           * @module ng
2201           * @param {Function} configFn Execute this function on module load. Useful for service
2202           *    configuration.
2203           * @description
2204           * Use this method to register work which needs to be performed on module loading.
2205           * For more about how to configure services, see
2206           * {@link providers#provider-recipe Provider Recipe}.
2207           */
2208          config: config,
2209
2210          /**
2211           * @ngdoc method
2212           * @name angular.Module#run
2213           * @module ng
2214           * @param {Function} initializationFn Execute this function after injector creation.
2215           *    Useful for application initialization.
2216           * @description
2217           * Use this method to register work which should be performed when the injector is done
2218           * loading all modules.
2219           */
2220          run: function(block) {
2221            runBlocks.push(block);
2222            return this;
2223          }
2224        };
2225
2226        if (configFn) {
2227          config(configFn);
2228        }
2229
2230        return moduleInstance;
2231
2232        /**
2233         * @param {string} provider
2234         * @param {string} method
2235         * @param {String=} insertMethod
2236         * @returns {angular.Module}
2237         */
2238        function invokeLater(provider, method, insertMethod, queue) {
2239          if (!queue) queue = invokeQueue;
2240          return function() {
2241            queue[insertMethod || 'push']([provider, method, arguments]);
2242            return moduleInstance;
2243          };
2244        }
2245
2246        /**
2247         * @param {string} provider
2248         * @param {string} method
2249         * @returns {angular.Module}
2250         */
2251        function invokeLaterAndSetModuleName(provider, method) {
2252          return function(recipeName, factoryFunction) {
2253            if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2254            invokeQueue.push([provider, method, arguments]);
2255            return moduleInstance;
2256          };
2257        }
2258      });
2259    };
2260  });
2261
2262}
2263
2264/* global: toDebugString: true */
2265
2266function serializeObject(obj) {
2267  var seen = [];
2268
2269  return JSON.stringify(obj, function(key, val) {
2270    val = toJsonReplacer(key, val);
2271    if (isObject(val)) {
2272
2273      if (seen.indexOf(val) >= 0) return '...';
2274
2275      seen.push(val);
2276    }
2277    return val;
2278  });
2279}
2280
2281function toDebugString(obj) {
2282  if (typeof obj === 'function') {
2283    return obj.toString().replace(/ \{[\s\S]*$/, '');
2284  } else if (isUndefined(obj)) {
2285    return 'undefined';
2286  } else if (typeof obj !== 'string') {
2287    return serializeObject(obj);
2288  }
2289  return obj;
2290}
2291
2292/* global angularModule: true,
2293  version: true,
2294
2295  $CompileProvider,
2296
2297  htmlAnchorDirective,
2298  inputDirective,
2299  inputDirective,
2300  formDirective,
2301  scriptDirective,
2302  selectDirective,
2303  styleDirective,
2304  optionDirective,
2305  ngBindDirective,
2306  ngBindHtmlDirective,
2307  ngBindTemplateDirective,
2308  ngClassDirective,
2309  ngClassEvenDirective,
2310  ngClassOddDirective,
2311  ngCloakDirective,
2312  ngControllerDirective,
2313  ngFormDirective,
2314  ngHideDirective,
2315  ngIfDirective,
2316  ngIncludeDirective,
2317  ngIncludeFillContentDirective,
2318  ngInitDirective,
2319  ngNonBindableDirective,
2320  ngPluralizeDirective,
2321  ngRepeatDirective,
2322  ngShowDirective,
2323  ngStyleDirective,
2324  ngSwitchDirective,
2325  ngSwitchWhenDirective,
2326  ngSwitchDefaultDirective,
2327  ngOptionsDirective,
2328  ngTranscludeDirective,
2329  ngModelDirective,
2330  ngListDirective,
2331  ngChangeDirective,
2332  patternDirective,
2333  patternDirective,
2334  requiredDirective,
2335  requiredDirective,
2336  minlengthDirective,
2337  minlengthDirective,
2338  maxlengthDirective,
2339  maxlengthDirective,
2340  ngValueDirective,
2341  ngModelOptionsDirective,
2342  ngAttributeAliasDirectives,
2343  ngEventDirectives,
2344
2345  $AnchorScrollProvider,
2346  $AnimateProvider,
2347  $CoreAnimateCssProvider,
2348  $$CoreAnimateJsProvider,
2349  $$CoreAnimateQueueProvider,
2350  $$AnimateRunnerFactoryProvider,
2351  $$AnimateAsyncRunFactoryProvider,
2352  $BrowserProvider,
2353  $CacheFactoryProvider,
2354  $ControllerProvider,
2355  $DocumentProvider,
2356  $ExceptionHandlerProvider,
2357  $FilterProvider,
2358  $$ForceReflowProvider,
2359  $InterpolateProvider,
2360  $IntervalProvider,
2361  $$HashMapProvider,
2362  $HttpProvider,
2363  $HttpParamSerializerProvider,
2364  $HttpParamSerializerJQLikeProvider,
2365  $HttpBackendProvider,
2366  $xhrFactoryProvider,
2367  $LocationProvider,
2368  $LogProvider,
2369  $ParseProvider,
2370  $RootScopeProvider,
2371  $QProvider,
2372  $$QProvider,
2373  $$SanitizeUriProvider,
2374  $SceProvider,
2375  $SceDelegateProvider,
2376  $SnifferProvider,
2377  $TemplateCacheProvider,
2378  $TemplateRequestProvider,
2379  $$TestabilityProvider,
2380  $TimeoutProvider,
2381  $$RAFProvider,
2382  $WindowProvider,
2383  $$jqLiteProvider,
2384  $$CookieReaderProvider
2385*/
2386
2387
2388/**
2389 * @ngdoc object
2390 * @name angular.version
2391 * @module ng
2392 * @description
2393 * An object that contains information about the current AngularJS version.
2394 *
2395 * This object has the following properties:
2396 *
2397 * - `full` – `{string}` – Full version string, such as "0.9.18".
2398 * - `major` – `{number}` – Major version number, such as "0".
2399 * - `minor` – `{number}` – Minor version number, such as "9".
2400 * - `dot` – `{number}` – Dot version number, such as "18".
2401 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2402 */
2403var version = {
2404  full: '1.4.9',    // all of these placeholder strings will be replaced by grunt's
2405  major: 1,    // package task
2406  minor: 4,
2407  dot: 9,
2408  codeName: 'implicit-superannuation'
2409};
2410
2411
2412function publishExternalAPI(angular) {
2413  extend(angular, {
2414    'bootstrap': bootstrap,
2415    'copy': copy,
2416    'extend': extend,
2417    'merge': merge,
2418    'equals': equals,
2419    'element': jqLite,
2420    'forEach': forEach,
2421    'injector': createInjector,
2422    'noop': noop,
2423    'bind': bind,
2424    'toJson': toJson,
2425    'fromJson': fromJson,
2426    'identity': identity,
2427    'isUndefined': isUndefined,
2428    'isDefined': isDefined,
2429    'isString': isString,
2430    'isFunction': isFunction,
2431    'isObject': isObject,
2432    'isNumber': isNumber,
2433    'isElement': isElement,
2434    'isArray': isArray,
2435    'version': version,
2436    'isDate': isDate,
2437    'lowercase': lowercase,
2438    'uppercase': uppercase,
2439    'callbacks': {counter: 0},
2440    'getTestability': getTestability,
2441    '$$minErr': minErr,
2442    '$$csp': csp,
2443    'reloadWithDebugInfo': reloadWithDebugInfo
2444  });
2445
2446  angularModule = setupModuleLoader(window);
2447
2448  angularModule('ng', ['ngLocale'], ['$provide',
2449    function ngModule($provide) {
2450      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2451      $provide.provider({
2452        $$sanitizeUri: $$SanitizeUriProvider
2453      });
2454      $provide.provider('$compile', $CompileProvider).
2455        directive({
2456            a: htmlAnchorDirective,
2457            input: inputDirective,
2458            textarea: inputDirective,
2459            form: formDirective,
2460            script: scriptDirective,
2461            select: selectDirective,
2462            style: styleDirective,
2463            option: optionDirective,
2464            ngBind: ngBindDirective,
2465            ngBindHtml: ngBindHtmlDirective,
2466            ngBindTemplate: ngBindTemplateDirective,
2467            ngClass: ngClassDirective,
2468            ngClassEven: ngClassEvenDirective,
2469            ngClassOdd: ngClassOddDirective,
2470            ngCloak: ngCloakDirective,
2471            ngController: ngControllerDirective,
2472            ngForm: ngFormDirective,
2473            ngHide: ngHideDirective,
2474            ngIf: ngIfDirective,
2475            ngInclude: ngIncludeDirective,
2476            ngInit: ngInitDirective,
2477            ngNonBindable: ngNonBindableDirective,
2478            ngPluralize: ngPluralizeDirective,
2479            ngRepeat: ngRepeatDirective,
2480            ngShow: ngShowDirective,
2481            ngStyle: ngStyleDirective,
2482            ngSwitch: ngSwitchDirective,
2483            ngSwitchWhen: ngSwitchWhenDirective,
2484            ngSwitchDefault: ngSwitchDefaultDirective,
2485            ngOptions: ngOptionsDirective,
2486            ngTransclude: ngTranscludeDirective,
2487            ngModel: ngModelDirective,
2488            ngList: ngListDirective,
2489            ngChange: ngChangeDirective,
2490            pattern: patternDirective,
2491            ngPattern: patternDirective,
2492            required: requiredDirective,
2493            ngRequired: requiredDirective,
2494            minlength: minlengthDirective,
2495            ngMinlength: minlengthDirective,
2496            maxlength: maxlengthDirective,
2497            ngMaxlength: maxlengthDirective,
2498            ngValue: ngValueDirective,
2499            ngModelOptions: ngModelOptionsDirective
2500        }).
2501        directive({
2502          ngInclude: ngIncludeFillContentDirective
2503        }).
2504        directive(ngAttributeAliasDirectives).
2505        directive(ngEventDirectives);
2506      $provide.provider({
2507        $anchorScroll: $AnchorScrollProvider,
2508        $animate: $AnimateProvider,
2509        $animateCss: $CoreAnimateCssProvider,
2510        $$animateJs: $$CoreAnimateJsProvider,
2511        $$animateQueue: $$CoreAnimateQueueProvider,
2512        $$AnimateRunner: $$AnimateRunnerFactoryProvider,
2513        $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
2514        $browser: $BrowserProvider,
2515        $cacheFactory: $CacheFactoryProvider,
2516        $controller: $ControllerProvider,
2517        $document: $DocumentProvider,
2518        $exceptionHandler: $ExceptionHandlerProvider,
2519        $filter: $FilterProvider,
2520        $$forceReflow: $$ForceReflowProvider,
2521        $interpolate: $InterpolateProvider,
2522        $interval: $IntervalProvider,
2523        $http: $HttpProvider,
2524        $httpParamSerializer: $HttpParamSerializerProvider,
2525        $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2526        $httpBackend: $HttpBackendProvider,
2527        $xhrFactory: $xhrFactoryProvider,
2528        $location: $LocationProvider,
2529        $log: $LogProvider,
2530        $parse: $ParseProvider,
2531        $rootScope: $RootScopeProvider,
2532        $q: $QProvider,
2533        $$q: $$QProvider,
2534        $sce: $SceProvider,
2535        $sceDelegate: $SceDelegateProvider,
2536        $sniffer: $SnifferProvider,
2537        $templateCache: $TemplateCacheProvider,
2538        $templateRequest: $TemplateRequestProvider,
2539        $$testability: $$TestabilityProvider,
2540        $timeout: $TimeoutProvider,
2541        $window: $WindowProvider,
2542        $$rAF: $$RAFProvider,
2543        $$jqLite: $$jqLiteProvider,
2544        $$HashMap: $$HashMapProvider,
2545        $$cookieReader: $$CookieReaderProvider
2546      });
2547    }
2548  ]);
2549}
2550
2551/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2552 *     Any commits to this file should be reviewed with security in mind.  *
2553 *   Changes to this file can potentially create security vulnerabilities. *
2554 *          An approval from 2 Core members with history of modifying      *
2555 *                         this file is required.                          *
2556 *                                                                         *
2557 *  Does the change somehow allow for arbitrary javascript to be executed? *
2558 *    Or allows for someone to change the prototype of built-in objects?   *
2559 *     Or gives undesired access to variables likes document or window?    *
2560 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2561
2562/* global JQLitePrototype: true,
2563  addEventListenerFn: true,
2564  removeEventListenerFn: true,
2565  BOOLEAN_ATTR: true,
2566  ALIASED_ATTR: true,
2567*/
2568
2569//////////////////////////////////
2570//JQLite
2571//////////////////////////////////
2572
2573/**
2574 * @ngdoc function
2575 * @name angular.element
2576 * @module ng
2577 * @kind function
2578 *
2579 * @description
2580 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2581 *
2582 * If jQuery is available, `angular.element` is an alias for the
2583 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2584 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
2585 *
2586 * jqLite is a tiny, API-compatible subset of jQuery that allows
2587 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
2588 * commonly needed functionality with the goal of having a very small footprint.
2589 *
2590 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
2591 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
2592 * specific version of jQuery if multiple versions exist on the page.
2593 *
2594 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
2595 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
2596 *
2597 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
2598 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
2599 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
2600 *
2601 * ## Angular's jqLite
2602 * jqLite provides only the following jQuery methods:
2603 *
2604 * - [`addClass()`](http://api.jquery.com/addClass/)
2605 * - [`after()`](http://api.jquery.com/after/)
2606 * - [`append()`](http://api.jquery.com/append/)
2607 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2608 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2609 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2610 * - [`clone()`](http://api.jquery.com/clone/)
2611 * - [`contents()`](http://api.jquery.com/contents/)
2612 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
2613 *   As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
2614 * - [`data()`](http://api.jquery.com/data/)
2615 * - [`detach()`](http://api.jquery.com/detach/)
2616 * - [`empty()`](http://api.jquery.com/empty/)
2617 * - [`eq()`](http://api.jquery.com/eq/)
2618 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2619 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2620 * - [`html()`](http://api.jquery.com/html/)
2621 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2622 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2623 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2624 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2625 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2626 * - [`prepend()`](http://api.jquery.com/prepend/)
2627 * - [`prop()`](http://api.jquery.com/prop/)
2628 * - [`ready()`](http://api.jquery.com/ready/)
2629 * - [`remove()`](http://api.jquery.com/remove/)
2630 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2631 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2632 * - [`removeData()`](http://api.jquery.com/removeData/)
2633 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2634 * - [`text()`](http://api.jquery.com/text/)
2635 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2636 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2637 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2638 * - [`val()`](http://api.jquery.com/val/)
2639 * - [`wrap()`](http://api.jquery.com/wrap/)
2640 *
2641 * ## jQuery/jqLite Extras
2642 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2643 *
2644 * ### Events
2645 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2646 *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2647 *    element before it is removed.
2648 *
2649 * ### Methods
2650 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2651 *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2652 *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2653 *   `'ngModel'`).
2654 * - `injector()` - retrieves the injector of the current element or its parent.
2655 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2656 *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2657 *   be enabled.
2658 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2659 *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2660 *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2661 *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2662 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2663 *   parent element is reached.
2664 *
2665 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2666 * @returns {Object} jQuery object.
2667 */
2668
2669JQLite.expando = 'ng339';
2670
2671var jqCache = JQLite.cache = {},
2672    jqId = 1,
2673    addEventListenerFn = function(element, type, fn) {
2674      element.addEventListener(type, fn, false);
2675    },
2676    removeEventListenerFn = function(element, type, fn) {
2677      element.removeEventListener(type, fn, false);
2678    };
2679
2680/*
2681 * !!! This is an undocumented "private" function !!!
2682 */
2683JQLite._data = function(node) {
2684  //jQuery always returns an object on cache miss
2685  return this.cache[node[this.expando]] || {};
2686};
2687
2688function jqNextId() { return ++jqId; }
2689
2690
2691var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2692var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2693var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2694var jqLiteMinErr = minErr('jqLite');
2695
2696/**
2697 * Converts snake_case to camelCase.
2698 * Also there is special case for Moz prefix starting with upper case letter.
2699 * @param name Name to normalize
2700 */
2701function camelCase(name) {
2702  return name.
2703    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2704      return offset ? letter.toUpperCase() : letter;
2705    }).
2706    replace(MOZ_HACK_REGEXP, 'Moz$1');
2707}
2708
2709var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2710var HTML_REGEXP = /<|&#?\w+;/;
2711var TAG_NAME_REGEXP = /<([\w:-]+)/;
2712var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2713
2714var wrapMap = {
2715  'option': [1, '<select multiple="multiple">', '</select>'],
2716
2717  'thead': [1, '<table>', '</table>'],
2718  'col': [2, '<table><colgroup>', '</colgroup></table>'],
2719  'tr': [2, '<table><tbody>', '</tbody></table>'],
2720  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2721  '_default': [0, "", ""]
2722};
2723
2724wrapMap.optgroup = wrapMap.option;
2725wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2726wrapMap.th = wrapMap.td;
2727
2728
2729function jqLiteIsTextNode(html) {
2730  return !HTML_REGEXP.test(html);
2731}
2732
2733function jqLiteAcceptsData(node) {
2734  // The window object can accept data but has no nodeType
2735  // Otherwise we are only interested in elements (1) and documents (9)
2736  var nodeType = node.nodeType;
2737  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2738}
2739
2740function jqLiteHasData(node) {
2741  for (var key in jqCache[node.ng339]) {
2742    return true;
2743  }
2744  return false;
2745}
2746
2747function jqLiteBuildFragment(html, context) {
2748  var tmp, tag, wrap,
2749      fragment = context.createDocumentFragment(),
2750      nodes = [], i;
2751
2752  if (jqLiteIsTextNode(html)) {
2753    // Convert non-html into a text node
2754    nodes.push(context.createTextNode(html));
2755  } else {
2756    // Convert html into DOM nodes
2757    tmp = tmp || fragment.appendChild(context.createElement("div"));
2758    tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2759    wrap = wrapMap[tag] || wrapMap._default;
2760    tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2761
2762    // Descend through wrappers to the right content
2763    i = wrap[0];
2764    while (i--) {
2765      tmp = tmp.lastChild;
2766    }
2767
2768    nodes = concat(nodes, tmp.childNodes);
2769
2770    tmp = fragment.firstChild;
2771    tmp.textContent = "";
2772  }
2773
2774  // Remove wrapper from fragment
2775  fragment.textContent = "";
2776  fragment.innerHTML = ""; // Clear inner HTML
2777  forEach(nodes, function(node) {
2778    fragment.appendChild(node);
2779  });
2780
2781  return fragment;
2782}
2783
2784function jqLiteParseHTML(html, context) {
2785  context = context || document;
2786  var parsed;
2787
2788  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2789    return [context.createElement(parsed[1])];
2790  }
2791
2792  if ((parsed = jqLiteBuildFragment(html, context))) {
2793    return parsed.childNodes;
2794  }
2795
2796  return [];
2797}
2798
2799
2800// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2801var jqLiteContains = Node.prototype.contains || function(arg) {
2802  // jshint bitwise: false
2803  return !!(this.compareDocumentPosition(arg) & 16);
2804  // jshint bitwise: true
2805};
2806
2807/////////////////////////////////////////////
2808function JQLite(element) {
2809  if (element instanceof JQLite) {
2810    return element;
2811  }
2812
2813  var argIsString;
2814
2815  if (isString(element)) {
2816    element = trim(element);
2817    argIsString = true;
2818  }
2819  if (!(this instanceof JQLite)) {
2820    if (argIsString && element.charAt(0) != '<') {
2821      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2822    }
2823    return new JQLite(element);
2824  }
2825
2826  if (argIsString) {
2827    jqLiteAddNodes(this, jqLiteParseHTML(element));
2828  } else {
2829    jqLiteAddNodes(this, element);
2830  }
2831}
2832
2833function jqLiteClone(element) {
2834  return element.cloneNode(true);
2835}
2836
2837function jqLiteDealoc(element, onlyDescendants) {
2838  if (!onlyDescendants) jqLiteRemoveData(element);
2839
2840  if (element.querySelectorAll) {
2841    var descendants = element.querySelectorAll('*');
2842    for (var i = 0, l = descendants.length; i < l; i++) {
2843      jqLiteRemoveData(descendants[i]);
2844    }
2845  }
2846}
2847
2848function jqLiteOff(element, type, fn, unsupported) {
2849  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2850
2851  var expandoStore = jqLiteExpandoStore(element);
2852  var events = expandoStore && expandoStore.events;
2853  var handle = expandoStore && expandoStore.handle;
2854
2855  if (!handle) return; //no listeners registered
2856
2857  if (!type) {
2858    for (type in events) {
2859      if (type !== '$destroy') {
2860        removeEventListenerFn(element, type, handle);
2861      }
2862      delete events[type];
2863    }
2864  } else {
2865
2866    var removeHandler = function(type) {
2867      var listenerFns = events[type];
2868      if (isDefined(fn)) {
2869        arrayRemove(listenerFns || [], fn);
2870      }
2871      if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2872        removeEventListenerFn(element, type, handle);
2873        delete events[type];
2874      }
2875    };
2876
2877    forEach(type.split(' '), function(type) {
2878      removeHandler(type);
2879      if (MOUSE_EVENT_MAP[type]) {
2880        removeHandler(MOUSE_EVENT_MAP[type]);
2881      }
2882    });
2883  }
2884}
2885
2886function jqLiteRemoveData(element, name) {
2887  var expandoId = element.ng339;
2888  var expandoStore = expandoId && jqCache[expandoId];
2889
2890  if (expandoStore) {
2891    if (name) {
2892      delete expandoStore.data[name];
2893      return;
2894    }
2895
2896    if (expandoStore.handle) {
2897      if (expandoStore.events.$destroy) {
2898        expandoStore.handle({}, '$destroy');
2899      }
2900      jqLiteOff(element);
2901    }
2902    delete jqCache[expandoId];
2903    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2904  }
2905}
2906
2907
2908function jqLiteExpandoStore(element, createIfNecessary) {
2909  var expandoId = element.ng339,
2910      expandoStore = expandoId && jqCache[expandoId];
2911
2912  if (createIfNecessary && !expandoStore) {
2913    element.ng339 = expandoId = jqNextId();
2914    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2915  }
2916
2917  return expandoStore;
2918}
2919
2920
2921function jqLiteData(element, key, value) {
2922  if (jqLiteAcceptsData(element)) {
2923
2924    var isSimpleSetter = isDefined(value);
2925    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2926    var massGetter = !key;
2927    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2928    var data = expandoStore && expandoStore.data;
2929
2930    if (isSimpleSetter) { // data('key', value)
2931      data[key] = value;
2932    } else {
2933      if (massGetter) {  // data()
2934        return data;
2935      } else {
2936        if (isSimpleGetter) { // data('key')
2937          // don't force creation of expandoStore if it doesn't exist yet
2938          return data && data[key];
2939        } else { // mass-setter: data({key1: val1, key2: val2})
2940          extend(data, key);
2941        }
2942      }
2943    }
2944  }
2945}
2946
2947function jqLiteHasClass(element, selector) {
2948  if (!element.getAttribute) return false;
2949  return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2950      indexOf(" " + selector + " ") > -1);
2951}
2952
2953function jqLiteRemoveClass(element, cssClasses) {
2954  if (cssClasses && element.setAttribute) {
2955    forEach(cssClasses.split(' '), function(cssClass) {
2956      element.setAttribute('class', trim(
2957          (" " + (element.getAttribute('class') || '') + " ")
2958          .replace(/[\n\t]/g, " ")
2959          .replace(" " + trim(cssClass) + " ", " "))
2960      );
2961    });
2962  }
2963}
2964
2965function jqLiteAddClass(element, cssClasses) {
2966  if (cssClasses && element.setAttribute) {
2967    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2968                            .replace(/[\n\t]/g, " ");
2969
2970    forEach(cssClasses.split(' '), function(cssClass) {
2971      cssClass = trim(cssClass);
2972      if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2973        existingClasses += cssClass + ' ';
2974      }
2975    });
2976
2977    element.setAttribute('class', trim(existingClasses));
2978  }
2979}
2980
2981
2982function jqLiteAddNodes(root, elements) {
2983  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
2984
2985  if (elements) {
2986
2987    // if a Node (the most common case)
2988    if (elements.nodeType) {
2989      root[root.length++] = elements;
2990    } else {
2991      var length = elements.length;
2992
2993      // if an Array or NodeList and not a Window
2994      if (typeof length === 'number' && elements.window !== elements) {
2995        if (length) {
2996          for (var i = 0; i < length; i++) {
2997            root[root.length++] = elements[i];
2998          }
2999        }
3000      } else {
3001        root[root.length++] = elements;
3002      }
3003    }
3004  }
3005}
3006
3007
3008function jqLiteController(element, name) {
3009  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3010}
3011
3012function jqLiteInheritedData(element, name, value) {
3013  // if element is the document object work with the html element instead
3014  // this makes $(document).scope() possible
3015  if (element.nodeType == NODE_TYPE_DOCUMENT) {
3016    element = element.documentElement;
3017  }
3018  var names = isArray(name) ? name : [name];
3019
3020  while (element) {
3021    for (var i = 0, ii = names.length; i < ii; i++) {
3022      if (isDefined(value = jqLite.data(element, names[i]))) return value;
3023    }
3024
3025    // If dealing with a document fragment node with a host element, and no parent, use the host
3026    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3027    // to lookup parent controllers.
3028    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3029  }
3030}
3031
3032function jqLiteEmpty(element) {
3033  jqLiteDealoc(element, true);
3034  while (element.firstChild) {
3035    element.removeChild(element.firstChild);
3036  }
3037}
3038
3039function jqLiteRemove(element, keepData) {
3040  if (!keepData) jqLiteDealoc(element);
3041  var parent = element.parentNode;
3042  if (parent) parent.removeChild(element);
3043}
3044
3045
3046function jqLiteDocumentLoaded(action, win) {
3047  win = win || window;
3048  if (win.document.readyState === 'complete') {
3049    // Force the action to be run async for consistent behaviour
3050    // from the action's point of view
3051    // i.e. it will definitely not be in a $apply
3052    win.setTimeout(action);
3053  } else {
3054    // No need to unbind this handler as load is only ever called once
3055    jqLite(win).on('load', action);
3056  }
3057}
3058
3059//////////////////////////////////////////
3060// Functions which are declared directly.
3061//////////////////////////////////////////
3062var JQLitePrototype = JQLite.prototype = {
3063  ready: function(fn) {
3064    var fired = false;
3065
3066    function trigger() {
3067      if (fired) return;
3068      fired = true;
3069      fn();
3070    }
3071
3072    // check if document is already loaded
3073    if (document.readyState === 'complete') {
3074      setTimeout(trigger);
3075    } else {
3076      this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3077      // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3078      // jshint -W064
3079      JQLite(window).on('load', trigger); // fallback to window.onload for others
3080      // jshint +W064
3081    }
3082  },
3083  toString: function() {
3084    var value = [];
3085    forEach(this, function(e) { value.push('' + e);});
3086    return '[' + value.join(', ') + ']';
3087  },
3088
3089  eq: function(index) {
3090      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3091  },
3092
3093  length: 0,
3094  push: push,
3095  sort: [].sort,
3096  splice: [].splice
3097};
3098
3099//////////////////////////////////////////
3100// Functions iterating getter/setters.
3101// these functions return self on setter and
3102// value on get.
3103//////////////////////////////////////////
3104var BOOLEAN_ATTR = {};
3105forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3106  BOOLEAN_ATTR[lowercase(value)] = value;
3107});
3108var BOOLEAN_ELEMENTS = {};
3109forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3110  BOOLEAN_ELEMENTS[value] = true;
3111});
3112var ALIASED_ATTR = {
3113  'ngMinlength': 'minlength',
3114  'ngMaxlength': 'maxlength',
3115  'ngMin': 'min',
3116  'ngMax': 'max',
3117  'ngPattern': 'pattern'
3118};
3119
3120function getBooleanAttrName(element, name) {
3121  // check dom last since we will most likely fail on name
3122  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3123
3124  // booleanAttr is here twice to minimize DOM access
3125  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3126}
3127
3128function getAliasedAttrName(name) {
3129  return ALIASED_ATTR[name];
3130}
3131
3132forEach({
3133  data: jqLiteData,
3134  removeData: jqLiteRemoveData,
3135  hasData: jqLiteHasData
3136}, function(fn, name) {
3137  JQLite[name] = fn;
3138});
3139
3140forEach({
3141  data: jqLiteData,
3142  inheritedData: jqLiteInheritedData,
3143
3144  scope: function(element) {
3145    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3146    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3147  },
3148
3149  isolateScope: function(element) {
3150    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3151    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3152  },
3153
3154  controller: jqLiteController,
3155
3156  injector: function(element) {
3157    return jqLiteInheritedData(element, '$injector');
3158  },
3159
3160  removeAttr: function(element, name) {
3161    element.removeAttribute(name);
3162  },
3163
3164  hasClass: jqLiteHasClass,
3165
3166  css: function(element, name, value) {
3167    name = camelCase(name);
3168
3169    if (isDefined(value)) {
3170      element.style[name] = value;
3171    } else {
3172      return element.style[name];
3173    }
3174  },
3175
3176  attr: function(element, name, value) {
3177    var nodeType = element.nodeType;
3178    if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3179      return;
3180    }
3181    var lowercasedName = lowercase(name);
3182    if (BOOLEAN_ATTR[lowercasedName]) {
3183      if (isDefined(value)) {
3184        if (!!value) {
3185          element[name] = true;
3186          element.setAttribute(name, lowercasedName);
3187        } else {
3188          element[name] = false;
3189          element.removeAttribute(lowercasedName);
3190        }
3191      } else {
3192        return (element[name] ||
3193                 (element.attributes.getNamedItem(name) || noop).specified)
3194               ? lowercasedName
3195               : undefined;
3196      }
3197    } else if (isDefined(value)) {
3198      element.setAttribute(name, value);
3199    } else if (element.getAttribute) {
3200      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3201      // some elements (e.g. Document) don't have get attribute, so return undefined
3202      var ret = element.getAttribute(name, 2);
3203      // normalize non-existing attributes to undefined (as jQuery)
3204      return ret === null ? undefined : ret;
3205    }
3206  },
3207
3208  prop: function(element, name, value) {
3209    if (isDefined(value)) {
3210      element[name] = value;
3211    } else {
3212      return element[name];
3213    }
3214  },
3215
3216  text: (function() {
3217    getText.$dv = '';
3218    return getText;
3219
3220    function getText(element, value) {
3221      if (isUndefined(value)) {
3222        var nodeType = element.nodeType;
3223        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3224      }
3225      element.textContent = value;
3226    }
3227  })(),
3228
3229  val: function(element, value) {
3230    if (isUndefined(value)) {
3231      if (element.multiple && nodeName_(element) === 'select') {
3232        var result = [];
vendor: 10,377 bytes, lines 3233-3577
3233        forEach(element.options, function(option) {
3234          if (option.selected) {
3235            result.push(option.value || option.text);
3236          }
3237        });
3238        return result.length === 0 ? null : result;
3239      }
3240      return element.value;
3241    }
3242    element.value = value;
3243  },
3244
3245  html: function(element, value) {
3246    if (isUndefined(value)) {
3247      return element.innerHTML;
3248    }
3249    jqLiteDealoc(element, true);
3250    element.innerHTML = value;
3251  },
3252
3253  empty: jqLiteEmpty
3254}, function(fn, name) {
3255  /**
3256   * Properties: writes return selection, reads return first value
3257   */
3258  JQLite.prototype[name] = function(arg1, arg2) {
3259    var i, key;
3260    var nodeCount = this.length;
3261
3262    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3263    // in a way that survives minification.
3264    // jqLiteEmpty takes no arguments but is a setter.
3265    if (fn !== jqLiteEmpty &&
3266        (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3267      if (isObject(arg1)) {
3268
3269        // we are a write, but the object properties are the key/values
3270        for (i = 0; i < nodeCount; i++) {
3271          if (fn === jqLiteData) {
3272            // data() takes the whole object in jQuery
3273            fn(this[i], arg1);
3274          } else {
3275            for (key in arg1) {
3276              fn(this[i], key, arg1[key]);
3277            }
3278          }
3279        }
3280        // return self for chaining
3281        return this;
3282      } else {
3283        // we are a read, so read the first child.
3284        // TODO: do we still need this?
3285        var value = fn.$dv;
3286        // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3287        var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3288        for (var j = 0; j < jj; j++) {
3289          var nodeValue = fn(this[j], arg1, arg2);
3290          value = value ? value + nodeValue : nodeValue;
3291        }
3292        return value;
3293      }
3294    } else {
3295      // we are a write, so apply to all children
3296      for (i = 0; i < nodeCount; i++) {
3297        fn(this[i], arg1, arg2);
3298      }
3299      // return self for chaining
3300      return this;
3301    }
3302  };
3303});
3304
3305function createEventHandler(element, events) {
3306  var eventHandler = function(event, type) {
3307    // jQuery specific api
3308    event.isDefaultPrevented = function() {
3309      return event.defaultPrevented;
3310    };
3311
3312    var eventFns = events[type || event.type];
3313    var eventFnsLength = eventFns ? eventFns.length : 0;
3314
3315    if (!eventFnsLength) return;
3316
3317    if (isUndefined(event.immediatePropagationStopped)) {
3318      var originalStopImmediatePropagation = event.stopImmediatePropagation;
3319      event.stopImmediatePropagation = function() {
3320        event.immediatePropagationStopped = true;
3321
3322        if (event.stopPropagation) {
3323          event.stopPropagation();
3324        }
3325
3326        if (originalStopImmediatePropagation) {
3327          originalStopImmediatePropagation.call(event);
3328        }
3329      };
3330    }
3331
3332    event.isImmediatePropagationStopped = function() {
3333      return event.immediatePropagationStopped === true;
3334    };
3335
3336    // Some events have special handlers that wrap the real handler
3337    var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3338
3339    // Copy event handlers in case event handlers array is modified during execution.
3340    if ((eventFnsLength > 1)) {
3341      eventFns = shallowCopy(eventFns);
3342    }
3343
3344    for (var i = 0; i < eventFnsLength; i++) {
3345      if (!event.isImmediatePropagationStopped()) {
3346        handlerWrapper(element, event, eventFns[i]);
3347      }
3348    }
3349  };
3350
3351  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3352  //       events on `element`
3353  eventHandler.elem = element;
3354  return eventHandler;
3355}
3356
3357function defaultHandlerWrapper(element, event, handler) {
3358  handler.call(element, event);
3359}
3360
3361function specialMouseHandlerWrapper(target, event, handler) {
3362  // Refer to jQuery's implementation of mouseenter & mouseleave
3363  // Read about mouseenter and mouseleave:
3364  // http://www.quirksmode.org/js/events_mouse.html#link8
3365  var related = event.relatedTarget;
3366  // For mousenter/leave call the handler if related is outside the target.
3367  // NB: No relatedTarget if the mouse left/entered the browser window
3368  if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3369    handler.call(target, event);
3370  }
3371}
3372
3373//////////////////////////////////////////
3374// Functions iterating traversal.
3375// These functions chain results into a single
3376// selector.
3377//////////////////////////////////////////
3378forEach({
3379  removeData: jqLiteRemoveData,
3380
3381  on: function jqLiteOn(element, type, fn, unsupported) {
3382    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3383
3384    // Do not add event handlers to non-elements because they will not be cleaned up.
3385    if (!jqLiteAcceptsData(element)) {
3386      return;
3387    }
3388
3389    var expandoStore = jqLiteExpandoStore(element, true);
3390    var events = expandoStore.events;
3391    var handle = expandoStore.handle;
3392
3393    if (!handle) {
3394      handle = expandoStore.handle = createEventHandler(element, events);
3395    }
3396
3397    // http://jsperf.com/string-indexof-vs-split
3398    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3399    var i = types.length;
3400
3401    var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3402      var eventFns = events[type];
3403
3404      if (!eventFns) {
3405        eventFns = events[type] = [];
3406        eventFns.specialHandlerWrapper = specialHandlerWrapper;
3407        if (type !== '$destroy' && !noEventListener) {
3408          addEventListenerFn(element, type, handle);
3409        }
3410      }
3411
3412      eventFns.push(fn);
3413    };
3414
3415    while (i--) {
3416      type = types[i];
3417      if (MOUSE_EVENT_MAP[type]) {
3418        addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3419        addHandler(type, undefined, true);
3420      } else {
3421        addHandler(type);
3422      }
3423    }
3424  },
3425
3426  off: jqLiteOff,
3427
3428  one: function(element, type, fn) {
3429    element = jqLite(element);
3430
3431    //add the listener twice so that when it is called
3432    //you can remove the original function and still be
3433    //able to call element.off(ev, fn) normally
3434    element.on(type, function onFn() {
3435      element.off(type, fn);
3436      element.off(type, onFn);
3437    });
3438    element.on(type, fn);
3439  },
3440
3441  replaceWith: function(element, replaceNode) {
3442    var index, parent = element.parentNode;
3443    jqLiteDealoc(element);
3444    forEach(new JQLite(replaceNode), function(node) {
3445      if (index) {
3446        parent.insertBefore(node, index.nextSibling);
3447      } else {
3448        parent.replaceChild(node, element);
3449      }
3450      index = node;
3451    });
3452  },
3453
3454  children: function(element) {
3455    var children = [];
3456    forEach(element.childNodes, function(element) {
3457      if (element.nodeType === NODE_TYPE_ELEMENT) {
3458        children.push(element);
3459      }
3460    });
3461    return children;
3462  },
3463
3464  contents: function(element) {
3465    return element.contentDocument || element.childNodes || [];
3466  },
3467
3468  append: function(element, node) {
3469    var nodeType = element.nodeType;
3470    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3471
3472    node = new JQLite(node);
3473
3474    for (var i = 0, ii = node.length; i < ii; i++) {
3475      var child = node[i];
3476      element.appendChild(child);
3477    }
3478  },
3479
3480  prepend: function(element, node) {
3481    if (element.nodeType === NODE_TYPE_ELEMENT) {
3482      var index = element.firstChild;
3483      forEach(new JQLite(node), function(child) {
3484        element.insertBefore(child, index);
3485      });
3486    }
3487  },
3488
3489  wrap: function(element, wrapNode) {
3490    wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3491    var parent = element.parentNode;
3492    if (parent) {
3493      parent.replaceChild(wrapNode, element);
3494    }
3495    wrapNode.appendChild(element);
3496  },
3497
3498  remove: jqLiteRemove,
3499
3500  detach: function(element) {
3501    jqLiteRemove(element, true);
3502  },
3503
3504  after: function(element, newElement) {
3505    var index = element, parent = element.parentNode;
3506    newElement = new JQLite(newElement);
3507
3508    for (var i = 0, ii = newElement.length; i < ii; i++) {
3509      var node = newElement[i];
3510      parent.insertBefore(node, index.nextSibling);
3511      index = node;
3512    }
3513  },
3514
3515  addClass: jqLiteAddClass,
3516  removeClass: jqLiteRemoveClass,
3517
3518  toggleClass: function(element, selector, condition) {
3519    if (selector) {
3520      forEach(selector.split(' '), function(className) {
3521        var classCondition = condition;
3522        if (isUndefined(classCondition)) {
3523          classCondition = !jqLiteHasClass(element, className);
3524        }
3525        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3526      });
3527    }
3528  },
3529
3530  parent: function(element) {
3531    var parent = element.parentNode;
3532    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3533  },
3534
3535  next: function(element) {
3536    return element.nextElementSibling;
3537  },
3538
3539  find: function(element, selector) {
3540    if (element.getElementsByTagName) {
3541      return element.getElementsByTagName(selector);
3542    } else {
3543      return [];
3544    }
3545  },
3546
3547  clone: jqLiteClone,
3548
3549  triggerHandler: function(element, event, extraParameters) {
3550
3551    var dummyEvent, eventFnsCopy, handlerArgs;
3552    var eventName = event.type || event;
3553    var expandoStore = jqLiteExpandoStore(element);
3554    var events = expandoStore && expandoStore.events;
3555    var eventFns = events && events[eventName];
3556
3557    if (eventFns) {
3558      // Create a dummy event to pass to the handlers
3559      dummyEvent = {
3560        preventDefault: function() { this.defaultPrevented = true; },
3561        isDefaultPrevented: function() { return this.defaultPrevented === true; },
3562        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3563        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3564        stopPropagation: noop,
3565        type: eventName,
3566        target: element
3567      };
3568
3569      // If a custom event was provided then extend our dummy event with it
3570      if (event.type) {
3571        dummyEvent = extend(dummyEvent, event);
3572      }
3573
3574      // Copy event handlers in case event handlers array is modified during execution.
3575      eventFnsCopy = shallowCopy(eventFns);
3576      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3577
3578      forEach(eventFnsCopy, function(fn) {
3579        if (!dummyEvent.isImmediatePropagationStopped()) {
3580          fn.apply(element, handlerArgs);
3581        }
3582      });
3583    }
3584  }
3585}, function(fn, name) {
3586  /**
3587   * chaining functions
3588   */
3589  JQLite.prototype[name] = function(arg1, arg2, arg3) {
3590    var value;
3591
3592    for (var i = 0, ii = this.length; i < ii; i++) {
3593      if (isUndefined(value)) {
3594        value = fn(this[i], arg1, arg2, arg3);
3595        if (isDefined(value)) {
3596          // any function which returns a value needs to be wrapped
3597          value = jqLite(value);
3598        }
3599      } else {
3600        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3601      }
3602    }
3603    return isDefined(value) ? value : this;
3604  };
3605
3606  // bind legacy bind/unbind to on/off
3607  JQLite.prototype.bind = JQLite.prototype.on;
3608  JQLite.prototype.unbind = JQLite.prototype.off;
3609});
3610
3611
3612// Provider for private $$jqLite service
3613function $$jqLiteProvider() {
3614  this.$get = function $$jqLite() {
3615    return extend(JQLite, {
3616      hasClass: function(node, classes) {
3617        if (node.attr) node = node[0];
3618        return jqLiteHasClass(node, classes);
3619      },
3620      addClass: function(node, classes) {
3621        if (node.attr) node = node[0];
3622        return jqLiteAddClass(node, classes);
3623      },
3624      removeClass: function(node, classes) {
3625        if (node.attr) node = node[0];
3626        return jqLiteRemoveClass(node, classes);
3627      }
3628    });
3629  };
3630}
3631
3632/**
3633 * Computes a hash of an 'obj'.
3634 * Hash of a:
3635 *  string is string
3636 *  number is number as string
3637 *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3638 *         that is also assigned to the $$hashKey property of the object.
3639 *
3640 * @param obj
3641 * @returns {string} hash string such that the same input will have the same hash string.
3642 *         The resulting string key is in 'type:hashKey' format.
3643 */
3644function hashKey(obj, nextUidFn) {
3645  var key = obj && obj.$$hashKey;
3646
3647  if (key) {
3648    if (typeof key === 'function') {
3649      key = obj.$$hashKey();
3650    }
3651    return key;
3652  }
3653
3654  var objType = typeof obj;
3655  if (objType == 'function' || (objType == 'object' && obj !== null)) {
3656    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3657  } else {
3658    key = objType + ':' + obj;
3659  }
3660
3661  return key;
3662}
3663
3664/**
3665 * HashMap which can use objects as keys
3666 */
3667function HashMap(array, isolatedUid) {
3668  if (isolatedUid) {
3669    var uid = 0;
3670    this.nextUid = function() {
3671      return ++uid;
3672    };
3673  }
3674  forEach(array, this.put, this);
3675}
3676HashMap.prototype = {
3677  /**
3678   * Store key value pair
3679   * @param key key to store can be any type
3680   * @param value value to store can be any type
3681   */
3682  put: function(key, value) {
3683    this[hashKey(key, this.nextUid)] = value;
3684  },
3685
3686  /**
3687   * @param key
3688   * @returns {Object} the value for the key
3689   */
3690  get: function(key) {
3691    return this[hashKey(key, this.nextUid)];
3692  },
3693
3694  /**
3695   * Remove the key/value pair
3696   * @param key
3697   */
3698  remove: function(key) {
3699    var value = this[key = hashKey(key, this.nextUid)];
3700    delete this[key];
3701    return value;
3702  }
3703};
3704
3705var $$HashMapProvider = [function() {
3706  this.$get = [function() {
3707    return HashMap;
3708  }];
3709}];
3710
3711/**
3712 * @ngdoc function
3713 * @module ng
3714 * @name angular.injector
3715 * @kind function
3716 *
3717 * @description
3718 * Creates an injector object that can be used for retrieving services as well as for
3719 * dependency injection (see {@link guide/di dependency injection}).
3720 *
3721 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3722 *     {@link angular.module}. The `ng` module must be explicitly added.
3723 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3724 *     disallows argument name annotation inference.
3725 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3726 *
3727 * @example
3728 * Typical usage
3729 * ```js
3730 *   // create an injector
3731 *   var $injector = angular.injector(['ng']);
3732 *
3733 *   // use the injector to kick off your application
3734 *   // use the type inference to auto inject arguments, or use impl
3734icit injection
3735 *   $injector.invoke(function($rootScope, $compile, $document) {
3736 *     $compile($document)($rootScope);
3737 *     $rootScope.$digest();
3738 *   });
3739 * ```
3740 *
3741 * Sometimes you want to get access to the injector of a currently running Angular app
3742 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3743 * application has been bootstrapped. You can do this using the extra `injector()` added
3744 * to JQuery/jqLite elements. See {@link angular.element}.
3745 *
3746 * *This is fairly rare but could be the case if a third party library is injecting the
3747 * markup.*
3748 *
3749 * In the following example a new block of HTML containing a `ng-controller`
3750 * directive is added to the end of the document body by JQuery. We then compile and link
3751 * it into the current AngularJS scope.
3752 *
3753 * ```js
3754 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3755 * $(document.body).append($div);
3756 *
3757 * angular.element(document).injector().invoke(function($compile) {
3758 *   var scope = angular.element($div).scope();
3759 *   $compile($div)(scope);
3760 * });
3761 * ```
3762 */
3763
3764
3765/**
3766 * @ngdoc module
3767 * @name auto
3768 * @description
3769 *
3770 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3771 */
3772
3773var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3774var FN_ARG_SPLIT = /,/;
3775var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3776var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3777var $injectorMinErr = minErr('$injector');
3778
3779function anonFn(fn) {
3780  // For anonymous functions, showing at the very least the function signature can help in
3781  // debugging.
3782  var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3783      args = fnText.match(FN_ARGS);
3784  if (args) {
3785    return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3786  }
3787  return 'fn';
3788}
3789
3790function annotate(fn, strictDi, name) {
3791  var $inject,
3792      fnText,
3793      argDecl,
3794      last;
3795
3796  if (typeof fn === 'function') {
3797    if (!($inject = fn.$inject)) {
3798      $inject = [];
3799      if (fn.length) {
3800        if (strictDi) {
3801          if (!isString(name) || !name) {
3802            name = fn.name || anonFn(fn);
3803          }
3804          throw $injectorMinErr('strictdi',
3805            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3806        }
3807        fnText = fn.toString().replace(STRIP_COMMENTS, '');
3808        argDecl = fnText.match(FN_ARGS);
3809        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3810          arg.replace(FN_ARG, function(all, underscore, name) {
3811            $inject.push(name);
3812          });
3813        });
3814      }
3815      fn.$inject = $inject;
3816    }
3817  } else if (isArray(fn)) {
3818    last = fn.length - 1;
3819    assertArgFn(fn[last], 'fn');
3820    $inject = fn.slice(0, last);
3821  } else {
3822    assertArgFn(fn, 'fn', true);
3823  }
3824  return $inject;
3825}
3826
3827///////////////////////////////////////
3828
3829/**
3830 * @ngdoc service
3831 * @name $injector
3832 *
3833 * @description
3834 *
3835 * `$injector` is used to retrieve object instances as defined by
3836 * {@link auto.$provide provider}, instantiate types, invoke methods,
3837 * and load modules.
3838 *
3839 * The following always holds true:
3840 *
3841 * ```js
3842 *   var $injector = angular.injector();
3843 *   expect($injector.get('$injector')).toBe($injector);
3844 *   expect($injector.invoke(function($injector) {
3845 *     return $injector;
3846 *   })).toBe($injector);
3847 * ```
3848 *
3849 * # Injection Function Annotation
3850 *
3851 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3852 * following are all valid ways of annotating function with injection arguments and are equivalent.
3853 *
3854 * ```js
3855 *   // inferred (only works if code not minified/obfuscated)
3856 *   $injector.invoke(function(serviceA){});
3857 *
3858 *   // annotated
3859 *   function explicit(serviceA) {};
3860 *   explicit.$inject = ['serviceA'];
3861 *   $injector.invoke(explicit);
3862 *
3863 *   // inline
3864 *   $injector.invoke(['serviceA', function(serviceA){}]);
3865 * ```
3866 *
3867 * ## Inference
3868 *
3869 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3870 * can then be parsed and the function arguments can be extracted. This method of discovering
3871 * annotations is disallowed when the injector is in strict mode.
3872 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3873 * argument names.
3874 *
3875 * ## `$inject` Annotation
3876 * By adding an `$inject` property onto a function the injection parameters can be specified.
3877 *
3878 * ## Inline
3879 * As an array of injection names, where the last item in the array is the function to call.
3880 */
3881
3882/**
3883 * @ngdoc method
3884 * @name $injector#get
3885 *
3886 * @description
3887 * Return an instance of the service.
3888 *
3889 * @param {string} name The name of the instance to retrieve.
3890 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3891 * @return {*} The instance.
3892 */
3893
3894/**
3895 * @ngdoc method
3896 * @name $injector#invoke
3897 *
3898 * @description
3899 * Invoke the method and supply the method arguments from the `$injector`.
3900 *
3901 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3902 *   injected according to the {@link guide/di $inject Annotation} rules.
3903 * @param {Object=} self The `this` for the invoked method.
3904 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3905 *                         object first, before the `$injector` is consulted.
3906 * @returns {*} the value returned by the invoked `fn` function.
3907 */
3908
3909/**
3910 * @ngdoc method
3911 * @name $injector#has
3912 *
3913 * @description
3914 * Allows the user to query if the particular service exists.
3915 *
3916 * @param {string} name Name of the service to query.
3917 * @returns {boolean} `true` if injector has given service.
3918 */
3919
3920/**
3921 * @ngdoc method
3922 * @name $injector#instantiate
3923 * @description
3924 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3925 * operator, and supplies all of the arguments to the constructor function as specified by the
3926 * constructor annotation.
3927 *
3928 * @param {Function} Type Annotated constructor function.
3929 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3930 * object first, before the `$injector` is consulted.
3931 * @returns {Object} new instance of `Type`.
3932 */
3933
3934/**
3935 * @ngdoc method
3936 * @name $injector#annotate
3937 *
3938 * @description
3939 * Returns an array of service names which the function is requesting for injection. This API is
3940 * used by the injector to determine which services need to be injected into the function when the
3941 * function is invoked. There are three ways in which the function can be annotated with the needed
3942 * dependencies.
3943 *
3944 * # Argument names
3945 *
3946 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3947 * by converting the function into a string using `toString()` method and extracting the argument
3948 * names.
3949 * ```js
3950 *   // Given
3951 *   function MyController($scope, $route) {
3952 *     // ...
3953 *   }
3954 *
3955 *   // Then
3956 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3957 * ```
3958 *
3959 * You can disallow this method by using strict injection mode.
3960 *
3961 * This method does not work with code minification / obfuscation. For this reason the following
3962 * annotation strategies are supported.
3963 *
3964 * # The `$inject` property
3965 *
3966 * If a function has an `$inject` property and its value is an array of strings, then the strings
3967 * represent names of services to be injected into the function.
3968 * ```js
3969 *   // Given
3970 *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
3971 *     // ...
3972 *   }
3973 *   // Define function dependencies
3974 *   MyController['$inject'] = ['$scope', '$route'];
3975 *
3976 *   // Then
3977 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3978 * ```
3979 *
3980 * # The array notation
3981 *
3982 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3983 * is very inconvenient. In these situations using the array notation to specify the dependencies in
3984 * a way that survives minification is a better choice:
3985 *
3986 * ```js
3987 *   // We wish to write this (not minification / obfuscation safe)
3988 *   injector.invoke(function($compile, $rootScope) {
3989 *     // ...
3990 *   });
3991 *
3992 *   // We are forced to write break inlining
3993 *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
3994 *     // ...
3995 *   };
3996 *   tmpFn.$inject = ['$compile', '$rootScope'];
3997 *   injector.invoke(tmpFn);
3998 *
3999 *   // To better support inline function the inline annotation is supported
4000 *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4001 *     // ...
4002 *   }]);
4003 *
4004 *   // Therefore
4005 *   expect(injector.annotate(
4006 *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4007 *    ).toEqual(['$compile', '$rootScope']);
4008 * ```
4009 *
4010 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4011 * be retrieved as described above.
4012 *
4013 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4014 *
4015 * @returns {Array.<string>} The names of the services which the function requires.
4016 */
4017
4018
4019
4020
4021/**
4022 * @ngdoc service
4023 * @name $provide
4024 *
4025 * @description
4026 *
4027 * The {@link auto.$provide $provide} service has a number of methods for registering components
4028 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4029 * {@link angular.Module}.
4030 *
4031 * An Angular **service** is a singleton object created by a **service factory**.  These **service
4032 * factories** are functions which, in turn, are created by a **service provider**.
4033 * The **service providers** are constructor functions. When instantiated they must contain a
4034 * property called `$get`, which holds the **service factory** function.
4035 *
4036 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4037 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4038 * function to get the instance of the **service**.
4039 *
4040 * Often services have no configuration options and there is no need to add methods to the service
4041 * provider.  The provider will be no more than a constructor function with a `$get` property. For
4042 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4043 * services without specifying a provider.
4044 *
4045 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4046 *     {@link auto.$injector $injector}
4047 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4048 *     providers and services.
4049 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4050 *     services, not providers.
4051 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4052 *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
4053 *     given factory function.
4054 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4055 *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4056 *      a new object using the given constructor function.
4057 *
4058 * See the individual methods for more information and examples.
4059 */
4060
4061/**
4062 * @ngdoc method
4063 * @name $provide#provider
4064 * @description
4065 *
4066 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4067 * are constructor functions, whose instances are responsible for "providing" a factory for a
4068 * service.
4069 *
4070 * Service provider names start with the name of the service they provide followed by `Provider`.
4071 * For example, the {@link ng.$log $log} service has a provider called
4072 * {@link ng.$logProvider $logProvider}.
4073 *
4074 * Service provider objects can have additional methods which allow configuration of the provider
4075 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4076 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4077 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4078 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4079 * console or not.
4080 *
4081 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4082                        'Provider'` key.
4083 * @param {(Object|function())} provider If the provider is:
4084 *
4085 *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4086 *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4087 *   - `Constructor`: a new instance of the provider will be created using
4088 *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4089 *
4090 * @returns {Object} registered provider instance
4091
4092 * @example
4093 *
4094 * The following example shows how to create a simple event tracking service and register it using
4095 * {@link auto.$provide#provider $provide.provider()}.
4096 *
4097 * ```js
4098 *  // Define the eventTracker provider
4099 *  function EventTrackerProvider() {
4100 *    var trackingUrl = '/track';
4101 *
4102 *    // A provider method for configuring where the tracked events should been saved
4103 *    this.setTrackingUrl = function(url) {
4104 *      trackingUrl = url;
4105 *    };
4106 *
4107 *    // The service factory function
4108 *    this.$get = ['$http', function($http) {
4109 *      var trackedEvents = {};
4110 *      return {
4111 *        // Call this to track an event
4112 *        event: function(event) {
4113 *          var count = trackedEvents[event] || 0;
4114 *          count += 1;
4115 *          trackedEvents[event] = count;
4116 *          return count;
4117 *        },
4118 *        // Call this to save the tracked events to the trackingUrl
4119 *        save: function() {
4120 *          $http.post(trackingUrl, trackedEvents);
4121 *        }
4122 *      };
4123 *    }];
4124 *  }
4125 *
4126 *  describe('eventTracker', function() {
4127 *    var postSpy;
4128 *
4129 *    beforeEach(module(function($provide) {
4130 *      // Register the eventTracker provider
4131 *      $provide.provider('eventTracker', EventTrackerProvider);
4132 *    }));
4133 *
4134 *    beforeEach(module(function(eventTrackerProvider) {
4135 *      // Configure eventTracker provider
4136 *      eventTrackerProvider.setTrackingUrl('/custom-track');
4137 *    }));
4138 *
4139 *    it('tracks events', inject(function(eventTracker) {
4140 *      expect(eventTracker.event('login')).toEqual(1);
4141 *      expect(eventTracker.event('login')).toEqual(2);
4142 *    }));
4143 *
4144 *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4145 *      postSpy = spyOn($http, 'post');
4146 *      eventTracker.event('login');
4147 *      eventTracker.save();
4148 *      expect(postSpy).toHaveBeenCalled();
4149 *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4150 *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4151 *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4152 *    }));
4153 *  });
4154 * ```
4155 */
4156
4157/**
4158 * @ngdoc method
4159 * @name $provide#factory
4160 * @description
4161 *
4162 * Register a **service factory**, which will be called to return the service instance.
4163 * This is short for registering a service where its provider consists of only a `$get` property,
4164 * which is the given service factory function.
4165 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4166 * configure your service in a provider.
4167 *
4168 * @param {string} name The name of the instance.
4169 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4170 *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4171 * @returns {Object} registered provider instance
4172 *
4173 * @example
4174 * Here is an example of registering a service
4175 * ```js
4176 *   $provide.factory('ping', ['$http', function($http) {
4177 *     return function ping() {
4178 *       return $http.send('/ping');
4179 *     };
4180 *   }]);
4181 * ```
4182 * You would then inject and use this service like this:
4183 * ```js
4184 *   someModule.controller('Ctrl', ['ping', function(ping) {
4185 *     ping();
4186 *   }]);
4187 * ```
4188 */
4189
4190
4191/**
4192 * @ngdoc method
4193 * @name $provide#service
4194 * @description
4195 *
4196 * Register a **service constructor**, which will be invoked with `new` to create the service
4197 * instance.
4198 * This is short for registering a service where its provider's `$get` property is the service
4199 * constructor function that will be used to instantiate the service instance.
4200 *
4201 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4202 * as a type/class.
4203 *
4204 * @param {string} name The name of the instance.
4205 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4206 *     that will be instantiated.
4207 * @returns {Object} registered provider instance
4208 *
4209 * @example
4210 * Here is an example of registering a service using
4211 * {@link auto.$provide#service $provide.service(class)}.
4212 * ```js
4213 *   var Ping = function($http) {
4214 *     this.$http = $http;
4215 *   };
4216 *
4217 *   Ping.$inject = ['$http'];
4218 *
4219 *   Ping.prototype.send = function() {
4220 *     return this.$http.get('/ping');
4221 *   };
4222 *   $provide.service('ping', Ping);
4223 * ```
4224 * You would then inject and use this service like this:
4225 * ```js
4226 *   someModule.controller('Ctrl', ['ping', function(ping) {
4227 *     ping.send();
4228 *   }]);
4229 * ```
4230 */
4231
4232
4233/**
4234 * @ngdoc method
4235 * @name $provide#value
4236 * @description
4237 *
4238 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4239 * number, an array, an object or a function.  This is short for registering a service where its
4240 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4241 * service**.
4242 *
4243 * Value services are similar to constant services, except that they cannot be injected into a
4244 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4245 * an Angular
4246 * {@link auto.$provide#decorator decorator}.
4247 *
4248 * @param {string} name The name of the instance.
4249 * @param {*} value The value.
4250 * @returns {Object} registered provider instance
4251 *
4252 * @example
4253 * Here are some examples of creating value services.
4254 * ```js
4255 *   $provide.value('ADMIN_USER', 'admin');
4256 *
4257 *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4258 *
4259 *   $provide.value('halfOf', function(value) {
4260 *     return value / 2;
4261 *   });
4262 * ```
4263 */
4264
4265
4266/**
4267 * @ngdoc method
4268 * @name $provide#constant
4269 * @description
4270 *
4271 * Register a **constant service**, such as a string, a number, an array, an object or a function,
4272 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
4273 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4274 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4275 *
4276 * @param {string} name The name of the constant.
4277 * @param {*} value The constant value.
4278 * @returns {Object} registered instance
4279 *
4280 * @example
4281 * Here a some examples of creating constants:
4282 * ```js
4283 *   $provide.constant('SHARD_HEIGHT', 306);
4284 *
4285 *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4286 *
4287 *   $provide.constant('double', function(value) {
4288 *     return value * 2;
4289 *   });
4290 * ```
4291 */
4292
4293
4294/**
4295 * @ngdoc method
4296 * @name $provide#decorator
4297 * @description
4298 *
4299 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4300 * intercepts the creation of a service, allowing it to override or modify the behavior of the
4301 * service. The object returned by the decorator may be the original service, or a new service
4302 * object which replaces or wraps and delegates to the original service.
4303 *
4304 * @param {string} name The name of the service to decorate.
4305 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4306 *    instantiated and should return the decorated service instance. The function is called using
4307 *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4308 *    Local injection arguments:
4309 *
4310 *    * `$delegate` - The original service instance, which can be monkey patched, configured,
4311 *      decorated or delegated to.
4312 *
4313 * @example
4314 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4315 * calls to {@link ng.$log#error $log.warn()}.
4316 * ```js
4317 *   $provide.decorator('$log', ['$delegate', function($delegate) {
4318 *     $delegate.warn = $delegate.error;
4319 *     return $delegate;
4320 *   }]);
4321 * ```
4322 */
4323
4324
4325function createInjector(modulesToLoad, strictDi) {
4326  strictDi = (strictDi === true);
4327  var INSTANTIATING = {},
4328      providerSuffix = 'Provider',
4329      path = [],
4330      loadedModules = new HashMap([], true),
4331      providerCache = {
4332        $provide: {
4333            provider: supportObject(provider),
4334            factory: supportObject(factory),
4335            service: supportObject(service),
4336            value: supportObject(value),
4337            constant: supportObject(constant),
4338            decorator: decorator
4339          }
4340      },
4341      providerInjector = (providerCache.$injector =
4342          createInternalInjector(providerCache, function(serviceName, caller) {
4343            if (angular.isString(caller)) {
4344              path.push(caller);
4345            }
4346            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4347          })),
4348      instanceCache = {},
4349      instanceInjector = (instanceCache.$injector =
4350          createInternalInjector(instanceCache, function(serviceName, caller) {
4351            var provider = providerInjector.get(serviceName + providerSuffix, caller);
4352            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4353          }));
4354
4355
4356  forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4357
4358  return instanceInjector;
4359
4360  ////////////////////////////////////
4361  // $provider
4362  ////////////////////////////////////
4363
4364  function supportObject(delegate) {
4365    return function(key, value) {
4366      if (isObject(key)) {
4367        forEach(key, reverseParams(delegate));
4368      } else {
4369        return delegate(key, value);
4370      }
4371    };
4372  }
4373
4374  function provider(name, provider_) {
4375    assertNotHasOwnProperty(name, 'service');
4376    if (isFunction(provider_) || isArray(provider_)) {
4377      provider_ = providerInjector.instantiate(provider_);
4378    }
4379    if (!provider_.$get) {
4380      throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4381    }
4382    return providerCache[name + providerSuffix] = provider_;
4383  }
4384
4385  function enforceReturnValue(name, factory) {
4386    return function enforcedReturnValue() {
4387      var result = instanceInjector.invoke(factory, this);
4388      if (isUndefined(result)) {
4389        throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4390      }
4391      return result;
4392    };
4393  }
4394
4395  function factory(name, factoryFn, enforce) {
4396    return provider(name, {
4397      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4398    });
4399  }
4400
4401  function service(name, constructor) {
4402    return factory(name, ['$injector', function($injector) {
4403      return $injector.instantiate(constructor);
4404    }]);
4405  }
4406
4407  function value(name, val) { return factory(name, valueFn(val), false); }
4408
4409  function constant(name, value) {
4410    assertNotHasOwnProperty(name, 'constant');
4411    providerCache[name] = value;
4412    instanceCache[name] = value;
4413  }
4414
4415  function decorator(serviceName, decorFn) {
4416    var origProvider = providerInjector.get(serviceName + providerSuffix),
4417        orig$get = origProvider.$get;
4418
4419    origProvider.$get = function() {
4420      var origInstance = instanceInjector.invoke(orig$get, origProvider);
4421      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4422    };
4423  }
4424
4425  ////////////////////////////////////
4426  // Module Loading
4427  ////////////////////////////////////
4428  function loadModules(modulesToLoad) {
4429    assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4430    var runBlocks = [], moduleFn;
4431    forEach(modulesToLoad, function(module) {
4432      if (loadedModules.get(module)) return;
4433      loadedModules.put(module, true);
4434
4435      function runInvokeQueue(queue) {
4436        var i, ii;
4437        for (i = 0, ii = queue.length; i < ii; i++) {
4438          var invokeArgs = queue[i],
4439              provider = providerInjector.get(invokeArgs[0]);
4440
4441          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4442        }
4443      }
4444
4445      try {
4446        if (isString(module)) {
4447          moduleFn = angularModule(module);
4448          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4449          runInvokeQueue(moduleFn._invokeQueue);
4450          runInvokeQueue(moduleFn._configBlocks);
4451        } else if (isFunction(module)) {
4452            runBlocks.push(providerInjector.invoke(module));
4453        } else if (isArray(module)) {
4454            runBlocks.push(providerInjector.invoke(module));
4455        } else {
4456          assertArgFn(module, 'module');
4457        }
4458      } catch (e) {
4459        if (isArray(module)) {
4460          module = module[module.length - 1];
4461        }
4462        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4463          // Safari & FF's stack traces don't contain error.message content
4464          // unlike those of Chrome and IE
4465          // So if stack doesn't contain message, we create a new string that contains both.
4466          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4467          /* jshint -W022 */
4468          e = e.message + '\n' + e.stack;
4469        }
4470        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4471                  module, e.stack || e.message || e);
4472      }
4473    });
4474    return runBlocks;
4475  }
4476
4477  ////////////////////////////////////
4478  // internal Injector
4479  ////////////////////////////////////
4480
4481  function createInternalInjector(cache, factory) {
4482
4483    function getService(serviceName, caller) {
4484      if (cache.hasOwnProperty(serviceName)) {
4485        if (cache[serviceName] === INSTANTIATING) {
4486          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4487                    serviceName + ' <- ' + path.join(' <- '));
4488        }
4489        return cache[serviceName];
4490      } else {
4491        try {
4492          path.unshift(serviceName);
4493          cache[serviceName] = INSTANTIATING;
4494          return cache[serviceName] = factory(serviceName, caller);
4495        } catch (err) {
4496          if (cache[serviceName] === INSTANTIATING) {
4497            delete cache[serviceName];
4498          }
4499          throw err;
4500        } finally {
4501          path.shift();
4502        }
4503      }
4504    }
4505
4506    function invoke(fn, self, locals, serviceName) {
4507      if (typeof locals === 'string') {
4508        serviceName = locals;
4509        locals = null;
4510      }
4511
4512      var args = [],
4513          $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4514          length, i,
4515          key;
4516
4517      for (i = 0, length = $inject.length; i < length; i++) {
4518        key = $inject[i];
4519        if (typeof key !== 'string') {
4520          throw $injectorMinErr('itkn',
4521                  'Incorrect injection token! Expected service name as string, got {0}', key);
4522        }
4523        args.push(
4524          locals && locals.hasOwnProperty(key)
4525          ? locals[key]
4526          : getService(key, serviceName)
4527        );
4528      }
4529      if (isArray(fn)) {
4530        fn = fn[length];
4531      }
4532
4533      // http://jsperf.com/angularjs-invoke-apply-vs-switch
4534      // #5388
4535      return fn.apply(self, args);
4536    }
4537
4538    function instantiate(Type, locals, serviceName) {
4539      // Check if Type is annotated and use just the given function at n-1 as parameter
4540      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4541      // Object creation: http://jsperf.com/create-constructor/2
4542      var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4543      var returnedValue = invoke(Type, instance, locals, serviceName);
4544
4545      return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4546    }
4547
4548    return {
4549      invoke: invoke,
4550      instantiate: instantiate,
4551      get: getService,
4552      annotate: createInjector.$$annotate,
4553      has: function(name) {
4554        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4555      }
4556    };
4557  }
4558}
4559
4560createInjector.$$annotate = annotate;
4561
4562/**
4563 * @ngdoc provider
4564 * @name $anchorScrollProvider
4565 *
4566 * @description
4567 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4568 * {@link ng.$location#hash $location.hash()} changes.
4569 */
4570function $AnchorScrollProvider() {
4571
4572  var autoScrollingEnabled = true;
4573
4574  /**
4575   * @ngdoc method
4576   * @name $anchorScrollProvider#disableAutoScrolling
4577   *
4578   * @description
4579   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4580   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4581   * Use this method to disable automatic scrolling.
4582   *
4583   * If automatic scrolling is disabled, one must explicitly call
4584   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4585   * current hash.
4586   */
4587  this.disableAutoScrolling = function() {
4588    autoScrollingEnabled = false;
4589  };
4590
4591  /**
4592   * @ngdoc service
4593   * @name $anchorScroll
4594   * @kind function
4595   * @requires $window
4596   * @requires $location
4597   * @requires $rootScope
4598   *
4599   * @description
4600   * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4601   * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4602   * in the
4603   * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4604   *
4605   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4606   * match any anchor whenever it changes. This can be disabled by calling
4607   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4608   *
4609   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4610   * vertical scroll-offset (either fixed or dynamic).
4611   *
4612   * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4613   *                       {@link ng.$location#hash $location.hash()} will be used.
4614   *
4615   * @property {(number|function|jqLite)} yOffset
4616   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4617   * positioned elements at the top of the page, such as navbars, headers etc.
4618   *
4619   * `yOffset` can be specified in various ways:
4620   * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4621   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4622   *   a number representing the offset (in pixels).<br /><br />
4623   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4624   *   the top of the page to the element's bottom will be used as offset.<br />
4625   *   **Note**: The element will be taken into account only as long as its `position` is set to
4626   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4627   *   their height and/or positioning according to the viewport's size.
4628   *
4629   * <br />
4630   * <div class="alert alert-warning">
4631   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4632   * not some child element.
4633   * </div>
4634   *
4635   * @example
4636     <example module="anchorScrollExample">
4637       <file name="index.html">
4638         <div id="scrollArea" ng-controller="ScrollController">
4639           <a ng-click="gotoBottom()">Go to bottom</a>
4640           <a id="bottom"></a> You're at the bottom!
4641         </div>
4642       </file>
4643       <file name="script.js">
4644         angular.module('anchorScrollExample', [])
4645           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4646             function ($scope, $location, $anchorScroll) {
4647               $scope.gotoBottom = function() {
4648                 // set the location.hash to the id of
4649                 // the element you wish to scroll to.
4650                 $location.hash('bottom');
4651
4652                 // call $anchorScroll()
4653                 $anchorScroll();
4654               };
4655             }]);
4656       </file>
4657       <file name="style.css">
4658         #scrollArea {
4659           height: 280px;
4660           overflow: auto;
4661         }
4662
4663         #bottom {
4664           display: block;
4665           margin-top: 2000px;
4666         }
4667       </file>
4668     </example>
4669   *
4670   * <hr />
4671   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4672   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4673   *
4674   * @example
4675     <example module="anchorScrollOffsetExample">
4676       <file name="index.html">
4677         <div class="fixed-header" ng-controller="headerCtrl">
4678           <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4679             Go to anchor {{x}}
4680           </a>
4681         </div>
4682         <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4683           Anchor {{x}} of 5
4684         </div>
4685       </file>
4686       <file name="script.js">
4687         angular.module('anchorScrollOffsetExample', [])
4688           .run(['$anchorScroll', function($anchorScroll) {
4689             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4690           }])
4691           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4692             function ($anchorScroll, $location, $scope) {
4693               $scope.gotoAnchor = function(x) {
4694                 var newHash = 'anchor' + x;
4695                 if ($location.hash() !== newHash) {
4696                   // set the $location.hash to `newHash` and
4697                   // $anchorScroll will automatically scroll to it
4698                   $location.hash('anchor' + x);
4699                 } else {
4700                   // call $anchorScroll() explicitly,
4701                   // since $location.hash hasn't changed
4702                   $anchorScroll();
4703                 }
4704               };
4705             }
4706           ]);
4707       </file>
4708       <file name="style.css">
4709         body {
4710           padding-top: 50px;
4711         }
4712
4713         .anchor {
4714           border: 2px dashed DarkOrchid;
4715           padding: 10px 10px 200px 10px;
4716         }
4717
4718         .fixed-header {
4719           background-color: rgba(0, 0, 0, 0.2);
4720           height: 50px;
4721           position: fixed;
4722           top: 0; left: 0; right: 0;
4723         }
4724
4725         .fixed-header > a {
4726           display: inline-block;
4727           margin: 5px 15px;
4728         }
4729       </file>
4730     </example>
4731   */
4732  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4733    var document = $window.document;
4734
4735    // Helper function to get first anchor from a NodeList
4736    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4737    //  and working in all supported browsers.)
4738    function getFirstAnchor(list) {
4739      var result = null;
4740      Array.prototype.some.call(list, function(element) {
4741        if (nodeName_(element) === 'a') {
4742          result = element;
4743          return true;
4744        }
4745      });
4746      return result;
4747    }
4748
4749    function getYOffset() {
4750
4751      var offset = scroll.yOffset;
4752
4753      if (isFunction(offset)) {
4754        offset = offset();
4755      } else if (isElement(offset)) {
4756        var elem = offset[0];
4757        var style = $window.getComputedStyle(elem);
4758        if (style.position !== 'fixed') {
4759          offset = 0;
4760        } else {
4761          offset = elem.getBoundingClientRect().bottom;
4762        }
4763      } else if (!isNumber(offset)) {
4764        offset = 0;
4765      }
4766
4767      return offset;
4768    }
4769
4770    function scrollTo(elem) {
4771      if (elem) {
4772        elem.scrollIntoView();
4773
4774        var offset = getYOffset();
4775
4776        if (offset) {
4777          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4778          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4779          // top of the viewport.
4780          //
4781          // IF the number of pixels from the top of `elem` to the end of the page's content is less
4782          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4783          // way down the page.
4784          //
4785          // This is often the case for elements near the bottom of the page.
4786          //
4787          // In such cases we do not need to scroll the whole `offset` up, just the difference between
4788          // the top of the element and the offset, which is enough to align the top of `elem` at the
4789          // desired position.
4790          var elemTop = elem.getBoundingClientRect().top;
4791          $window.scrollBy(0, elemTop - offset);
4792        }
4793      } else {
4794        $window.scrollTo(0, 0);
4795      }
4796    }
4797
4798    function scroll(hash) {
4799      hash = isString(hash) ? hash : $location.hash();
4800      var elm;
4801
4802      // empty hash, scroll to the top of the page
4803      if (!hash) scrollTo(null);
4804
4805      // element with given id
4806      else if ((elm = document.getElementById(hash))) scrollTo(elm);
4807
4808      // first anchor with given name :-D
4809      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4810
4811      // no element and hash == 'top', scroll to the top of the page
4812      else if (hash === 'top') scrollTo(null);
4813    }
4814
4815    // does not scroll when user clicks on anchor link that is currently on
4816    // (no url change, no $location.hash() change), browser native does scroll
4817    if (autoScrollingEnabled) {
4818      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4819        function autoScrollWatchAction(newVal, oldVal) {
4820          // skip the initial scroll if $location.hash is empty
4821          if (newVal === oldVal && newVal === '') return;
4822
4823          jqLiteDocumentLoaded(function() {
4824            $rootScope.$evalAsync(scroll);
4825          });
4826        });
4827    }
4828
4829    return scroll;
4830  }];
4831}
4832
4833var $animateMinErr = minErr('$animate');
4834var ELEMENT_NODE = 1;
4835var NG_ANIMATE_CLASSNAME = 'ng-animate';
4836
4837function mergeClasses(a,b) {
4838  if (!a && !b) return '';
4839  if (!a) return b;
4840  if (!b) return a;
4841  if (isArray(a)) a = a.join(' ');
4842  if (isArray(b)) b = b.join(' ');
4843  return a + ' ' + b;
4844}
4845
4846function extractElementNode(element) {
4847  for (var i = 0; i < element.length; i++) {
4848    var elm = element[i];
4849    if (elm.nodeType === ELEMENT_NODE) {
4850      return elm;
4851    }
4852  }
4853}
4854
4855function splitClasses(classes) {
4856  if (isString(classes)) {
4857    classes = classes.split(' ');
4858  }
4859
4860  // Use createMap() to prevent class assumptions involving property names in
4861  // Object.prototype
4862  var obj = createMap();
4863  forEach(classes, function(klass) {
4864    // sometimes the split leaves empty string values
4865    // incase extra spaces were applied to the options
4866    if (klass.length) {
4867      obj[klass] = true;
4868    }
4869  });
4870  return obj;
4871}
4872
4873// if any other type of options value besides an Object value is
4874// passed into the $animate.method() animation then this helper code
4875// will be run which will ignore it. While this patch is not the
4876// greatest solution to this, a lot of existing plugins depend on
4877// $animate to either call the callback (< 1.2) or return a promise
4878// that can be changed. This helper function ensures that the options
4879// are wiped clean incase a callback function is provided.
4880function prepareAnimateOptions(options) {
4881  return isObject(options)
4882      ? options
4883      : {};
4884}
4885
4886var $$CoreAnimateJsProvider = function() {
4887  this.$get = function() {};
4888};
4889
4890// this is prefixed with Core since it conflicts with
4891// the animateQueueProvider defined in ngAnimate/animateQueue.js
4892var $$CoreAnimateQueueProvider = function() {
4893  var postDigestQueue = new HashMap();
4894  var postDigestElements = [];
4895
4896  this.$get = ['$$AnimateRunner', '$rootScope',
4897       function($$AnimateRunner,   $rootScope) {
4898    return {
4899      enabled: noop,
4900      on: noop,
4901      off: noop,
4902      pin: noop,
4903
4904      push: function(element, event, options, domOperation) {
4905        domOperation        && domOperation();
4906
4907        options = options || {};
4908        options.from        && element.css(options.from);
4909        options.to          && element.css(options.to);
4910
4911        if (options.addClass || options.removeClass) {
4912          addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
4913        }
4914
4915        var runner = new $$AnimateRunner(); // jshint ignore:line
4916
4917        // since there are no animations to run the runner needs to be
4918        // notified that the animation call is complete.
4919        runner.complete();
4920        return runner;
4921      }
4922    };
4923
4924
4925    function updateData(data, classes, value) {
4926      var changed = false;
4927      if (classes) {
4928        classes = isString(classes) ? classes.split(' ') :
4929                  isArray(classes) ? classes : [];
4930        forEach(classes, function(className) {
4931          if (className) {
4932            changed = true;
4933            data[className] = value;
4934          }
4935        });
4936      }
4937      return changed;
4938    }
4939
4940    function handleCSSClassChanges() {
4941      forEach(postDigestElements, function(element) {
4942        var data = postDigestQueue.get(element);
4943        if (data) {
4944          var existing = splitClasses(element.attr('class'));
4945          var toAdd = '';
4946          var toRemove = '';
4947          forEach(data, function(status, className) {
4948            var hasClass = !!existing[className];
4949            if (status !== hasClass) {
4950              if (status) {
4951                toAdd += (toAdd.length ? ' ' : '') + className;
4952              } else {
4953                toRemove += (toRemove.length ? ' ' : '') + className;
4954              }
4955            }
4956          });
4957
4958          forEach(element, function(elm) {
4959            toAdd    && jqLiteAddClass(elm, toAdd);
4960            toRemove && jqLiteRemoveClass(elm, toRemove);
4961          });
4962          postDigestQueue.remove(element);
4963        }
4964      });
4965      postDigestElements.length = 0;
4966    }
4967
4968
4969    function addRemoveClassesPostDigest(element, add, remove) {
4970      var data = postDigestQueue.get(element) || {};
4971
4972      var classesAdded = updateData(data, add, true);
4973      var classesRemoved = updateData(data, remove, false);
4974
4975      if (classesAdded || classesRemoved) {
4976
4977        postDigestQueue.put(element, data);
4978        postDigestElements.push(element);
4979
4980        if (postDigestElements.length === 1) {
4981          $rootScope.$$postDigest(handleCSSClassChanges);
4982        }
4983      }
4984    }
4985  }];
4986};
4987
4988/**
4989 * @ngdoc provider
4990 * @name $animateProvider
4991 *
4992 * @description
4993 * Default implementation of $animate that doesn't perform any animations, instead just
4994 * synchronously performs DOM updates and resolves the returned runner promise.
4995 *
4996 * In order to enable animations the `ngAnimate` module has to be loaded.
4997 *
4998 * To see the functional implementation check out `src/ngAnimate/animate.js`.
4999 */
5000var $AnimateProvider = ['$provide', function($provide) {
5001  var provider = this;
5002
5003  this.$$registeredAnimations = Object.create(null);
5004
5005   /**
5006   * @ngdoc method
5007   * @name $animateProvider#register
5008   *
5009   * @description
5010   * Registers a new injectable animation factory function. The factory function produces the
5011   * animation object which contains callback functions for each event that is expected to be
5012   * animated.
5013   *
5014   *   * `eventFn`: `function(element, ... , doneFunction, options)`
5015   *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
5016   *   on the type of animation additional arguments will be injected into the animation function. The
5017   *   list below explains the function signatures for the different animation methods:
5018   *
5019   *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5020   *   - addClass: function(element, addedClasses, doneFunction, options)
5021   *   - removeClass: function(element, removedClasses, doneFunction, options)
5022   *   - enter, leave, move: function(element, doneFunction, options)
5023   *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
5024   *
5025   *   Make sure to trigger the `doneFunction` once the animation is fully complete.
5026   *
5027   * ```js
5028   *   return {
5029   *     //enter, leave, move signature
5030   *     eventFn : function(element, done, options) {
5031   *       //code to run the animation
5032   *       //once complete, then run done()
5033   *       return function endFunction(wasCancelled) {
5034   *         //code to cancel the animation
5035   *       }
5036   *     }
5037   *   }
5038   * ```
5039   *
5040   * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5041   * @param {Function} factory The factory function that will be executed to return the animation
5042   *                           object.
5043   */
5044  this.register = function(name, factory) {
5045    if (name && name.charAt(0) !== '.') {
5046      throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5047    }
5048
5049    var key = name + '-animation';
5050    provider.$$registeredAnimations[name.substr(1)] = key;
5051    $provide.factory(key, factory);
5052  };
5053
5054  /**
5055   * @ngdoc method
5056   * @name $animateProvider#classNameFilter
5057   *
5058   * @description
5059   * Sets and/or returns the CSS class regular expression that is checked when performing
5060   * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5061   * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5062   * When setting the `classNameFilter` value, animations will only be performed on elements
5063   * that successfully match the filter expression. This in turn can boost performance
5064   * for low-powered devices as well as applications containing a lot of structural operations.
5065   * @param {RegExp=} expression The className expression which will be checked against all animations
5066   * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5067   */
5068  this.classNameFilter = function(expression) {
5069    if (arguments.length === 1) {
5070      this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5071      if (this.$$classNameFilter) {
5072        var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5073        if (reservedRegex.test(this.$$classNameFilter.toString())) {
5074          throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5075
5076        }
5077      }
5078    }
5079    return this.$$classNameFilter;
5080  };
5081
5082  this.$get = ['$$animateQueue', function($$animateQueue) {
5083    function domInsert(element, parentElement, afterElement) {
5084      // if for some reason the previous element was removed
5085      // from the dom sometime before this code runs then let's
5086      // just stick to using the parent element as the anchor
5087      if (afterElement) {
5088        var afterNode = extractElementNode(afterElement);
5089        if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5090          afterElement = null;
5091        }
5092      }
5093      afterElement ? afterElement.after(element) : parentElement.prepend(element);
5094    }
5095
5096    /**
5097     * @ngdoc service
5098     * @name $animate
5099     * @description The $animate service exposes a series of DOM utility methods that provide support
5100     * for animation hooks. The default behavior is the application of DOM operations, however,
5101     * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5102     * to ensure that animation runs with the triggered DOM operation.
5103     *
5104     * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5105     * included and only when it is active then the animation hooks that `$animate` triggers will be
5106     * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5107     * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5108     * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5109     *
5110     * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5111     *
5112     * To learn more about enabling animation support, click here to visit the
5113     * {@link ngAnimate ngAnimate module page}.
5114     */
5115    return {
5116      // we don't call it directly since non-existant arguments may
5117      // be interpreted as null within the sub enabled function
5118
5119      /**
5120       *
5121       * @ngdoc method
5122       * @name $animate#on
5123       * @kind function
5124       * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5125       *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5126       *    is fired with the following params:
5127       *
5128       * ```js
5129       * $animate.on('enter', container,
5130       *    function callback(element, phase) {
5131       *      // cool we detected an enter animation within the container
5132       *    }
5133       * );
5134       * ```
5135       *
5136       * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5137       * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5138       *     as well as among its children
5139       * @param {Function} callback the callback function that will be fired when the listener is triggered
5140       *
5141       * The arguments present in the callback function are:
5142       * * `element` - The captured DOM element that the animation was fired on.
5143       * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5144       */
5145      on: $$animateQueue.on,
5146
5147      /**
5148       *
5149       * @ngdoc method
5150       * @name $animate#off
5151       * @kind function
5152       * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5153       * can be used in three different ways depending on the arguments:
5154       *
5155       * ```js
5156       * // remove all the animation event listeners listening for `enter`
5157       * $animate.off('enter');
5158       *
5159       * // remove all the animation event listeners listening for `enter` on the given element and its children
5160       * $animate.off('enter', container);
5161       *
5162       * // remove the event listener function provided by `listenerFn` that is set
5163       * // to listen for `enter` on the given `element` as well as its children
5164       * $animate.off('enter', container, callback);
5165       * ```
5166       *
5167       * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5168       * @param {DOMElement=} container the container element the event listener was placed on
5169       * @param {Function=} callback the callback function that was registered as the listener
5170       */
5171      off: $$animateQueue.off,
5172
5173      /**
5174       * @ngdoc method
5175       * @name $animate#pin
5176       * @kind function
5177       * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5178       *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5179       *    element despite being outside the realm of the application or within another application. Say for example if the application
5180       *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5181       *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5182       *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5183       *
5184       *    Note that this feature is only active when the `ngAnimate` module is used.
5185       *
5186       * @param {DOMElement} element the external element that will be pinned
5187       * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5188       */
5189      pin: $$animateQueue.pin,
5190
5191      /**
5192       *
5193       * @ngdoc method
5194       * @name $animate#enabled
5195       * @kind function
5196       * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5197       * function can be called in four ways:
5198       *
5199       * ```js
5200       * // returns true or false
5201       * $animate.enabled();
5202       *
5203       * // changes the enabled state for all animations
5204       * $animate.enabled(false);
5205       * $animate.enabled(true);
5206       *
5207       * // returns true or false if animations are enabled for an element
5208       * $animate.enabled(element);
5209       *
5210       * // changes the enabled state for an element and its children
5211       * $animate.enabled(element, true);
5212       * $animate.enabled(element, false);
5213       * ```
5214       *
5215       * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5216       * @param {boolean=} enabled whether or not the animations will be enabled for the element
5217       *
5218       * @return {boolean} whether or not animations are enabled
5219       */
5220      enabled: $$animateQueue.enabled,
5221
5222      /**
5223       * @ngdoc method
5224       * @name $animate#cancel
5225       * @kind function
5226       * @description Cancels the provided animation.
5227       *
5228       * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5229       */
5230      cancel: function(runner) {
5231        runner.end && runner.end();
5232      },
5233
5234      /**
5235       *
5236       * @ngdoc method
5237       * @name $animate#enter
5238       * @kind function
5239       * @description Inserts the element into the DOM either after the `after` element (if provided) or
5240       *   as the first child within the `parent` element and then triggers an animation.
5241       *   A promise is returned that will be resolved during the next digest once the animation
5242       *   has completed.
5243       *
5244       * @param {DOMElement} element the element which will be inserted into the DOM
5245       * @param {DOMElement} parent the parent element which will append the element as
5246       *   a child (so long as the after element is not present)
5247       * @param {DOMElement=} after the sibling element after which the element will be appended
5248       * @param {object=} options an optional collection of options/styles that will be applied to the element
5249       *
5250       * @return {Promise} the animation callback promise
5251       */
5252      enter: function(element, parent, after, options) {
5253        parent = parent && jqLite(parent);
5254        after = after && jqLite(after);
5255        parent = parent || after.parent();
5256        domInsert(element, parent, after);
5257        return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5258      },
5259
5260      /**
5261       *
5262       * @ngdoc method
5263       * @name $animate#move
5264       * @kind function
5265       * @description Inserts (moves) the element into its new position in the DOM either after
5266       *   the `after` element (if provided) or as the first child within the `parent` element
5267       *   and then triggers an animation. A promise is returned that will be resolved
5268       *   during the next digest once the animation has completed.
5269       *
5270       * @param {DOMElement} element the element which will be moved into the new DOM position
5271       * @param {DOMElement} parent the parent element which will append the element as
5272       *   a child (so long as the after element is not present)
5273       * @param {DOMElement=} after the sibling element after which the element will be appended
5274       * @param {object=} options an optional collection of options/styles that will be applied to the element
5275       *
5276       * @return {Promise} the animation callback promise
5277       */
5278      move: function(element, parent, after, options) {
5279        parent = parent && jqLite(parent);
5280        after = after && jqLite(after);
5281        parent = parent || after.parent();
5282        domInsert(element, parent, after);
5283        return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5284      },
5285
5286      /**
5287       * @ngdoc method
5288       * @name $animate#leave
5289       * @kind function
5290       * @description Triggers an animation and then removes the element from the DOM.
5291       * When the function is called a promise is returned that will be resolved during the next
5292       * digest once the animation has completed.
5293       *
5294       * @param {DOMElement} element the element which will be removed from the DOM
5295       * @param {object=} options an optional collection of options/styles that will be applied to the element
5296       *
5297       * @return {Promise} the animation callback promise
5298       */
5299      leave: function(element, options) {
5300        return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5301          element.remove();
5302        });
5303      },
5304
5305      /**
5306       * @ngdoc method
5307       * @name $animate#addClass
5308       * @kind function
5309       *
5310       * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5311       *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5312       *   animation if element already contains the CSS class or if the class is removed at a later step.
5313       *   Note that class-based animations are treated differently compared to structural animations
5314       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5315       *   depending if CSS or JavaScript animations are used.
5316       *
5317       * @param {DOMElement} element the element which the CSS classes will be applied to
5318       * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5319       * @param {object=} options an optional collection of options/styles that will be applied to the element
5320       *
5321       * @return {Promise} the animation callback promise
5322       */
5323      addClass: function(element, className, options) {
5324        options = prepareAnimateOptions(options);
5325        options.addClass = mergeClasses(options.addclass, className);
5326        return $$animateQueue.push(element, 'addClass', options);
5327      },
5328
5329      /**
5330       * @ngdoc method
5331       * @name $animate#removeClass
5332       * @kind function
5333       *
5334       * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5335       *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5336       *   animation if element does not contain the CSS class or if the class is added at a later step.
5337       *   Note that class-based animations are treated differently compared to structural animations
5338       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5339       *   depending if CSS or JavaScript animations are used.
5340       *
5341       * @param {DOMElement} element the element which the CSS classes will be applied to
5342       * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5343       * @param {object=} options an optional collection of options/styles that will be applied to the element
5344       *
5345       * @return {Promise} the animation callback promise
5346       */
5347      removeClass: function(element, className, options) {
5348        options = prepareAnimateOptions(options);
5349        options.removeClass = mergeClasses(options.removeClass, className);
5350        return $$animateQueue.push(element, 'removeClass', options);
5351      },
5352
5353      /**
5354       * @ngdoc method
5355       * @name $animate#setClass
5356       * @kind function
5357       *
5358       * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5359       *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5360       *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5361       *    passed. Note that class-based animations are treated differently compared to structural animations
5362       *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5363       *    depending if CSS or JavaScript animations are used.
5364       *
5365       * @param {DOMElement} element the element which the CSS classes will be applied to
5366       * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5367       * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5368       * @param {object=} options an optional collection of options/styles that will be applied to the element
5369       *
5370       * @return {Promise} the animation callback promise
5371       */
5372      setClass: function(element, add, remove, options) {
5373        options = prepareAnimateOptions(options);
5374        options.addClass = mergeClasses(options.addClass, add);
5375        options.removeClass = mergeClasses(options.removeClass, remove);
5376        return $$animateQueue.push(element, 'setClass', options);
5377      },
5378
5379      /**
5380       * @ngdoc method
5381       * @name $animate#animate
5382       * @kind function
5383       *
5384       * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5385       * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
5386       * on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and
5387       * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
5388       * style in `to`, the style in `from` is applied immediately, and no animation is run.
5389       * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
5390       * method (or as part of the `options` parameter):
5391       *
5392       * ```js
5393       * ngModule.animation('.my-inline-animation', function() {
5394       *   return {
5395       *     animate : function(element, from, to, done, options) {
5396       *       //animation
5397       *       done();
5398       *     }
5399       *   }
5400       * });
5401       * ```
5402       *
5403       * @param {DOMElement} element the element which the CSS styles will be applied to
5404       * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5405       * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5406       * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5407       *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5408       *    (Note that if no animation is detected then this value will not be appplied to the element.)
5409       * @param {object=} options an optional collection of options/styles that will be applied to the element
5410       *
5411       * @return {Promise} the animation callback promise
5412       */
5413      animate: function(element, from, to, className, options) {
5414        options = prepareAnimateOptions(options);
5415        options.from = options.from ? extend(options.from, from) : from;
5416        options.to   = options.to   ? extend(options.to, to)     : to;
5417
5418        className = className || 'ng-inline-animate';
vendor: 4,147 bytes, lines 5419-5596
5419        options.tempClasses = mergeClasses(options.tempClasses, className);
5420        return $$animateQueue.push(element, 'animate', options);
5421      }
5422    };
5423  }];
5424}];
5425
5426var $$AnimateAsyncRunFactoryProvider = function() {
5427  this.$get = ['$$rAF', function($$rAF) {
5428    var waitQueue = [];
5429
5430    function waitForTick(fn) {
5431      waitQueue.push(fn);
5432      if (waitQueue.length > 1) return;
5433      $$rAF(function() {
5434        for (var i = 0; i < waitQueue.length; i++) {
5435          waitQueue[i]();
5436        }
5437        waitQueue = [];
5438      });
5439    }
5440
5441    return function() {
5442      var passed = false;
5443      waitForTick(function() {
5444        passed = true;
5445      });
5446      return function(callback) {
5447        passed ? callback() : waitForTick(callback);
5448      };
5449    };
5450  }];
5451};
5452
5453var $$AnimateRunnerFactoryProvider = function() {
5454  this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
5455       function($q,   $sniffer,   $$animateAsyncRun,   $document,   $timeout) {
5456
5457    var INITIAL_STATE = 0;
5458    var DONE_PENDING_STATE = 1;
5459    var DONE_COMPLETE_STATE = 2;
5460
5461    AnimateRunner.chain = function(chain, callback) {
5462      var index = 0;
5463
5464      next();
5465      function next() {
5466        if (index === chain.length) {
5467          callback(true);
5468          return;
5469        }
5470
5471        chain[index](function(response) {
5472          if (response === false) {
5473            callback(false);
5474            return;
5475          }
5476          index++;
5477          next();
5478        });
5479      }
5480    };
5481
5482    AnimateRunner.all = function(runners, callback) {
5483      var count = 0;
5484      var status = true;
5485      forEach(runners, function(runner) {
5486        runner.done(onProgress);
5487      });
5488
5489      function onProgress(response) {
5490        status = status && response;
5491        if (++count === runners.length) {
5492          callback(status);
5493        }
5494      }
5495    };
5496
5497    function AnimateRunner(host) {
5498      this.setHost(host);
5499
5500      var rafTick = $$animateAsyncRun();
5501      var timeoutTick = function(fn) {
5502        $timeout(fn, 0, false);
5503      };
5504
5505      this._doneCallbacks = [];
5506      this._tick = function(fn) {
5507        var doc = $document[0];
5508
5509        // the document may not be ready or attached
5510        // to the module for some internal tests
5511        if (doc && doc.hidden) {
5512          timeoutTick(fn);
5513        } else {
5514          rafTick(fn);
5515        }
5516      };
5517      this._state = 0;
5518    }
5519
5520    AnimateRunner.prototype = {
5521      setHost: function(host) {
5522        this.host = host || {};
5523      },
5524
5525      done: function(fn) {
5526        if (this._state === DONE_COMPLETE_STATE) {
5527          fn();
5528        } else {
5529          this._doneCallbacks.push(fn);
5530        }
5531      },
5532
5533      progress: noop,
5534
5535      getPromise: function() {
5536        if (!this.promise) {
5537          var self = this;
5538          this.promise = $q(function(resolve, reject) {
5539            self.done(function(status) {
5540              status === false ? reject() : resolve();
5541            });
5542          });
5543        }
5544        return this.promise;
5545      },
5546
5547      then: function(resolveHandler, rejectHandler) {
5548        return this.getPromise().then(resolveHandler, rejectHandler);
5549      },
5550
5551      'catch': function(handler) {
5552        return this.getPromise()['catch'](handler);
5553      },
5554
5555      'finally': function(handler) {
5556        return this.getPromise()['finally'](handler);
5557      },
5558
5559      pause: function() {
5560        if (this.host.pause) {
5561          this.host.pause();
5562        }
5563      },
5564
5565      resume: function() {
5566        if (this.host.resume) {
5567          this.host.resume();
5568        }
5569      },
5570
5571      end: function() {
5572        if (this.host.end) {
5573          this.host.end();
5574        }
5575        this._resolve(true);
5576      },
5577
5578      cancel: function() {
5579        if (this.host.cancel) {
5580          this.host.cancel();
5581        }
5582        this._resolve(false);
5583      },
5584
5585      complete: function(response) {
5586        var self = this;
5587        if (self._state === INITIAL_STATE) {
5588          self._state = DONE_PENDING_STATE;
5589          self._tick(function() {
5590            self._resolve(response);
5591          });
5592        }
5593      },
5594
5595      _resolve: function(response) {
5596        if (this._state !== DONE_COMPLETE_STATE) {
5597          forEach(this._doneCallbacks, function(fn) {
5598            fn(response);
5599          });
5600          this._doneCallbacks.length = 0;
5601          this._state = DONE_COMPLETE_STATE;
5602        }
5603      }
5604    };
5605
5606    return AnimateRunner;
5607  }];
5608};
5609
5610/**
5611 * @ngdoc service
5612 * @name $animateCss
5613 * @kind object
5614 *
5615 * @description
5616 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5617 * then the `$animateCss` service will actually perform animations.
5618 *
5619 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5620 */
5621var $CoreAnimateCssProvider = function() {
5622  this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
5623
5624    return function(element, initialOptions) {
5625      // all of the animation functions should create
5626      // a copy of the options data, however, if a
5627      // parent service has already created a copy then
5628      // we should stick to using that
5629      var options = initialOptions || {};
5630      if (!options.$$prepared) {
5631        options = copy(options);
5632      }
5633
5634      // there is no point in applying the styles since
5635      // there is no animation that goes on at all in
5636      // this version of $animateCss.
5637      if (options.cleanupStyles) {
5638        options.from = options.to = null;
5639      }
5640
5641      if (options.from) {
5642        element.css(options.from);
5643        options.from = null;
5644      }
5645
5646      /* jshint newcap: false*/
5647      var closed, runner = new $$AnimateRunner();
5648      return {
5649        start: run,
5650        end: run
5651      };
5652
5653      function run() {
5654        $$rAF(function() {
5655          applyAnimationContents();
5656          if (!closed) {
5657            runner.complete();
5658          }
5659          closed = true;
5660        });
5661        return runner;
5662      }
5663
5664      function applyAnimationContents() {
5665        if (options.addClass) {
5666          element.addClass(options.addClass);
5667          options.addClass = null;
5668        }
5669        if (options.removeClass) {
5670          element.removeClass(options.removeClass);
5671          options.removeClass = null;
5672        }
5673        if (options.to) {
5674          element.css(options.to);
5675          options.to = null;
5676        }
5677      }
5678    };
5679  }];
5680};
5681
5682/* global stripHash: true */
5683
5684/**
5685 * ! This is a private undocumented service !
5686 *
5687 * @name $browser
5688 * @requires $log
5689 * @description
5690 * This object has two goals:
5691 *
5692 * - hide all the global state in the browser caused by the window object
5693 * - abstract away all the browser specific features and inconsistencies
5694 *
5695 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5696 * service, which can be used for convenient testing of the application without the interaction with
5697 * the real browser apis.
5698 */
5699/**
5700 * @param {object} window The global window object.
5701 * @param {object} document jQuery wrapped document.
5702 * @param {object} $log window.console or an object with the same interface.
5703 * @param {object} $sniffer $sniffer service
5704 */
5705function Browser(window, document, $log, $sniffer) {
5706  var self = this,
5707      rawDocument = document[0],
5708      location = window.location,
5709      history = window.history,
5710      setTimeout = window.setTimeout,
5711      clearTimeout = window.clearTimeout,
5712      pendingDeferIds = {};
5713
5714  self.isMock = false;
5715
5716  var outstandingRequestCount = 0;
5717  var outstandingRequestCallbacks = [];
5718
5719  // TODO(vojta): remove this temporary api
5720  self.$$completeOutstandingRequest = completeOutstandingRequest;
5721  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5722
5723  /**
5724   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5725   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5726   */
5727  function completeOutstandingRequest(fn) {
5728    try {
5729      fn.apply(null, sliceArgs(arguments, 1));
5730    } finally {
5731      outstandingRequestCount--;
5732      if (outstandingRequestCount === 0) {
5733        while (outstandingRequestCallbacks.length) {
5734          try {
5735            outstandingRequestCallbacks.pop()();
5736          } catch (e) {
5737            $log.error(e);
5738          }
5739        }
5740      }
5741    }
5742  }
5743
5744  function getHash(url) {
5745    var index = url.indexOf('#');
5746    return index === -1 ? '' : url.substr(index);
5747  }
5748
5749  /**
5750   * @private
5751   * Note: this method is used only by scenario runner
5752   * TODO(vojta): prefix this method with $$ ?
5753   * @param {function()} callback Function that will be called when no outstanding request
5754   */
5755  self.notifyWhenNoOutstandingRequests = function(callback) {
5756    if (outstandingRequestCount === 0) {
5757      callback();
5758    } else {
5759      outstandingRequestCallbacks.push(callback);
5760    }
5761  };
5762
5763  //////////////////////////////////////////////////////////////
5764  // URL API
5765  //////////////////////////////////////////////////////////////
5766
5767  var cachedState, lastHistoryState,
5768      lastBrowserUrl = location.href,
5769      baseElement = document.find('base'),
5770      pendingLocation = null;
5771
5772  cacheState();
5773  lastHistoryState = cachedState;
5774
5775  /**
5776   * @name $browser#url
5777   *
5778   * @description
5779   * GETTER:
5780   * Without any argument, this method just returns current value of location.href.
5781   *
5782   * SETTER:
5783   * With at least one argument, this method sets url to new value.
5784   * If html5 history api supported, pushState/replaceState is used, otherwise
5785   * location.href/location.replace is used.
5786   * Returns its own instance to allow chaining
5787   *
5788   * NOTE: this api is intended for use only by the $location service. Please use the
5789   * {@link ng.$location $location service} to change url.
5790   *
5791   * @param {string} url New url (when used as setter)
5792   * @param {boolean=} replace Should new url replace current history record?
5793   * @param {object=} state object to use with pushState/replaceState
5794   */
5795  self.url = function(url, replace, state) {
5796    // In modern browsers `history.state` is `null` by default; treating it separately
5797    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5798    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5799    if (isUndefined(state)) {
5800      state = null;
5801    }
5802
5803    // Android Browser BFCache causes location, history reference to become stale.
5804    if (location !== window.location) location = window.location;
5805    if (history !== window.history) history = window.history;
5806
5807    // setter
5808    if (url) {
5809      var sameState = lastHistoryState === state;
5810
5811      // Don't change anything if previous and current URLs and states match. This also prevents
5812      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5813      // See https://github.com/angular/angular.js/commit/ffb2701
5814      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5815        return self;
5816      }
5817      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5818      lastBrowserUrl = url;
5819      lastHistoryState = state;
5820      // Don't use history API if only the hash changed
5821      // due to a bug in IE10/IE11 which leads
5822      // to not firing a `hashchange` nor `popstate` event
5823      // in some cases (see #9143).
5824      if ($sniffer.history && (!sameBase || !sameState)) {
5825        history[replace ? 'replaceState' : 'pushState'](state, '', url);
5826        cacheState();
5827        // Do the assignment again so that those two variables are referentially identical.
5828        lastHistoryState = cachedState;
5829      } else {
5830        if (!sameBase || pendingLocation) {
5831          pendingLocation = url;
5832        }
5833        if (replace) {
5834          location.replace(url);
5835        } else if (!sameBase) {
5836          location.href = url;
5837        } else {
5838          location.hash = getHash(url);
5839        }
5840        if (location.href !== url) {
5841          pendingLocation = url;
5842        }
5843      }
5844      return self;
5845    // getter
5846    } else {
5847      // - pendingLocation is needed as browsers don't allow to read out
5848      //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5849      //   https://openradar.appspot.com/22186109).
5850      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5851      return pendingLocation || location.href.replace(/%27/g,"'");
5852    }
5853  };
5854
5855  /**
5856   * @name $browser#state
5857   *
5858   * @description
5859   * This method is a getter.
5860   *
5861   * Return history.state or null if history.state is undefined.
5862   *
5863   * @returns {object} state
5864   */
5865  self.state = function() {
5866    return cachedState;
5867  };
5868
5869  var urlChangeListeners = [],
5870      urlChangeInit = false;
5871
5872  function cacheStateAndFireUrlChange() {
5873    pendingLocation = null;
5874    cacheState();
5875    fireUrlChange();
5876  }
5877
5878  function getCurrentState() {
5879    try {
5880      return history.state;
5881    } catch (e) {
5882      // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5883    }
5884  }
5885
5886  // This variable should be used *only* inside the cacheState function.
5887  var lastCachedState = null;
5888  function cacheState() {
5889    // This should be the only place in $browser where `history.state` is read.
5890    cachedState = getCurrentState();
5891    cachedState = isUndefined(cachedState) ? null : cachedState;
5892
5893    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5894    if (equals(cachedState, lastCachedState)) {
5895      cachedState = lastCachedState;
5896    }
5897    lastCachedState = cachedState;
5898  }
5899
5900  function fireUrlChange() {
5901    if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5902      return;
5903    }
5904
5905    lastBrowserUrl = self.url();
5906    lastHistoryState = cachedState;
5907    forEach(urlChangeListeners, function(listener) {
5908      listener(self.url(), cachedState);
5909    });
5910  }
5911
5912  /**
5913   * @name $browser#onUrlChange
5914   *
5915   * @description
5916   * Register callback function that will be called, when url changes.
5917   *
5918   * It's only called when the url is changed from outside of angular:
5919   * - user types different url into address bar
5920   * - user clicks on history (forward/back) button
5921   * - user clicks on a link
5922   *
5923   * It's not called when url is changed by $browser.url() method
5924   *
5925   * The listener gets called with new url as parameter.
5926   *
5927   * NOTE: this api is intended for use only by the $location service. Please use the
5928   * {@link ng.$location $location service} to monitor url changes in angular apps.
5929   *
5930   * @param {function(string)} listener Listener function to be called when url changes.
5931   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5932   */
5933  self.onUrlChange = function(callback) {
5934    // TODO(vojta): refactor to use node's syntax for events
5935    if (!urlChangeInit) {
5936      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5937      // don't fire popstate when user change the address bar and don't fire hashchange when url
5938      // changed by push/replaceState
5939
5940      // html5 history api - popstate event
5941      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5942      // hashchange event
5943      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5944
5945      urlChangeInit = true;
5946    }
5947
5948    urlChangeListeners.push(callback);
5949    return callback;
5950  };
5951
5952  /**
5953   * @private
5954   * Remove popstate and hashchange handler from window.
5955   *
5956   * NOTE: this api is intended for use only by $rootScope.
5957   */
5958  self.$$applicationDestroyed = function() {
5959    jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5960  };
5961
5962  /**
5963   * Checks whether the url has changed outside of Angular.
5964   * Needs to be exported to be able to check for changes that have been done in sync,
5965   * as hashchange/popstate events fire in async.
5966   */
5967  self.$$checkUrlChange = fireUrlChange;
5968
5969  //////////////////////////////////////////////////////////////
5970  // Misc API
5971  //////////////////////////////////////////////////////////////
5972
5973  /**
5974   * @name $browser#baseHref
5975   *
5976   * @description
5977   * Returns current <base href>
5978   * (always relative - without domain)
5979   *
5980   * @returns {string} The current base href
5981   */
5982  self.baseHref = function() {
5983    var href = baseElement.attr('href');
5984    return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5985  };
5986
5987  /**
5988   * @name $browser#defer
5989   * @param {function()} fn A function, who's execution should be deferred.
5990   * @param {number=} [delay=0] of milliseconds to defer the function execution.
5991   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5992   *
5993   * @description
5994   * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5995   *
5996   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5997   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5998   * via `$browser.defer.flush()`.
5999   *
6000   */
6001  self.defer = function(fn, delay) {
6002    var timeoutId;
6003    outstandingRequestCount++;
6004    timeoutId = setTimeout(function() {
6005      delete pendingDeferIds[timeoutId];
6006      completeOutstandingRequest(fn);
6007    }, delay || 0);
6008    pendingDeferIds[timeoutId] = true;
6009    return timeoutId;
6010  };
6011
6012
6013  /**
6014   * @name $browser#defer.cancel
6015   *
6016   * @description
6017   * Cancels a deferred task identified with `deferId`.
6018   *
6019   * @param {*} deferId Token returned by the `$browser.defer` function.
6020   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
6021   *                    canceled.
6022   */
6023  self.defer.cancel = function(deferId) {
6024    if (pendingDeferIds[deferId]) {
6025      delete pendingDeferIds[deferId];
6026      clearTimeout(deferId);
6027      completeOutstandingRequest(noop);
6028      return true;
6029    }
6030    return false;
6031  };
6032
6033}
6034
6035function $BrowserProvider() {
6036  this.$get = ['$window', '$log', '$sniffer', '$document',
6037      function($window, $log, $sniffer, $document) {
6038        return new Browser($window, $document, $log, $sniffer);
6039      }];
6040}
6041
6042/**
6043 * @ngdoc service
6044 * @name $cacheFactory
6045 *
6046 * @description
6047 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
6048 * them.
6049 *
6050 * ```js
6051 *
6052 *  var cache = $cacheFactory('cacheId');
6053 *  expect($cacheFactory.get('cacheId')).toBe(cache);
6054 *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
6055 *
6056 *  cache.put("key", "value");
6057 *  cache.put("another key", "another value");
6058 *
6059 *  // We've specified no options on creation
6060 *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
6061 *
6062 * ```
6063 *
6064 *
6065 * @param {string} cacheId Name or id of the newly created cache.
6066 * @param {object=} options Options object that specifies the cache behavior. Properties:
6067 *
6068 *   - `{number=}` `capacity` — turns the cache into LRU cache.
6069 *
6070 * @returns {object} Newly created cache object with the following set of methods:
6071 *
6072 * - `{object}` `info()` — Returns id, size, and options of cache.
6073 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
6074 *   it.
6075 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
6076 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
6077 * - `{void}` `removeAll()` — Removes all cached values.
6078 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
6079 *
6080 * @example
6081   <example module="cacheExampleApp">
6082     <file name="index.html">
6083       <div ng-controller="CacheController">
6084         <input ng-model="newCacheKey" placeholder="Key">
6085         <input ng-model="newCacheValue" placeholder="Value">
6086         <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
6087
6088         <p ng-if="keys.length">Cached Values</p>
6089         <div ng-repeat="key in keys">
6090           <span ng-bind="key"></span>
6091           <span>: </span>
6092           <b ng-bind="cache.get(key)"></b>
6093         </div>
6094
6095         <p>Cache Info</p>
6096         <div ng-repeat="(key, value) in cache.info()">
6097           <span ng-bind="key"></span>
6098           <span>: </span>
6099           <b ng-bind="value"></b>
6100         </div>
6101       </div>
6102     </file>
6103     <file name="script.js">
6104       angular.module('cacheExampleApp', []).
6105         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6106           $scope.keys = [];
6107           $scope.cache = $cacheFactory('cacheId');
6108           $scope.put = function(key, value) {
6109             if (angular.isUndefined($scope.cache.get(key))) {
6110               $scope.keys.push(key);
6111             }
6112             $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6113           };
6114         }]);
6115     </file>
6116     <file name="style.css">
6117       p {
6118         margin: 10px 0 3px;
6119       }
6120     </file>
6121   </example>
6122 */
6123function $CacheFactoryProvider() {
6124
6125  this.$get = function() {
6126    var caches = {};
6127
6128    function cacheFactory(cacheId, options) {
6129      if (cacheId in caches) {
6130        throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6131      }
6132
6133      var size = 0,
6134          stats = extend({}, options, {id: cacheId}),
6135          data = createMap(),
6136          capacity = (options && options.capacity) || Number.MAX_VALUE,
6137          lruHash = createMap(),
6138          freshEnd = null,
6139          staleEnd = null;
6140
6141      /**
6142       * @ngdoc type
6143       * @name $cacheFactory.Cache
6144       *
6145       * @description
6146       * A cache object used to store and retrieve data, primarily used by
6147       * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6148       * templates and other data.
6149       *
6150       * ```js
6151       *  angular.module('superCache')
6152       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6153       *      return $cacheFactory('super-cache');
6154       *    }]);
6155       * ```
6156       *
6157       * Example test:
6158       *
6159       * ```js
6160       *  it('should behave like a cache', inject(function(superCache) {
6161       *    superCache.put('key', 'value');
6162       *    superCache.put('another key', 'another value');
6163       *
6164       *    expect(superCache.info()).toEqual({
6165       *      id: 'super-cache',
6166       *      size: 2
6167       *    });
6168       *
6169       *    superCache.remove('another key');
6170       *    expect(superCache.get('another key')).toBeUndefined();
6171       *
6172       *    superCache.removeAll();
6173       *    expect(superCache.info()).toEqual({
6174       *      id: 'super-cache',
6175       *      size: 0
6176       *    });
6177       *  }));
6178       * ```
6179       */
6180      return caches[cacheId] = {
6181
6182        /**
6183         * @ngdoc method
6184         * @name $cacheFactory.Cache#put
6185         * @kind function
6186         *
6187         * @description
6188         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6189         * retrieved later, and incrementing the size of the cache if the key was not already
6190         * present in the cache. If behaving like an LRU cache, it will also remove stale
6191         * entries from the set.
6192         *
6193         * It will not insert undefined values into the cache.
6194         *
6195         * @param {string} key the key under which the cached data is stored.
6196         * @param {*} value the value to store alongside the key. If it is undefined, the key
6197         *    will not be stored.
6198         * @returns {*} the value stored.
6199         */
6200        put: function(key, value) {
6201          if (isUndefined(value)) return;
6202          if (capacity < Number.MAX_VALUE) {
6203            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6204
6205            refresh(lruEntry);
6206          }
6207
6208          if (!(key in data)) size++;
6209          data[key] = value;
6210
6211          if (size > capacity) {
6212            this.remove(staleEnd.key);
6213          }
6214
6215          return value;
6216        },
6217
6218        /**
6219         * @ngdoc method
6220         * @name $cacheFactory.Cache#get
6221         * @kind function
6222         *
6223         * @description
6224         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6225         *
6226         * @param {string} key the key of the data to be retrieved
6227         * @returns {*} the value stored.
6228         */
6229        get: function(key) {
6230          if (capacity < Number.MAX_VALUE) {
6231            var lruEntry = lruHash[key];
6232
6233            if (!lruEntry) return;
6234
6235            refresh(lruEntry);
6236          }
6237
6238          return data[key];
6239        },
6240
6241
6242        /**
6243         * @ngdoc method
6244         * @name $cacheFactory.Cache#remove
6245         * @kind function
6246         *
6247         * @description
6248         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6249         *
6250         * @param {string} key the key of the entry to be removed
6251         */
6252        remove: function(key) {
6253          if (capacity < Number.MAX_VALUE) {
6254            var lruEntry = lruHash[key];
6255
6256            if (!lruEntry) return;
6257
6258            if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6259            if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6260            link(lruEntry.n,lruEntry.p);
6261
6262            delete lruHash[key];
6263          }
6264
6265          if (!(key in data)) return;
6266
6267          delete data[key];
6268          size--;
6269        },
6270
6271
6272        /**
6273         * @ngdoc method
6274         * @name $cacheFactory.Cache#removeAll
6275         * @kind function
6276         *
6277         * @description
6278         * Clears the cache object of any entries.
6279         */
6280        removeAll: function() {
6281          data = createMap();
6282          size = 0;
6283          lruHash = createMap();
6284          freshEnd = staleEnd = null;
6285        },
6286
6287
6288        /**
6289         * @ngdoc method
6290         * @name $cacheFactory.Cache#destroy
6291         * @kind function
6292         *
6293         * @description
6294         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6295         * removing it from the {@link $cacheFactory $cacheFactory} set.
6296         */
6297        destroy: function() {
6298          data = null;
6299          stats = null;
6300          lruHash = null;
6301          delete caches[cacheId];
6302        },
6303
6304
6305        /**
6306         * @ngdoc method
6307         * @name $cacheFactory.Cache#info
6308         * @kind function
6309         *
6310         * @description
6311         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6312         *
6313         * @returns {object} an object with the following properties:
6314         *   <ul>
6315         *     <li>**id**: the id of the cache instance</li>
6316         *     <li>**size**: the number of entries kept in the cache instance</li>
6317         *     <li>**...**: any additional properties from the options object when creating the
6318         *       cache.</li>
6319         *   </ul>
6320         */
6321        info: function() {
6322          return extend({}, stats, {size: size});
6323        }
6324      };
6325
6326
6327      /**
6328       * makes the `entry` the freshEnd of the LRU linked list
6329       */
6330      function refresh(entry) {
6331        if (entry != freshEnd) {
6332          if (!staleEnd) {
6333            staleEnd = entry;
6334          } else if (staleEnd == entry) {
6335            staleEnd = entry.n;
6336          }
6337
6338          link(entry.n, entry.p);
6339          link(entry, freshEnd);
6340          freshEnd = entry;
6341          freshEnd.n = null;
6342        }
6343      }
6344
6345
6346      /**
6347       * bidirectionally links two entries of the LRU linked list
6348       */
6349      function link(nextEntry, prevEntry) {
6350        if (nextEntry != prevEntry) {
6351          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6352          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6353        }
6354      }
6355    }
6356
6357
6358  /**
6359   * @ngdoc method
6360   * @name $cacheFactory#info
6361   *
6362   * @description
6363   * Get information about all the caches that have been created
6364   *
6365   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6366   */
6367    cacheFactory.info = function() {
6368      var info = {};
6369      forEach(caches, function(cache, cacheId) {
6370        info[cacheId] = cache.info();
6371      });
6372      return info;
6373    };
6374
6375
6376  /**
6377   * @ngdoc method
6378   * @name $cacheFactory#get
6379   *
6380   * @description
6381   * Get access to a cache object by the `cacheId` used when it was created.
6382   *
6383   * @param {string} cacheId Name or id of a cache to access.
6384   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6385   */
6386    cacheFactory.get = function(cacheId) {
6387      return caches[cacheId];
6388    };
6389
6390
6391    return cacheFactory;
6392  };
6393}
6394
6395/**
6396 * @ngdoc service
6397 * @name $templateCache
6398 *
6399 * @description
6400 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6401 * can load templates directly into the cache in a `script` tag, or by consuming the
6402 * `$templateCache` service directly.
6403 *
6404 * Adding via the `script` tag:
6405 *
6406 * ```html
6407 *   <script type="text/ng-template" id="templateId.html">
6408 *     <p>This is the content of the template</p>
6409 *   </script>
6410 * ```
6411 *
6412 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6413 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6414 * element with ng-app attribute), otherwise the template will be ignored.
6415 *
6416 * Adding via the `$templateCache` service:
6417 *
6418 * ```js
6419 * var myApp = angular.module('myApp', []);
6420 * myApp.run(function($templateCache) {
6421 *   $templateCache.put('templateId.html', 'This is the content of the template');
6422 * });
6423 * ```
6424 *
6425 * To retrieve the template later, simply use it in your HTML:
6426 * ```html
6427 * <div ng-include=" 'templateId.html' "></div>
6428 * ```
6429 *
6430 * or get it via Javascript:
6431 * ```js
6432 * $templateCache.get('templateId.html')
6433 * ```
6434 *
6435 * See {@link ng.$cacheFactory $cacheFactory}.
6436 *
6437 */
6438function $TemplateCacheProvider() {
6439  this.$get = ['$cacheFactory', function($cacheFactory) {
6440    return $cacheFactory('templates');
6441  }];
6442}
6443
6444/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6445 *     Any commits to this file should be reviewed with security in mind.  *
6446 *   Changes to this file can potentially create security vulnerabilities. *
6447 *          An approval from 2 Core members with history of modifying      *
6448 *                         this file is required.                          *
6449 *                                                                         *
6450 *  Does the change somehow allow for arbitrary javascript to be executed? *
6451 *    Or allows for someone to change the prototype of built-in objects?   *
6452 *     Or gives undesired access to variables likes document or window?    *
6453 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6454
6455/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6456 *
6457 * DOM-related variables:
6458 *
6459 * - "node" - DOM Node
6460 * - "element" - DOM Element or Node
6461 * - "$node" or "$element" - jqLite-wrapped node or element
6462 *
6463 *
6464 * Compiler related stuff:
6465 *
6466 * - "linkFn" - linking fn of a single directive
6467 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6468 * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
6469 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6470 */
6471
6472
6473/**
6474 * @ngdoc service
6475 * @name $compile
6476 * @kind function
6477 *
6478 * @description
6479 * Compiles an HTML string or DOM into a template and produces a template function, which
6480 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6481 *
6482 * The compilation is a process of walking the DOM tree and matching DOM elements to
6483 * {@link ng.$compileProvider#directive directives}.
6484 *
6485 * <div class="alert alert-warning">
6486 * **Note:** This document is an in-depth reference of all directive options.
6487 * For a gentle introduction to directives with examples of common use cases,
6488 * see the {@link guide/directive directive guide}.
6489 * </div>
6490 *
6491 * ## Comprehensive Directive API
6492 *
6493 * There are many different options for a directive.
6494 *
6495 * The difference resides in the return value of the factory function.
6496 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6497 * or just the `postLink` function (all other properties will have the default values).
6498 *
6499 * <div class="alert alert-success">
6500 * **Best Practice:** It's recommended to use the "directive definition object" form.
6501 * </div>
6502 *
6503 * Here's an example directive declared with a Directive Definition Object:
6504 *
6505 * ```js
6506 *   var myModule = angular.module(...);
6507 *
6508 *   myModule.directive('directiveName', function factory(injectables) {
6509 *     var directiveDefinitionObject = {
6510 *       priority: 0,
6511 *       template: '<div></div>
6511', // or // function(tElement, tAttrs) { ... },
6512 *       // or
6513 *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6514 *       transclude: false,
6515 *       restrict: 'A',
6516 *       templateNamespace: 'html',
6517 *       scope: false,
6518 *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6519 *       controllerAs: 'stringIdentifier',
6520 *       bindToController: false,
6521 *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6522 *       compile: function compile(tElement, tAttrs, transclude) {
6523 *         return {
6524 *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6525 *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
6526 *         }
6527 *         // or
6528 *         // return function postLink( ... ) { ... }
6529 *       },
6530 *       // or
6531 *       // link: {
6532 *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6533 *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
6534 *       // }
6535 *       // or
6536 *       // link: function postLink( ... ) { ... }
6537 *     };
6538 *     return directiveDefinitionObject;
6539 *   });
6540 * ```
6541 *
6542 * <div class="alert alert-warning">
6543 * **Note:** Any unspecified options will use the default value. You can see the default values below.
6544 * </div>
6545 *
6546 * Therefore the above can be simplified as:
6547 *
6548 * ```js
6549 *   var myModule = angular.module(...);
6550 *
6551 *   myModule.directive('directiveName', function factory(injectables) {
6552 *     var directiveDefinitionObject = {
6553 *       link: function postLink(scope, iElement, iAttrs) { ... }
6554 *     };
6555 *     return directiveDefinitionObject;
6556 *     // or
6557 *     // return function postLink(scope, iElement, iAttrs) { ... }
6558 *   });
6559 * ```
6560 *
6561 *
6562 *
6563 * ### Directive Definition Object
6564 *
6565 * The directive definition object provides instructions to the {@link ng.$compile
6566 * compiler}. The attributes are:
6567 *
6568 * #### `multiElement`
6569 * When this property is set to true, the HTML compiler will collect DOM nodes between
6570 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6571 * together as the directive elements. It is recommended that this feature be used on directives
6572 * which are not strictly behavioural (such as {@link ngClick}), and which
6573 * do not manipulate or replace child nodes (such as {@link ngInclude}).
6574 *
6575 * #### `priority`
6576 * When there are multiple directives defined on a single DOM element, sometimes it
6577 * is necessary to specify the order in which the directives are applied. The `priority` is used
6578 * to sort the directives before their `compile` functions get called. Priority is defined as a
6579 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6580 * are also run in priority order, but post-link functions are run in reverse order. The order
6581 * of directives with the same priority is undefined. The default priority is `0`.
6582 *
6583 * #### `terminal`
6584 * If set to true then the current `priority` will be the last set of directives
6585 * which will execute (any directives at the current priority will still execute
6586 * as the order of execution on same `priority` is undefined). Note that expressions
6587 * and other directives used in the directive's template will also be excluded from execution.
6588 *
6589 * #### `scope`
6590 * The scope property can be `true`, an object or a falsy value:
6591 *
6592 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6593 *
6594 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6595 * the directive's element. If multiple directives on the same element request a new scope,
6596 * only one new scope is created. The new scope rule does not apply for the root of the template
6597 * since the root of the template always gets a new scope.
6598 *
6599 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6600 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6601 * scope. This is useful when creating reusable components, which should not accidentally read or modify
6602 * data in the parent scope.
6603 *
6604 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6605 * directive's element. These local properties are useful for aliasing values for templates. The keys in
6606 * the object hash map to the name of the property on the isolate scope; the values define how the property
6607 * is bound to the parent scope, via matching attributes on the directive's element:
6608 *
6609 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6610 *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
6611 *   attribute name is assumed to be the same as the local name.
6612 *   Given `<widget my-attr="hello {{name}}">` and widget definition
6613 *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
6614 *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
6615 *   `localName` property on the widget scope. The `name` is read from the parent scope (not
6616 *   component scope).
6617 *
6618 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
6619 *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
6620 *   name is specified then the attribute name is assumed to be the same as the local name.
6621 *   Given `<widget my-attr="parentModel">` and widget definition of
6622 *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
6623 *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6624 *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
6625 *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
6626 *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
6627 *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
6628 *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
6629 *
6630 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
6631 *   If no `attr` name is specified then the attribute name is assumed to be the same as the
6632 *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
6633 *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
6634 *   a function wrapper for the `count = count + value` expression. Often it's desirable to
6635 *   pass data from the isolated scope via an expression to the parent scope, this can be
6636 *   done by passing a map of local variable names and values into the expression wrapper fn.
6637 *   For example, if the expression is `increment(amount)` then we can specify the amount value
6638 *   by calling the `localFn` as `localFn({amount: 22})`.
6639 *
6640 * In general it's possible to apply more than one directive to one element, but there might be limitations
6641 * depending on the type of scope required by the directives. The following points will help explain these limitations.
6642 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6643 *
6644 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6645 * * **child scope** + **no scope** =>  Both directives will share one single child scope
6646 * * **child scope** + **child scope** =>  Both directives will share one single child scope
6647 * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
6648 * its parent's scope
6649 * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6650 * be applied to the same element.
6651 * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
6652 * cannot be applied to the same element.
6653 *
6654 *
6655 * #### `bindToController`
6656 * This property is used to bind scope properties directly to the controller. It can be either
6657 * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
6658 * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
6659 * definition: `controller: 'myCtrl as myAlias'`.
6660 *
6661 * When an isolate scope is used for a directive (see above), `bindToController: true` will
6662 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6663 * is instantiated, the initial values of the isolate scope bindings are already available.
6664 *
6665 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
6666 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
6667 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
6668 * scope (useful for component directives).
6669 *
6670 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
6671 *
6672 *
6673 * #### `controller`
6674 * Controller constructor function. The controller is instantiated before the
6675 * pre-linking phase and can be accessed by other directives (see
6676 * `require` attribute). This allows the directives to communicate with each other and augment
6677 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6678 *
6679 * * `$scope` - Current scope associated with the element
6680 * * `$element` - Current element
6681 * * `$attrs` - Current attributes object for the element
6682 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6683 *   `function([scope], cloneLinkingFn, futureParentElement)`.
6684 *    * `scope`: optional argument to override the scope.
6685 *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6686 *    * `futureParentElement`:
6687 *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6688 *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6689 *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6690 *          and when the `cloneLinkinFn` is passed,
6691 *          as those elements need to created and cloned in a special way when they are defined outside their
6692 *          usual containers (e.g. like `<svg>`).
6693 *        * See also the `directive.templateNamespace` property.
6694 *
6695 *
6696 * #### `require`
6697 * Require another directive and inject its controller as the fourth argument to the linking function. The
6698 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6699 * injected argument will be an array in corresponding order. If no such directive can be
6700 * found, or if the directive does not have a controller, then an error is raised (unless no link function
6701 * is specified, in which case error checking is skipped). The name can be prefixed with:
6702 *
6703 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6704 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6705 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6706 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6707 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6708 *   `null` to the `link` fn if not found.
6709 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6710 *   `null` to the `link` fn if not found.
6711 *
6712 *
6713 * #### `controllerAs`
6714 * Identifier name for a reference to the controller in the directive's scope.
6715 * This allows the controller to be referenced from the directive template. This is especially
6716 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6717 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6718 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6719 *
6720 *
6721 * #### `restrict`
6722 * String of subset of `EACM` which restricts the directive to a specific directive
6723 * declaration style. If omitted, the defaults (elements and attributes) are used.
6724 *
6725 * * `E` - Element name (default): `<my-directive></my-directive>`
6726 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6727 * * `C` - Class: `<div class="my-directive: exp;"></div>`
6728 * * `M` - Comment: `<!-- directive: my-directive exp -->`
6729 *
6730 *
6731 * #### `templateNamespace`
6732 * String representing the document type used by the markup in the template.
6733 * AngularJS needs this information as those elements need to be created and cloned
6734 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6735 *
6736 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6737 *   top-level elements such as `<svg>` or `<math>`.
6738 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6739 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6740 *
6741 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6742 *
6743 * #### `template`
6744 * HTML markup that may:
6745 * * Replace the contents of the directive's element (default).
6746 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6747 * * Wrap the contents of the directive's element (if `transclude` is true).
6748 *
6749 * Value may be:
6750 *
6751 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6752 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6753 *   function api below) and returns a string value.
6754 *
6755 *
6756 * #### `templateUrl`
6757 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6758 *
6759 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6760 * for later when the template has been resolved.  In the meantime it will continue to compile and link
6761 * sibling and parent elements as though this element had not contained any directives.
6762 *
6763 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6764 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6765 * case when only one deeply nested directive has `templateUrl`.
6766 *
6767 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6768 *
6769 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6770 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6771 * a string value representing the url.  In either case, the template URL is passed through {@link
6772 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6773 *
6774 *
6775 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6776 * specify what the template should replace. Defaults to `false`.
6777 *
6778 * * `true` - the template will replace the directive's element.
6779 * * `false` - the template will replace the contents of the directive's element.
6780 *
6781 * The replacement process migrates all of the attributes / classes from the old element to the new
6782 * one. See the {@link guide/directive#template-expanding-directive
6783 * Directives Guide} for an example.
6784 *
6785 * There are very few scenarios where element replacement is required for the application function,
6786 * the main one being reusable custom components that are used within SVG contexts
6787 * (because SVG doesn't work with custom elements in the DOM tree).
6788 *
6789 * #### `transclude`
6790 * Extract the contents of the element where the directive appears and make it available to the directive.
6791 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6792 * {@link $compile#transclusion Transclusion} section below.
6793 *
6794 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6795 * directive's element or the entire element:
6796 *
6797 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6798 * * `'element'` - transclude the whole of the directive's element including any directives on this
6799 *   element that defined at a lower priority than this directive. When used, the `template`
6800 *   property is ignored.
6801 *
6802 *
6803 * #### `compile`
6804 *
6805 * ```js
6806 *   function compile(tElement, tAttrs, transclude) { ... }
6807 * ```
6808 *
6809 * The compile function deals with transforming the template DOM. Since most directives do not do
6810 * template transformation, it is not used often. The compile function takes the following arguments:
6811 *
6812 *   * `tElement` - template element - The element where the directive has been declared. It is
6813 *     safe to do template transformation on the element and child elements only.
6814 *
6815 *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6816 *     between all directive compile functions.
6817 *
6818 *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6819 *
6820 * <div class="alert alert-warning">
6821 * **Note:** The template instance and the link instance may be different objects if the template has
6822 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6823 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6824 * should be done in a linking function rather than in a compile function.
6825 * </div>
6826
6827 * <div class="alert alert-warning">
6828 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6829 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6830 * stack overflow errors.
6831 *
6832 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6833 * a directive's template instead of relying on automatic template compilation via `template` or
6834 * `templateUrl` declaration or manual compilation inside the compile function.
6835 * </div>
6836 *
6837 * <div class="alert alert-danger">
6838 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6839 *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6840 *   to the link function instead.
6841 * </div>
6842
6843 * A compile function can have a return value which can be either a function or an object.
6844 *
6845 * * returning a (post-link) function - is equivalent to registering the linking function via the
6846 *   `link` property of the config object when the compile function is empty.
6847 *
6848 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6849 *   control when a linking function should be called during the linking phase. See info about
6850 *   pre-linking and post-linking functions below.
6851 *
6852 *
6853 * #### `link`
6854 * This property is used only if the `compile` property is not defined.
6855 *
6856 * ```js
6857 *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6858 * ```
6859 *
6860 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6861 * executed after the template has been cloned. This is where most of the directive logic will be
6862 * put.
6863 *
6864 *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6865 *     directive for registering {@link ng.$rootScope.Scope#$watch w
6865atches}.
6866 *
6867 *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6868 *     manipulate the children of the element only in `postLink` function since the children have
6869 *     already been linked.
6870 *
6871 *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6872 *     between all directive linking functions.
6873 *
6874 *   * `controller` - the directive's required controller instance(s) - Instances are shared
6875 *     among all directives, which allows the directives to use the controllers as a communication
6876 *     channel. The exact value depends on the directive's `require` property:
6877 *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6878 *       * `string`: the controller instance
6879 *       * `array`: array of controller instances
6880 *
6881 *     If a required controller cannot be found, and it is optional, the instance is `null`,
6882 *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6883 *
6884 *     Note that you can also require the directive's own controller - it will be made available like
6885 *     any other controller.
6886 *
6887 *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6888 *     This is the same as the `$transclude`
6889 *     parameter of directive controllers, see there for details.
6890 *     `function([scope], cloneLinkingFn, futureParentElement)`.
6891 *
6892 * #### Pre-linking function
6893 *
6894 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6895 * compiler linking function will fail to locate the correct elements for linking.
6896 *
6897 * #### Post-linking function
6898 *
6899 * Executed after the child elements are linked.
6900 *
6901 * Note that child elements that contain `templateUrl` directives will not have been compiled
6902 * and linked since they are waiting for their template to load asynchronously and their own
6903 * compilation and linking has been suspended until that occurs.
6904 *
6905 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6906 * for their async templates to be resolved.
6907 *
6908 *
6909 * ### Transclusion
6910 *
6911 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6912 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6913 * scope from where they were taken.
6914 *
6915 * Transclusion is used (often with {@link ngTransclude}) to insert the
6916 * original contents of a directive's element into a specified place in the template of the directive.
6917 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6918 * content has access to the properties on the scope from which it was taken, even if the directive
6919 * has isolated scope.
6920 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6921 *
6922 * This makes it possible for the widget to have private state for its template, while the transcluded
6923 * content has access to its originating scope.
6924 *
6925 * <div class="alert alert-warning">
6926 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6927 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6928 * Testing Transclusion Directives}.
6929 * </div>
6930 *
6931 * #### Transclusion Functions
6932 *
6933 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6934 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6935 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6936 *
6937 * <div class="alert alert-info">
6938 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6939 * ngTransclude will deal with it for us.
6940 * </div>
6941 *
6942 * If you want to manually control the insertion and removal of the transcluded content in your directive
6943 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6944 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6945 *
6946 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6947 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6948 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6949 *
6950 * <div class="alert alert-info">
6951 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6952 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6953 * </div>
6954 *
6955 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6956 * attach function**:
6957 *
6958 * ```js
6959 * var transcludedContent, transclusionScope;
6960 *
6961 * $transclude(function(clone, scope) {
6962 *   element.append(clone);
6963 *   transcludedContent = clone;
6964 *   transclusionScope = scope;
6965 * });
6966 * ```
6967 *
6968 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6969 * associated transclusion scope:
6970 *
6971 * ```js
6972 * transcludedContent.remove();
6973 * transclusionScope.$destroy();
6974 * ```
6975 *
6976 * <div class="alert alert-info">
6977 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6978 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
6979 * then you are also responsible for calling `$destroy` on the transclusion scope.
6980 * </div>
6981 *
6982 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6983 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6984 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6985 *
6986 *
6987 * #### Transclusion Scopes
6988 *
6989 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6990 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6991 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6992 * was taken.
6993 *
6994 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6995 * like this:
6996 *
6997 * ```html
6998 * <div ng-app>
6999 *   <div isolate>
7000 *     <div transclusion>
7001 *     </div>
7002 *   </div>
7003 * </div>
7004 * ```
7005 *
7006 * The `$parent` scope hierarchy will look like this:
7007 *
7008   ```
7009   - $rootScope
7010     - isolate
7011       - transclusion
7012   ```
7013 *
7014 * but the scopes will inherit prototypically from different scopes to their `$parent`.
7015 *
7016   ```
7017   - $rootScope
7018     - transclusion
7019   - isolate
7020   ```
7021 *
7022 *
7023 * ### Attributes
7024 *
7025 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
7026 * `link()` or `compile()` functions. It has a variety of uses.
7027 *
7028 * accessing *Normalized attribute names:*
7029 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
7030 * the attributes object allows for normalized access to
7031 *   the attributes.
7032 *
7033 * * *Directive inter-communication:* All directives share the same instance of the attributes
7034 *   object which allows the directives to use the attributes object as inter directive
7035 *   communication.
7036 *
7037 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
7038 *   allowing other directives to read the interpolated value.
7039 *
7040 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
7041 *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
7042 *   the only way to easily get the actual value because during the linking phase the interpolation
7043 *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
7044 *
7045 * ```js
7046 * function linkingFn(scope, elm, attrs, ctrl) {
7047 *   // get the attribute value
7048 *   console.log(attrs.ngModel);
7049 *
7050 *   // change the attribute
7051 *   attrs.$set('ngModel', 'new value');
7052 *
7053 *   // observe changes to interpolated attribute
7054 *   attrs.$observe('ngModel', function(value) {
7055 *     console.log('ngModel has changed value to ' + value);
7056 *   });
7057 * }
7058 * ```
7059 *
7060 * ## Example
7061 *
7062 * <div class="alert alert-warning">
7063 * **Note**: Typically directives are registered with `module.directive`. The example below is
7064 * to illustrate how `$compile` works.
7065 * </div>
7066 *
7067 <example module="compileExample">
7068   <file name="index.html">
7069    <script>
7070      angular.module('compileExample', [], function($compileProvider) {
7071        // configure new 'compile' directive by passing a directive
7072        // factory function. The factory function injects the '$compile'
7073        $compileProvider.directive('compile', function($compile) {
7074          // directive factory creates a link function
7075          return function(scope, element, attrs) {
7076            scope.$watch(
7077              function(scope) {
7078                 // watch the 'compile' expression for changes
7079                return scope.$eval(attrs.compile);
7080              },
7081              function(value) {
7082                // when the 'compile' expression changes
7083                // assign it into the current DOM
7084                element.html(value);
7085
7086                // compile the new DOM and link it to the current
7087                // scope.
7088                // NOTE: we only compile .childNodes so that
7089                // we don't get into infinite loop compiling ourselves
7090                $compile(element.contents())(scope);
7091              }
7092            );
7093          };
7094        });
7095      })
7096      .controller('GreeterController', ['$scope', function($scope) {
7097        $scope.name = 'Angular';
7098        $scope.html = 'Hello {{name}}';
7099      }]);
7100    </script>
7101    <div ng-controller="GreeterController">
7102      <input ng-model="name"> <br/>
7103      <textarea ng-model="html"></textarea> <br/>
7104      <div compile="html"></div>
7105    </div>
7106   </file>
7107   <file name="protractor.js" type="protractor">
7108     it('should auto compile', function() {
7109       var textarea = $('textarea');
7110       var output = $('div[compile]');
7111       // The initial state reads 'Hello Angular'.
7112       expect(output.getText()).toBe('Hello Angular');
7113       textarea.clear();
7114       textarea.sendKeys('{{name}}!');
7115       expect(output.getText()).toBe('Angular!');
7116     });
7117   </file>
7118 </example>
7119
7120 *
7121 *
7122 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7123 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7124 *
7125 * <div class="alert alert-danger">
7126 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7127 *   e.g. will not use the right outer scope. Please pass the transclude function as a
7128 *   `parentBoundTranscludeFn` to the link function instead.
7129 * </div>
7130 *
7131 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7132 *                 root element(s), not their children)
7133 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7134 * (a DOM element/tree) to a scope. Where:
7135 *
7136 *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7137 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7138 *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
7139 *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7140 *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7141 *
7142 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
7143 *      * `scope` - is the current scope with which the linking function is working with.
7144 *
7145 *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
7146 *  keys may be used to control linking behavior:
7147 *
7148 *      * `parentBoundTranscludeFn` - the transclude function made available to
7149 *        directives; if given, it will be passed through to the link functions of
7150 *        directives found in `element` during compilation.
7151 *      * `transcludeControllers` - an object hash with keys that map controller names
7152 *        to a hash with the key `instance`, which maps to the controller instance;
7153 *        if given, it will make the controllers available to directives on the compileNode:
7154 *        ```
7155 *        {
7156 *          parent: {
7157 *            instance: parentControllerInstance
7158 *          }
7159 *        }
7160 *        ```
7161 *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7162 *        the cloned elements; only needed for transcludes that are allowed to contain non html
7163 *        elements (e.g. SVG elements). See also the directive.controller property.
7164 *
7165 * Calling the linking function returns the element of the template. It is either the original
7166 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7167 *
7168 * After linking the view is not updated until after a call to $digest which typically is done by
7169 * Angular automatically.
7170 *
7171 * If you need access to the bound view, there are two ways to do it:
7172 *
7173 * - If you are not asking the linking function to clone the template, create the DOM element(s)
7174 *   before you send them to the compiler and keep this reference around.
7175 *   ```js
7176 *     var element = $compile('<p>{{total}}</p>')(scope);
7177 *   ```
7178 *
7179 * - if on the other hand, you need the element to be cloned, the view reference from the original
7180 *   example would not point to the clone, but rather to the original template that was cloned. In
7181 *   this case, you can access the clone via the cloneAttachFn:
7182 *   ```js
7183 *     var templateElement = angular.element('<p>{{total}}</p>'),
7184 *         scope = ....;
7185 *
7186 *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7187 *       //attach the clone to DOM document at the right place
7188 *     });
7189 *
7190 *     //now we have reference to the cloned DOM via `clonedElement`
7191 *   ```
7192 *
7193 *
7194 * For information on how the compiler works, see the
7195 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7196 */
7197
7198var $compileMinErr = minErr('$compile');
7199
7200/**
7201 * @ngdoc provider
7202 * @name $compileProvider
7203 *
7204 * @description
7205 */
7206$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7207function $CompileProvider($provide, $$sanitizeUriProvider) {
7208  var hasDirectives = {},
7209      Suffix = 'Directive',
7210      COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7211      CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7212      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7213      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7214
7215  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7216  // The assumption is that future DOM event attribute names will begin with
7217  // 'on' and be composed of only English letters.
7218  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7219
7220  function parseIsolateBindings(scope, directiveName, isController) {
7221    var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
7222
7223    var bindings = {};
7224
vendor: 5,044 bytes, lines 7225-7350
7225    forEach(scope, function(definition, scopeName) {
7226      var match = definition.match(LOCAL_REGEXP);
7227
7228      if (!match) {
7229        throw $compileMinErr('iscp',
7230            "Invalid {3} for directive '{0}'." +
7231            " Definition: {... {1}: '{2}' ...}",
7232            directiveName, scopeName, definition,
7233            (isController ? "controller bindings definition" :
7234            "isolate scope definition"));
7235      }
7236
7237      bindings[scopeName] = {
7238        mode: match[1][0],
7239        collection: match[2] === '*',
7240        optional: match[3] === '?',
7241        attrName: match[4] || scopeName
7242      };
7243    });
7244
7245    return bindings;
7246  }
7247
7248  function parseDirectiveBindings(directive, directiveName) {
7249    var bindings = {
7250      isolateScope: null,
7251      bindToController: null
7252    };
7253    if (isObject(directive.scope)) {
7254      if (directive.bindToController === true) {
7255        bindings.bindToController = parseIsolateBindings(directive.scope,
7256                                                         directiveName, true);
7257        bindings.isolateScope = {};
7258      } else {
7259        bindings.isolateScope = parseIsolateBindings(directive.scope,
7260                                                     directiveName, false);
7261      }
7262    }
7263    if (isObject(directive.bindToController)) {
7264      bindings.bindToController =
7265          parseIsolateBindings(directive.bindToController, directiveName, true);
7266    }
7267    if (isObject(bindings.bindToController)) {
7268      var controller = directive.controller;
7269      var controllerAs = directive.controllerAs;
7270      if (!controller) {
7271        // There is no controller, there may or may not be a controllerAs property
7272        throw $compileMinErr('noctrl',
7273              "Cannot bind to controller without directive '{0}'s controller.",
7274              directiveName);
7275      } else if (!identifierForController(controller, controllerAs)) {
7276        // There is a controller, but no identifier or controllerAs property
7277        throw $compileMinErr('noident',
7278              "Cannot bind to controller without identifier for directive '{0}'.",
7279              directiveName);
7280      }
7281    }
7282    return bindings;
7283  }
7284
7285  function assertValidDirectiveName(name) {
7286    var letter = name.charAt(0);
7287    if (!letter || letter !== lowercase(letter)) {
7288      throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
7289    }
7290    if (name !== name.trim()) {
7291      throw $compileMinErr('baddir',
7292            "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7293            name);
7294    }
7295  }
7296
7297  /**
7298   * @ngdoc method
7299   * @name $compileProvider#directive
7300   * @kind function
7301   *
7302   * @description
7303   * Register a new directive with the compiler.
7304   *
7305   * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7306   *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7307   *    names and the values are the factories.
7308   * @param {Function|Array} directiveFactory An injectable directive factory function. See
7309   *    {@link guide/directive} for more info.
7310   * @returns {ng.$compileProvider} Self for chaining.
7311   */
7312   this.directive = function registerDirective(name, directiveFactory) {
7313    assertNotHasOwnProperty(name, 'directive');
7314    if (isString(name)) {
7315      assertValidDirectiveName(name);
7316      assertArg(directiveFactory, 'directiveFactory');
7317      if (!hasDirectives.hasOwnProperty(name)) {
7318        hasDirectives[name] = [];
7319        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7320          function($injector, $exceptionHandler) {
7321            var directives = [];
7322            forEach(hasDirectives[name], function(directiveFactory, index) {
7323              try {
7324                var directive = $injector.invoke(directiveFactory);
7325                if (isFunction(directive)) {
7326                  directive = { compile: valueFn(directive) };
7327                } else if (!directive.compile && directive.link) {
7328                  directive.compile = valueFn(directive.link);
7329                }
7330                directive.priority = directive.priority || 0;
7331                directive.index = index;
7332                directive.name = directive.name || name;
7333                directive.require = directive.require || (directive.controller && directive.name);
7334                directive.restrict = directive.restrict || 'EA';
7335                var bindings = directive.$$bindings =
7336                    parseDirectiveBindings(directive, directive.name);
7337                if (isObject(bindings.isolateScope)) {
7338                  directive.$$isolateBindings = bindings.isolateScope;
7339                }
7340                directive.$$moduleName = directiveFactory.$$moduleName;
7341                directives.push(directive);
7342              } catch (e) {
7343                $exceptionHandler(e);
7344              }
7345            });
7346            return directives;
7347          }]);
7348      }
7349      hasDirectives[name].push(directiveFactory);
7350    } else {
7351      forEach(name, reverseParams(registerDirective));
7352    }
7353    return this;
7354  };
7355
7356
7357  /**
7358   * @ngdoc method
7359   * @name $compileProvider#aHrefSanitizationWhitelist
7360   * @kind function
7361   *
7362   * @description
7363   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7364   * urls during a[href] sanitization.
7365   *
7366   * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7367   *
7368   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7369   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7370   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7371   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7372   *
7373   * @param {RegExp=} regexp New regexp to whitelist urls with.
7374   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7375   *    chaining otherwise.
7376   */
7377  this.aHrefSanitizationWhitelist = function(regexp) {
7378    if (isDefined(regexp)) {
7379      $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7380      return this;
7381    } else {
7382      return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7383    }
7384  };
7385
7386
7387  /**
7388   * @ngdoc method
7389   * @name $compileProvider#imgSrcSanitizationWhitelist
7390   * @kind function
7391   *
7392   * @description
7393   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7394   * urls during img[src] sanitization.
7395   *
7396   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7397   *
7398   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7399   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7400   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7401   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7402   *
7403   * @param {RegExp=} regexp New regexp to whitelist urls with.
7404   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7405   *    chaining otherwise.
7406   */
7407  this.imgSrcSanitizationWhitelist = function(regexp) {
7408    if (isDefined(regexp)) {
7409      $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7410      return this;
7411    } else {
7412      return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7413    }
7414  };
7415
7416  /**
7417   * @ngdoc method
7418   * @name  $compileProvider#debugInfoEnabled
7419   *
7420   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7421   * current debugInfoEnabled state
7422   * @returns {*} current value if used as getter or itself (chaining) if used as setter
7423   *
7424   * @kind function
7425   *
7426   * @description
7427   * Call this method to enable/disable various debug runtime information in the compiler such as adding
7428   * binding information and a reference to the current scope on to DOM elements.
7429   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7430   * * `ng-binding` CSS class
7431   * * `$binding` data property containing an array of the binding expressions
7432   *
7433   * You may want to disable this in production for a significant performance boost. See
7434   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7435   *
7436   * The default value is true.
7437   */
7438  var debugInfoEnabled = true;
7439  this.debugInfoEnabled = function(enabled) {
7440    if (isDefined(enabled)) {
7441      debugInfoEnabled = enabled;
7442      return this;
7443    }
7444    return debugInfoEnabled;
7445  };
7446
7447  this.$get = [
7448            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7449            '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
7450    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
7451             $controller,   $rootScope,   $sce,   $animate,   $$sanitizeUri) {
7452
7453    var Attributes = function(element, attributesToCopy) {
7454      if (attributesToCopy) {
7455        var keys = Object.keys(attributesToCopy);
7456        var i, l, key;
7457
7458        for (i = 0, l = keys.length; i < l; i++) {
7459          key = keys[i];
7460          this[key] = attributesToCopy[key];
7461        }
7462      } else {
7463        this.$attr = {};
7464      }
7465
7466      this.$$element = element;
7467    };
7468
7469    Attributes.prototype = {
7470      /**
7471       * @ngdoc method
7472       * @name $compile.directive.Attributes#$normalize
7473       * @kind function
7474       *
7475       * @description
7476       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7477       * `data-`) to its normalized, camelCase form.
7478       *
7479       * Also there is special case for Moz prefix starting with upper case letter.
7480       *
7481       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7482       *
7483       * @param {string} name Name to normalize
7484       */
7485      $normalize: directiveNormalize,
7486
7487
7488      /**
7489       * @ngdoc method
7490       * @name $compile.directive.Attributes#$addClass
7491       * @kind function
7492       *
7493       * @description
7494       * Adds the CSS class value specified by the classVal parameter to the element. If animations
7495       * are enabled then an animation will be triggered for the class addition.
7496       *
7497       * @param {string} classVal The className value that will be added to the element
7498       */
7499      $addClass: function(classVal) {
7500        if (classVal && classVal.length > 0) {
7501          $animate.addClass(this.$$element, classVal);
7502        }
7503      },
7504
7505      /**
7506       * @ngdoc method
7507       * @name $compile.directive.Attributes#$removeClass
7508       * @kind function
7509       *
7510       * @description
7511       * Removes the CSS class value specified by the classVal parameter from the element. If
7512       * animations are enabled then an animation will be triggered for the class removal.
7513       *
7514       * @param {string} classVal The className value that will be removed from the element
7515       */
7516      $removeClass: function(classVal) {
7517        if (classVal && classVal.length > 0) {
7518          $animate.removeClass(this.$$element, classVal);
7519        }
7520      },
7521
7522      /**
7523       * @ngdoc method
7524       * @name $compile.directive.Attributes#$updateClass
7525       * @kind function
7526       *
7527       * @description
7528       * Adds and removes the appropriate CSS class values to the element based on the difference
7529       * between the new and old CSS class values (specified as newClasses and oldClasses).
7530       *
7531       * @param {string} newClasses The current CSS className value
7532       * @param {string} oldClasses The former CSS className value
7533       */
7534      $updateClass: function(newClasses, oldClasses) {
7535        var toAdd = tokenDifference(newClasses, oldClasses);
7536        if (toAdd && toAdd.length) {
7537          $animate.addClass(this.$$element, toAdd);
7538        }
7539
7540        var toRemove = tokenDifference(oldClasses, newClasses);
7541        if (toRemove && toRemove.length) {
7542          $animate.removeClass(this.$$element, toRemove);
7543        }
7544      },
7545
7546      /**
7547       * Set a normalized attribute on the element in a way such that all directives
7548       * can share the attribute. This function properly handles boolean attributes.
7549       * @param {string} key Normalized key. (ie ngAttribute)
7550       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7551       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7552       *     Defaults to true.
7553       * @param {string=} attrName Optional none normalized name. Defaults to key.
7554       */
7555      $set: function(key, value, writeAttr, attrName) {
7556        // TODO: decide whether or not to throw an error if "class"
7557        //is set through this function since it may cause $updateClass to
7558        //become unstable.
7559
7560        var node = this.$$element[0],
7561            booleanKey = getBooleanAttrName(node, key),
7562            aliasedKey = getAliasedAttrName(key),
7563            observer = key,
7564            nodeName;
7565
7566        if (booleanKey) {
7567          this.$$element.prop(key, value);
7568          attrName = booleanKey;
7569        } else if (aliasedKey) {
7570          this[aliasedKey] = value;
7571          observer = aliasedKey;
7572        }
7573
7574        this[key] = value;
7575
7576        // translate normalized key to actual key
7577        if (attrName) {
7578          this.$attr[key] = attrName;
7579        } else {
7580          attrName = this.$attr[key];
7581          if (!attrName) {
7582            this.$attr[key] = attrName = snake_case(key, '-');
7583          }
7584        }
7585
7586        nodeName = nodeName_(this.$$element);
7587
7588        if ((nodeName === 'a' && key === 'href') ||
7589            (nodeName === 'img' && key === 'src')) {
7590          // sanitize a[href] and img[src] values
7591          this[key] = value = $$sanitizeUri(value, key === 'src');
7592        } else if (nodeName === 'img' && key === 'srcset') {
7593          // sanitize img[srcset] values
7594          var result = "";
7595
7596          // first check if there are spaces because it's not the same pattern
7597          var trimmedSrcset = trim(value);
7598          //                (   999x   ,|   999w   ,|   ,|,   )
7599          var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7600          var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7601
7602          // split srcset into tuple of uri and descriptor except for the last item
7603          var rawUris = trimmedSrcset.split(pattern);
7604
7605          // for each tuples
7606          var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7607          for (var i = 0; i < nbrUrisWith2parts; i++) {
7608            var innerIdx = i * 2;
7609            // sanitize the uri
7610            result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7611            // add the descriptor
7612            result += (" " + trim(rawUris[innerIdx + 1]));
7613          }
7614
7615          // split the last item into uri and descriptor
7616          var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7617
7618          // sanitize the last uri
7619          result += $$sanitizeUri(trim(lastTuple[0]), true);
7620
7621          // and add the last descriptor if any
7622          if (lastTuple.length === 2) {
7623            result += (" " + trim(lastTuple[1]));
7624          }
7625          this[key] = value = result;
7626        }
7627
7628        if (writeAttr !== false) {
7629          if (value === null || isUndefined(value)) {
7630            this.$$element.removeAttr(attrName);
7631          } else {
7632            this.$$element.attr(attrName, value);
7633          }
7634        }
7635
7636        // fire observers
7637        var $$observers = this.$$observers;
7638        $$observers && forEach($$observers[observer], function(fn) {
7639          try {
7640            fn(value);
7641          } catch (e) {
7642            $exceptionHandler(e);
7643          }
7644        });
7645      },
7646
7647
7648      /**
7649       * @ngdoc method
7650       * @name $compile.directive.Attributes#$observe
7651       * @kind function
7652       *
7653       * @description
7654       * Observes an interpolated attribute.
7655       *
7656       * The observer function will be invoked once during the next `$digest` following
7657       * compilation. The observer is then invoked whenever the interpolated value
7658       * changes.
7659       *
7660       * @param {string} key Normalized key. (ie ngAttribute) .
7661       * @param {function(interpolatedValue)} fn Function that will be called whenever
7662                the interpolated value of the attribute changes.
7663       *        See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
7664       *        guide} for more info.
7665       * @returns {function()} Returns a deregistration function for this observer.
7666       */
7667      $observe: function(key, fn) {
7668        var attrs = this,
7669            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7670            listeners = ($$observers[key] || ($$observers[key] = []));
7671
7672        listeners.push(fn);
7673        $rootScope.$evalAsync(function() {
7674          if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7675            // no one registered attribute interpolation function, so lets call it manually
7676            fn(attrs[key]);
7677          }
7678        });
7679
7680        return function() {
7681          arrayRemove(listeners, fn);
7682        };
7683      }
7684    };
7685
7686
7687    function safeAddClass($element, className) {
7688      try {
7689        $element.addClass(className);
7690      } catch (e) {
7691        // ignore, since it means that we are trying to set class on
7692        // SVG element, where class name is read-only.
7693      }
7694    }
7695
7696
7697    var startSymbol = $interpolate.startSymbol(),
7698        endSymbol = $interpolate.endSymbol(),
7699        denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
7700            ? identity
7701            : function denormalizeTemplate(template) {
7702              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7703        },
7704        NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7705    var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
7706
7707    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7708      var bindings = $element.data('$binding') || [];
7709
7710      if (isArray(binding)) {
7711        bindings = bindings.concat(binding);
7712      } else {
7713        bindings.push(binding);
7714      }
7715
7716      $element.data('$binding', bindings);
7717    } : noop;
7718
7719    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7720      safeAddClass($element, 'ng-binding');
7721    } : noop;
7722
7723    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7724      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') 
7724: '$scope';
7725      $element.data(dataName, scope);
7726    } : noop;
7727
7728    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7729      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7730    } : noop;
7731
7732    return compile;
7733
7734    //================================
7735
7736    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7737                        previousCompileContext) {
7738      if (!($compileNodes instanceof jqLite)) {
7739        // jquery always rewraps, whereas we need to preserve the original selector so that we can
7740        // modify it.
7741        $compileNodes = jqLite($compileNodes);
7742      }
7743      // We can not compile top level text elements since text nodes can be merged and we will
7744      // not be able to attach scope data to them, so we will wrap them in <span>
7745      forEach($compileNodes, function(node, index) {
7746        if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
7747          $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
7748        }
7749      });
7750      var compositeLinkFn =
7751              compileNodes($compileNodes, transcludeFn, $compileNodes,
7752                           maxPriority, ignoreDirective, previousCompileContext);
7753      compile.$$addScopeClass($compileNodes);
7754      var namespace = null;
7755      return function publicLinkFn(scope, cloneConnectFn, options) {
7756        assertArg(scope, 'scope');
7757
7758        if (previousCompileContext && previousCompileContext.needsNewScope) {
7759          // A parent directive did a replace and a directive on this element asked
7760          // for transclusion, which caused us to lose a layer of element on which
7761          // we could hold the new transclusion scope, so we will create it manually
7762          // here.
7763          scope = scope.$parent.$new();
7764        }
7765
7766        options = options || {};
7767        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7768          transcludeControllers = options.transcludeControllers,
7769          futureParentElement = options.futureParentElement;
7770
7771        // When `parentBoundTranscludeFn` is passed, it is a
7772        // `controllersBoundTransclude` function (it was previously passed
7773        // as `transclude` to directive.link) so we must unwrap it to get
7774        // its `boundTranscludeFn`
7775        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7776          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7777        }
7778
7779        if (!namespace) {
7780          namespace = detectNamespaceForChildElements(futureParentElement);
7781        }
7782        var $linkNode;
7783        if (namespace !== 'html') {
7784          // When using a directive with replace:true and templateUrl the $compileNodes
7785          // (or a child element inside of them)
7786          // might change, so we need to recreate the namespace adapted compileNodes
7787          // for call to the link function.
7788          // Note: This will already clone the nodes...
7789          $linkNode = jqLite(
7790            wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7791          );
7792        } else if (cloneConnectFn) {
7793          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7794          // and sometimes changes the structure of the DOM.
7795          $linkNode = JQLitePrototype.clone.call($compileNodes);
7796        } else {
7797          $linkNode = $compileNodes;
7798        }
7799
7800        if (transcludeControllers) {
7801          for (var controllerName in transcludeControllers) {
7802            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7803          }
7804        }
7805
7806        compile.$$addScopeInfo($linkNode, scope);
7807
7808        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7809        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7810        return $linkNode;
7811      };
7812    }
7813
7814    function detectNamespaceForChildElements(parentElement) {
7815      // TODO: Make this detect MathML as well...
7816      var node = parentElement && parentElement[0];
7817      if (!node) {
7818        return 'html';
7819      } else {
7820        return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7821      }
7822    }
7823
7824    /**
7825     * Compile function matches each node in nodeList against the directives. Once all directives
7826     * for a particular node are collected their compile functions are executed. The compile
7827     * functions return values - the linking functions - are combined into a composite linking
7828     * function, which is the a linking function for the node.
7829     *
7830     * @param {NodeList} nodeList an array of nodes or NodeList to compile
7831     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7832     *        scope argument is auto-generated to the new child of the transcluded parent scope.
7833     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7834     *        the rootElement must be set the jqLite collection of the compile root. This is
7835     *        needed so that the jqLite collection items can be replaced with widgets.
7836     * @param {number=} maxPriority Max directive priority.
7837     * @returns {Function} A composite linking function of all of the matched directives or null.
7838     */
7839    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7840                            previousCompileContext) {
7841      var linkFns = [],
7842          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7843
7844      for (var i = 0; i < nodeList.length; i++) {
7845        attrs = new Attributes();
7846
7847        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7848        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7849                                        ignoreDirective);
7850
7851        nodeLinkFn = (directives.length)
7852            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7853                                      null, [], [], previousCompileContext)
7854            : null;
7855
7856        if (nodeLinkFn && nodeLinkFn.scope) {
7857          compile.$$addScopeClass(attrs.$$element);
7858        }
7859
7860        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7861                      !(childNodes = nodeList[i].childNodes) ||
7862                      !childNodes.length)
7863            ? null
7864            : compileNodes(childNodes,
7865                 nodeLinkFn ? (
7866                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7867                     && nodeLinkFn.transclude) : transcludeFn);
7868
7869        if (nodeLinkFn || childLinkFn) {
7870          linkFns.push(i, nodeLinkFn, childLinkFn);
7871          linkFnFound = true;
7872          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7873        }
7874
7875        //use the previous context only for the first element in the virtual group
7876        previousCompileContext = null;
7877      }
7878
7879      // return a linking function if we have found anything, null otherwise
7880      return linkFnFound ? compositeLinkFn : null;
7881
7882      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7883        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7884        var stableNodeList;
7885
7886
7887        if (nodeLinkFnFound) {
7888          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7889          // offsets don't get screwed up
7890          var nodeListLength = nodeList.length;
7891          stableNodeList = new Array(nodeListLength);
7892
7893          // create a sparse array by only copying the elements which have a linkFn
7894          for (i = 0; i < linkFns.length; i+=3) {
7895            idx = linkFns[i];
7896            stableNodeList[idx] = nodeList[idx];
7897          }
7898        } else {
7899          stableNodeList = nodeList;
7900        }
7901
7902        for (i = 0, ii = linkFns.length; i < ii;) {
7903          node = stableNodeList[linkFns[i++]];
7904          nodeLinkFn = linkFns[i++];
7905          childLinkFn = linkFns[i++];
7906
7907          if (nodeLinkFn) {
7908            if (nodeLinkFn.scope) {
7909              childScope = scope.$new();
7910              compile.$$addScopeInfo(jqLite(node), childScope);
7911            } else {
7912              childScope = scope;
7913            }
7914
7915            if (nodeLinkFn.transcludeOnThisElement) {
7916              childBoundTranscludeFn = createBoundTranscludeFn(
7917                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7918
7919            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7920              childBoundTranscludeFn = parentBoundTranscludeFn;
7921
7922            } else if (!parentBoundTranscludeFn && transcludeFn) {
7923              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7924
7925            } else {
7926              childBoundTranscludeFn = null;
7927            }
7928
7929            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7930
7931          } else if (childLinkFn) {
7932            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7933          }
7934        }
7935      }
7936    }
7937
7938    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7939
7940      var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7941
7942        if (!transcludedScope) {
7943          transcludedScope = scope.$new(false, containingScope);
7944          transcludedScope.$$transcluded = true;
7945        }
7946
7947        return transcludeFn(transcludedScope, cloneFn, {
7948          parentBoundTranscludeFn: previousBoundTranscludeFn,
7949          transcludeControllers: controllers,
7950          futureParentElement: futureParentElement
7951        });
7952      };
7953
7954      return boundTranscludeFn;
7955    }
7956
7957    /**
7958     * Looks for directives on the given node and adds them to the directive collection which is
7959     * sorted.
7960     *
7961     * @param node Node to search.
7962     * @param directives An array to which the directives are added to. This array is sorted before
7963     *        the function returns.
7964     * @param attrs The shared attrs object which is used to populate the normalized attributes.
7965     * @param {number=} maxPriority Max directive priority.
7966     */
7967    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7968      var nodeType = node.nodeType,
7969          attrsMap = attrs.$attr,
7970          match,
7971          className;
7972
7973      switch (nodeType) {
7974        case NODE_TYPE_ELEMENT: /* Element */
7975          // use the node name: <directive>
7976          addDirective(directives,
7977              directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7978
7979          // iterate over the attributes
7980          for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7981                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7982            var attrStartName = false;
7983            var attrEndName = false;
7984
7985            attr = nAttrs[j];
7986            name = attr.name;
7987            value = trim(attr.value);
7988
7989            // support ngAttr attribute binding
7990            ngAttrName = directiveNormalize(name);
7991            if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7992              name = name.replace(PREFIX_REGEXP, '')
7993                .substr(8).replace(/_(.)/g, function(match, letter) {
7994                  return letter.toUpperCase();
7995                });
7996            }
7997
7998            var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
7999            if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
8000              attrStartName = name;
8001              attrEndName = name.substr(0, name.length - 5) + 'end';
8002              name = name.substr(0, name.length - 6);
8003            }
8004
8005            nName = directiveNormalize(name.toLowerCase());
8006            attrsMap[nName] = name;
8007            if (isNgAttr || !attrs.hasOwnProperty(nName)) {
8008                attrs[nName] = value;
8009                if (getBooleanAttrName(node, nName)) {
8010                  attrs[nName] = true; // presence means true
8011                }
8012            }
8013            addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
8014            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
8015                          attrEndName);
8016          }
8017
8018          // use class as directive
8019          className = node.className;
8020          if (isObject(className)) {
8021              // Maybe SVGAnimatedString
8022              className = className.animVal;
8023          }
8024          if (isString(className) && className !== '') {
8025            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
8026              nName = directiveNormalize(match[2]);
8027              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
8028                attrs[nName] = trim(match[3]);
8029              }
8030              className = className.substr(match.index + match[0].length);
8031            }
8032          }
8033          break;
8034        case NODE_TYPE_TEXT: /* Text Node */
8035          if (msie === 11) {
8036            // Workaround for #11781
8037            while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
8038              node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
8039              node.parentNode.removeChild(node.nextSibling);
8040            }
8041          }
8042          addTextInterpolateDirective(directives, node.nodeValue);
8043          break;
8044        case NODE_TYPE_COMMENT: /* Comment */
8045          try {
8046            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
8047            if (match) {
8048              nName = directiveNormalize(match[1]);
8049              if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
8050                attrs[nName] = trim(match[2]);
8051              }
8052            }
8053          } catch (e) {
8054            // turns out that under some circumstances IE9 throws errors when one attempts to read
8055            // comment's node value.
8056            // Just ignore it and continue. (Can't seem to reproduce in test case.)
8057          }
8058          break;
8059      }
8060
8061      directives.sort(byPriority);
8062      return directives;
8063    }
8064
8065    /**
8066     * Given a node with an directive-start it collects all of the siblings until it finds
8067     * directive-end.
8068     * @param node
8069     * @param attrStart
8070     * @param attrEnd
8071     * @returns {*}
8072     */
8073    function groupScan(node, attrStart, attrEnd) {
8074      var nodes = [];
8075      var depth = 0;
8076      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
8077        do {
8078          if (!node) {
8079            throw $compileMinErr('uterdir',
8080                      "Unterminated attribute, found '{0}' but no matching '{1}' found.",
8081                      attrStart, attrEnd);
8082          }
8083          if (node.nodeType == NODE_TYPE_ELEMENT) {
8084            if (node.hasAttribute(attrStart)) depth++;
8085            if (node.hasAttribute(attrEnd)) depth--;
8086          }
8087          nodes.push(node);
8088          node = node.nextSibling;
8089        } while (depth > 0);
8090      } else {
8091        nodes.push(node);
8092      }
8093
8094      return jqLite(nodes);
8095    }
8096
8097    /**
8098     * Wrapper for linking function which converts normal linking function into a grouped
8099     * linking function.
8100     * @param linkFn
8101     * @param attrStart
8102     * @param attrEnd
8103     * @returns {Function}
8104     */
8105    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
8106      return function(scope, element, attrs, controllers, transcludeFn) {
8107        element = groupScan(element[0], attrStart, attrEnd);
8108        return linkFn(scope, element, attrs, controllers, transcludeFn);
8109      };
8110    }
8111
8112    /**
8113     * Once the directives have been collected, their compile functions are executed. This method
8114     * is responsible for inlining directive templates as well as terminating the application
8115     * of the directives if the terminal directive has been reached.
8116     *
8117     * @param {Array} directives Array of collected directives to execute their compile function.
8118     *        this needs to be pre-sorted by priority order.
8119     * @param {Node} compileNode The raw DOM node to apply the compile functions to
8120     * @param {Object} templateAttrs The shared attribute function
8121     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8122     *                                                  scope argument is auto-generated to the new
8123     *                                                  child of the transcluded parent scope.
8124     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8125     *                              argument has the root jqLite array so that we can replace nodes
8126     *                              on it.
8127     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8128     *                                           compiling the transclusion.
8129     * @param {Array.<Function>} preLinkFns
8130     * @param {Array.<Function>} postLinkFns
8131     * @param {Object} previousCompileContext Context used for previous compilation of the current
8132     *                                        node
8133     * @returns {Function} linkFn
8134     */
8135    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8136                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8137                                   previousCompileContext) {
8138      previousCompileContext = previousCompileContext || {};
8139
8140      var terminalPriority = -Number.MAX_VALUE,
8141          newScopeDirective = previousCompileContext.newScopeDirective,
8142          controllerDirectives = previousCompileContext.controllerDirectives,
8143          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8144          templateDirective = previousCompileContext.templateDirective,
8145          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8146          hasTranscludeDirective = false,
8147          hasTemplate = false,
8148          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8149          $compileNode = templateAttrs.$$element = jqLite(compileNode),
8150          directive,
8151          directiveName,
8152          $template,
8153          replaceDirective = originalReplaceDirective,
8154          childTranscludeFn = transcludeFn,
8155          linkFn,
8156          directiveValue;
8157
8158      // executes all directives on the current element
8159      for (var i = 0, ii = directives.length; i < ii; i++) {
8160        directive = directives[i];
8161        var attrStart = directive.$$start;
8162        var attrEnd = directive.$$end;
8163
8164        // collect multiblock sections
8165        if (attrStart) {
8166          $compileNode = groupScan(compileNode, attrStart, attrEnd);
8167        }
8168        $template = undefined;
8169
8170        if (terminalPriority > directive.priority) {
8171          break; // prevent further processing of directives
8172        }
8173
8174        if (directiveValue = directive.scope) {
8175
8176          // skip the check for directives with async templates, we'll check the derived sync
8177          // directive when the template arrives
8178          if (!directive.templateUrl) {
8179            if (isObject(directiveValue)) {
8180              // This directive is trying to add an isolated scope.
8181              // Check that there is no scope of any kind already
8182              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8183                                directive, $compileNode);
8184              newIsolateScopeDirective = directive;
8185            } else {
8186              // This directive is trying to add a child scope.
8187              // Check that there is no isolated scope already
8188              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8189                                $compileNode);
8190            }
8191          }
8192
8193          newScopeDirective = newScopeDirective || directive;
8194        }
8195
8196        directiveName = directive.name;
8197
8198        if (!directive.templateUrl && directive.controller) {
8199          directiveValue = directive.controller;
8200          controllerDirectives = controllerDirectives || createMap();
8201          assertNoDuplicate("'" + directiveName + "' controller",
8202              controllerDirectives[directiveName], directive, $compileNode);
8203          controllerDirectives[directiveName] = directive;
8204        }
8205
8206        if (directiveValue = directive.transclude) {
8207          hasTranscludeDirective = true;
8208
8209          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8210          // This option should only be used by directives that know how to safely handle element transclusion,
8211          // where the transcluded nodes are added or replaced after linking.
8212          if (!directive.$$tlb) {
8213            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8214            nonTlbTranscludeDirective = directive;
8215          }
8216
8217          if (directiveValue == 'element') {
8218            hasElementTranscludeDirective = true;
8219            terminalPriority = directive.priority;
8220            $template = $compileNode;
8221            $compileNode = templateAttrs.$$element =
8222                jqLite(document.createComment(' ' + directiveName + ': ' +
8223                                              templateAttrs[directiveName] + ' '));
8224            compileNode = $compileNode[0];
8225            replaceWith(jqCollection, sliceArgs($template), compileNode);
8226
8227            childTranscludeFn = compile($template, transcludeFn, terminalPriority,
8228                                        replaceDirective && replaceDirective.name, {
8229                                          // Don't pass in:
8230                                          // - controllerDirectives - otherwise we'll create duplicates controllers
8231                                          // - newIsolateScopeDirective or templateDirective - comb
vendor: 5,222 bytes, lines 8231-8343
8231ining templates with
8232                                          //   element transclusion doesn't make sense.
8233                                          //
8234                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8235                                          // on the same element more than once.
8236                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective
8237                                        });
8238          } else {
8239            $template = jqLite(jqLiteClone(compileNode)).contents();
8240            $compileNode.empty(); // clear contents
8241            childTranscludeFn = compile($template, transcludeFn, undefined,
8242                undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8243          }
8244        }
8245
8246        if (directive.template) {
8247          hasTemplate = true;
8248          assertNoDuplicate('template', templateDirective, directive, $compileNode);
8249          templateDirective = directive;
8250
8251          directiveValue = (isFunction(directive.template))
8252              ? directive.template($compileNode, templateAttrs)
8253              : directive.template;
8254
8255          directiveValue = denormalizeTemplate(directiveValue);
8256
8257          if (directive.replace) {
8258            replaceDirective = directive;
8259            if (jqLiteIsTextNode(directiveValue)) {
8260              $template = [];
8261            } else {
8262              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8263            }
8264            compileNode = $template[0];
8265
8266            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8267              throw $compileMinErr('tplrt',
8268                  "Template for directive '{0}' must have exactly one root element. {1}",
8269                  directiveName, '');
8270            }
8271
8272            replaceWith(jqCollection, $compileNode, compileNode);
8273
8274            var newTemplateAttrs = {$attr: {}};
8275
8276            // combine directives from the original node and from the template:
8277            // - take the array of directives for this element
8278            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8279            // - collect directives from the template and sort them by priority
8280            // - combine directives as: processed + template + unprocessed
8281            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8282            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8283
8284            if (newIsolateScopeDirective || newScopeDirective) {
8285              // The original directive caused the current element to be replaced but this element
8286              // also needs to have a new scope, so we need to tell the template directives
8287              // that they would need to get their scope from further up, if they require transclusion
8288              markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8289            }
8290            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8291            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8292
8293            ii = directives.length;
8294          } else {
8295            $compileNode.html(directiveValue);
8296          }
8297        }
8298
8299        if (directive.templateUrl) {
8300          hasTemplate = true;
8301          assertNoDuplicate('template', templateDirective, directive, $compileNode);
8302          templateDirective = directive;
8303
8304          if (directive.replace) {
8305            replaceDirective = directive;
8306          }
8307
8308          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8309              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8310                controllerDirectives: controllerDirectives,
8311                newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8312                newIsolateScopeDirective: newIsolateScopeDirective,
8313                templateDirective: templateDirective,
8314                nonTlbTranscludeDirective: nonTlbTranscludeDirective
8315              });
8316          ii = directives.length;
8317        } else if (directive.compile) {
8318          try {
8319            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8320            if (isFunction(linkFn)) {
8321              addLinkFns(null, linkFn, attrStart, attrEnd);
8322            } else if (linkFn) {
8323              addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8324            }
8325          } catch (e) {
8326            $exceptionHandler(e, startingTag($compileNode));
8327          }
8328        }
8329
8330        if (directive.terminal) {
8331          nodeLinkFn.terminal = true;
8332          terminalPriority = Math.max(terminalPriority, directive.priority);
8333        }
8334
8335      }
8336
8337      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8338      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8339      nodeLinkFn.templateOnThisElement = hasTemplate;
8340      nodeLinkFn.transclude = childTranscludeFn;
8341
8342      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8343
8344      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8345      return nodeLinkFn;
8346
8347      ////////////////////
8348
8349      function addLinkFns(pre, post, attrStart, attrEnd) {
8350        if (pre) {
8351          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8352          pre.require = directive.require;
8353          pre.directiveName = directiveName;
8354          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8355            pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8356          }
8357          preLinkFns.push(pre);
8358        }
8359        if (post) {
8360          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8361          post.require = directive.require;
8362          post.directiveName = directiveName;
8363          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8364            post = cloneAndAnnotateFn(post, {isolateScope: true});
8365          }
8366          postLinkFns.push(post);
8367        }
8368      }
8369
8370
8371      function getControllers(directiveName, require, $element, elementControllers) {
8372        var value;
8373
8374        if (isString(require)) {
8375          var match = require.match(REQUIRE_PREFIX_REGEXP);
8376          var name = require.substring(match[0].length);
8377          var inheritType = match[1] || match[3];
8378          var optional = match[2] === '?';
8379
8380          //If only parents then start at the parent element
8381          if (inheritType === '^^') {
8382            $element = $element.parent();
8383          //Otherwise attempt getting the controller from elementControllers in case
8384          //the element is transcluded (and has no data) and to avoid .data if possible
8385          } else {
8386            value = elementControllers && elementControllers[name];
8387            value = value && value.instance;
8388          }
8389
8390          if (!value) {
8391            var dataName = '$' + name + 'Controller';
8392            value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8393          }
8394
8395          if (!value && !optional) {
8396            throw $compileMinErr('ctreq',
8397                "Controller '{0}', required by directive '{1}', can't be found!",
8398                name, directiveName);
8399          }
8400        } else if (isArray(require)) {
8401          value = [];
8402          for (var i = 0, ii = require.length; i < ii; i++) {
8403            value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8404          }
8405        }
8406
8407        return value || null;
8408      }
8409
8410      function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8411        var elementControllers = createMap();
8412        for (var controllerKey in controllerDirectives) {
8413          var directive = controllerDirectives[controllerKey];
8414          var locals = {
8415            $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8416            $element: $element,
8417            $attrs: attrs,
8418            $transclude: transcludeFn
8419          };
8420
8421          var controller = directive.controller;
8422          if (controller == '@') {
8423            controller = attrs[directive.name];
8424          }
8425
8426          var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8427
8428          // For directives with element transclusion the element is a comment,
8429          // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
8430          // clean up (http://bugs.jquery.com/ticket/8335).
8431          // Instead, we save the controllers for the element in a local hash and attach to .data
8432          // later, once we have the actual element.
8433          elementControllers[directive.name] = controllerInstance;
8434          if (!hasElementTranscludeDirective) {
8435            $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8436          }
8437        }
8438        return elementControllers;
8439      }
8440
8441      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8442        var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8443            attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8444
8445        if (compileNode === linkNode) {
8446          attrs = templateAttrs;
8447          $element = templateAttrs.$$element;
8448        } else {
8449          $element = jqLite(linkNode);
8450          attrs = new Attributes($element, templateAttrs);
8451        }
8452
8453        controllerScope = scope;
8454        if (newIsolateScopeDirective) {
8455          isolateScope = scope.$new(true);
8456        } else if (newScopeDirective) {
8457          controllerScope = scope.$parent;
8458        }
8459
8460        if (boundTranscludeFn) {
8461          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8462          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8463          transcludeFn = controllersBoundTransclude;
8464          transcludeFn.$$boundTransclude = boundTranscludeFn;
8465        }
8466
8467        if (controllerDirectives) {
8468          elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8469        }
8470
8471        if (newIsolateScopeDirective) {
8472          // Initialize isolate scope bindings for new isolate scope directive.
8473          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8474              templateDirective === newIsolateScopeDirective.$$originalDirective)));
8475          compile.$$addScopeClass($element, true);
8476          isolateScope.$$isolateBindings =
8477              newIsolateScopeDirective.$$isolateBindings;
8478          removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8479                                        isolateScope.$$isolateBindings,
8480                                        newIsolateScopeDirective);
8481          if (removeScopeBindingWatches) {
8482            isolateScope.$on('$destroy', removeScopeBindingWatches);
8483          }
8484        }
8485
8486        // Initialize bindToController bindings
8487        for (var name in elementControllers) {
8488          var controllerDirective = controllerDirectives[name];
8489          var controller = elementControllers[name];
8490          var bindings = controllerDirective.$$bindings.bindToController;
8491
8492          if (controller.identifier && bindings) {
8493            removeControllerBindingWatches =
8494              initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8495          }
8496
8497          var controllerResult = controller();
8498          if (controllerResult !== controller.instance) {
8499            // If the controller constructor has a return value, overwrite the instance
8500            // from setupControllers
8501            controller.instance = controllerResult;
8502            $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8503            removeControllerBindingWatches && removeControllerBindingWatches();
8504            removeControllerBindingWatches =
8505              initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8506          }
8507        }
8508
8509        // PRELINKING
8510        for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8511          linkFn = preLinkFns[i];
8512          invokeLinkFn(linkFn,
8513              linkFn.isolateScope ? isolateScope : scope,
8514              $element,
8515              attrs,
8516              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8517              transcludeFn
8518          );
8519        }
8520
8521        // RECURSION
8522        // We only pass the isolate scope, if the isolate directive has a template,
8523        // otherwise the child elements do not belong to the isolate directive.
8524        var scopeToChild = scope;
8525        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8526          scopeToChild = isolateScope;
8527        }
8528        childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8529
8530        // POSTLINKING
8531        for (i = postLinkFns.length - 1; i >= 0; i--) {
8532          linkFn = postLinkFns[i];
8533          invokeLinkFn(linkFn,
8534              linkFn.isolateScope ? isolateScope : scope,
8535              $element,
8536              attrs,
8537              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8538              transcludeFn
8539          );
8540        }
8541
8542        // This is the function that is injected as `$transclude`.
8543        // Note: all arguments are optional!
8544        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8545          var transcludeControllers;
8546
8547          // No scope passed in:
8548          if (!isScope(scope)) {
8549            futureParentElement = cloneAttachFn;
8550            cloneAttachFn = scope;
8551            scope = undefined;
8552          }
8553
8554          if (hasElementTranscludeDirective) {
8555            transcludeControllers = elementControllers;
8556          }
8557          if (!futureParentElement) {
8558            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8559          }
8560          return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8561        }
8562      }
8563    }
8564
8565    // Depending upon the context in which a directive finds itself it might need to have a new isolated
8566    // or child scope created. For instance:
8567    // * if the directive has been pulled into a template because another directive with a higher priority
8568    // asked for element transclusion
8569    // * if the directive itself asks for transclusion but it is at the root of a template and the original
8570    // element was replaced. See https://github.com/angular/angular.js/issues/12936
8571    function markDirectiveScope(directives, isolateScope, newScope) {
8572      for (var j = 0, jj = directives.length; j < jj; j++) {
8573        directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
8574      }
8575    }
8576
8577    /**
8578     * looks up the directive and decorates it with exception handling and proper parameters. We
8579     * call this the boundDirective.
8580     *
8581     * @param {string} name name of the directive to look up.
8582     * @param {string} location The directive must be found in specific format.
8583     *   String containing any of theses characters:
8584     *
8585     *   * `E`: element name
8586     *   * `A': attribute
8587     *   * `C`: class
8588     *   * `M`: comment
8589     * @returns {boolean} true if directive was added.
8590     */
8591    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8592                          endAttrName) {
8593      if (name === ignoreDirective) return null;
8594      var match = null;
8595      if (hasDirectives.hasOwnProperty(name)) {
8596        for (var directive, directives = $injector.get(name + Suffix),
8597            i = 0, ii = directives.length; i < ii; i++) {
8598          try {
8599            directive = directives[i];
8600            if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8601                 directive.restrict.indexOf(location) != -1) {
8602              if (startAttrName) {
8603                directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8604              }
8605              tDirectives.push(directive);
8606              match = directive;
8607            }
8608          } catch (e) { $exceptionHandler(e); }
8609        }
8610      }
8611      return match;
8612    }
8613
8614
8615    /**
8616     * looks up the directive and returns true if it is a multi-element directive,
8617     * and therefore requires DOM nodes between -start and -end markers to be grouped
8618     * together.
8619     *
8620     * @param {string} name name of the directive to look up.
8621     * @returns true if directive was registered as multi-element.
8622     */
8623    function directiveIsMultiElement(name) {
8624      if (hasDirectives.hasOwnProperty(name)) {
8625        for (var directive, directives = $injector.get(name + Suffix),
8626            i = 0, ii = directives.length; i < ii; i++) {
8627          directive = directives[i];
8628          if (directive.multiElement) {
8629            return true;
8630          }
8631        }
8632      }
8633      return false;
8634    }
8635
8636    /**
8637     * When the element is replaced with HTML template then the new attributes
8638     * on the template need to be merged with the existing attributes in the DOM.
8639     * The desired effect is to have both of the attributes present.
8640     *
8641     * @param {object} dst destination attributes (original DOM)
8642     * @param {object} src source attributes (from the directive template)
8643     */
8644    function mergeTemplateAttributes(dst, src) {
8645      var srcAttr = src.$attr,
8646          dstAttr = dst.$attr,
8647          $element = dst.$$element;
8648
8649      // reapply the old attributes to the new element
8650      forEach(dst, function(value, key) {
8651        if (key.charAt(0) != '$') {
8652          if (src[key] && src[key] !== value) {
8653            value += (key === 'style' ? ';' : ' ') + src[key];
8654          }
8655          dst.$set(key, value, true, srcAttr[key]);
8656        }
8657      });
8658
8659      // copy the new attributes on the old attrs object
8660      forEach(src, function(value, key) {
8661        if (key == 'class') {
8662          safeAddClass($element, value);
8663          dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8664        } else if (key == 'style') {
8665          $element.attr('style', $element.attr('style') + ';' + value);
8666          dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8667          // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8668          // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8669          // have an attribute like "has-own-property" or "data-has-own-property", etc.
8670        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8671          dst[key] = value;
8672          dstAttr[key] = srcAttr[key];
8673        }
8674      });
8675    }
8676
8677
8678    function compileTemplateUrl(directives, $compileNode, tAttrs,
8679        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8680      var linkQueue = [],
8681          afterTemplateNodeLinkFn,
8682          afterTemplateChildLinkFn,
8683          beforeTemplateCompileNode = $compileNode[0],
8684          origAsyncDirective = directives.shift(),
8685          derivedSyncDirective = inherit(origAsyncDirective, {
8686            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8687          }),
8688          templateUrl = (isFunction(origAsyncDirective.templateUrl))
8689              ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8690              : origAsyncDirective.templateUrl,
8691          templateNamespace = origAsyncDirective.templateNamespace;
8692
8693      $compileNode.empty();
8694
8695      $templateRequest(templateUrl)
8696        .then(function(content) {
8697          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8698
8699          content = denormalizeTemplate(content);
8700
8701          if (origAsyncDirective.replace) {
8702            if (jqLiteIsTextNode(content)) {
8703              $template = [];
8704            } else {
8705              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8706            }
8707            compileNode = $template[0];
8708
8709            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8710              throw $compileMinErr('tplrt',
8711                  "Template for directive '{0}' must have exactly one root element. {1}",
8712                  origAsyncDirective.name, templateUrl);
8713            }
8714
8715            tempTemplateAttrs = {$attr: {}};
8716            replaceWith($rootElement, $compileNode, compileNode);
8717            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8718
8719            if (isObject(origAsyncDirective.scope)) {
8720              // the original directive that caused the template to be loaded async required
8721              // an isolate scope
8722              markDirectiveScope(templateDirectives, true);
8723            }
8724            directives = templateDirectives.concat(directives);
8725            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8726          } else {
8727            compileNode = beforeTemplateCompileNode;
8728            $compileNode.html(content);
8729          }
8730
8731          directives.unshift(derivedSyncDirective);
8732
8733          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8734              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8735              previousCompileContext);
8736          forEach($rootElement, function(node, i) {
8737            if (node == compileNode) {
8738              $rootElement[i] = $compileNode[0];
8739            }
8740          });
8741          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8742
8743          while (linkQueue.length) {
8744            var scope = linkQueue.shift(),
8745                beforeTemplateLinkNode = linkQueue.shift(),
8746                linkRootElement = linkQueue.shift(),
8747                boundTranscludeFn = linkQueue.shift(),
8748                linkNode = $compileNode[0];
8749
8750            if (scope.$$destroyed) continue;
8751
8752            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8753              var oldClasses = beforeTemplateLinkNode.className;
8754
8755              if (!(previousCompileContext.hasElementTranscludeDirective &&
8756                  origAsyncDirective.replace)) {
8757                // it was cloned therefore we have to clone as well.
8758                linkNode = jqLiteClone(compileNode);
8759              }
8760              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8761
8762              // Copy in CSS classes from original node
8763              safeAddClass(jqLite(linkNode), oldClasses);
8764            }
8765            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8766              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8767            } else {
8768              childBoundTranscludeFn = boundTranscludeFn;
8769            }
8770            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8771              childBoundTranscludeFn);
8772          }
8773          linkQueue = null;
8774        });
8775
8776      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8777        var childBoundTranscludeFn = boundTranscludeFn;
8778        if (scope.$$destroyed) return;
8779        if (linkQueue) {
8780          linkQueue.push(scope,
8781                         node,
8782                         rootElement,
8783                         childBoundTranscludeFn);
8784        } else {
8785          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8786            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8787          }
8788          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8789        }
8790      };
8791    }
8792
8793
8794    /**
8795     * Sorting function for bound directives.
8796     */
8797    function byPriority(a, b) {
8798      var diff = b.priority - a.priority;
8799      if (diff !== 0) return diff;
8800      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8801      return a.index - b.index;
8802    }
8803
8804    function assertNoDuplicate(what, previousDirective, directive, element) {
8805
8806      function wrapModuleNameIfDefined(moduleName) {
8807        return moduleName ?
8808          (' (module: ' + moduleName + ')') :
8809          '';
8810      }
8811
8812      if (previousDirective) {
8813        throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8814            previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8815            directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8816      }
8817    }
8818
8819
8820    function addTextInterpolateDirective(directives, text) {
8821      var interpolateFn = $interpolate(text, true);
8822      if (interpolateFn) {
8823        directives.push({
8824          priority: 0,
8825          compile: function textInterpolateCompileFn(templateNode) {
8826            var templateNodeParent = templateNode.parent(),
8827                hasCompileParent = !!templateNodeParent.length;
8828
8829            // When transcluding a template that has bindings in the root
8830            // we don't have a parent and thus need to add the class during linking fn.
8831            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8832
8833            return function textInterpolateLinkFn(scope, node) {
8834              var parent = node.parent();
8835              if (!hasCompileParent) compile.$$addBindingClass(parent);
8836              compile.$$addBindingInfo(parent, interpolateFn.expressions);
8837              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8838                node[0].nodeValue = value;
8839              });
8840            };
8841          }
8842        });
8843      }
8844    }
8845
8846
8847    function wrapTemplate(type, template) {
8848      type = lowercase(type || 'html');
8849      switch (type) {
8850      case 'svg':
8851      case 'math':
8852        var wrapper = document.createElement('div');
8853        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8854        return wrapper.childNodes[0].childNodes;
8855      default:
8856        return template;
8857      }
8858    }
8859
8860
8861    function getTrustedContext(node, attrNormalizedName) {
8862      if (attrNormalizedName == "srcdoc") {
8863        return $sce.HTML;
8864      }
8865      var tag = nodeName_(node);
8866      // maction[xlink:href] can source SVG.  It's not limited to <maction>.
8867      if (attrNormalizedName == "xlinkHref" ||
8868          (tag == "form" && attrNormalizedName == "action") ||
8869          (tag != "img" && (attrNormalizedName == "src" ||
8870                            attrNormalizedName == "ngSrc"))) {
8871        return $sce.RESOURCE_URL;
8872      }
8873    }
8874
8875
8876    function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8877      var trustedContext = getTrustedContext(node, name);
8878      allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8879
8880      var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8881
8882      // no interpolation found -> ignore
8883      if (!interpolateFn) return;
8884
8885
8886      if (name === "multiple" && nodeName_(node) === "select") {
8887        throw $compileMinErr("selmulti",
8888            "Binding to the 'multiple' attribute is not supported. Element: {0}",
8889            startingTag(node));
8890      }
8891
8892      directives.push({
8893        priority: 100,
8894        compile: function() {
8895            return {
8896              pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8897                var $$observers = (attr.$$observers || (attr.$$observers = createMap())
8897);
8898
8899                if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8900                  throw $compileMinErr('nodomevents',
8901                      "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
8902                          "ng- versions (such as ng-click instead of onclick) instead.");
8903                }
8904
8905                // If the attribute has changed since last $interpolate()ed
8906                var newValue = attr[name];
8907                if (newValue !== value) {
8908                  // we need to interpolate again since the attribute value has been updated
8909                  // (e.g. by another directive's compile function)
8910                  // ensure unset/empty values make interpolateFn falsy
8911                  interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8912                  value = newValue;
8913                }
8914
8915                // if attribute was updated so that there is no interpolation going on we don't want to
8916                // register any observers
8917                if (!interpolateFn) return;
8918
8919                // initialize attr object so that it's ready in case we need the value for isolate
8920                // scope initialization, otherwise the value would not be available from isolate
8921                // directive's linking fn during linking phase
8922                attr[name] = interpolateFn(scope);
8923
8924                ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8925                (attr.$$observers && attr.$$observers[name].$$scope || scope).
8926                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8927                    //special case for class attribute addition + removal
8928                    //so that class changes can tap into the animation
8929                    //hooks provided by the $animate service. Be sure to
8930                    //skip animations when the first digest occurs (when
8931                    //both the new and the old values are the same) since
8932                    //the CSS classes are the non-interpolated values
8933                    if (name === 'class' && newValue != oldValue) {
8934                      attr.$updateClass(newValue, oldValue);
8935                    } else {
8936                      attr.$set(name, newValue);
8937                    }
8938                  });
8939              }
8940            };
8941          }
8942      });
8943    }
8944
8945
8946    /**
8947     * This is a special jqLite.replaceWith, which can replace items which
8948     * have no parents, provided that the containing jqLite collection is provided.
8949     *
8950     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8951     *                               in the root of the tree.
8952     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8953     *                                  the shell, but replace its DOM node reference.
8954     * @param {Node} newNode The new DOM node.
8955     */
8956    function replaceWith($rootElement, elementsToRemove, newNode) {
8957      var firstElementToRemove = elementsToRemove[0],
8958          removeCount = elementsToRemove.length,
8959          parent = firstElementToRemove.parentNode,
8960          i, ii;
8961
8962      if ($rootElement) {
8963        for (i = 0, ii = $rootElement.length; i < ii; i++) {
8964          if ($rootElement[i] == firstElementToRemove) {
8965            $rootElement[i++] = newNode;
8966            for (var j = i, j2 = j + removeCount - 1,
8967                     jj = $rootElement.length;
8968                 j < jj; j++, j2++) {
8969              if (j2 < jj) {
8970                $rootElement[j] = $rootElement[j2];
8971              } else {
8972                delete $rootElement[j];
8973              }
8974            }
8975            $rootElement.length -= removeCount - 1;
8976
8977            // If the replaced element is also the jQuery .context then replace it
8978            // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8979            // http://api.jquery.com/context/
8980            if ($rootElement.context === firstElementToRemove) {
8981              $rootElement.context = newNode;
8982            }
8983            break;
8984          }
8985        }
8986      }
8987
8988      if (parent) {
8989        parent.replaceChild(newNode, firstElementToRemove);
8990      }
8991
8992      // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8993      var fragment = document.createDocumentFragment();
8994      fragment.appendChild(firstElementToRemove);
8995
8996      if (jqLite.hasData(firstElementToRemove)) {
8997        // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8998        // data here because there's no public interface in jQuery to do that and copying over
8999        // event listeners (which is the main use of private data) wouldn't work anyway.
9000        jqLite.data(newNode, jqLite.data(firstElementToRemove));
9001
9002        // Remove data of the replaced element. We cannot just call .remove()
9003        // on the element it since that would deallocate scope that is needed
9004        // for the new node. Instead, remove the data "manually".
9005        if (!jQuery) {
9006          delete jqLite.cache[firstElementToRemove[jqLite.expando]];
9007        } else {
9008          // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
9009          // the replaced element. The cleanData version monkey-patched by Angular would cause
9010          // the scope to be trashed and we do need the very same scope to work with the new
9011          // element. However, we cannot just cache the non-patched version and use it here as
9012          // that would break if another library patches the method after Angular does (one
9013          // example is jQuery UI). Instead, set a flag indicating scope destroying should be
9014          // skipped this one time.
9015          skipDestroyOnNextJQueryCleanData = true;
9016          jQuery.cleanData([firstElementToRemove]);
9017        }
9018      }
9019
9020      for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
9021        var element = elementsToRemove[k];
9022        jqLite(element).remove(); // must do this way to clean up expando
9023        fragment.appendChild(element);
9024        delete elementsToRemove[k];
9025      }
9026
9027      elementsToRemove[0] = newNode;
9028      elementsToRemove.length = 1;
9029    }
9030
9031
9032    function cloneAndAnnotateFn(fn, annotation) {
9033      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
9034    }
9035
9036
9037    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
9038      try {
9039        linkFn(scope, $element, attrs, controllers, transcludeFn);
9040      } catch (e) {
9041        $exceptionHandler(e, startingTag($element));
9042      }
9043    }
9044
9045
9046    // Set up $watches for isolate scope and controller bindings. This process
9047    // only occurs for isolate scopes and new scopes with controllerAs.
9048    function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
9049      var removeWatchCollection = [];
9050      forEach(bindings, function(definition, scopeName) {
9051        var attrName = definition.attrName,
9052        optional = definition.optional,
9053        mode = definition.mode, // @, =, or &
9054        lastValue,
9055        parentGet, parentSet, compare;
9056
9057        switch (mode) {
9058
9059          case '@':
9060            if (!optional && !hasOwnProperty.call(attrs, attrName)) {
9061              destination[scopeName] = attrs[attrName] = void 0;
9062            }
9063            attrs.$observe(attrName, function(value) {
9064              if (isString(value)) {
9065                destination[scopeName] = value;
9066              }
9067            });
9068            attrs.$$observers[attrName].$$scope = scope;
9069            if (isString(attrs[attrName])) {
9070              // If the attribute has been provided then we trigger an interpolation to ensure
9071              // the value is there for use in the link fn
9072              destination[scopeName] = $interpolate(attrs[attrName])(scope);
9073            }
9074            break;
9075
9076          case '=':
9077            if (!hasOwnProperty.call(attrs, attrName)) {
9078              if (optional) break;
9079              attrs[attrName] = void 0;
9080            }
9081            if (optional && !attrs[attrName]) break;
9082
9083            parentGet = $parse(attrs[attrName]);
9084            if (parentGet.literal) {
9085              compare = equals;
9086            } else {
9087              compare = function(a, b) { return a === b || (a !== a && b !== b); };
9088            }
9089            parentSet = parentGet.assign || function() {
9090              // reset the change, or we will throw this exception on every $digest
9091              lastValue = destination[scopeName] = parentGet(scope);
9092              throw $compileMinErr('nonassign',
9093                  "Expression '{0}' used with directive '{1}' is non-assignable!",
9094                  attrs[attrName], directive.name);
9095            };
9096            lastValue = destination[scopeName] = parentGet(scope);
9097            var parentValueWatch = function parentValueWatch(parentValue) {
9098              if (!compare(parentValue, destination[scopeName])) {
9099                // we are out of sync and need to copy
9100                if (!compare(parentValue, lastValue)) {
9101                  // parent changed and it has precedence
9102                  destination[scopeName] = parentValue;
9103                } else {
9104                  // if the parent can be assigned then do so
9105                  parentSet(scope, parentValue = destination[scopeName]);
9106                }
9107              }
9108              return lastValue = parentValue;
9109            };
9110            parentValueWatch.$stateful = true;
9111            var removeWatch;
9112            if (definition.collection) {
9113              removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
9114            } else {
9115              removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
9116            }
9117            removeWatchCollection.push(removeWatch);
9118            break;
9119
9120          case '&':
9121            // Don't assign Object.prototype method to scope
9122            parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
9123
9124            // Don't assign noop to destination if expression is not val
9124id
9125            if (parentGet === noop && optional) break;
9126
9127            destination[scopeName] = function(locals) {
9128              return parentGet(scope, locals);
9129            };
9130            break;
9131        }
9132      });
9133
9134      return removeWatchCollection.length && function removeWatches() {
9135        for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
9136          removeWatchCollection[i]();
9137        }
9138      };
9139    }
9140  }];
9141}
9142
9143var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9144/**
9145 * Converts all accepted directives format into proper directive name.
9146 * @param name Name to normalize
9147 */
9148function directiveNormalize(name) {
9149  return camelCase(name.replace(PREFIX_REGEXP, ''));
9150}
9151
9152/**
9153 * @ngdoc type
9154 * @name $compile.directive.Attributes
9155 *
9156 * @description
9157 * A shared object between directive compile / linking functions which contains normalized DOM
9158 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9159 * needed since all of these are treated as equivalent in Angular:
9160 *
9161 * ```
9162 *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9163 * ```
9164 */
9165
9166/**
9167 * @ngdoc property
9168 * @name $compile.directive.Attributes#$attr
9169 *
9170 * @description
9171 * A map of DOM element attribute names to the normalized name. This is
9172 * needed to do reverse lookup from normalized name back to actual name.
9173 */
9174
9175
9176/**
9177 * @ngdoc method
9178 * @name $compile.directive.Attributes#$set
9179 * @kind function
9180 *
9181 * @description
9182 * Set DOM element attribute value.
9183 *
9184 *
9185 * @param {string} name Normalized element attribute name of the property to modify. The name is
9186 *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9187 *          property to the original name.
9188 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9189 */
9190
9191
9192
9193/**
9194 * Closure compiler type information
9195 */
9196
9197function nodesetLinkingFn(
9198  /* angular.Scope */ scope,
9199  /* NodeList */ nodeList,
9200  /* Element */ rootElement,
9201  /* function(Function) */ boundTranscludeFn
9202) {}
9203
9204function directiveLinkingFn(
9205  /* nodesetLinkingFn */ nodesetLinkingFn,
9206  /* angular.Scope */ scope,
9207  /* Node */ node,
9208  /* Element */ rootElement,
9209  /* function(Function) */ boundTranscludeFn
9210) {}
9211
9212function tokenDifference(str1, str2) {
9213  var values = '',
9214      tokens1 = str1.split(/\s+/),
9215      tokens2 = str2.split(/\s+/);
9216
9217  outer:
9218  for (var i = 0; i < tokens1.length; i++) {
9219    var token = tokens1[i];
9220    for (var j = 0; j < tokens2.length; j++) {
9221      if (token == tokens2[j]) continue outer;
9222    }
9223    values += (values.length > 0 ? ' ' : '') + token;
9224  }
9225  return values;
9226}
9227
9228function removeComments(jqNodes) {
9229  jqNodes = jqLite(jqNodes);
9230  var i = jqNodes.length;
9231
9232  if (i <= 1) {
9233    return jqNodes;
9234  }
9235
9236  while (i--) {
9237    var node = jqNodes[i];
9238    if (node.nodeType === NODE_TYPE_COMMENT) {
9239      splice.call(jqNodes, i, 1);
9240    }
9241  }
9242  return jqNodes;
9243}
9244
9245var $controllerMinErr = minErr('$controller');
9246
9247
9248var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
9249function identifierForController(controller, ident) {
9250  if (ident && isString(ident)) return ident;
9251  if (isString(controller)) {
9252    var match = CNTRL_REG.exec(controller);
9253    if (match) return match[3];
9254  }
9255}
9256
9257
9258/**
9259 * @ngdoc provider
9260 * @name $controllerProvider
9261 * @description
9262 * The {@link ng.$controller $controller service} is used by Angular to create new
9263 * controllers.
9264 *
9265 * This provider allows controller registration via the
9266 * {@link ng.$controllerProvider#register register} method.
9267 */
9268function $ControllerProvider() {
9269  var controllers = {},
9270      globals = false;
9271
9272  /**
9273   * @ngdoc method
9274   * @name $controllerProvider#register
9275   * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9276   *    the names and the values are the constructors.
9277   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9278   *    annotations in the array notation).
9279   */
9280  this.register = function(name, constructor) {
9281    assertNotHasOwnProperty(name, 'controller');
9282    if (isObject(name)) {
9283      extend(controllers, name);
9284    } else {
9285      controllers[name] = constructor;
9286    }
9287  };
9288
9289  /**
9290   * @ngdoc method
9291   * @name $controllerProvider#allowGlobals
9292   * @description If called, allows `$controller` to find controller constructors on `window`
9293   */
9294  this.allowGlobals = function() {
9295    globals = true;
9296  };
9297
9298
9299  this.$get = ['$injector', '$window', function($injector, $window) {
9300
9301    /**
9302     * @ngdoc service
9303     * @name $controller
9304     * @requires $injector
9305     *
9306     * @param {Function|string} constructor If called with a function then it's considered to be the
9307     *    controller constructor function. Otherwise it's considered to be a string which is used
9308     *    to retrieve the controller constructor using the following steps:
9309     *
9310     *    * check if a controller with given name is registered via `$controllerProvider`
9311     *    * check if evaluating the string on the current scope returns a constructor
9312     *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9313     *      `window` object (not recommended)
9314     *
9315     *    The string can use the `controller as property` syntax, where the controller instance is published
9316     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9317     *    to work correctly.
9318     *
9319     * @param {Object} locals Injection locals for Controller.
9320     * @return {Object} Instance of given controller.
9321     *
9322     * @description
9323     * `$controller` service is responsible for instantiating controllers.
9324     *
9325     * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9326     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9327     */
9328    return function(expression, locals, later, ident) {
9329      // PRIVATE API:
9330      //   param `later` --- indicates that the controller's constructor is invoked at a later time.
9331      //                     If true, $controller will allocate the object with the correct
9332      //                     prototype chain, but will not invoke the controller until a returned
9333      //                     callback is invoked.
9334      //   param `ident` --- An optional label which overrides the label parsed from the controller
9335      //                     expression, if any.
9336      var instance, match, constructor, identifier;
9337      later = later === true;
9338      if (ident && isString(ident)) {
9339        identifier = ident;
9340      }
9341
9342      if (isString(expression)) {
9343        match = expression.match(CNTRL_REG);
9344        if (!match) {
9345          throw $controllerMinErr('ctrlfmt',
9346            "Badly formed controller string '{0}'. " +
9347            "Must match `__name__ as __id__` or `__name__`.", expression);
9348        }
9349        constructor = match[1],
9350        identifier = identifier || match[3];
9351        expression = controllers.hasOwnProperty(constructor)
9352            ? controllers[constructor]
9353            : getter(locals.$scope, constructor, true) ||
9354                (globals ? getter($window, constructor, true) : undefined);
9355
9356        assertArgFn(expression, constructor, true);
9357      }
9358
9359      if (later) {
9360        // Instantiate controller later:
9361        // This machinery is used to create an instance of the object before calling the
9362        // controller's constructor itself.
9363        //
9364        // This allows properties to be added to the controller before the constructor is
9365        // invoked. Primarily, this is used for isolate scope bindings in $compile.
9366        //
9367        // This feature is not intended for use by applications, and is thus not documented
9368        // publicly.
9369        // Object creation: http://jsperf.com/create-constructor/2
9370        var controllerPrototype = (isArray(expression) ?
9371          expression[expression.length - 1] : expression).prototype;
9372        instance = Object.create(controllerPrototype || null);
9373
9374        if (identifier) {
9375          addIdentifier(locals, identifier, instance, constructor || expression.name);
9376        }
9377
9378        var instantiate;
9379        return instantiate = extend(function() {
9380          var result = $injector.invoke(expression, instance, locals, constructor);
9381          if (result !== instance && (isObject(result) || isFunction(result))) {
9382            instance = result;
9383            if (identifier) {
9384              // If result changed, re-assign controllerAs value to scope.
9385              addIdentifier(locals, identifier, instance, constructor || expression.name);
9386            }
9387          }
9388          return instance;
9389        }, {
9390          instance: instance,
9391          identifier: identifier
9392        });
9393      }
9394
9395      instance = $injector.instantiate(expression, locals, constructor);
9396
9397      if (identifier) {
9398        addIdentifier(locals, identifier, instance, constructor || expression.name);
9399      }
9400
9401      return instance;
9402    };
9403
9404    function addIdentifier(locals, identifier, instance, name) {
9405      if (!(locals && isObject(locals.$scope))) {
9406        throw minErr('$controller')('noscp',
9407          "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9408          name, identifier);
9409      }
9410
9411      locals.$scope[identifier] = instance;
9412    }
9413  }];
9414}
9415
9416/**
9417 * @ngdoc service
9418 * @name $document
9419 * @requires $window
9420 *
9421 * @description
9422 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9423 *
9424 * @example
9425   <example module="documentExample">
9426     <file name="index.html">
9427       <div ng-controller="ExampleController">
9428         <p>$document title: <b ng-bind="title"></b></p>
9429         <p>window.document title: <b ng-bind="windowTitle"></b></p>
9430       </div>
9431     </file>
9432     <file name="script.js">
9433       angular.module('documentExample', [])
9434         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9435           $scope.title = $document[0].title;
9436           $scope.windowTitle = angular.element(window.document)[0].title;
9437         }]);
9438     </file>
9439   </example>
9440 */
9441function $DocumentProvider() {
9442  this.$get = ['$window', function(window) {
9443    return jqLite(window.document);
9444  }];
9445}
9446
9447/**
9448 * @ngdoc service
9449 * @name $exceptionHandler
9450 * @requires ng.$log
9451 *
9452 * @description
9453 * Any uncaught exception in angular expressions is delegated to this service.
9454 * The default implementation simply delegates to `$log.error` which logs it into
9455 * the browser console.
9456 *
9457 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9458 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9459 *
9460 * ## Example:
9461 *
9462 * ```js
9463 *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9464 *     return function(exception, cause) {
9465 *       exception.message += ' (caused by "' + cause + '")';
9466 *       throw exception;
9467 *     };
9468 *   });
9469 * ```
9470 *
9471 * This example will override the normal action of `$exceptionHandler`, to make angular
9472 * exceptions fail hard when they happen, instead of just logging to the console.
9473 *
9474 * <hr />
9475 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9476 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9477 * (unless executed during a digest).
9478 *
9479 * If you wish, you can manually delegate exceptions, e.g.
9480 * `try { ... } catch(e) { $exceptionHandler(e); }`
9481 *
9482 * @param {Error} exception Exception associated with the error.
9483 * @param {string=} cause optional information about the context in which
9484 *       the error was thrown.
9485 *
9486 */
9487function $ExceptionHandlerProvider() {
9488  this.$get = ['$log', function($log) {
9489    return function(exception, cause) {
9490      $log.error.apply($log, arguments);
9491    };
9492  }];
9493}
9494
9495var $$ForceReflowProvider = function() {
9496  this.$get = ['$document', function($document) {
9497    return function(domNode) {
9498      //the line below will force the browser to perform a repaint so
9499      //that all the animated elements within the animation frame will
9500      //be properly updated and drawn on screen. This is required to
9501      //ensure that the preparation animation is properly flushed so that
9502      //the active state picks up from there. DO NOT REMOVE THIS LINE.
9503      //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9504      //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9505      //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9506      if (domNode) {
9507        if (!domNode.nodeType && domNode instanceof jqLite) {
9508          domNode = domNode[0];
9509        }
9510      } else {
9511        domNode = $document[0].body;
9512      }
9513      return domNode.offsetWidth + 1;
9514    };
9515  }];
9516};
9517
9518var APPLICATION_JSON = 'application/json';
9519var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9520var JSON_START = /^\[|^\{(?!\{)/;
9521var JSON_ENDS = {
9522  '[': /]$/,
9523  '{': /}$/
9524};
9525var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9526var $httpMinErr = minErr('$http');
9527var $httpMinErrLegacyFn = function(method) {
9528  return function() {
9529    throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9530  };
9531};
9532
9533function serializeValue(v) {
9534  if (isObject(v)) {
9535    return isDate(v) ? v.toISOString() : toJson(v);
9536  }
9537  return v;
9538}
9539
9540
9541function $HttpParamSerializerProvider() {
9542  /**
9543   * @ngdoc service
9544   * @name $httpParamSerializer
9545   * @description
9546   *
9547   * Default {@link $http `$http`} params serializer that converts objects to strings
9548   * according to the following rules:
9549   *
9550   * * `{'foo': 'bar'}` results in `foo=bar`
9551   * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9552   * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9553   * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9554   *
9555   * Note that serializer will sort the request parameters alphabetically.
9556   * */
9557
9558  this.$get = function() {
9559    return function ngParamSerializer(params) {
9560      if (!params) return '';
9561      var parts = [];
vendor: 3,682 bytes, lines 9562-9688
9562      forEachSorted(params, function(value, key) {
9563        if (value === null || isUndefined(value)) return;
9564        if (isArray(value)) {
9565          forEach(value, function(v, k) {
9566            parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
9567          });
9568        } else {
9569          parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9570        }
9571      });
9572
9573      return parts.join('&');
9574    };
9575  };
9576}
9577
9578function $HttpParamSerializerJQLikeProvider() {
9579  /**
9580   * @ngdoc service
9581   * @name $httpParamSerializerJQLike
9582   * @description
9583   *
9584   * Alternative {@link $http `$http`} params serializer that follows
9585   * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9586   * The serializer will also sort the params alphabetically.
9587   *
9588   * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9589   *
9590   * ```js
9591   * $http({
9592   *   url: myUrl,
9593   *   method: 'GET',
9594   *   params: myParams,
9595   *   paramSerializer: '$httpParamSerializerJQLike'
9596   * });
9597   * ```
9598   *
9599   * It is also possible to set it as the default `paramSerializer` in the
9600   * {@link $httpProvider#defaults `$httpProvider`}.
9601   *
9602   * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9603   * form data for submission:
9604   *
9605   * ```js
9606   * .controller(function($http, $httpParamSerializerJQLike) {
9607   *   //...
9608   *
9609   *   $http({
9610   *     url: myUrl,
9611   *     method: 'POST',
9612   *     data: $httpParamSerializerJQLike(myData),
9613   *     headers: {
9614   *       'Content-Type': 'application/x-www-form-urlencoded'
9615   *     }
9616   *   });
9617   *
9618   * });
9619   * ```
9620   *
9621   * */
9622  this.$get = function() {
9623    return function jQueryLikeParamSerializer(params) {
9624      if (!params) return '';
9625      var parts = [];
9626      serialize(params, '', true);
9627      return parts.join('&');
9628
9629      function serialize(toSerialize, prefix, topLevel) {
9630        if (toSerialize === null || isUndefined(toSerialize)) return;
9631        if (isArray(toSerialize)) {
9632          forEach(toSerialize, function(value, index) {
9633            serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
9634          });
9635        } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9636          forEachSorted(toSerialize, function(value, key) {
9637            serialize(value, prefix +
9638                (topLevel ? '' : '[') +
9639                key +
9640                (topLevel ? '' : ']'));
9641          });
9642        } else {
9643          parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9644        }
9645      }
9646    };
9647  };
9648}
9649
9650function defaultHttpResponseTransform(data, headers) {
9651  if (isString(data)) {
9652    // Strip json vulnerability protection prefix and trim whitespace
9653    var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9654
9655    if (tempData) {
9656      var contentType = headers('Content-Type');
9657      if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9658        data = fromJson(tempData);
9659      }
9660    }
9661  }
9662
9663  return data;
9664}
9665
9666function isJsonLike(str) {
9667    var jsonStart = str.match(JSON_START);
9668    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9669}
9670
9671/**
9672 * Parse headers into key value object
9673 *
9674 * @param {string} headers Raw headers as a string
9675 * @returns {Object} Parsed headers as key value object
9676 */
9677function parseHeaders(headers) {
9678  var parsed = createMap(), i;
9679
9680  function fillInParsed(key, val) {
9681    if (key) {
9682      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9683    }
9684  }
9685
9686  if (isString(headers)) {
9687    forEach(headers.split('\n'), function(line) {
9688      i = line.indexOf(':');
vendor: 4,257 bytes, lines 9689-9826
9689      fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9690    });
9691  } else if (isObject(headers)) {
9692    forEach(headers, function(headerVal, headerKey) {
9693      fillInParsed(lowercase(headerKey), trim(headerVal));
9694    });
9695  }
9696
9697  return parsed;
9698}
9699
9700
9701/**
9702 * Returns a function that provides access to parsed headers.
9703 *
9704 * Headers are lazy parsed when first requested.
9705 * @see parseHeaders
9706 *
9707 * @param {(string|Object)} headers Headers to provide access to.
9708 * @returns {function(string=)} Returns a getter function which if called with:
9709 *
9710 *   - if called with single an argument returns a single header value or null
9711 *   - if called with no arguments returns an object containing all headers.
9712 */
9713function headersGetter(headers) {
9714  var headersObj;
9715
9716  return function(name) {
9717    if (!headersObj) headersObj =  parseHeaders(headers);
9718
9719    if (name) {
9720      var value = headersObj[lowercase(name)];
9721      if (value === void 0) {
9722        value = null;
9723      }
9724      return value;
9725    }
9726
9727    return headersObj;
9728  };
9729}
9730
9731
9732/**
9733 * Chain all given functions
9734 *
9735 * This function is used for both request and response transforming
9736 *
9737 * @param {*} data Data to transform.
9738 * @param {function(string=)} headers HTTP headers getter fn.
9739 * @param {number} status HTTP status code of the response.
9740 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9741 * @returns {*} Transformed data.
9742 */
9743function transformData(data, headers, status, fns) {
9744  if (isFunction(fns)) {
9745    return fns(data, headers, status);
9746  }
9747
9748  forEach(fns, function(fn) {
9749    data = fn(data, headers, status);
9750  });
9751
9752  return data;
9753}
9754
9755
9756function isSuccess(status) {
9757  return 200 <= status && status < 300;
9758}
9759
9760
9761/**
9762 * @ngdoc provider
9763 * @name $httpProvider
9764 * @description
9765 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9766 * */
9767function $HttpProvider() {
9768  /**
9769   * @ngdoc property
9770   * @name $httpProvider#defaults
9771   * @description
9772   *
9773   * Object containing default values for all {@link ng.$http $http} requests.
9774   *
9775   * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
9776   * that will provide the cache for all requests who set their `cache` property to `true`.
9777   * If you set the `defaults.cache = false` then only requests that specify their own custom
9778   * cache object will be cached. See {@link $http#caching $http Caching} for more information.
9779   *
9780   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9781   * Defaults value is `'XSRF-TOKEN'`.
9782   *
9783   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9784   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9785   *
9786   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9787   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9788   * setting default headers.
9789   *     - **`defaults.headers.common`**
9790   *     - **`defaults.headers.post`**
9791   *     - **`defaults.headers.put`**
9792   *     - **`defaults.headers.patch`**
9793   *
9794   *
9795   * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9796   *  used to the prepare string representation of request parameters (specified as an object).
9797   *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9798   *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9799   *
9800   **/
9801  var defaults = this.defaults = {
9802    // transform incoming response data
9803    transformResponse: [defaultHttpResponseTransform],
9804
9805    // transform outgoing request data
9806    transformRequest: [function(d) {
9807      return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9808    }],
9809
9810    // default headers
9811    headers: {
9812      common: {
9813        'Accept': 'application/json, text/plain, */*'
9814      },
9815      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9816      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9817      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9818    },
9819
9820    xsrfCookieName: 'XSRF-TOKEN',
9821    xsrfHeaderName: 'X-XSRF-TOKEN',
9822
9823    paramSerializer: '$httpParamSerializer'
9824  };
9825
9826  var useApplyAsync = false;
vendor: 4,307 bytes, lines 9827-9937
9827  /**
9828   * @ngdoc method
9829   * @name $httpProvider#useApplyAsync
9830   * @description
9831   *
9832   * Configure $http service to combine processing of multiple http responses received at around
9833   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9834   * significant performance improvement for bigger applications that make many HTTP requests
9835   * concurrently (common during application bootstrap).
9836   *
9837   * Defaults to false. If no value is specified, returns the current configured value.
9838   *
9839   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9840   *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9841   *    to load and share the same digest cycle.
9842   *
9843   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9844   *    otherwise, returns the current configured value.
9845   **/
9846  this.useApplyAsync = function(value) {
9847    if (isDefined(value)) {
9848      useApplyAsync = !!value;
9849      return this;
9850    }
9851    return useApplyAsync;
9852  };
9853
9854  var useLegacyPromise = true;
9855  /**
9856   * @ngdoc method
9857   * @name $httpProvider#useLegacyPromiseExtensions
9858   * @description
9859   *
9860   * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9861   * This should be used to make sure that applications work without these methods.
9862   *
9863   * Defaults to true. If no value is specified, returns the current configured value.
9864   *
9865   * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
9866   *
9867   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9868   *    otherwise, returns the current configured value.
9869   **/
9870  this.useLegacyPromiseExtensions = function(value) {
9871    if (isDefined(value)) {
9872      useLegacyPromise = !!value;
9873      return this;
9874    }
9875    return useLegacyPromise;
9876  };
9877
9878  /**
9879   * @ngdoc property
9880   * @name $httpProvider#interceptors
9881   * @description
9882   *
9883   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9884   * pre-processing of request or postprocessing of responses.
9885   *
9886   * These service factories are ordered by request, i.e. they are applied in the same order as the
9887   * array, on request, but reverse order, on response.
9888   *
9889   * {@link ng.$http#interceptors Interceptors detailed info}
9890   **/
9891  var interceptorFactories = this.interceptors = [];
9892
9893  this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9894      function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9895
9896    var defaultCache = $cacheFactory('$http');
9897
9898    /**
9899     * Make sure that default param serializer is exposed as a function
9900     */
9901    defaults.paramSerializer = isString(defaults.paramSerializer) ?
9902      $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9903
9904    /**
9905     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9906     * The reversal is needed so that we can build up the interception chain around the
9907     * server request.
9908     */
9909    var reversedInterceptors = [];
9910
9911    forEach(interceptorFactories, function(interceptorFactory) {
9912      reversedInterceptors.unshift(isString(interceptorFactory)
9913          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9914    });
9915
9916    /**
9917     * @ngdoc service
9918     * @kind function
9919     * @name $http
9920     * @requires ng.$httpBackend
9921     * @requires $cacheFactory
9922     * @requires $rootScope
9923     * @requires $q
9924     * @requires $injector
9925     *
9926     * @description
9927     * The `$http` service is a core Angular service that facilitates communication with the remote
9928     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9929     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9930     *
9931     * For unit testing applications that use `$http` service, see
9932     * {@link ngMock.$httpBackend $httpBackend mock}.
9933     *
9934     * For a higher level of abstraction, please check out the {@link ngResource.$resource
9935     * $resource} service.
9936     *
9937     * The $http API is based on the {@link ng.$q deferred/promise APIs}
9937 exposed by
9938     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9939     * it is important to familiarize yourself with these APIs and the guarantees they provide.
9940     *
9941     *
9942     * ## General usage
9943     * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
9944     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
9945     *
9946     * ```js
9947     *   // Simple GET request example:
9948     *   $http({
9949     *     method: 'GET',
9950     *     url: '/someUrl'
9951     *   }).then(function successCallback(response) {
9952     *       // this callback will be called asynchronously
9953     *       // when the response is available
9954     *     }, function errorCallback(response) {
9955     *       // called asynchronously if an error occurs
9956     *       // or server returns response with an error status.
9957     *     });
9958     * ```
9959     *
9960     * The response object has these properties:
9961     *
9962     *   - **data** – `{string|Object}` – The response body transformed with the transform
9963     *     functions.
9964     *   - **status** – `{number}` – HTTP status code of the response.
9965     *   - **headers** – `{function([headerName])}` – Header getter function.
9966     *   - **config** – `{Object}` – The configuration object that was used to generate the request.
9967     *   - **statusText** – `{string}` – HTTP status text of the response.
9968     *
9969     * A response status code between 200 and 299 is considered a success status and
9970     * will result in the success callback being called. Note that if the response is a redirect,
9971     * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9972     * called for such responses.
9973     *
9974     *
9975     * ## Shortcut methods
9976     *
9977     * Shortcut methods are also available. All shortcut methods require passing in the URL, and
9978     * request data must be passed in for POST/PUT requests. An optional config can be passed as the
9979     * last argument.
9980     *
9981     * ```js
9982     *   $http.get('/someUrl', config).then(successCallback, errorCallback);
9983     *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
9984     * ```
9985     *
9986     * Complete list of shortcut methods:
9987     *
9988     * - {@link ng.$http#get $http.get}
9989     * - {@link ng.$http#head $http.head}
9990     * - {@link ng.$http#post $http.post}
9991     * - {@link ng.$http#put $http.put}
9992     * - {@link ng.$http#delete $http.delete}
9993     * - {@link ng.$http#jsonp $http.jsonp}
9994     * - {@link ng.$http#patch $http.patch}
9995     *
9996     *
9997     * ## Writing Unit Tests that use $http
9998     * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9999     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
10000     * request using trained responses.
10001     *
10002     * ```
10003     * $httpBackend.expectGET(...);
10004     * $http.get(...);
10005     * $httpBackend.flush();
10006     * ```
10007     *
10008     * ## Deprecation Notice
10009     * <div class="alert alert-danger">
10010     *   The `$http` legacy promise methods `success` and `error` have been deprecated.
10011     *   Use the standard `then` method instead.
10012     *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
10013     *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
10014     * </div>
10015     *
10016     * ## Setting HTTP Headers
10017     *
10018     * The $http service will automatically add certain HTTP headers to all requests. These defaults
10019     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
10020     * object, which currently contains this default configuration:
10021     *
10022     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
10023     *   - `Accept: application/json, text/plain, * / *`
10024     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
10025     *   - `Content-Type: application/json`
10026     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
10027     *   - `Content-Type: application/json`
10028     *
10029     * To add or overwrite these defaults, simply add or remove a property from these configuration
10030     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
10031     * with the lowercased HTTP method name as the key, e.g.
10032     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
10033     *
10034     * The defaults can also be set at runtime via the `$http.defaults` object in the same
10035     * fashion. For example:
10036     *
10037     * ```
10038     * module.run(function($http) {
10039     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
10040     * });
10041     * ```
10042     *
10043     * In addition, you can supply a `headers` property in the config object passed when
10044     * calling `$http(config)`, which overrides the defaults without changing them globally.
10045     *
10046     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
10047     * Use the `headers` property, setting the desired header to `undefined`. For example:
10048     *
10049     * ```js
10050     * var req = {
10051     *  method: 'POST',
10052     *  url: 'http://example.com',
10053     *  headers: {
10054     *    'Content-Type': undefined
10055     *  },
10056     *  data: { test: 'test' }
10057     * }
10058     *
10059     * $http(req).then(function(){...}, function(){...});
10060     * ```
10061     *
10062     * ## Transforming Requests and Responses
10063     *
10064     * Both requests and responses can be transformed using transformation functions: `transformRequest`
10065     * and `transformResponse`. These properties can be a single function that returns
10066     * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
10067     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
10068     *
10069     * ### Default Transformations
10070     *
10071     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
10072     * `defaults.transformResponse` properties. If a request does not provide its own transformations
10073     * then these will be applied.
10074     *
10075     * You can augment or replace the default transformations by modifying these properties by adding to or
10076     * replacing the array.
10077     *
10078     * Angular provides the following default transformations:
10079     *
10080     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
10081     *
10082     * - If the `data` property of the request configuration object contains an object, serialize it
10083     *   into JSON format.
10084     *
10085     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
10086     *
10087     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
10088     *  - If JSON response is detected, deserialize it using a JSON parser.
10089     *
10090     *
10091     * ### Overriding the Default Transformations Per Request
10092     *
10093     * If you wish override the request/response transformations only for a single request then provide
10094     * `transformRequest` and/or `transformResponse` properties on the configuration object passed
10095     * into `$http`.
10096     *
10097     * Note that if you provide these properties on the config object the default transformations will be
10098     * overwritten. If you wish to augment the default transformations then you must include them in your
10099     * local transformation array.
10100     *
10101     * The following code demonstrates adding a new response transformation to be run after the default response
10102     * transformations have been run.
10103     *
10104     * ```js
10105     * function appendTransform(defaults, transform) {
10106     *
10107     *   // We can't guarantee that the default transformation is an array
10108     *   defaults = angular.isArray(defaults) ? defaults : [defaults];
10109     *
10110     *   // Append the new transformation to the defaults
10111     *   return defaults.concat(transform);
10112     * }
10113     *
10114     * $http({
10115     *   url: '...',
10116     *   method: 'GET',
10117     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
10118     *     return doTransform(value);
10119     *   })
10120     * });
10121     * ```
10122     *
10123     *
10124     * ## Caching
10125     *
10126     * To enable caching, set the request configuration `cache` property to `true` (to use default
10127     * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
10128     * When the cache is enabled, `$http` stores the response from the server in the specified
10129     * cache. The next time the same request is made, the response is served from the cache without
10130     * sending a request to the server.
10131     *
10132     * Note that even if the response is served from cache, delivery of the data is asynchronous in
10133     * the same way that real requests are.
10134     *
10135     * If there are multiple GET requests for the same URL that should be cached using the same
10136     * cache, but the cache is not populated yet, only one request to the server will be made and
10137     * the remaining requests will be fulfilled using the response from the first request.
10138     *
10139     * You can change the default cache to a new object (built with
10140     * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
10141     * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
10142     * their `cache` property to `true` will now use this cache object.
10143     *
10144     * If you set the default cache to `false` then only requests that specify their own custom
10145     * cache object will be cached.
10146     *
10147     * ## Interceptors
10148     *
10149     * Before you start creating interceptors, be sure to understand the
10150     * {@link ng.$q $q and deferred/promise APIs}.
10151     *
10152     * For purposes of global error handling, authentication, or any kind of synchronous or
10153     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10154     * able to intercept requests before they are handed to the server and
10155     * responses before they are handed over to the application code that
10156     * initiated these requests. The interceptors leverage the {@link ng.$q
10157     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10158     *
10159     * The interceptors are service factories that are registered with the `$httpProvider` by
10160     * adding them to the `$httpProvider.interceptors` array. The factory is called and
10161     * injected with dependencies (if specified) and returns the interceptor.
10162     *
10163     * There are two kinds of interceptors (and two kinds of rejection interceptors):
10164     *
10165     *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10166     *     modify the `config` object or create a new one. The function needs to return the `config`
10167     *     object directly, or a promise containing the `config` or a new `config` object.
10168     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
10169     *     resolved with a rejection.
10170     *   * `response`: interceptors get called with http `response` object. The function is free to
10171     *     modify the `response` object or create a new one. The function needs to return the `response`
10172     *     object directly, or as a promise containing the `response` or a new `response` object.
10173     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
10174     *     resolved with a rejection.
10175     *
10176     *
10177     * ```js
10178     *   // register the interceptor as a service
10179     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10180     *     return {
10181     *       // optional method
10182     *       'request': function(config) {
10183     *         // do something on success
10184     *         return config;
10185     *       },
10186     *
10187     *       // optional method
10188     *      'requestError': function(rejection) {
10189     *         // do something on error
10190     *         if (canRecover(rejection)) {
10191     *           return responseOrNewPromise
10192     *         }
10193     *         return $q.reject(rejection);
10194     *       },
10195     *
10196     *
10197     *
10198     *       // optional method
10199     *       'response': function(response) {
10200     *         // do something on success
10201     *         return response;
10202     *       },
10203     *
10204     *       // optional method
10205     *      'responseError': function(rejection) {
10206     *         // do something on error
10207     *         if (canRecover(rejection)) {
10208     *           return responseOrNewPromise
10209     *         }
10210     *         return $q.reject(rejection);
10211     *       }
10212     *     };
10213     *   });
10214     *
10215     *   $httpProvider.interceptors.push('myHttpInterceptor');
10216     *
10217     *
10218     *   // alternatively, register the interceptor via an anonymous factory
10219     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10220     *     return {
10221     *      'request': function(config) {
10222     *          // same as above
10223     *       },
10224     *
10225     *       'response': function(response) {
10226     *          // same as above
10227     *       }
10228     *     };
10229     *   });
10230     * ```
10231     *
10232     * ## Security Considerations
10233     *
10234     * When designing web applications, consider security threats from:
10235     *
10236     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10237     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10238     *
10239     * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10240     * pre-configured with strategies that address these issues, but for this to work backend server
10241     * cooperation is required.
10242     *
10243     * ### JSON Vulnerability Protection
10244     *
10245     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10246     * allows third party website to turn your JSON resource URL into
10247     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10248     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10249     * Angular will automatically strip the prefix before processing it as JSON.
10250     *
10251     * For example if your server needs to return:
10252     * ```js
10253     * ['one','two']
10254     * ```
10255     *
10256     * which is vulnerable to attack, your server can return:
10257     * ```js
10258     * )]}',
10259     * ['one','two']
10260     * ```
10261     *
10262     * Angular will strip the prefix, before processing the JSON.
10263     *
10264     *
10265     * ### Cross Site Request Forgery (XSRF) Protection
10266     *
10267     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
10268     * an unauthorized site can gain your user's private data. Angular provides a mechanism
10269     * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
10270     * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
10271     * JavaScript that runs on your domain could read the cookie, your server can be assured that
10272     * the XHR came from JavaScript running on your domain. The header will not be set for
10273     * cross-domain requests.
10274     *
10275     * To take advantage of this, your server needs to set a token in a JavaScript readable session
10276     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10277     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10278     * that only JavaScript running on your domain could have sent the request. The token must be
10279     * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10280     * making up its own tokens). We recommend that the token is a digest of your site's
10281     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
10282     * for added security.
10283     *
10284     * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10285     * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10286     * or the per-request config object.
10287     *
10288     * In order to prevent collisions in environments where multiple Angular apps share the
10289     * same domain or subdomain, we recommend that each application uses unique cookie name.
10290     *
10291     * @param {object} config Object describing the request to be made and how it should be
10292     *    processed. The object has following properties:
10293     *
10294     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10295     *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10296     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10297     *      with the `paramSerializer` and appended as GET parameters.
10298     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
10299     *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
10300     *      HTTP headers to send to the server. If the return value of a function is null, the
10301     *      header will not be sent. Functions accept a config object as an argument.
10302     *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10303     *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10304     *    - **transformRequest** –
10305     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>
10305}` –
10306     *      transform function or an array of such functions. The transform function takes the http
10307     *      request body and headers and returns its transformed (typically serialized) version.
10308     *      See {@link ng.$http#overriding-the-default-transformations-per-request
10309     *      Overriding the Default Transformations}
10310     *    - **transformResponse** –
10311     *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10312     *      transform function or an array of such functions. The transform function takes the http
10313     *      response body, headers and status and returns its transformed (typically deserialized) version.
10314     *      See {@link ng.$http#overriding-the-default-transformations-per-request
10315     *      Overriding the Default TransformationjqLiks}
10316     *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10317     *      prepare the string representation of request parameters (specified as an object).
10318     *      If specified as string, it is interpreted as function registered with the
10319     *      {@link $injector $injector}, which means you can create your own serializer
10320     *      by registering it as a {@link auto.$provide#service service}.
10321     *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10322     *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10323     *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
10324     *      GET request, otherwise if a cache instance built with
10325     *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
10326     *      caching.
10327     *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10328     *      that should abort the request when resolved.
10329     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10330     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10331     *      for more information.
10332     *    - **responseType** - `{string}` - see
10333     *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10334     *
10335     * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10336     *                        when the request succeeds or fails.
10337     *
10338     *
10339     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10340     *   requests. This is primarily meant to be used for debugging purposes.
10341     *
10342     *
10343     * @example
10344<example module="httpExample">
10345<file name="index.html">
10346  <div ng-controller="FetchController">
10347    <select ng-model="method" aria-label="Request method">
10348      <option>GET</option>
10349      <option>JSONP</option>
10350    </select>
10351    <input type="text" ng-model="url" size="80" aria-label="URL" />
10352    <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10353    <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10354    <button id="samplejsonpbtn"
10355      ng-click="updateModel('JSONP',
10356                    'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10357      Sample JSONP
10358    </button>
10359    <button id="invalidjsonpbtn"
10360      ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10361        Invalid JSONP
10362      </button>
10363    <pre>http status code: {{status}}</pre>
10364    <pre>http response data: {{data}}</pre>
10365  </div>
10366</file>
10367<file name="script.js">
10368  angular.module('httpExample', [])
10369    .controller('FetchController', ['$scope', '$http', '$templateCache',
10370      function($scope, $http, $templateCache) {
10371        $scope.method = 'GET';
10372        $scope.url = 'http-hello.html';
10373
10374        $scope.fetch = function() {
10375          $scope.code = null;
10376          $scope.response = null;
10377
10378          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10379            then(function(response) {
10380              $scope.status = response.status;
10381              $scope.data = response.data;
10382            }, function(response) {
10383              $scope.data = response.data || "Request failed";
10384              $scope.status = response.status;
10385          });
10386        };
10387
10388        $scope.updateModel = function(method, url) {
10389          $scope.method = method;
10390          $scope.url = url;
10391        };
10392      }]);
10393</file>
10394<file name="http-hello.html">
10395  Hello, $http!
10396</file>
10397<file name="protractor.js" type="protractor">
10398  var status = element(by.binding('status'));
10399  var data = element(by.binding('data'));
10400  var fetchBtn = element(by.id('fetchbtn'));
10401  var sampleGetBtn = element(by.id('samplegetbtn'));
10402  var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10403  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10404
10405  it('should make an xhr GET request', function() {
10406    sampleGetBtn.click();
10407    fetchBtn.click();
10408    expect(status.getText()).toMatch('200');
10409    expect(data.getText()).toMatch(/Hello, \$http!/);
10410  });
10411
10412// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10413// it('should make a JSONP request to angularjs.org', function() {
10414//   sampleJsonpBtn.click();
10415//   fetchBtn.click();
10416//   expect(status.getText()).toMatch('200');
10417//   expect(data.getText()).toMatch(/Super Hero!/);
10418// });
10419
10420  it('should make JSONP request to invalid URL and invoke the error handler',
10421      function() {
10422    invalidJsonpBtn.click();
10423    fetchBtn.click();
10424    expect(status.getText()).toMatch('0');
10425    expect(data.getText()).toMatch('Request failed');
10426  });
10427</file>
10428</example>
10429     */
10430    function $http(requestConfig) {
10431
10432      if (!angular.isObject(requestConfig)) {
10433        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
10434      }
10435
10436      if (!isString(requestConfig.url)) {
10437        throw minErr('$http')('badreq', 'Http request configuration url must be a string.  Received: {0}', requestConfig.url);
10438      }
10439
10440      var config = extend({
10441        method: 'get',
10442        transformRequest: defaults.transformRequest,
10443        transformResponse: defaults.transformResponse,
10444        paramSerializer: defaults.paramSerializer
10445      }, requestConfig);
10446
10447      config.headers = mergeHeaders(requestConfig);
10448      config.method = uppercase(config.method);
10449      config.paramSerializer = isString(config.paramSerializer) ?
10450        $injector.get(config.paramSerializer) : config.paramSerializer;
10451
10452      var serverRequest = function(config) {
10453        var headers = config.headers;
10454        var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10455
10456        // strip content-type if data is undefined
10457        if (isUndefined(reqData)) {
10458          forEach(headers, function(value, header) {
10459            if (lowercase(header) === 'content-type') {
10460                delete headers[header];
10461            }
10462          });
10463        }
10464
10465        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10466          config.withCredentials = defaults.withCredentials;
10467        }
10468
10469        // send request
10470        return sendReq(config, reqData).then(transformResponse, transformResponse);
10471      };
10472
10473      var chain = [serverRequest, undefined];
10474      var promise = $q.when(config);
10475
10476      // apply interceptors
10477      forEach(reversedInterceptors, function(interceptor) {
10478        if (interceptor.request || interceptor.requestError) {
10479          chain.unshift(interceptor.request, interceptor.requestError);
10480        }
10481        if (interceptor.response || interceptor.responseError) {
10482          chain.push(interceptor.response, interceptor.responseError);
10483        }
10484      });
10485
10486      while (chain.length) {
10487        var thenFn = chain.shift();
10488        var rejectFn = chain.shift();
10489
10490        promise = promise.then(thenFn, rejectFn);
10491      }
10492
10493      if (useLegacyPromise) {
10494        promise.success = function(fn) {
10495          assertArgFn(fn, 'fn');
10496
10497          promise.then(function(response) {
10498            fn(response.data, response.status, response.headers, config);
10499          });
10500          return promise;
10501        };
10502
10503        promise.error = function(fn) {
10504          assertArgFn(fn, 'fn');
10505
10506          promise.then(null, function(response) {
10507            fn(response.data, response.status, response.headers, config);
10508          });
10509          return promise;
10510        };
10511      } else {
10512        promise.success = $httpMinErrLegacyFn('success');
10513        promise.error = $httpMinErrLegacyFn('error');
10514      }
10515
10516      return promise;
10517
10518      function transformResponse(response) {
10519        // make a copy since the response must be cacheable
10520        var resp = extend({}, response);
10521        resp.data = transformData(response.data, response.headers, response.status,
10522                                  config.transformResponse);
10523        return (isSuccess(response.status))
10524          ? resp
10525          : $q.reject(resp);
10526      }
10527
10528      function executeHeaderFns(headers, config) {
10529        var headerContent, processedHeaders = {};
10530
10531        forEach(headers, function(headerFn, header) {
10532          if (isFunction(headerFn)) {
10533            headerContent = headerFn(config);
10534            if (headerContent != null) {
10535              processedHeaders[header] = headerContent;
10536            }
10537          } else {
10538            processedHeaders[header] = headerFn;
10539          }
10540        });
10541
10542        return processedHeaders;
10543      }
10544
10545      function mergeHeaders(config) {
10546        var defHeaders = defaults.headers,
10547            reqHeaders = extend({}, config.headers),
10548            defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10549
10550        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10551
vendor: 9,098 bytes, lines 10552-10852
10552        // using for-in instead of forEach to avoid unecessary iteration after header has been found
10553        defaultHeadersIteration:
10554        for (defHeaderName in defHeaders) {
10555          lowercaseDefHeaderName = lowercase(defHeaderName);
10556
10557          for (reqHeaderName in reqHeaders) {
10558            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10559              continue defaultHeadersIteration;
10560            }
10561          }
10562
10563          reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10564        }
10565
10566        // execute if header value is a function for merged headers
10567        return executeHeaderFns(reqHeaders, shallowCopy(config));
10568      }
10569    }
10570
10571    $http.pendingRequests = [];
10572
10573    /**
10574     * @ngdoc method
10575     * @name $http#get
10576     *
10577     * @description
10578     * Shortcut method to perform `GET` request.
10579     *
10580     * @param {string} url Relative or absolute URL specifying the destination of the request
10581     * @param {Object=} config Optional configuration object
10582     * @returns {HttpPromise} Future object
10583     */
10584
10585    /**
10586     * @ngdoc method
10587     * @name $http#delete
10588     *
10589     * @description
10590     * Shortcut method to perform `DELETE` request.
10591     *
10592     * @param {string} url Relative or absolute URL specifying the destination of the request
10593     * @param {Object=} config Optional configuration object
10594     * @returns {HttpPromise} Future object
10595     */
10596
10597    /**
10598     * @ngdoc method
10599     * @name $http#head
10600     *
10601     * @description
10602     * Shortcut method to perform `HEAD` request.
10603     *
10604     * @param {string} url Relative or absolute URL specifying the destination of the request
10605     * @param {Object=} config Optional configuration object
10606     * @returns {HttpPromise} Future object
10607     */
10608
10609    /**
10610     * @ngdoc method
10611     * @name $http#jsonp
10612     *
10613     * @description
10614     * Shortcut method to perform `JSONP` request.
10615     *
10616     * @param {string} url Relative or absolute URL specifying the destination of the request.
10617     *                     The name of the callback should be the string `JSON_CALLBACK`.
10618     * @param {Object=} config Optional configuration object
10619     * @returns {HttpPromise} Future object
10620     */
10621    createShortMethods('get', 'delete', 'head', 'jsonp');
10622
10623    /**
10624     * @ngdoc method
10625     * @name $http#post
10626     *
10627     * @description
10628     * Shortcut method to perform `POST` request.
10629     *
10630     * @param {string} url Relative or absolute URL specifying the destination of the request
10631     * @param {*} data Request content
10632     * @param {Object=} config Optional configuration object
10633     * @returns {HttpPromise} Future object
10634     */
10635
10636    /**
10637     * @ngdoc method
10638     * @name $http#put
10639     *
10640     * @description
10641     * Shortcut method to perform `PUT` request.
10642     *
10643     * @param {string} url Relative or absolute URL specifying the destination of the request
10644     * @param {*} data Request content
10645     * @param {Object=} config Optional configuration object
10646     * @returns {HttpPromise} Future object
10647     */
10648
10649     /**
10650      * @ngdoc method
10651      * @name $http#patch
10652      *
10653      * @description
10654      * Shortcut method to perform `PATCH` request.
10655      *
10656      * @param {string} url Relative or absolute URL specifying the destination of the request
10657      * @param {*} data Request content
10658      * @param {Object=} config Optional configuration object
10659      * @returns {HttpPromise} Future object
10660      */
10661    createShortMethodsWithData('post', 'put', 'patch');
10662
10663        /**
10664         * @ngdoc property
10665         * @name $http#defaults
10666         *
10667         * @description
10668         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10669         * default headers, withCredentials as well as request and response transformations.
10670         *
10671         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10672         */
10673    $http.defaults = defaults;
10674
10675
10676    return $http;
10677
10678
10679    function createShortMethods(names) {
10680      forEach(arguments, function(name) {
10681        $http[name] = function(url, config) {
10682          return $http(extend({}, config || {}, {
10683            method: name,
10684            url: url
10685          }));
10686        };
10687      });
10688    }
10689
10690
10691    function createShortMethodsWithData(name) {
10692      forEach(arguments, function(name) {
10693        $http[name] = function(url, data, config) {
10694          return $http(extend({}, config || {}, {
10695            method: name,
10696            url: url,
10697            data: data
10698          }));
10699        };
10700      });
10701    }
10702
10703
10704    /**
10705     * Makes the request.
10706     *
10707     * !!! ACCESSES CLOSURE VARS:
10708     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10709     */
10710    function sendReq(config, reqData) {
10711      var deferred = $q.defer(),
10712          promise = deferred.promise,
10713          cache,
10714          cachedResp,
10715          reqHeaders = config.headers,
10716          url = buildUrl(config.url, config.paramSerializer(config.params));
10717
10718      $http.pendingRequests.push(config);
10719      promise.then(removePendingReq, removePendingReq);
10720
10721
10722      if ((config.cache || defaults.cache) && config.cache !== false &&
10723          (config.method === 'GET' || config.method === 'JSONP')) {
10724        cache = isObject(config.cache) ? config.cache
10725              : isObject(defaults.cache) ? defaults.cache
10726              : defaultCache;
10727      }
10728
10729      if (cache) {
10730        cachedResp = cache.get(url);
10731        if (isDefined(cachedResp)) {
10732          if (isPromiseLike(cachedResp)) {
10733            // cached request has already been sent, but there is no response yet
10734            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10735          } else {
10736            // serving from cache
10737            if (isArray(cachedResp)) {
10738              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10739            } else {
10740              resolvePromise(cachedResp, 200, {}, 'OK');
10741            }
10742          }
10743        } else {
10744          // put the promise for the non-transformed response into cache as a placeholder
10745          cache.put(url, promise);
10746        }
10747      }
10748
10749
10750      // if we won't have the response in cache, set the xsrf headers and
10751      // send the request to the backend
10752      if (isUndefined(cachedResp)) {
10753        var xsrfValue = urlIsSameOrigin(config.url)
10754            ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10755            : undefined;
10756        if (xsrfValue) {
10757          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10758        }
10759
10760        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10761            config.withCredentials, config.responseType);
10762      }
10763
10764      return promise;
10765
10766
10767      /**
10768       * Callback registered to $httpBackend():
10769       *  - caches the response if desired
10770       *  - resolves the raw $http promise
10771       *  - calls $apply
10772       */
10773      function done(status, response, headersString, statusText) {
10774        if (cache) {
10775          if (isSuccess(status)) {
10776            cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10777          } else {
10778            // remove promise from the cache
10779            cache.remove(url);
10780          }
10781        }
10782
10783        function resolveHttpPromise() {
10784          resolvePromise(response, status, headersString, statusText);
10785        }
10786
10787        if (useApplyAsync) {
10788          $rootScope.$applyAsync(resolveHttpPromise);
10789        } else {
10790          resolveHttpPromise();
10791          if (!$rootScope.$$phase) $rootScope.$apply();
10792        }
10793      }
10794
10795
10796      /**
10797       * Resolves the raw $http promise.
10798       */
10799      function resolvePromise(response, status, headers, statusText) {
10800        //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10801        status = status >= -1 ? status : 0;
10802
10803        (isSuccess(status) ? deferred.resolve : deferred.reject)({
10804          data: response,
10805          status: status,
10806          headers: headersGetter(headers),
10807          config: config,
10808          statusText: statusText
10809        });
10810      }
10811
10812      function resolvePromiseWithResult(result) {
10813        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10814      }
10815
10816      function removePendingReq() {
10817        var idx = $http.pendingRequests.indexOf(config);
10818        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10819      }
10820    }
10821
10822
10823    function buildUrl(url, serializedParams) {
10824      if (serializedParams.length > 0) {
10825        url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10826      }
10827      return url;
10828    }
10829  }];
10830}
10831
10832/**
10833 * @ngdoc service
10834 * @name $xhrFactory
10835 *
10836 * @description
10837 * Factory function used to create XMLHttpRequest objects.
10838 *
10839 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10840 *
10841 * ```
10842 * angular.module('myApp', [])
10843 * .factory('$xhrFactory', function() {
10844 *   return function createXhr(method, url) {
10845 *     return new window.XMLHttpRequest({mozSystem: true});
10846 *   };
10847 * });
10848 * ```
10849 *
10850 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10851 * @param {string} url URL of the request.
10852 */
10853function $xhrFactoryProvider() {
10854  this.$get = function() {
10855    return function createXhr() {
10856      return new window.XMLHttpRequest();
10857    };
10858  };
10859}
10860
10861/**
10862 * @ngdoc service
10863 * @name $httpBackend
10864 * @requires $window
10865 * @requires $document
10866 * @requires $xhrFactory
10867 *
10868 * @description
10869 * HTTP backend used by the {@link ng.$http service} that delegates to
10870 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10871 *
10872 * You should never need to use this service directly, instead use the higher-level abstractions:
10873 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10874 *
10875 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10876 * $httpBackend} which can be trained with responses.
10877 */
10878function $HttpBackendProvider() {
10879  this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10880    return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10881  }];
10882}
10883
10884function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10885  // TODO(vojta): fix the signature
10886  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10887    $browser.$$incOutstandingRequestCount();
10888    url = url || $browser.url();
10889
10890    if (lowercase(method) == 'jsonp') {
10891      var callbackId = '_' + (callbacks.counter++).toString(36);
10892      callbacks[callbackId] = function(data) {
10893        callbacks[callbackId].data = data;
10894        callbacks[callbackId].called = true;
10895      };
10896
10897      var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10898          callbackId, function(status, text) {
10899        completeRequest(callback, status, callbacks[callbackId].data, "", text);
10900        callbacks[callbackId] = noop;
10901      });
10902    } else {
10903
10904      var xhr = createXhr(method, url);
10905
10906      xhr.open(method, url, true);
10907      forEach(headers, function(value, key) {
10908        if (isDefined(value)) {
10909            xhr.setRequestHeader(key, value);
10910        }
10911      });
10912
10913      xhr.onload = function requestLoaded() {
10914        var statusText = xhr.statusText || '';
10915
10916        // responseText is the old-school way of retrieving response (supported by IE9)
10917        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10918        var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10919
10920        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10921        var status = xhr.status === 1223 ? 204 : xhr.status;
10922
10923        // fix status code when it is 0 (0 status is undocumented).
10924        // Occurs when accessing file resources or on Android 4.1 stock browser
10925        // while retrieving files from application cache.
10926        if (status === 0) {
10927          status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10928        }
10929
10930        completeRequest(callback,
10931            status,
10932            response,
10933            xhr.getAllResponseHeaders(),
10934            statusText);
10935      };
10936
10937      var requestError = function() {
10938        // The response is always empty
10939        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
10940        completeRequest(callback, -1, null, null, '');
10941      };
10942
10943      xhr.onerror = requestError;
10944      xhr.onabort = requestError;
10945
10946      if (withCredentials) {
10947        xhr.withCredentials = true;
10948      }
10949
10950      if (responseType) {
10951        try {
10952          xhr.responseType = responseType;
10953        } catch (e) {
10954          // WebKit added support for the json responseType value on 09/03/2013
10955          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
10956          // known to throw when setting the value "json" as the response type. Other older
10957          // browsers implementing the responseType
10958          //
10959          // The json response type can be ignored if not supported, because JSON payloads are
10960          // parsed on the client-side regardless.
10961          if (responseType !== 'json') {
10962            throw e;
10963          }
10964        }
10965      }
10966
10967      xhr.send(isUndefined(post) ? null : post);
10968    }
10969
10970    if (timeout > 0) {
10971      var timeoutId = $browserDefer(timeoutRequest, timeout);
10972    } else if (isPromiseLike(timeout)) {
10973      timeout.then(timeoutRequest);
10974    }
10975
10976
10977    function timeoutRequest() {
10978      jsonpDone && jsonpDone();
10979      xhr && xhr.abort();
10980    }
10981
10982    function completeRequest(callback, status, response, headersString, statusText) {
10983      // cancel timeout and subsequent timeout promise resolution
10984      if (isDefined(timeoutId)) {
10985        $browserDefer.cancel(timeoutId);
10986      }
10987      jsonpDone = xhr = null;
10988
10989      callback(status, response, headersString, statusText);
10990      $browser.$$completeOutstandingRequest(noop);
10991    }
10992  };
10993
10994  function jsonpReq(url, callbackId, done) {
10995    // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
10996    // - fetches local scripts via XHR and evals them
10997    // - adds and immediately removes script elements from the document
10998    var script = rawDocument.createElement('script'), callback = null;
10999    script.type = "text/javascript";
11000    script.src = url;
11001    script.async = true;
11002
11003    callback = function(event) {
11004      removeEventListenerFn(script, "load", callback);
11005      removeEventListenerFn(script, "error", callback);
11006      rawDocument.body.removeChild(script);
11007      script = null;
11008      var status = -1;
11009      var text = "unknown";
11010
11011      if (event) {
11012        if (event.type === "load" && !callbacks[callbackId].called) {
11013          event = { type: "error" };
11014        }
11015        text = event.type;
11016        status = event.type === "error" ? 404 : 200;
11017      }
11018
11019      if (done) {
11020        done(status, text);
11021      }
11022    };
11023
11024    addEventListenerFn(script, "load", callback);
11025    addEventListenerFn(script, "error", callback);
11026    rawDocument.body.appendChild(script);
11027    return callback;
11028  }
11029}
11030
11031var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
11032$interpolateMinErr.throwNoconcat = function(text) {
11033  throw $interpolateMinErr('noconcat',
11034      "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
11035      "interpolations that concatenate multiple expressions when a trusted value is " +
11036      "required.  See http://docs.angularjs.org/api/ng.$sce", text);
11037};
11038
11039$interpolateMinErr.interr = function(text, err) {
11040  return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
11041};
11042
11043/**
11044 * @ngdoc provider
11045 * @name $interpolateProvider
11046 *
11047 * @description
11048 *
11049 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
11050 *
11051 * @example
11052<example name="custom-interpolation-markup" module="customInterpolationApp">
11053<file name="index.html">
11054<script>
11055  var customInterpolationApp = angular.module('customInterpolationApp', []);
11056
11057  customInterpolationApp.config(function($interpolateProvider) {
11058    $interpolateProvider.startSymbol('//');
11059    $interpolateProvider.endSymbol('//');
11060  });
11061
11062
11063  customInterpolationApp.controller('DemoController', function() {
11064      this.label = "This binding is brought you by // interpolation symbols.";
11065  });
11066</script>
11067<div ng-controller="DemoController as demo">
11068    //demo.label//
11069</div>
11070</file>
11071<file name="protractor.js" type="protractor">
11072  it('should interpolate binding with custom symbols', function() {
11073    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
11074  });
11075</file>
11076</example>
11077 */
11078function $InterpolateProvider() {
11079  var startSymbol = '{{';
11080  var endSymbol = '}}';
11081
11082  /**
11083   * @ngdoc method
11084   * @name $interpolateProvider#startSymbol
11085   * @description
11086   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
11087   *
11088   * @param {string=} value new value to set the starting symbol to.
11089   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11090   */
11091  this.startSymbol = function(value) {
11092    if (value) {
11093      startSymbol = value;
11094      return this;
11095    } else {
11096      return startSymbol;
11097    }
11098  };
11099
11100  /**
11101   * @ngdoc method
11102   * @name $interpolateProvider#endSymbol
11103   * @description
11104   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11105   *
11106   * @param {string=} value new value to set the ending symbol to.
11107   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11108   */
11109  this.endSymbol = function(value) {
11110    if (value) {
11111      endSymbol = value;
11112      return this;
11113    } else {
11114      return endSymbol;
11115    }
11116  };
11117
11118
11119  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
11120    var startSymbolLength = startSymbol.length,
11121        endSymbolLength = endSymbol.length,
11122        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
11123        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
11124
11125    function escape(ch) {
11126      return '\\\\\\' + ch;
11127    }
11128
11129    function unescapeText(text) {
11130      return text.replace(escapedStartRegexp, startSymbol).
11131        replace(escapedEndRegexp, endSymbol);
11132    }
11133
11134    function stringify(value) {
11135      if (value == null) { // null || undefined
11136        return '';
11137      }
11138      switch (typeof value) {
11139        case 'string':
11140          break;
11141        case 'number':
11142          value = '' + value;
11143          break;
11144        default:
11145          value = toJson(value);
11146      }
11147
11148      return value;
11149    }
11150
11151    /**
11152     * @ngdoc service
11153     * @name $interpolate
11154     * @kind function
11155     *
11156     * @requires $parse
11157     * @requires $sce
11158     *
11159     * @description
11160     *
11161     * Compiles a string with markup into an interpolation function. This service is used by the
11162     * HTML {@link ng.$compile $compile} service for data binding. See
11163     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11164     * interpolation markup.
11165     *
11166     *
11167     * ```js
11168     *   var $interpolate = ...; // injected
11169     *   var exp = $interpolate('Hello {{name | uppercase}}!');
11170     *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11171     * ```
11172     *
11173     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11174     * `true`, the interpolation function will return `undefined` unless all embedded expressions
11175     * evaluate to a value other than `undefined`.
11176     *
11177     * ```js
11178     *   var $interpolate = ...; // injected
11179     *   var context = {greeting: 'Hello', name: undefined };
11180     *
11181     *   // default "forgiving" mode
11182     *   var exp = $interpolate('{{greeting}} {{name}}!');
11183     *   expect(exp(context)).toEqual('Hello !');
11184     *
11185     *   // "allOrNothing" mode
11186     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11187     *   expect(exp(context)).toBeUndefined();
11188     *   context.name = 'Angular';
11189     *   expect(exp(context)).toEqual('Hello Angular!');
11190     * ```
11191     *
11192     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
11193     *
11194     * ####Escaped Interpolation
11195     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
11196     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
11197     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
11198     * or binding.
11199     *
11200     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
11201     * degree, while also enabling code examples to work without relying on the
11202     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11203     *
11204     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11205     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
11206     * interpolation start/end markers with their escaped counterparts.**
11207     *
11208     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11209     * output when the $interpolate service processes the text. So, for HTML elements interpolated
11210     * by {@link ng.$compile $compile}
11210, or otherwise interpolated with the `mustHaveExpression` parameter
11211     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11212     * this is typically useful only when user-data is used in rendering a template from the server, or
11213     * when otherwise untrusted data is used by a directive.
11214     *
11215     * <example>
11216     *  <file name="index.html">
11217     *    <div ng-init="username='A user'">
11218     *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11219     *        </p>
11220     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
11221     *        application, but fails to accomplish their task, because the server has correctly
11222     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11223     *        characters.</p>
11224     *      <p>Instead, the result of the attempted script injection is visible, and can be removed
11225     *        from the database by an administrator.</p>
11226     *    </div>
11227     *  </file>
11228     * </example>
11229     *
11230     * @param {string} text The text with markup to interpolate.
11231     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11232     *    embedded expression in order to return an interpolation function. Strings with no
11233     *    embedded expression will return null for the interpolation function.
11234     * @param {string=} trustedContext when provided, the returned function passes the interpolated
11235     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11236     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
11237     *    provides Strict Contextual Escaping for details.
11238     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11239     *    unless all embedded expressions evaluate to a value other than `undefined`.
11240     * @returns {function(context)} an interpolation function which is used to compute the
11241     *    interpolated string. The function has these parameters:
11242     *
11243     * - `context`: evaluation context for all expressions embedded in the interpolated text
11244     */
11245    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11246      allOrNothing = !!allOrNothing;
11247      var startIndex,
11248          endIndex,
11249          index = 0,
11250          expressions = [],
11251          parseFns = [],
11252          textLength = text.length,
11253          exp,
11254          concat = [],
11255          expressionPositions = [];
11256
11257      while (index < textLength) {
11258        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11259             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11260          if (index !== startIndex) {
11261            concat.push(unescapeText(text.substring(index, startIndex)));
11262          }
11263          exp = text.substring(startIndex + startSymbolLength, endIndex);
11264          expressions.push(exp);
11265          parseFns.push($parse(exp, parseStringifyInterceptor));
11266          index = endIndex + endSymbolLength;
11267          expressionPositions.push(concat.length);
11268          concat.push('');
11269        } else {
11270          // we did not find an interpolation, so we have to add the remainder to the separators array
11271          if (index !== textLength) {
11272            concat.push(unescapeText(text.substring(index)));
11273          }
11274          break;
11275        }
11276      }
11277
11278      // Concatenating expressions makes it hard to reason about whether some combination of
11279      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
11280      // single expression be used for iframe[src], object[src], etc., we ensure that the value
11281      // that's used is assigned or constructed by some JS code somewhere that is more testable or
11282      // make it obvious that you bound the value to some user controlled value.  This helps reduce
11283      // the load when auditing for XSS issues.
11284      if (trustedContext && concat.length > 1) {
11285          $interpolateMinErr.throwNoconcat(text);
11286      }
11287
11288      if (!mustHaveExpression || expressions.length) {
11289        var compute = function(values) {
11290          for (var i = 0, ii = expressions.length; i < ii; i++) {
11291            if (allOrNothing && isUndefined(values[i])) return;
11292            concat[expressionPositions[i]] = values[i];
11293          }
11294          return concat.join('');
11295        };
11296
11297        var getValue = function(value) {
11298          return trustedContext ?
11299            $sce.getTrusted(trustedContext, value) :
11300            $sce.valueOf(value);
11301        };
11302
11303        return extend(function interpolationFn(context) {
11304            var i = 0;
11305            var ii = expressions.length;
11306            var values = new Array(ii);
11307
11308            try {
11309              for (; i < ii; i++) {
11310                values[i] = parseFns[i](context);
11311              }
11312
11313              return compute(values);
11314            } catch (err) {
11315              $exceptionHandler($interpolateMinErr.interr(text, err));
11316            }
11317
11318          }, {
11319          // all of these properties are undocumented for now
11320          exp: text, //just for compatibility with regular watchers created via $watch
11321          expressions: expressions,
11322          $$watchDelegate: function(scope, listener) {
11323            var lastValue;
11324            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11325              var currValue = compute(values);
11326              if (isFunction(listener)) {
11327                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11328              }
11329              lastValue = currValue;
11330            });
11331          }
11332        });
11333      }
11334
11335      function parseStringifyInterceptor(value) {
11336        try {
11337          value = getValue(value);
11338          return allOrNothing && !isDefined(value) ? value : stringify(value);
11339        } catch (err) {
11340          $exceptionHandler($interpolateMinErr.interr(text, err));
11341        }
11342      }
11343    }
11344
11345
11346    /**
11347     * @ngdoc method
11348     * @name $interpolate#startSymbol
11349     * @description
11350     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11351     *
11352     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11353     * the symbol.
11354     *
11355     * @returns {string} start symbol.
11356     */
11357    $interpolate.startSymbol = function() {
11358      return startSymbol;
11359    };
11360
11361
11362    /**
11363     * @ngdoc method
11364     * @name $interpolate#endSymbol
11365     * @description
11366     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11367     *
11368     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11369     * the symbol.
11370     *
11371     * @returns {string} end symbol.
11372     */
11373    $interpolate.endSymbol = function() {
11374      return endSymbol;
11375    };
11376
11377    return $interpolate;
11378  }];
11379}
11380
11381function $IntervalProvider() {
11382  this.$get = ['$rootScope', '$window', '$q', '$$q',
11383       function($rootScope,   $window,   $q,   $$q) {
11384    var intervals = {};
11385
11386
11387     /**
11388      * @ngdoc service
11389      * @name $interval
11390      *
11391      * @description
11392      * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11393      * milliseconds.
11394      *
11395      * The return value of registering an interval function is a promise. This promise will be
11396      * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11397      * run indefinitely if `count` is not defined. The value of the notification will be the
11398      * number of iterations that have run.
11399      * To cancel an interval, call `$interval.cancel(promise)`.
11400      *
11401      * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11402      * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11403      * time.
11404      *
11405      * <div class="alert alert-warning">
11406      * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11407      * with them.  In particular they are not automatically destroyed when a controller's scope or a
11408      * directive's element are destroyed.
11409      * You should take this into consideration and make sure to always cancel the interval at the
11410      * appropriate moment.  See the example below for more details on how and when to do this.
11411      * </div>
11412      *
11413      * @param {function()} fn A function that should be called repeatedly.
11414      * @param {number} delay Number of milliseconds between each function call.
11415      * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11416      *   indefinitely.
11417      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11418      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11419      * @param {...*=} Pass additional parameters to the executed function.
11420      * @returns {promise} A promise which will be notified on each iteration.
11421      *
11422      * @example
11423      * <example module="intervalExample">
11424      * <file name="index.html">
11425      *   <script>
11426      *     angular.module('intervalExample', [])
11427      *       .controller('ExampleController', ['$scope', '$interval',
11428      *         function($scope, $interval) {
11429      *           $scope.format = 'M/d/yy h:mm:ss a';
11430      *           $scope.blood_1 = 100;
11431      *           $scope.blood_2 = 120;
11432      *
11433      *           var stop;
11434      *           $scope.fight = function() {
11435      *             // Don't start a new fight if we are already fighting
11436      *             if ( angular.isDefined(stop) ) return;
11437      *
11438      *             stop = $interval(function() {
11439      *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11440      *                 $scope.blood_1 = $scope.blood_1 - 3;
11441      *                 $scope.blood_2 = $scope.blood_2 - 4;
11442      *               } else {
11443      *                 $scope.stopFight();
11444      *               }
11445      *             }, 100);
11446      *           };
11447      *
11448      *           $scope.stopFight = function() {
11449      *             if (angular.isDefined(stop)) {
11450      *               $interval.cancel(stop);
11451      *               stop = undefined;
11452      *             }
11453      *           };
11454      *
11455      *           $scope.resetFight = function() {
11456      *             $scope.blood_1 = 100;
11457      *             $scope.blood_2 = 120;
11458      *           };
11459      *
11460      *           $scope.$on('$destroy', function() {
11461      *             // Make sure that the interval is destroyed too
11462      *             $scope.stopFight();
11463      *           });
11464      *         }])
11465      *       // Register the 'myCurrentTime' directive factory method.
11466      *       // We inject $interval and dateFilter service since the factory method is DI.
11467      *       .directive('myCurrentTime', ['$interval', 'dateFilter',
11468      *         function($interval, dateFilter) {
11469      *           // return the directive link function. (compile function not needed)
11470      *           return function(scope, element, attrs) {
11471      *             var format,  // date format
11472      *                 stopTime; // so that we can cancel the time updates
11473      *
11474      *             // used to update the UI
11475      *             function updateTime() {
11476      *               element.text(dateFilter(new Date(), format));
11477      *             }
11478      *
11479      *             // watch the expression, and update the UI on change.
11480      *             scope.$watch(attrs.myCurrentTime, function(value) {
11481      *               format = value;
11482      *               updateTime();
11483      *             });
11484      *
11485      *             stopTime = $interval(updateTime, 1000);
11486      *
11487      *             // listen on DOM destroy (removal) event, and cancel the next UI update
11488      *             // to prevent updating time after the DOM element was removed.
11489      *             element.on('$destroy', function() {
11490      *               $interval.cancel(stopTime);
11491      *             });
11492      *           }
11493      *         }]);
11494      *   </script>
11495      *
11496      *   <div>
11497      *     <div ng-controller="ExampleController">
11498      *       <label>Date format: <input ng-model="format"></label> <hr/>
11499      *       Current time is: <span my-current-time="format"></span>
11500      *       <hr/>
11501      *       Blood 1 : <font color='red'>{{blood_1}}</font>
11502      *       Blood 2 : <font color='red'>{{blood_2}}</font>
11503      *       <button type="button" data-ng-click="fight()">Fight</button>
11504      *       <button type="button" data-ng-click="stopFight()">StopFight</button>
11505      *       <button type="button" data-ng-click="resetFight()">resetFight</button>
11506      *     </div>
11507      *   </div>
11508      *
11509      * </file>
11510      * </example>
11511      */
11512    function interval(fn, delay, count, invokeApply) {
11513      var hasParams = arguments.length > 4,
11514          args = hasParams ? sliceArgs(arguments, 4) : [],
11515          setInterval = $window.setInterval,
11516          clearInterval = $window.clearInterval,
11517          iteration = 0,
11518          skipApply = (isDefined(invokeApply) && !invokeApply),
11519          deferred = (skipApply ? $$q : $q).defer(),
11520          promise = deferred.promise;
11521
11522      count = isDefined(count) ? count : 0;
11523
11524      promise.then(null, null, (!hasParams) ? fn : function() {
11525        fn.apply(null, args);
11526      });
11527
11528      promise.$$intervalId = setInterval(function tick() {
11529        deferred.notify(iteration++);
11530
11531        if (count > 0 && iteration >= count) {
11532          deferred.resolve(iteration);
11533          clearInterval(promise.$$intervalId);
11534          delete intervals[promise.$$intervalId];
11535        }
11536
11537        if (!skipApply) $rootScope.$apply();
11538
11539      }, delay);
11540
11541      intervals[promise.$$intervalId] = deferred;
11542
11543      return promise;
11544    }
11545
11546
11547     /**
11548      * @ngdoc method
11549      * @name $interval#cancel
11550      *
11551      * @description
11552      * Cancels a task associated with the `promise`.
11553      *
11554      * @param {Promise=} promise returned by the `$interval` function.
11555      * @returns {boolean} Returns `true` if the task was successfully canceled.
11556      */
11557    interval.cancel = function(promise) {
11558      if (promise && promise.$$intervalId in intervals) {
11559        intervals[promise.$$intervalId].reject('canceled');
11560        $window.clearInterval(promise.$$intervalId);
11561        delete intervals[promise.$$intervalId];
11562        return true;
11563      }
11564      return false;
11565    };
11566
11567    return interval;
11568  }];
11569}
11570
11571/**
11572 * @ngdoc service
11573 * @name $locale
11574 *
11575 * @description
11576 * $locale service provides localization rules for various Angular components. As of right now the
11577 * only public api is:
11578 *
11579 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11580 */
11581
11582var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11583    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11584var $locationMinErr = minErr('$location');
11585
11586
11587/**
11588 * Encode path using encodeUriSegment, ignoring forward slashes
11589 *
11590 * @param {string} path Path to encode
11591 * @returns {string}
11592 */
11593function encodePath(path) {
11594  var segments = path.split('/'),
11595      i = segments.length;
11596
11597  while (i--) {
11598    segments[i] = encodeUriSegment(segments[i]);
11599  }
11600
11601  return segments.join('/');
11602}
11603
11604function parseAbsoluteUrl(absoluteUrl, locationObj) {
11605  var parsedUrl = urlResolve(absoluteUrl);
11606
11607  locationObj.$$protocol = parsedUrl.protocol;
11608  locationObj.$$host = parsedUrl.hostname;
11609  locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11610}
11611
11612
11613function parseAppUrl(relativeUrl, locationObj) {
11614  var prefixed = (relativeUrl.charAt(0) !== '/');
11615  if (prefixed) {
11616    relativeUrl = '/' + relativeUrl;
11617  }
11618  var match = urlResolve(relativeUrl);
11619  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11620      match.pathname.substring(1) : match.pathname);
11621  locationObj.$$search = parseKeyValue(match.search);
11622  locationObj.$$hash = decodeURIComponent(match.hash);
11623
11624  // make sure path starts with '/';
11625  if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11626    locationObj.$$path = '/' + locationObj.$$path;
11627  }
11628}
11629
11630
11631/**
11632 *
11633 * @param {string} begin
11634 * @param {string} whole
11635 * @returns {string} returns text from whole after begin or undefined if it does not begin with
11636 *                   expected string.
11637 */
11638function beginsWith(begin, whole) {
11639  if (whole.indexOf(begin) === 0) {
11640    return whole.substr(begin.length);
11641  }
11642}
11643
11644
11645function stripHash(url) {
11646  var index = url.indexOf('#');
11647  return index == -1 ? url : url.substr(0, index);
11648}
11649
11650function trimEmptyHash(url) {
11651  return url.replace(/(#.+)|#$/, '$1');
11652}
11653
11654
11655function stripFile(url) {
11656  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11657}
11658
11659/* return the server only (scheme://host:port) */
11660function serverBase(url) {
11661  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11662}
11663
11664
11665/**
11666 * LocationHtml5Url represents an url
11667 * This object is exposed as $location service when HTML5 mode is enabled and supported
11668 *
11669 * @constructor
11670 * @param {string} appBase application base URL
11671 * @param {string} appBaseNoFile application base URL stripped of any filename
11672 * @param {string} basePrefix url path prefix
11673 */
11674function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11675  this.$$html5 = true;
11676  basePrefix = basePrefix || '';
11677  parseAbsoluteUrl(appBase, this);
11678
11679
11680  /**
11681   * Parse given html5 (regular) url string into properties
11682   * @param {string} url HTML5 url
11683   * @private
11684   */
11685  this.$$parse = function(url) {
11686    var pathUrl = beginsWith(appBaseNoFile, url);
11687    if (!isString(pathUrl)) {
11688      throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11689          appBaseNoFile);
11690    }
11691
11692    parseAppUrl(pathUrl, this);
11693
11694    if (!this.$$path) {
11695      this.$$path = '/';
11696    }
11697
11698    this.$$compose();
11699  };
11700
11701  /**
11702   * Compose url and update `absUrl` property
11703   * @private
11704   */
11705  this.$$compose = function() {
11706    var search = toKeyValue(this.$$search),
11707        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11708
11709    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11710    this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11711  };
11712
11713  this.$$parseLinkUrl = function(url, relHref) {
11714    if (relHref && relHref[0] === '#') {
11715      // special case for links to hash fragments:
11716      // keep the old url and only replace the hash fragment
11717      this.hash(relHref.slice(1));
11718      return true;
11719    }
11720    var appUrl, prevAppUrl;
11721    var rewrittenUrl;
11722
11723    if (isDefined(appUrl = beginsWith(appBase, url))) {
11724      prevAppUrl = appUrl;
11725      if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11726        rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11727      } else {
11728        rewrittenUrl = appBase + prevAppUrl;
11729      }
11730    } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11731      rewrittenUrl = appBaseNoFile + appUrl;
11732    } else if (appBaseNoFile == url + '/') {
11733      rewrittenUrl = appBaseNoFile;
11734    }
11735    if (rewrittenUrl) {
11736      this.$$parse(rewrittenUrl);
11737    }
11738    return !!rewrittenUrl;
11739  };
11740}
11741
11742
11743/**
11744 * LocationHashbangUrl represents url
11745 * This object is exposed as $location service when developer doesn't opt into html5 mode.
11746 * It also serves as the base class for html5 mode fallback on legacy browsers.
11747 *
11748 * @constructor
11749 * @param {string} appBase application base URL
11750 * @param {string} appBaseNoFile application base URL stripped of any filename
11751 * @param {string} hashPrefix hashbang prefix
11752 */
11753function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
11754
11755  parseAbsoluteUrl(appBase, this);
11756
11757
11758  /**
11759   * Parse given hashbang url into properties
11760   * @param {string} url Hashbang url
11761   * @private
11762   */
11763  this.$$parse = function(url) {
11764    var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11765    var withoutHashUrl;
11766
11767    if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
11768
11769      // The rest of the url starts with a hash so we have
11770      // got either a hashbang path or a plain hash fragment
11771      withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11772      if (isUndefined(withoutHashUrl)) {
11773        // There was no hashbang prefix so we just have a hash fragment
11774        withoutHashUrl = withoutBaseUrl;
11775      }
11776
11777    } else {
11778      // There was no hashbang path nor hash fragment:
11779      // If we are in HTML5 mode we use what is left as the path;
11780      // Otherwise we ignore what is left
11781      if (this.$$html5) {
11782        withoutHashUrl = withoutBaseUrl;
11783      } else {
11784        withoutHashUrl = '';
11785        if (isUndefined(withoutBaseUrl)) {
11786          appBase = url;
11787          this.replace();
11788        }
11789      }
11790    }
11791
11792    parseAppUrl(withoutHashUrl, this);
11793
11794    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11795
11796    this.$$compose();
11797
11798    /*
11799     * In Windows, on an anchor node on documents loaded from
11800     * the filesystem, the browser will return a pathname
11801     * prefixed with the drive name ('/C:/path') when a
11802     * pathname without a drive is set:
11803     *  * a.setAttribute('href', '/foo')
11804     *   * a.pathname === '/C:/foo' //true
11805     *
11806     * Inside of Angular, we're always using pathnames that
11807     * do not include drive names for routing.
11808     */
11809    function removeWindowsDriveName(path, url, base) {
11810      /*
11811      Matches paths for file protocol on windows,
11812      such as /C:/foo/bar, and captures only /foo/bar.
11813      */
11814      var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11815
11816      var firstPathSegmentMatch;
11817
11818      //Get the relative path from the input URL.
11819      if (url.indexOf(base) === 0) {
11820        url = url.replace(base, '');
11821      }
11822
11823      // The input URL intentionally contains a first path segment that ends with a colon.
11824      if (windowsFilePathExp.exec(url)) {
11825        return path;
11826      }
11827
11828      firstPathSegmentMatch = windowsFilePathExp.exec(path);
11829      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11830    }
11831  };
11832
11833  /**
11834   * Compose hashbang url and update `absUrl` property
11835   * @private
11836   */
11837  this.$$compose = function() {
11838    var search = toKeyValue(this.$$search),
11839        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11840
11841    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11842    this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11843  };
11844
11845  this.$$parseLinkUrl = function(url, relHref) {
11846    if (stripHash(appBase) == stripHash(url)) {
11847      this.$$parse(url);
11848      return true;
11849    }
11850    return false;
11851  };
11852}
11853
11854
11855/**
11856 * LocationHashbangUrl represents url
11857 * This object is exposed as $location service when html5 history api is enabled but the browser
11858 * does not support it.
11859 *
11860 * @constructor
11861 * @param {string} appBase application base URL
11862 * @param {string} appBaseNoFile application base URL stripped of any filename
11863 * @param {string} hashPrefix hashbang prefix
11864 */
11865function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
11866  this.$$html5 = true;
11867  LocationHashbangUrl.apply(this, arguments);
11868
11869  this.$$parseLinkUrl = function(url, relHref) {
11870    if (relHref && relHref[0] === '#') {
11871      // special case for links to hash fragments:
11872      // keep the old url and only replace the hash fragment
11873      this.hash(relHref.slice(1));
11874      return true;
11875    }
11876
11877    var rewrittenUrl;
11878    var appUrl;
11879
11880    if (appBase == stripHash(url)) {
11881      rewrittenUrl = url;
11882    } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11883      rewrittenUrl = appBase + hashPrefix + appUrl;
11884    } else if (appBaseNoFile === url + '/') {
11885      rewrittenUrl = appBaseNoFile;
11886    }
11887    if (rewrittenUrl) {
11888      this.$$parse(rewrittenUrl);
11889    }
11890    return !!rewrittenUrl;
11891  };
11892
11893  this.$$compose = function() {
11894    var search = toKeyValue(this.$$search),
11895        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11896
11897    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11898    // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
11899    this.$$absUrl = appBase + hashPrefix + this.$$url;
11900  };
11901
11902}
11903
11904
11905var locationPrototype = {
11906
11907  /**
11908   * Are we in html5 mode?
11909   * @private
11910   */
11911  $$html5: false,
11912
11913  /**
11914   * Has any change been replacing?
11915   * @private
11916   */
11917  $$replace: false,
11918
11919  /**
11920   * @ngdoc method
11921   * @name $location#absUrl
11922   *
11923   * @description
11924   * This method is getter only.
11925   *
11926   * Return full url representation with all segments encoded according to rules specified in
11927   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11928   *
11929   *
11930   * ```js
11931   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11932   * var absUrl = $location.absUrl();
11933   * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11934   * ```
11935   *
11936   * @return {string} full url
11937   */
11938  absUrl: locationGetter('$$absUrl'),
11939
11940  /**
11941   * @ngdoc method
11942   * @name $location#url
11943   *
11944   * @description
11945   * This method is getter / setter.
11946   *
11947   * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
11948   *
11949   * Change path, search and hash, when called with parameter and return `$location`.
11950   *
11951   *
11952   * ```js
11953   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11954   * var url = $location.url();
11955   * // => "/some/path?foo=bar&baz=xoxo"
11956   * ```
11957   *
11958   * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
11959   * @return {string} url
11960   */
11961  url: function(url) {
11962    if (isUndefined(url)) {
11963      return this.$$url;
11964    }
11965
11966    var match = PATH_MATCH.exec(url);
11967    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
11968    if (match[2] || match[1] || url === '') this.search(match[3] || '');
11969    this.hash(match[5] || '');
11970
11971    return this;
11972  },
11973
11974  /**
11975   * @ngdoc method
11976   * @name $location#protocol
11977   *
11978   * @description
11979   * This method is getter only.
11980   *
11981   * Return protocol of current url.
11982   *
11983   *
11984   * ```js
11985   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11986   * var protocol = $location.protocol();
11987   * // => "http"
11988   * ```
11989   *
11990   * @return {string} protocol of current url
11991   */
11992  protocol: locationGetter('$$protocol'),
11993
11994  /**
11995   * @ngdoc method
11996   * @name $location#host
11997   *
11998   * @description
11999   * This method is getter only.
12000   *
12001   * Return host of current url.
12002   *
12003   * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
12004   *
12005   *
12006   * ```js
12007   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12008   * var host = $location.host();
12009   * // => "example.com"
12010   *
12011   * // given url http://user:[email protected]:8080/#/some/path?foo=bar&baz=xoxo
12012   * host = $location.host();
12013   * // => "example.com"
12014   * host = location.host;
12015   * // => "example.com:8080"
12016   * ```
12017   *
12018   * @return {string} host of current url.
12019   */
12020  host: locationGetter('$$host'),
12021
12022  /**
12023   * @ngdoc method
12024   * @name $location#port
12025   *
12026   * @description
12027   * This method is getter only.
12028   *
12029   * Return port of current url.
12030   *
12031   *
12032   * ```js
12033   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12034   * var port = $location.port();
12035   * // => 80
12036   * ```
12037   *
12038   * @return {Number} port
12039   */
12040  port: locationGetter('$$port'),
12041
12042  /**
12043   * @ngdoc method
12044   * @name $location#path
12045   *
12046   * @description
12047   * This method is getter / setter.
12048   *
12049   * Return path of current url when called without any parameter.
12050   *
12051   * Change path when called with parameter and return `$location`.
12052   *
12053   * Note: Path should always begin with forward slash (/), this method will add the forward slash
12054   * if it is missing.
12055   *
12056   *
12057   * ```js
12058   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12059   * var path = $location.path();
12060   * // => "/some/path"
12061   * ```
12062   *
12063   * @param {(string|number)=} path New path
12064   * @return {string} path
12065   */
12066  path: locationGetterSetter('$$path', function(path) {
12067    path = path !== null ? path.toString() : '';
12068    return path.charAt(0) == '/' ? path : '/' + path;
12069  }),
12070
12071  /**
12072   * @ngdoc method
12073   * @name $location#search
12074   *
12075   * @description
12076   * This method is getter / setter.
12077   *
12078   * Return search part (as object) of current url when called without any parameter.
12079   *
12080   * Change search part when called with parameter and return `$location`.
12081   *
12082   *
12083   * ```js
12084   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12085   * var searchObject = $location.search();
12086   * // => {foo: 'bar', baz: 'xoxo'}
12087   *
12088   * // set foo to 'yipee'
12089   * $location.search('foo', 'yipee');
12090   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
12091   * ```
12092   *
12093   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
12094   * hash object.
12095   *
12096   * When called with a single argument the method acts as a setter, setting the `search` component
12097   * of `$location` to the specified value.
12098   *
12099   * If the argument is a hash object containing an array of values, these values will be encoded
12100   * as duplicate search parameters in the url.
12101   *
12102   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
12103   * will override only a single search property.
12104   *
12105   * If `paramValue` is an array, it will override the property of the `search` component of
12106   * `$location` specified via the first argument.
12107   *
12108   * If `paramValue` is `null`, the property specified via the first argument will be deleted.
12109   *
12110   * If `paramValue` is `true`, the property specified via the first argument will be added with no
12111   * value nor trailing equal sign.
12112   *
12113   * @return {Object} If called with no arguments returns the parsed `search` object. If called with
12114   * one or more arguments returns `$location` object itself.
12115   */
12116  search: function(search, paramValue) {
12117    switch (arguments.length) {
12118      case 0:
12119        return this.$$search;
12120      case 1:
12121        if (isString(search) || isNumber(search)) {
12122          search = search.toString();
12123          this.$$search = parseKeyValue(search);
12124        } else if (isObject(search)) {
12125          search = copy(search, {});
12126          // remove object undefined or null properties
12127          forEach(search, function(value, key) {
12128            if (value == null) delete search[key];
12129          });
12130
12131          this.$$search = search;
12132        } else {
12133          throw $locationMinErr('isrcharg',
12134              'The first argument of the `$location#search()` call must be a string or an object.');
12135        }
12136        break;
12137      default:
12138        if (isUndefined(paramValue) || paramValue === null) {
12139          delete this.$$search[search];
12140        } else {
12141          this.$$search[search] = paramValue;
12142        }
12143    }
12144
12145    this.$$compose();
12146    return this;
12147  },
12148
12149  /**
12150   * @ngdoc method
12151   * @name $location#hash
12152   *
12153   * @description
12154   * This method is getter / setter.
12155   *
12156   * Returns the hash fragment when called without any parameters.
12157   *
12158   * Changes the hash fragment when called with a parameter and returns `$location`.
12159   *
12160   *
12161   * ```js
12162   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12163   * var hash = $location.hash();
12164   * // => "hashValue"
12165   * ```
12166   *
12167   * @param {(string|number)=} hash New hash fragment
12168   * @return {string} hash
12169   */
12170  hash: locationGetterSetter('$$hash', function(hash) {
12171    return hash !== null ? hash.toString() : '';
12172  }),
12173
12174  /**
12175   * @ngdoc method
12176   * @name $location#replace
12177   *
12178   * @description
12179   * If called, all changes to $location during the current `$digest` will replace the current history
12180   * record, instead of adding a new one.
12181   */
12182  replace: function() {
12183    this.$$replace = true;
12184    return this;
12185  }
12186};
12187
12188forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
12189  Location.prototype = Object.create(locationPrototype);
12190
12191  /**
12192   * @ngdoc method
12193   * @name $location#state
12194   *
12195   * @description
12196   * This method is getter / setter.
12197   *
12198   * Return the history state object when called without any parameter.
12199   *
12200   * Change the history state object when called with one parameter and return `$location`.
12201   * The state object is later passed to `pushState` or `replaceState`.
12202   *
12203   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12204   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12205   * older browsers (like IE9 or Android < 4.0), don't use this method.
12206   *
12207   * @param {object=} state State object for pushState or replaceState
12208   * @return {object} state
12209   */
12210  Location.prototype.state = function(state) {
12211    if (!arguments.length) {
12212      return this.$$state;
12213    }
12214
12215    if (Location !== LocationHtml5Url || !this.$$html5) {
12216      throw $locationMinErr('nostate', 'History API state support is available only ' +
12217        'in HTML5 mode and only in browsers supporting HTML5 History API');
12218    }
12219    // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12220    // but we're changing the $$state reference to $browser.state() during the $digest
12221    // so the modification window is narrow.
12222    this.$$state = isUndefined(state) ? null : state;
12223
12224    return this;
12225  };
12226});
12227
12228
12229function locationGetter(property) {
12230  return function() {
12231    return this[property];
12232  };
12233}
12234
12235
12236function locationGetterSetter(property, preprocess) {
12237  return function(value) {
12238    if (isUndefined(value)) {
12239      return this[property];
12240    }
12241
12242    this[property] = preprocess(value);
12243    this.$$compose();
12244
12245    return this;
12246  };
12247}
12248
12249
12250/**
12251 * @ngdoc service
12252 * @name $location
12253 *
12254 * @requires $rootElement
12255 *
12256 * @description
12257 * The $location service parses the URL in the browser address bar (based on the
12258 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12259 * available to your application. Changes to the URL in the address bar are reflected into
12260 * $location service and changes to $location are reflected into the browser address bar.
12261 *
12262 * **The $location service:**
12263 *
12264 * - Exposes the current URL in the browser address bar, so you can
12265 *   - Watch and observe the URL.
12266 *   - Change the URL.
12267 * - Synchronizes the URL with the browser when the user
12268 *   - Changes the address bar.
12269 *   - Clicks the back or forward button (or clicks a History link).
12270 *   - Clicks on a link.
12271 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12272 *
12273 * For more information see {@link guide/$location Developer Guide: Using $location}
12274 */
12275
12276/**
12277 * @ngdoc provider
12278 * @name $locationProvider
12279 * @description
12280 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12281 */
12282function $LocationProvider() {
12283  var hashPrefix = '',
12284      html5Mode = {
12285        enabled: false,
12286        requireBase: true,
12287        rewriteLinks: true
12288      };
12289
12290  /**
12291   * @ngdoc method
12292   * @name $locationProvider#hashPrefix
12293   * @description
12294   * @param {string=} prefix Prefix for hash part (containing path and search)
12295   * @returns {*} current value if used as getter or itself (chaining) if used as setter
12296   */
12297  this.hashPrefix = function(prefix) {
12298    if (isDefined(prefix)) {
12299      hashPrefix = prefix;
12300      return this;
12301    } else {
12302      return hashPrefix;
12303    }
12304  };
12305
12306  /**
12307   * @ngdoc method
12308   * @name $locationProvider#html5Mode
12309   * @description
12310   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12311   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12312   *   properties:
12313   *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12314   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12315   *     support `pushState`.
12316   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12317   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12318   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
12319   *     See the {@link guide/$location $location guide for more information}
12320   *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12321   *     enables/disables url rewriting for relative links.
12322   *
12323   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12324   */
12325  this.html5Mode = function(mode) {
12326    if (isBoolean(mode)) {
12327      html5Mode.enabled = mode;
12328      return this;
12329    } else if (isObject(mode)) {
12330
12331      if (isBoolean(mode.enabled)) {
12332        html5Mode.enabled = mode.enabled;
12333      }
12334
12335      if (isBoolean(mode.requireBase)) {
12336        html5Mode.requireBase = mode.requireBase;
12337      }
12338
12339      if (isBoolean(mode.rewriteLinks)) {
12340        html5Mode.rewriteLinks = mode.rewriteLinks;
12341      }
12342
12343      return this;
12344    } else {
12345      return html5Mode;
12346    }
12347  };
12348
12349  /**
12350   * @ngdoc event
12351   * @name $location#$locationChangeStart
12352   * @eventType broadcast on root scope
12353   * @description
12354   * Broadcasted before a URL will change.
12355   *
12356   * This change can be prevented by calling
12357   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12358   * details about event object. Upon successful change
12359   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12360   *
12361   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12362   * the browser supports the HTML5 History API.
12363   *
12364   * @param {Object} angularEvent Synthetic event object.
12365   * @param {string} newUrl New URL
12366   * @param {string=} oldUrl URL that was before it was changed.
12367   * @param {string=} newState New history state object
12368   * @param {string=} oldState History state object that was before it was changed.
12369   */
12370
12371  /**
12372   * @ngdoc event
12373   * @name $location#$locationChangeSuccess
12374   * @eventType broadcast on root scope
12375   * @description
12376   * Broadcasted after a URL was changed.
12377   *
12378   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12379   * the browser supports the HTML5 History API.
12380   *
12381   * @param {Object} angularEvent Synthetic event object.
12382   * @param {string} newUrl New URL
12383   * @param {string=} oldUrl URL that was before it was changed.
12384   * @param {string=} newState New history state object
12385   * @param {string=} oldState History state object that was before it was changed.
12386   */
12387
12388  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12389      function($rootScope, $browser, $sniffer, $rootElement, $window) {
12390    var $location,
12391        LocationMode,
12392        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12393        initialUrl = $browser.url(),
12394        appBase;
12395
12396    if (html5Mode.enabled) {
12397      if (!baseHref && html5Mode.requireBase) {
12398        throw $locationMinErr('nobase',
12399          "$location in HTML5 mode requires a <base> tag to be present!");
12400      }
12401      appBase = serverBase(initialUrl) + (baseHref || '/');
12402      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12403    } else {
12404      appBase = stripHash(initialUrl);
12405      LocationMode = LocationHashbangUrl;
12406    }
12407    var appBaseNoFile = stripFile(appBase);
12408
12409    $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12410    $location.$$parseLinkUrl(initialUrl, initialUrl);
12411
12412    $location.$$state = $browser.state();
12413
12414    var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12415
12416    function setBrowserUrlWithFallback(url, replace, state) {
12417      var oldUrl = $location.url();
12418      var oldState = $location.$$state;
12419      try {
12420        $browser.url(url, replace, state);
12421
12422        // Make sure $location.state() returns referentially identical (not just deeply equal)
12423        // state object; this makes possible quick checking if the state changed in the digest
12424        // loop. Checking deep equality would be too expensive.
12425        $location.$$state = $browser.state();
12426      } catch (e) {
12427        // Restore old values if pushState fails
12428        $location.url(oldUrl);
12429        $location.$$state = oldState;
12430
12431        throw e;
12432      }
12433    }
12434
12435    $rootElement.on('click', function(event) {
12436      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12437      // currently we open nice url link and redirect then
12438
12439      if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12440
12441      var elm = jqLite(event.target);
12442
12443      // traverse the DOM up to find first A tag
12444      while (nodeName_(elm[0]) !== 'a') {
12445        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12446        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12447      }
12448
12449      var absHref = elm.prop('href');
12450      // get the actual href attribute - see
12451      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12452      var relHref = elm.attr('href') || elm.attr('xlink:href');
12453
12454      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12455        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12456        // an animation.
12457        absHref = urlResolve(absHref.animVal).href;
12458      }
12459
12460      // Ignore when url is started with javascript: or mailto:
12461      if (IGNORE_URI_REGEXP.test(absHref)) return;
12462
12463      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12464        if ($location.$$parseLinkUrl(absHref, relHref)) {
12465          // We do a preventDefault for all urls that are part of the angular application,
12466          // in html5mode and also without, so that we are able to abort navigation without
12467          // getting double entries in the location history.
12468          event.preventDefault();
12469          // update location manually
12470          if ($location.absUrl() != $browser.url()) {
12471            $rootScope.$apply();
12472            // hack to work around FF6 bug 684208 when scenario runner clicks on links
12473            $window.angular['ff-684208-preventDefault'] = true;
12474          }
12475        }
12476      }
12477    });
12478
12479
12480    // rewrite hashbang url <> html5 url
12481    if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12482      $browser.url($location.absUrl(), true);
12483    }
12484
12485    var initializing = true;
12486
12487    // update $location when $browser url changes
12488    $browser.onUrlChange(function(newUrl, newState) {
12489
12490      if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12491        // If we are navigating outside of the app then force a reload
12492        $window.location.href = newUrl;
12493        return;
12494      }
12495
12496      $rootScope.$evalAsync(function() {
12497        var oldUrl = $location.absUrl();
12498        var oldState = $location.$$state;
12499        var defaultPrevented;
12500        newUrl = trimEmptyHash(newUrl);
12501        $location.$$parse(newUrl);
12502        $location.$$state = newState;
12503
12504        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12505            newState, oldState).defaultPrevented;
12506
12507        // if the location was changed by a `$locationChangeStart` handler then stop
12508        // processing this location change
12509        if ($location.absUrl() !== newUrl) return;
12510
12511        if (defaultPrevented) {
12512          $location.$$parse(oldUrl);
12513          $location.$$state = oldState;
12514          setBrowserUrlWithFallback(oldUrl, false, oldState);
12515        } else {
12516          initializing = false;
12517          afterLocationChange(oldUrl, oldState);
12518        }
12519      });
12520      if (!$rootScope.$$phase) $rootScope.$digest();
12521    });
12522
12523    // update browser
12524    $rootScope.$watch(function $locationWatch() {
12525      var oldUrl = trimEmptyHash($browser.url());
12526      var newUrl = trimEmptyHash($location.absUrl());
12527      var oldState = $browser.state();
12528      var currentReplace = $location.$$replace;
12529      var urlOrStateChanged = oldUrl !== newUrl ||
12530        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12531
12532      if (initializing || urlOrStateChanged) {
12533        initializing = false;
12534
12535        $rootScope.$evalAsync(function() {
12536          var newUrl = $location.absUrl();
12537          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12538              $location.$$state, oldState).defaultPrevented;
12539
12540          // if the location was changed by a `$locationChangeStart` handler then stop
12541          // processing this location change
12542          if ($location.absUrl() !== newUrl) return;
12543
12544          if (defaultPrevented) {
12545            $location.$$parse(oldUrl);
12546            $location.$$state = oldState;
12547          } else {
12548            if (urlOrStateChanged) {
12549              setBrowserUrlWithFallback(newUrl, currentReplace,
12550                                        oldState === $location.$$state ? null : $location.$$state);
12551            }
12552            afterLocationChange(oldUrl, oldState);
12553          }
12554        });
12555      }
12556
12557      $location.$$replace = false;
12558
12559      // we don't need to return anything because $evalAsync will make the digest loop dirty when
12560      // there is a change
12561    });
12562
12563    return $location;
12564
12565    function afterLocationChange(oldUrl, oldState) {
12566      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12567        $location.$$state, oldState);
12568    }
12569}];
12570}
12571
12572/**
12573 * @ngdoc service
12574 * @name $log
12575 * @requires $window
12576 *
12577 * @description
12578 * Simple service for logging. Default implementation safely writes the message
12579 * into the browser's console (if present).
12580 *
12581 * The main purpose of this service is to simplify debugging and troubleshooting.
12582 *
12583 * The default is to log `debug` messages. You can use
12584 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12585 *
12586 * @example
12587   <example module="logExample">
12588     <file name="script.js">
12589       angular.module('logExample', [])
12590         .controller('LogController', ['$scope', '$log', function($scope, $log) {
12591           $scope.$log = $log;
12592           $scope.message = 'Hello World!';
12593         }]);
12594     </file>
12595     <file name="index.html">
12596       <div ng-controller="LogController">
12597         <p>Reload this page with open console, enter text and hit the log button...</p>
12598         <label>Message:
12599         <input type="text" ng-model="message" /></label>
12600         <button ng-click="$log.log(message)">log</button>
12601         <button ng-click="$log.warn(message)">warn</button>
12602         <button ng-click="$log.info(message)">info</button>
12603         <button ng-click="$log.error(message)">error</button>
12604         <button ng-click="$log.debug(message)">debug</button>
12605       </div>
12606     </file>
12607   </example>
12608 */
12609
12610/**
12611 * @ngdoc provider
12612 * @name $logProvider
12613 * @description
12614 * Use the `$logProvider` to configure how the application logs messages
12615 */
12616function $LogProvider() {
12617  var debug = true,
12618      self = this;
12619
12620  /**
12621   * @ngdoc method
12622   * @name $logProvider#debugEnabled
12623   * @description
12624   * @param {boolean=} flag enable or disable debug level messages
12625   * @returns {*} current value if used as getter or itself (chaining) if used as setter
12626   */
12627  this.debugEnabled = function(flag) {
12628    if (isDefined(flag)) {
12629      debug = flag;
12630    return this;
12631    } else {
12632      return debug;
12633    }
12634  };
12635
12636  this.$get = ['$window', function($window) {
12637    return {
12638      /**
12639       * @ngdoc method
12640       * @name $log#log
12641       *
12642       * @description
12643       * Write a log message
12644       */
12645      log: consoleLog('log'),
12646
12647      /**
12648       * @ngdoc method
12649       * @name $log#info
12650       *
12651       * @description
12652       * Write an information message
12653       */
12654      info: consoleLog('info'),
12655
12656      /**
12657       * @ngdoc method
12658       * @name $log#warn
12659       *
12660       * @description
12661       * Write a warning message
12662       */
12663      warn: consoleLog('warn'),
12664
12665      /**
12666       * @ngdoc method
12667       * @name $log#error
12668       *
12669       * @description
12670       * Write an error message
12671       */
12672      error: consoleLog('error'),
12673
12674      /**
12675       * @ngdoc method
12676       * @name $log#debug
12677       *
12678       * @description
12679       * Write a debug message
12680       */
12681      debug: (function() {
12682        var fn = consoleLog('debug');
12683
12684        return function() {
12685          if (debug) {
12686            fn.apply(self, arguments);
12687          }
12688        };
12689      }())
12690    };
12691
12692    function formatError(arg) {
12693      if (arg instanceof Error) {
12694        if (arg.stack) {
12695          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12696              ? 'Error: ' + arg.message + '\n' + arg.stack
12697              : arg.stack;
12698        } else if (arg.sourceURL) {
12699          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12700        }
12701      }
12702      return arg;
12703    }
12704
12705    function consoleLog(type) {
12706      var console = $window.console || {},
12707          logFn = console[type] || console.log || noop,
12708          hasApply = false;
12709
12710      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12711      // The reason behind this is that console.log has type "object" in IE8...
12712      try {
12713        hasApply = !!logFn.apply;
12714      } catch (e) {}
12715
12716      if (hasApply) {
12717        return function() {
12718          var args = [];
12719          forEach(arguments, function(arg) {
12720            args.push(formatError(arg));
12721          });
12722          return logFn.apply(console, args);
12723        };
12724      }
12725
12726      // we are IE which either doesn't have window.console => this is noop and we do nothing,
12727      // or we are IE where console.log doesn't have apply so we log at least first 2 args
12728      return function(arg1, arg2) {
12729        logFn(arg1, arg2 == null ? '' : arg2);
12730      };
12731    }
12732  }];
12733}
12734
12735/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12736 *     Any commits to this file should be reviewed with security in mind.  *
12737 *   Changes to this file can potentially create security vulnerabilities. *
12738 *          An approval from 2 Core members with history of modifying      *
12739 *                         this file is required.                          *
12740 *                                                                         *
12741 *  Does the change somehow allow for arbitrary javascript to be executed? *
12742 *    Or allows for someone to change the prototype of built-in objects?   *
12743 *     Or gives undesired access to variables likes document or window?    *
12744 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12745
12746var $parseMinErr = minErr('$parse');
12747
12748// Sandboxing Angular Expressions
12749// ------------------------------
12750// Angular expressions are generally considered safe because these expressions only have direct
12751// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12752// obtaining a reference to native JS functions such as the Function constructor.
12753//
12754// As an example, consider the following Angular expression:
12755//
12756//   {}.toString.constructor('alert("evil JS code")')
12757//
12758// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12759// against the expression language, but not to prevent exploits that were enabled by exposing
12760// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12761// practice and therefore we are not even trying to protect against interaction with an object
12762// explicitly exposed in this way.
12763//
12764// In general, it is not possible to access a Window object from an angular expression unless a
12765// window or some DOM object that has a reference to window is published onto a Scope.
12766// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12767// native objects.
12768//
12769// See https://docs.angularjs.org/guide/security
12770
12771
12772function ensureSafeMemberName(name, fullExpression) {
12773  if (name === "__defineGetter__" || name === "__defineSetter__"
12774      || name === "__lookupGetter__" || name === "__lookupSetter__"
12775      || name === "__proto__") {
12776    throw $parseMinErr('isecfld',
12777        'Attempting to access a disallowed field in Angular expressions! '
12778        + 'Expression: {0}', fullExpression);
12779  }
12780  return name;
12781}
12782
12783function getStringValue(name, fullExpression) {
12784  // From the JavaScript docs:
12785  // Property names must be strings. This means that non-string objects cannot be used
12786  // as keys in an object. Any non-string object, including a number, is typecasted
12787  // into a string via the toString method.
12788  //
12789  // So, to ensure that we are checking the same `name` that JavaScript would use,
12790  // we cast it to a string, if possible.
12791  // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12792  // this is, this will handle objects that misbehave.
12793  name = name + '';
12794  if (!isString(name)) {
12795    throw $parseMinErr('iseccst',
12796        'Cannot convert object to primitive value! '
12797        + 'Expression: {0}', fullExpression);
12798  }
12799  return name;
12800}
12801
12802function ensureSafeObject(obj, fullExpression) {
12803  // nifty check if obj is Function that is fast and works across iframes and other contexts
12804  if (obj) {
12805    if (obj.constructor === obj) {
12806      throw $parseMinErr('isecfn',
12807          'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12808          fullExpression);
12809    } else if (// isWindow(obj)
12810        obj.window === obj) {
12811      throw $parseMinErr('isecwindow',
12812          'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12813          fullExpression);
12814    } else if (// isElement(obj)
12815        obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12816      throw $parseMinErr('isecdom',
12817          'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12818          fullExpression);
12819    } else if (// block Object so that we can't get hold of dangerous Object.* methods
12820        obj === Object) {
12821      throw $parseMinErr('isecobj',
12822          'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12823          fullExpression);
12824    }
12825  }
12826  return obj;
12827}
12828
12829var CALL = Function.prototype.call;
12830var APPLY = Function.prototype.apply;
12831var BIND = Function.prototype.bind;
12832
12833function ensureSafeFunction(obj, fullExpression) {
12834  if (obj) {
12835    if (obj.constructor === obj) {
12836      throw $parseMinErr('isecfn',
12837        'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12838        fullExpression);
12839    } else if (obj === CALL || obj === APPLY || obj === BIND) {
12840      throw $parseMinErr('isecff',
12841        'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12842        fullExpression);
12843    }
12844  }
12845}
12846
12847function ensureSafeAssignContext(obj, fullExpression) {
12848  if (obj) {
12849    if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12850        obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12851      throw $parseMinErr('isecaf',
12852        'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12853    }
12854  }
12855}
12856
12857var OPERATORS = createMap();
12858forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12859var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12860
12861
vendor: 37,280 bytes, lines 12862-14089
12862/////////////////////////////////////////
12863
12864
12865/**
12866 * @constructor
12867 */
12868var Lexer = function(options) {
12869  this.options = options;
12870};
12871
12872Lexer.prototype = {
12873  constructor: Lexer,
12874
12875  lex: function(text) {
12876    this.text = text;
12877    this.index = 0;
12878    this.tokens = [];
12879
12880    while (this.index < this.text.length) {
12881      var ch = this.text.charAt(this.index);
12882      if (ch === '"' || ch === "'") {
12883        this.readString(ch);
12884      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12885        this.readNumber();
12886      } else if (this.isIdent(ch)) {
12887        this.readIdent();
12888      } else if (this.is(ch, '(){}[].,;:?')) {
12889        this.tokens.push({index: this.index, text: ch});
12890        this.index++;
12891      } else if (this.isWhitespace(ch)) {
12892        this.index++;
12893      } else {
12894        var ch2 = ch + this.peek();
12895        var ch3 = ch2 + this.peek(2);
12896        var op1 = OPERATORS[ch];
12897        var op2 = OPERATORS[ch2];
12898        var op3 = OPERATORS[ch3];
12899        if (op1 || op2 || op3) {
12900          var token = op3 ? ch3 : (op2 ? ch2 : ch);
12901          this.tokens.push({index: this.index, text: token, operator: true});
12902          this.index += token.length;
12903        } else {
12904          this.throwError('Unexpected next character ', this.index, this.index + 1);
12905        }
12906      }
12907    }
12908    return this.tokens;
12909  },
12910
12911  is: function(ch, chars) {
12912    return chars.indexOf(ch) !== -1;
12913  },
12914
12915  peek: function(i) {
12916    var num = i || 1;
12917    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12918  },
12919
12920  isNumber: function(ch) {
12921    return ('0' <= ch && ch <= '9') && typeof ch === "string";
12922  },
12923
12924  isWhitespace: function(ch) {
12925    // IE treats non-breaking space as \u00A0
12926    return (ch === ' ' || ch === '\r' || ch === '\t' ||
12927            ch === '\n' || ch === '\v' || ch === '\u00A0');
12928  },
12929
12930  isIdent: function(ch) {
12931    return ('a' <= ch && ch <= 'z' ||
12932            'A' <= ch && ch <= 'Z' ||
12933            '_' === ch || ch === '$');
12934  },
12935
12936  isExpOperator: function(ch) {
12937    return (ch === '-' || ch === '+' || this.isNumber(ch));
12938  },
12939
12940  throwError: function(error, start, end) {
12941    end = end || this.index;
12942    var colStr = (isDefined(start)
12943            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
12944            : ' ' + end);
12945    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
12946        error, colStr, this.text);
12947  },
12948
12949  readNumber: function() {
12950    var number = '';
12951    var start = this.index;
12952    while (this.index < this.text.length) {
12953      var ch = lowercase(this.text.charAt(this.index));
12954      if (ch == '.' || this.isNumber(ch)) {
12955        number += ch;
12956      } else {
12957        var peekCh = this.peek();
12958        if (ch == 'e' && this.isExpOperator(peekCh)) {
12959          number += ch;
12960        } else if (this.isExpOperator(ch) &&
12961            peekCh && this.isNumber(peekCh) &&
12962            number.charAt(number.length - 1) == 'e') {
12963          number += ch;
12964        } else if (this.isExpOperator(ch) &&
12965            (!peekCh || !this.isNumber(peekCh)) &&
12966            number.charAt(number.length - 1) == 'e') {
12967          this.throwError('Invalid exponent');
12968        } else {
12969          break;
12970        }
12971      }
12972      this.index++;
12973    }
12974    this.tokens.push({
12975      index: start,
12976      text: number,
12977      constant: true,
12978      value: Number(number)
12979    });
12980  },
12981
12982  readIdent: function() {
12983    var start = this.index;
12984    while (this.index < this.text.length) {
12985      var ch = this.text.charAt(this.index);
12986      if (!(this.isIdent(ch) || this.isNumber(ch))) {
12987        break;
12988      }
12989      this.index++;
12990    }
12991    this.tokens.push({
12992      index: start,
12993      text: this.text.slice(start, this.index),
12994      identifier: true
12995    });
12996  },
12997
12998  readString: function(quote) {
12999    var start = this.index;
13000    this.index++;
13001    var string = '';
13002    var rawString = quote;
13003    var escape = false;
13004    while (this.index < this.text.length) {
13005      var ch = this.text.charAt(this.index);
13006      rawString += ch;
13007      if (escape) {
13008        if (ch === 'u') {
13009          var hex = this.text.substring(this.index + 1, this.index + 5);
13010          if (!hex.match(/[\da-f]{4}/i)) {
13011            this.throwError('Invalid unicode escape [\\u' + hex + ']');
13012          }
13013          this.index += 4;
13014          string += String.fromCharCode(parseInt(hex, 16));
13015        } else {
13016          var rep = ESCAPE[ch];
13017          string = string + (rep || ch);
13018        }
13019        escape = false;
13020      } else if (ch === '\\') {
13021        escape = true;
13022      } else if (ch === quote) {
13023        this.index++;
13024        this.tokens.push({
13025          index: start,
13026          text: rawString,
13027          constant: true,
13028          value: string
13029        });
13030        return;
13031      } else {
13032        string += ch;
13033      }
13034      this.index++;
13035    }
13036    this.throwError('Unterminated quote', start);
13037  }
13038};
13039
13040var AST = function(lexer, options) {
13041  this.lexer = lexer;
13042  this.options = options;
13043};
13044
13045AST.Program = 'Program';
13046AST.ExpressionStatement = 'ExpressionStatement';
13047AST.AssignmentExpression = 'AssignmentExpression';
13048AST.ConditionalExpression = 'ConditionalExpression';
13049AST.LogicalExpression = 'LogicalExpression';
13050AST.BinaryExpression = 'BinaryExpression';
13051AST.UnaryExpression = 'UnaryExpression';
13052AST.CallExpression = 'CallExpression';
13053AST.MemberExpression = 'MemberExpression';
13054AST.Identifier = 'Identifier';
13055AST.Literal = 'Literal';
13056AST.ArrayExpression = 'ArrayExpression';
13057AST.Property = 'Property';
13058AST.ObjectExpression = 'ObjectExpression';
13059AST.ThisExpression = 'ThisExpression';
13060
13061// Internal use only
13062AST.NGValueParameter = 'NGValueParameter';
13063
13064AST.prototype = {
13065  ast: function(text) {
13066    this.text = text;
13067    this.tokens = this.lexer.lex(text);
13068
13069    var value = this.program();
13070
13071    if (this.tokens.length !== 0) {
13072      this.throwError('is an unexpected token', this.tokens[0]);
13073    }
13074
13075    return value;
13076  },
13077
13078  program: function() {
13079    var body = [];
13080    while (true) {
13081      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
13082        body.push(this.expressionStatement());
13083      if (!this.expect(';')) {
13084        return { type: AST.Program, body: body};
13085      }
13086    }
13087  },
13088
13089  expressionStatement: function() {
13090    return { type: AST.ExpressionStatement, expression: this.filterChain() };
13091  },
13092
13093  filterChain: function() {
13094    var left = this.expression();
13095    var token;
13096    while ((token = this.expect('|'))) {
13097      left = this.filter(left);
13098    }
13099    return left;
13100  },
13101
13102  expression: function() {
13103    return this.assignment();
13104  },
13105
13106  assignment: function() {
13107    var result = this.ternary();
13108    if (this.expect('=')) {
13109      result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
13110    }
13111    return result;
13112  },
13113
13114  ternary: function() {
13115    var test = this.logicalOR();
13116    var alternate;
13117    var consequent;
13118    if (this.expect('?')) {
13119      alternate = this.expression();
13120      if (this.consume(':')) {
13121        consequent = this.expression();
13122        return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
13123      }
13124    }
13125    return test;
13126  },
13127
13128  logicalOR: function() {
13129    var left = this.logicalAND();
13130    while (this.expect('||')) {
13131      left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
13132    }
13133    return left;
13134  },
13135
13136  logicalAND: function() {
13137    var left = this.equality();
13138    while (this.expect('&&')) {
13139      left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
13140    }
13141    return left;
13142  },
13143
13144  equality: function() {
13145    var left = this.relational();
13146    var token;
13147    while ((token = this.expect('==','!=','===','!=='))) {
13148      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
13149    }
13150    return left;
13151  },
13152
13153  relational: function() {
13154    var left = this.additive();
13155    var token;
13156    while ((token = this.expect('<', '>', '<=', '>='))) {
13157      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
13158    }
13159    return left;
13160  },
13161
13162  additive: function() {
13163    var left = this.multiplicative();
13164    var token;
13165    while ((token = this.expect('+','-'))) {
13166      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
13167    }
13168    return left;
13169  },
13170
13171  multiplicative: function() {
13172    var left = this.unary();
13173    var token;
13174    while ((token = this.expect('*','/','%'))) {
13175      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
13176    }
13177    return left;
13178  },
13179
13180  unary: function() {
13181    var token;
13182    if ((token = this.expect('+', '-', '!'))) {
13183      return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
13184    } else {
13185      return this.primary();
13186    }
13187  },
13188
13189  primary: function() {
13190    var primary;
13191    if (this.expect('(')) {
13192      primary = this.filterChain();
13193      this.consume(')');
13194    } else if (this.expect('[')) {
13195      primary = this.arrayDeclaration();
13196    } else if (this.expect('{')) {
13197      primary = this.object();
13198    } else if (this.constants.hasOwnProperty(this.peek().text)) {
13199      primary = copy(this.constants[this.consume().text]);
13200    } else if (this.peek().identifier) {
13201      primary = this.identifier();
13202    } else if (this.peek().constant) {
13203      primary = this.constant();
13204    } else {
13205      this.throwError('not a primary expression', this.peek());
13206    }
13207
13208    var next;
13209    while ((next = this.expect('(', '[', '.'))) {
13210      if (next.text === '(') {
13211        primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13212        this.consume(')');
13213      } else if (next.text === '[') {
13214        primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13215        this.consume(']');
13216      } else if (next.text === '.') {
13217        primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13218      } else {
13219        this.throwError('IMPOSSIBLE');
13220      }
13221    }
13222    return primary;
13223  },
13224
13225  filter: function(baseExpression) {
13226    var args = [baseExpression];
13227    var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13228
13229    while (this.expect(':')) {
13230      args.push(this.expression());
13231    }
13232
13233    return result;
13234  },
13235
13236  parseArguments: function() {
13237    var args = [];
13238    if (this.peekToken().text !== ')') {
13239      do {
13240        args.push(this.expression());
13241      } while (this.expect(','));
13242    }
13243    return args;
13244  },
13245
13246  identifier: function() {
13247    var token = this.consume();
13248    if (!token.identifier) {
13249      this.throwError('is not a valid identifier', token);
13250    }
13251    return { type: AST.Identifier, name: token.text };
13252  },
13253
13254  constant: function() {
13255    // TODO check that it is a constant
13256    return { type: AST.Literal, value: this.consume().value };
13257  },
13258
13259  arrayDeclaration: function() {
13260    var elements = [];
13261    if (this.peekToken().text !== ']') {
13262      do {
13263        if (this.peek(']')) {
13264          // Support trailing commas per ES5.1.
13265          break;
13266        }
13267        elements.push(this.expression());
13268      } while (this.expect(','));
13269    }
13270    this.consume(']');
13271
13272    return { type: AST.ArrayExpression, elements: elements };
13273  },
13274
13275  object: function() {
13276    var properties = [], property;
13277    if (this.peekToken().text !== '}') {
13278      do {
13279        if (this.peek('}')) {
13280          // Support trailing commas per ES5.1.
13281          break;
13282        }
13283        property = {type: AST.Property, kind: 'init'};
13284        if (this.peek().constant) {
13285          property.key = this.constant();
13286        } else if (this.peek().identifier) {
13287          property.key = this.identifier();
13288        } else {
13289          this.throwError("invalid key", this.peek());
13290        }
13291        this.consume(':');
13292        property.value = this.expression();
13293        properties.push(property);
13294      } while (this.expect(','));
13295    }
13296    this.consume('}');
13297
13298    return {type: AST.ObjectExpression, properties: properties };
13299  },
13300
13301  throwError: function(msg, token) {
13302    throw $parseMinErr('syntax',
13303        'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13304          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13305  },
13306
13307  consume: function(e1) {
13308    if (this.tokens.length === 0) {
13309      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13310    }
13311
13312    var token = this.expect(e1);
13313    if (!token) {
13314      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13315    }
13316    return token;
13317  },
13318
13319  peekToken: function() {
13320    if (this.tokens.length === 0) {
13321      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13322    }
13323    return this.tokens[0];
13324  },
13325
13326  peek: function(e1, e2, e3, e4) {
13327    return this.peekAhead(0, e1, e2, e3, e4);
13328  },
13329
13330  peekAhead: function(i, e1, e2, e3, e4) {
13331    if (this.tokens.length > i) {
13332      var token = this.tokens[i];
13333      var t = token.text;
13334      if (t === e1 || t === e2 || t === e3 || t === e4 ||
13335          (!e1 && !e2 && !e3 && !e4)) {
13336        return token;
13337      }
13338    }
13339    return false;
13340  },
13341
13342  expect: function(e1, e2, e3, e4) {
13343    var token = this.peek(e1, e2, e3, e4);
13344    if (token) {
13345      this.tokens.shift();
13346      return token;
13347    }
13348    return false;
13349  },
13350
13351
13352  /* `undefined` is not a constant, it is an identifier,
13353   * but using it as an identifier is not supported
13354   */
13355  constants: {
13356    'true': { type: AST.Literal, value: true },
13357    'false': { type: AST.Literal, value: false },
13358    'null': { type: AST.Literal, value: null },
13359    'undefined': {type: AST.Literal, value: undefined },
13360    'this': {type: AST.ThisExpression }
13361  }
13362};
13363
13364function ifDefined(v, d) {
13365  return typeof v !== 'undefined' ? v : d;
13366}
13367
13368function plusFn(l, r) {
13369  if (typeof l === 'undefined') return r;
13370  if (typeof r === 'undefined') return l;
13371  return l + r;
13372}
13373
13374function isStateless($filter, filterName) {
13375  var fn = $filter(filterName);
13376  return !fn.$stateful;
13377}
13378
13379function findConstantAndWatchExpressions(ast, $filter) {
13380  var allConstants;
13381  var argsToWatch;
13382  switch (ast.type) {
13383  case AST.Program:
13384    allConstants = true;
13385    forEach(ast.body, function(expr) {
13386      findConstantAndWatchExpressions(expr.expression, $filter);
13387      allConstants = allConstants && expr.expression.constant;
13388    });
13389    ast.constant = allConstants;
13390    break;
13391  case AST.Literal:
13392    ast.constant = true;
13393    ast.toWatch = [];
13394    break;
13395  case AST.UnaryExpression:
13396    findConstantAndWatchExpressions(ast.argument, $filter);
13397    ast.constant = ast.argument.constant;
13398    ast.toWatch = ast.argument.toWatch;
13399    break;
13400  case AST.BinaryExpression:
13401    findConstantAndWatchExpressions(ast.left, $filter);
13402    findConstantAndWatchExpressions(ast.right, $filter);
13403    ast.constant = ast.left.constant && ast.right.constant;
13404    ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13405    break;
13406  case AST.LogicalExpression:
13407    findConstantAndWatchExpressions(ast.left, $filter);
13408    findConstantAndWatchExpressions(ast.right, $filter);
13409    ast.constant = ast.left.constant && ast.right.constant;
13410    ast.toWatch = ast.constant ? [] : [ast];
13411    break;
13412  case AST.ConditionalExpression:
13413    findConstantAndWatchExpressions(ast.test, $filter);
13414    findConstantAndWatchExpressions(ast.alternate, $filter);
13415    findConstantAndWatchExpressions(ast.consequent, $filter);
13416    ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13417    ast.toWatch = ast.constant ? [] : [ast];
13418    break;
13419  case AST.Identifier:
13420    ast.constant = false;
13421    ast.toWatch = [ast];
13422    break;
13423  case AST.MemberExpression:
13424    findConstantAndWatchExpressions(ast.object, $filter);
13425    if (ast.computed) {
13426      findConstantAndWatchExpressions(ast.property, $filter);
13427    }
13428    ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13429    ast.toWatch = [ast];
13430    break;
13431  case AST.CallExpression:
13432    allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13433    argsToWatch = [];
13434    forEach(ast.arguments, function(expr) {
13435      findConstantAndWatchExpressions(expr, $filter);
13436      allConstants = allConstants && expr.constant;
13437      if (!expr.constant) {
13438        argsToWatch.push.apply(argsToWatch, expr.toWatch);
13439      }
13440    });
13441    ast.constant = allConstants;
13442    ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13443    break;
13444  case AST.AssignmentExpression:
13445    findConstantAndWatchExpressions(ast.left, $filter);
13446    findConstantAndWatchExpressions(ast.right, $filter);
13447    ast.constant = ast.left.constant && ast.right.constant;
13448    ast.toWatch = [ast];
13449    break;
13450  case AST.ArrayExpression:
13451    allConstants = true;
13452    argsToWatch = [];
13453    forEach(ast.elements, function(expr) {
13454      findConstantAndWatchExpressions(expr, $filter);
13455      allConstants = allConstants && expr.constant;
13456      if (!expr.constant) {
13457        argsToWatch.push.apply(argsToWatch, expr.toWatch);
13458      }
13459    });
13460    ast.constant = allConstants;
13461    ast.toWatch = argsToWatch;
13462    break;
13463  case AST.ObjectExpression:
13464    allConstants = true;
13465    argsToWatch = [];
13466    forEach(ast.properties, function(property) {
13467      findConstantAndWatchExpressions(property.value, $filter);
13468      allConstants = allConstants && property.value.constant;
13469      if (!property.value.constant) {
13470        argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13471      }
13472    });
13473    ast.constant = allConstants;
13474    ast.toWatch = argsToWatch;
13475    break;
13476  case AST.ThisExpression:
13477    ast.constant = false;
13478    ast.toWatch = [];
13479    break;
13480  }
13481}
13482
13483function getInputs(body) {
13484  if (body.length != 1) return;
13485  var lastExpression = body[0].expression;
13486  var candidate = lastExpression.toWatch;
13487  if (candidate.length !== 1) return candidate;
13488  return candidate[0] !== lastExpression ? candidate : undefined;
13489}
13490
13491function isAssignable(ast) {
13492  return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13493}
13494
13495function assignableAST(ast) {
13496  if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13497    return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13498  }
13499}
13500
13501function isLiteral(ast) {
13502  return ast.body.length === 0 ||
13503      ast.body.length === 1 && (
13504      ast.body[0].expression.type === AST.Literal ||
13505      ast.body[0].expression.type === AST.ArrayExpression ||
13506      ast.body[0].expression.type === AST.ObjectExpression);
13507}
13508
13509function isConstant(ast) {
13510  return ast.constant;
13511}
13512
13513function ASTCompiler(astBuilder, $filter) {
13514  this.astBuilder = astBuilder;
13515  this.$filter = $filter;
13516}
13517
13518ASTCompiler.prototype = {
13519  compile: function(expression, expensiveChecks) {
13520    var self = this;
13521    var ast = this.astBuilder.ast(expression);
13522    this.state = {
13523      nextId: 0,
13524      filters: {},
13525      expensiveChecks: expensiveChecks,
13526      fn: {vars: [], body: [], own: {}},
13527      assign: {vars: [], body: [], own: {}},
13528      inputs: []
13529    };
13530    findConstantAndWatchExpressions(ast, self.$filter);
13531    var extra = '';
13532    var assignable;
13533    this.stage = 'assign';
13534    if ((assignable = assignableAST(ast))) {
13535      this.state.computing = 'assign';
13536      var result = this.nextId();
13537      this.recurse(assignable, result);
13538      this.return_(result);
13539      extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13540    }
13541    var toWatch = getInputs(ast.body);
13542    self.stage = 'inputs';
13543    forEach(toWatch, function(watch, key) {
13544      var fnKey = 'fn' + key;
13545      self.state[fnKey] = {vars: [], body: [], own: {}};
13546      self.state.computing = fnKey;
13547      var intoId = self.nextId();
13548      self.recurse(watch, intoId);
13549      self.return_(intoId);
13550      self.state.inputs.push(fnKey);
13551      watch.watchId = key;
13552    });
13553    this.state.computing = 'fn';
13554    this.stage = 'main';
13555    this.recurse(ast);
13556    var fnString =
13557      // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13558      // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13559      '"' + this.USE + ' ' + this.STRICT + '";\n' +
13560      this.filterPrefix() +
13561      'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13562      extra +
13563      this.watchFns() +
13564      'return fn;';
13565
13566    /* jshint -W054 */
13567    var fn = (new Function('$filter',
13568        'ensureSafeMemberName',
13569        'ensureSafeObject',
13570        'ensureSafeFunction',
13571        'getStringValue',
13572        'ensureSafeAssignContext',
13573        'ifDefined',
13574        'plus',
13575        'text',
13576        fnString))(
13577          this.$filter,
13578          ensureSafeMemberName,
13579          ensureSafeObject,
13580          ensureSafeFunction,
13581          getStringValue,
13582          ensureSafeAssignContext,
13583          ifDefined,
13584          plusFn,
13585          expression);
13586    /* jshint +W054 */
13587    this.state = this.stage = undefined;
13588    fn.literal = isLiteral(ast);
13589    fn.constant = isConstant(ast);
13590    return fn;
13591  },
13592
13593  USE: 'use',
13594
13595  STRICT: 'strict',
13596
13597  watchFns: function() {
13598    var result = [];
13599    var fns = this.state.inputs;
13600    var self = this;
13601    forEach(fns, function(name) {
13602      result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13603    });
13604    if (fns.length) {
13605      result.push('fn.inputs=[' + fns.join(',') + '];');
13606    }
13607    return result.join('');
13608  },
13609
13610  generateFunction: function(name, params) {
13611    return 'function(' + params + '){' +
13612        this.varsPrefix(name) +
13613        this.body(name) +
13614        '};';
13615  },
13616
13617  filterPrefix: function() {
13618    var parts = [];
13619    var self = this;
13620    forEach(this.state.filters, function(id, filter) {
13621      parts.push(id + '=$filter(' + self.escape(filter) + ')');
13622    });
13623    if (parts.length) return 'var ' + parts.join(',') + ';';
13624    return '';
13625  },
13626
13627  varsPrefix: function(section) {
13628    return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13629  },
13630
13631  body: function(section) {
13632    return this.state[section].body.join('');
13633  },
13634
13635  recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13636    var left, right, self = this, args, expression;
13637    recursionFn = recursionFn || noop;
13638    if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13639      intoId = intoId || this.nextId();
13640      this.if_('i',
13641        this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13642        this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13643      );
13644      return;
13645    }
13646    switch (ast.type) {
13647    case AST.Program:
13648      forEach(ast.body, function(expression, pos) {
13649        self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13650        if (pos !== ast.body.length - 1) {
13651          self.current().body.push(right, ';');
13652        } else {
13653          self.return_(right);
13654        }
13655      });
13656      break;
13657    case AST.Literal:
13658      expression = this.escape(ast.value);
13659      this.assign(intoId, expression);
13660      recursionFn(expression);
13661      break;
13662    case AST.UnaryExpression:
13663      this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13664      expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13665      this.assign(intoId, expression);
13666      recursionFn(expression);
13667      break;
13668    case AST.BinaryExpression:
13669      this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13670      this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13671      if (ast.operator === '+') {
13672        expression = this.plus(left, right);
13673      } else if (ast.operator === '-') {
13674        expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13675      } else {
13676        expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13677      }
13678      this.assign(intoId, expression);
13679      recursionFn(expression);
13680      break;
13681    case AST.LogicalExpression:
13682      intoId = intoId || this.nextId();
13683      self.recurse(ast.left, intoId);
13684      self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13685      recursionFn(intoId);
13686      break;
13687    case AST.ConditionalExpression:
13688      intoId = intoId || this.nextId();
13689      self.recurse(ast.test, intoId);
13690      self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13691      recursionFn(intoId);
13692      break;
13693    case AST.Identifier:
13694      intoId = intoId || this.nextId();
13695      if (nameId) {
13696        nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13697        nameId.computed = false;
13698        nameId.name = ast.name;
13699      }
13700      ensureSafeMemberName(ast.name);
13701      self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13702        function() {
13703          self.if_(self.stage === 'inputs' || 's', function() {
13704            if (create && create !== 1) {
13705              self.if_(
13706                self.not(self.nonComputedMember('s', ast.name)),
13707                self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13708            }
13709            self.assign(intoId, self.nonComputedMember('s', ast.name));
13710          });
13711        }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13712        );
13713      if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13714        self.addEnsureSafeObject(intoId);
13715      }
13716      recursionFn(intoId);
13717      break;
13718    case AST.MemberExpression:
13719      left = nameId && (nameId.context = this.nextId()) || this.nextId();
13720      intoId = intoId || this.nextId();
13721      self.recurse(ast.object, left, undefined, function() {
13722        self.if_(self.notNull(left), function() {
13723          if (ast.computed) {
13724            right = self.nextId();
13725            self.recurse(ast.property, right);
13726            self.getStringValue(right);
13727            self.addEnsureSafeMemberName(right);
13728            if (create && create !== 1) {
13729              self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13730            }
13731            expression = self.ensureSafeObject(self.computedMember(left, right));
13732            self.assign(intoId, expression);
13733            if (nameId) {
13734              nameId.computed = true;
13735              nameId.name = right;
13736            }
13737          } else {
13738            ensureSafeMemberName(ast.property.name);
13739            if (create && create !== 1) {
13740              self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13741            }
13742            expression = self.nonComputedMember(left, ast.property.name);
13743            if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13744              expression = self.ensureSafeObject(expression);
13745            }
13746            self.assign(intoId, expression);
13747            if (nameId) {
13748              nameId.computed = false;
13749              nameId.name = ast.property.name;
13750            }
13751          }
13752        }, function() {
13753          self.assign(intoId, 'undefined');
13754        });
13755        recursionFn(intoId);
13756      }, !!create);
13757      break;
13758    case AST.CallExpression:
13759      intoId = intoId || this.nextId();
13760      if (ast.filter) {
13761        right = self.filter(ast.callee.name);
13762        args = [];
13763        forEach(ast.arguments, function(expr) {
13764          var argument = self.nextId();
13765          self.recurse(expr, argument);
13766          args.push(argument);
13767        });
13768        expression = right + '(' + args.join(',') + ')';
13769        self.assign(intoId, expression);
13770        recursionFn(intoId);
13771      } else {
13772        right = self.nextId();
13773        left = {};
13774        args = [];
13775        self.recurse(ast.callee, right, left, function() {
13776          self.if_(self.notNull(right), function() {
13777            self.addEnsureSafeFunction(right);
13778            forEach(ast.arguments, function(expr) {
13779              self.recurse(expr, self.nextId(), undefined, function(argument) {
13780                args.push(self.ensureSafeObject(argument));
13781              });
13782            });
13783            if (left.name) {
13784              if (!self.state.expensiveChecks) {
13785                self.addEnsureSafeObject(left.context);
13786              }
13787              expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13788            } else {
13789              expression = right + '(' + args.join(',') + ')';
13790            }
13791            expression = self.ensureSafeObject(expression);
13792            self.assign(intoId, expression);
13793          }, function() {
13794            self.assign(intoId, 'undefined');
13795          });
13796          recursionFn(intoId);
13797        });
13798      }
13799      break;
13800    case AST.AssignmentExpression:
13801      right = this.nextId();
13802      left = {};
13803      if (!isAssignable(ast.left)) {
13804        throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
13805      }
13806      this.recurse(ast.left, undefined, left, function() {
13807        self.if_(self.notNull(left.context), function() {
13808          self.recurse(ast.right, right);
13809          self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13810          self.addEnsureSafeAssignContext(left.context);
13811          expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13812          self.assign(intoId, expression);
13813          recursionFn(intoId || expression);
13814        });
13815      }, 1);
13816      break;
13817    case AST.ArrayExpression:
13818      args = [];
13819      forEach(ast.elements, function(expr) {
13820        self.recurse(expr, self.nextId(), undefined, function(argument) {
13821          args.push(argument);
13822        });
13823      });
13824      expression = '[' + args.join(',') + ']';
13825      this.assign(intoId, expression);
13826      recursionFn(expression);
13827      break;
13828    case AST.ObjectExpression:
13829      args = [];
13830      forEach(ast.properties, function(property) {
13831        self.recurse(property.value, self.nextId(), undefined, function(expr) {
13832          args.push(self.escape(
13833              property.key.type === AST.Identifier ? property.key.name :
13834                ('' + property.key.value)) +
13835              ':' + expr);
13836        });
13837      });
13838      expression = '{' + args.join(',') + '}';
13839      this.assign(intoId, expression);
13840      recursionFn(expression);
13841      break;
13842    case AST.ThisExpression:
13843      this.assign(intoId, 's');
13844      recursionFn('s');
13845      break;
13846    case AST.NGValueParameter:
13847      this.assign(intoId, 'v');
13848      recursionFn('v');
13849      break;
13850    }
13851  },
13852
13853  getHasOwnProperty: function(element, property) {
13854    var key = element + '.' + property;
13855    var own = this.current().own;
13856    if (!own.hasOwnProperty(key)) {
13857      own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13858    }
13859    return own[key];
13860  },
13861
13862  assign: function(id, value) {
13863    if (!id) return;
13864    this.current().body.push(id, '=', value, ';');
13865    return id;
13866  },
13867
13868  filter: function(filterName) {
13869    if (!this.state.filters.hasOwnProperty(filterName)) {
13870      this.state.filters[filterName] = this.nextId(true);
13871    }
13872    return this.state.filters[filterName];
13873  },
13874
13875  ifDefined: function(id, defaultValue) {
13876    return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13877  },
13878
13879  plus: function(left, right) {
13880    return 'plus(' + left + ',' + right + ')';
13881  },
13882
13883  return_: function(id) {
13884    this.current().body.push('return ', id, ';');
13885  },
13886
13887  if_: function(test, alternate, consequent) {
13888    if (test === true) {
13889      alternate();
13890    } else {
13891      var body = this.current().body;
13892      body.push('if(', test, '){');
13893      alternate();
13894      body.push('}');
13895      if (consequent) {
13896        body.push('else{');
13897        consequent();
13898        body.push('}');
13899      }
13900    }
13901  },
13902
13903  not: function(expression) {
13904    return '!(' + expression + ')';
13905  },
13906
13907  notNull: function(expression) {
13908    return expression + '!=null';
13909  },
13910
13911  nonComputedMember: function(left, right) {
13912    return left + '.' + right;
13913  },
13914
13915  computedMember: function(left, right) {
13916    return left + '[' + right + ']';
13917  },
13918
13919  member: function(left, right, computed) {
13920    if (computed) return this.computedMember(left, right);
13921    return this.nonComputedMember(left, right);
13922  },
13923
13924  addEnsureSafeObject: function(item) {
13925    this.current().body.push(this.ensureSafeObject(item), ';');
13926  },
13927
13928  addEnsureSafeMemberName: function(item) {
13929    this.current().body.push(this.ensureSafeMemberName(item), ';');
13930  },
13931
13932  addEnsureSafeFunction: function(item) {
13933    this.current().body.push(this.ensureSafeFunction(item), ';');
13934  },
13935
13936  addEnsureSafeAssignContext: function(item) {
13937    this.current().body.push(this.ensureSafeAssignContext(item), ';');
13938  },
13939
13940  ensureSafeObject: function(item) {
13941    return 'ensureSafeObject(' + item + ',text)';
13942  },
13943
13944  ensureSafeMemberName: function(item) {
13945    return 'ensureSafeMemberName(' + item + ',text)';
13946  },
13947
13948  ensureSafeFunction: function(item) {
13949    return 'ensureSafeFunction(' + item + ',text)';
13950  },
13951
13952  getStringValue: function(item) {
13953    this.assign(item, 'getStringValue(' + item + ',text)');
13954  },
13955
13956  ensureSafeAssignContext: function(item) {
13957    return 'ensureSafeAssignContext(' + item + ',text)';
13958  },
13959
13960  lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13961    var self = this;
13962    return function() {
13963      self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
13964    };
13965  },
13966
13967  lazyAssign: function(id, value) {
13968    var self = this;
13969    return function() {
13970      self.assign(id, value);
13971    };
13972  },
13973
13974  stringEscapeRegex: /[^ a-zA-Z0-9]/g,
13975
13976  stringEscapeFn: function(c) {
13977    return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
13978  },
13979
13980  escape: function(value) {
13981    if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
13982    if (isNumber(value)) return value.toString();
13983    if (value === true) return 'true';
13984    if (value === false) return 'false';
13985    if (value === null) return 'null';
13986    if (typeof value === 'undefined') return 'undefined';
13987
13988    throw $parseMinErr('esc', 'IMPOSSIBLE');
13989  },
13990
13991  nextId: function(skip, init) {
13992    var id = 'v' + (this.state.nextId++);
13993    if (!skip) {
13994      this.current().vars.push(id + (init ? '=' + init : ''));
13995    }
13996    return id;
13997  },
13998
13999  current: function() {
14000    return this.state[this.state.computing];
14001  }
14002};
14003
14004
14005function ASTInterpreter(astBuilder, $filter) {
14006  this.astBuilder = astBuilder;
14007  this.$filter = $filter;
14008}
14009
14010ASTInterpreter.prototype = {
14011  compile: function(expression, expensiveChecks) {
14012    var self = this;
14013    var ast = this.astBuilder.ast(expression);
14014    this.expression = expression;
14015    this.expensiveChecks = expensiveChecks;
14016    findConstantAndWatchExpressions(ast, self.$filter);
14017    var assignable;
14018    var assign;
14019    if ((assignable = assignableAST(ast))) {
14020      assign = this.recurse(assignable);
14021    }
14022    var toWatch = getInputs(ast.body);
14023    var inputs;
14024    if (toWatch) {
14025      inputs = [];
14026      forEach(toWatch, function(watch, key) {
14027        var input = self.recurse(watch);
14028        watch.input = input;
14029        inputs.push(input);
14030        watch.watchId = key;
14031      });
14032    }
14033    var expressions = [];
14034    forEach(ast.body, function(expression) {
14035      expressions.push(self.recurse(expression.expression));
14036    });
14037    var fn = ast.body.length === 0 ? function() {} :
14038             ast.body.length === 1 ? expressions[0] :
14039             function(scope, locals) {
14040               var lastValue;
14041               forEach(expressions, function(exp) {
14042                 lastValue = exp(scope, locals);
14043               });
14044               return lastValue;
14045             };
14046    if (assign) {
14047      fn.assign = function(scope, value, locals) {
14048        return assign(scope, locals, value);
14049      };
14050    }
14051    if (inputs) {
14052      fn.inputs = inputs;
14053    }
14054    fn.literal = isLiteral(ast);
14055    fn.constant = isConstant(ast);
14056    return fn;
14057  },
14058
14059  recurse: function(ast, context, create) {
14060    var left, right, self = this, args, expression;
14061    if (ast.input) {
14062      return this.inputs(ast.input, ast.watchId);
14063    }
14064    switch (ast.type) {
14065    case AST.Literal:
14066      return this.value(ast.value, context);
14067    case AST.UnaryExpression:
14068      right = this.recurse(ast.argument);
14069      return this['unary' + ast.operator](right, context);
14070    case AST.BinaryExpression:
14071      left = this.recurse(ast.left);
14072      right = this.recurse(ast.right);
14073      return this['binary' + ast.operator](left, right, context);
14074    case AST.LogicalExpression:
14075      left = this.recurse(ast.left);
14076      right = this.recurse(ast.right);
14077      return this['binary' + ast.operator](left, right, context);
14078    case AST.ConditionalExpression:
14079      return this['ternary?:'](
14080        this.recurse(ast.test),
14081        this.recurse(ast.alternate),
14082        this.recurse(ast.consequent),
14083        context
14084      );
14085    case AST.Identifier:
14086      ensureSafeMemberName(ast.name, self.expression);
14087      return self.identifier(ast.name,
14088                             self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
14089                             context, create, self.expression);
14090    case AST.MemberExpression:
14091      left = this.recurse(ast.object, false, !!create);
14092      if (!ast.computed) {
14093        ensureSafeMemberName(ast.property.name, self.expression);
14094        right = ast.property.name;
14095      }
14096      if (ast.computed) right = this.recurse(ast.property);
14097      return ast.computed ?
14098        this.computedMember(left, right, context, create, self.expression) :
14099        this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
14100    case AST.CallExpression:
14101      args = [];
14102      forEach(ast.arguments, function(expr) {
14103        args.push(self.recurse(expr));
14104      });
14105      if (ast.filter) right = this.$filter(ast.callee.name);
14106      if (!ast.filter) right = this.recurse(ast.callee, true);
14107      return ast.filter ?
14108        function(scope, locals, assign, inputs) {
14109          var values = [];
14110          for (var i = 0; i < args.length; ++i) {
14111            values.push(args[i](scope, locals, assign, inputs));
14112          }
14113          var value = right.apply(undefined, values, inputs);
14114          return context ? {context: undefined, name: undefined, value: value} : value;
14115        } :
14116        function(scope, locals, assign, inputs) {
14117          var rhs = right(scope, locals, assign, inputs);
14118          var value;
14119          if (rhs.value != null) {
14120            ensureSafeObject(rhs.context, self.expression);
14121            ensureSafeFunction(rhs.value, self.expression);
14122            var values = [];
14123            for (var i = 0; i < args.length; ++i) {
14124              values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
14125            }
14126            value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
14127          }
14128          return context ? {value: value} : value;
14129        };
14130    case AST.AssignmentExpression:
14131      left = this.recurse(ast.left, true, 1);
14132      right = this.recurse(ast.right);
14133      return function(scope, locals, assign, inputs) {
14134        var lhs = left(scope, locals, assign, inputs);
14135        var rhs = right(scope, locals, assign, inputs);
14136        ensureSafeObject(lhs.value, self.expression);
14137        ensureSafeAssignContext(lhs.context);
14138        lhs.context[lhs.name] = rhs;
14139        return context ? {value: rhs} : rhs;
14140      };
14141    case AST.ArrayExpression:
14142      args = [];
14143      forEach(ast.elements, function(expr) {
14144        args.push(self.recurse(expr));
14145      });
14146      return function(scope, locals, assign, inputs) {
14147        var value = [];
14148        for (var i = 0; i < args.length; ++i) {
14149          value.push(args[i](scope, locals, assign, inputs));
14150        }
14151        return context ? {value: value} : value;
14152      };
14153    case AST.ObjectExpression:
14154      args = [];
14155      forEach(ast.properties, function(property) {
14156        args.push({key: property.key.type === AST.Identifier ?
14157                        property.key.name :
14158                        ('' + property.key.value),
14159                   value: self.recurse(property.value)
14160        });
14161      });
14162      return function(scope, locals, assign, inputs) {
14163        var value = {};
14164        for (var i = 0; i < args.length; ++i) {
14165          value[args[i].key] = args[i].value(scope, locals, assign, inputs);
14166        }
14167        return context ? {value: value} : value;
14168      };
14169    case AST.ThisExpression:
14170      return function(scope) {
14171        return context ? {value: scope} : scope;
14172      };
14173    case AST.NGValueParameter:
14174      return function(scope, locals, assign, inputs) {
14175        return context ? {value: assign} : assign;
14176      };
14177    }
14178  },
14179
14180  'unary+': function(argument, context) {
14181    return function(scope, locals, assign, inputs) {
14182      var arg = argument(scope, locals, assign, inputs);
14183      if (isDefined(arg)) {
14184        arg = +arg;
14185      } else {
14186        arg = 0;
14187      }
14188      return context ? {value: arg} : arg;
14189    };
14190  },
14191  'unary-': function(argument, context) {
14192    return function(scope, locals, assign, inputs) {
14193      var arg = argument(scope, locals, assign, inputs);
14194      if (isDefined(arg)) {
14195        arg = -arg;
14196      } else {
14197        arg = 0;
14198      }
14199      return context ? {value: arg} : arg;
14200    };
14201  },
14202  'unary!': function(argument, context) {
14203    return function(scope, locals, assign, inputs) {
14204      var arg = !argument(scope, locals, assign, inputs);
14205      return context ? {value: arg} : arg;
14206    };
14207  },
14208  'binary+': function(left, right, context) {
14209    return function(scope, locals, assign, inputs) {
14210      var lhs = left(scope, locals, assign, inputs);
14211      var rhs = right(scope, locals, assign, inputs);
14212      var arg = plusFn(lhs, rhs);
14213      return context ? {value: arg} : arg;
14214    };
14215  },
14216  'binary-': function(left, right, context) {
14217    return function(scope, locals, assign, inputs) {
14218      var lhs = left(scope, locals, assign, inputs);
14219      var rhs = right(scope, locals, assign, inputs);
14220      var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14221      return context ? {value: arg} : arg;
14222    };
14223  },
14224  'binary*': function(left, right, context) {
14225    return function(scope, locals, assign, inputs) {
14226      var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14227      return context ? {value: arg} : arg;
14228    };
14229  },
14230  'binary/': function(left, right, context) {
14231    return function(scope, locals, assign, inputs) {
14232      var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14233      return context ? {value: arg} : arg;
14234    };
14235  },
14236  'binary%': function(left, right, context) {
14237    return function(scope, locals, assign, inputs) {
14238      var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14239      return context ? {value: arg} : arg;
14240    };
14241  },
14242  'binary===': function(left, right, context) {
14243    return function(scope, locals, assign, inputs) {
14244      var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14245      return context ? {value: arg} : arg;
14246    };
14247  },
14248  'binary!==': function(left, right, context) {
14249    return function(scope, locals, assign, inputs) {
14250      var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14251      return context ? {value: arg} : arg;
14252    };
14253  },
14254  'binary==': function(left, right, context) {
14255    return function(scope, locals, assign, inputs) {
14256      var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14257      return context ? {value: arg} : arg;
14258    };
14259  },
14260  'binary!=': function(left, right, context) {
14261    return function(scope, locals, assign, inputs) {
14262      var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14263      return context ? {value: arg} : arg;
14264    };
14265  },
14266  'binary<': function(left, right, context) {
14267    return function(scope, locals, assign, inputs) {
14268      var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14269      return context ? {value: arg} : arg;
14270    };
14271  },
14272  'binary>': function(left, right, context) {
14273    return function(scope, locals, assign, inputs) {
14274      var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14275      return context ? {value: arg} : arg;
14276    };
14277  },
14278  'binary<=': function(left, right, context) {
14279    return function(scope, locals, assign, inputs) {
14280      var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14281      return context ? {value: arg} : arg;
14282    };
14283  },
14284  'binary>=': function(left, right, context) {
14285    return function(scope, locals, assign, inputs) {
14286      var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14287      return context ? {value: arg} : arg;
14288    };
14289  },
14290  'binary&&': function(left, right, context) {
14291    return function(scope, locals, assign, inputs) {
14292      var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14293      return context ? {value: arg} : arg;
14294    };
14295  },
14296  'binary||': function(left, right, context) {
14297    return function(scope, locals, assign, inputs) {
14298      var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14299      return context ? {value: arg} : arg;
14300    };
14301  },
14302  'ternary?:': function(test, alternate, consequent, context) {
14303    return function(scope, locals, assign, inputs) {
14304      var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14305      return context ? {value: arg} : arg;
14306    };
14307  },
14308  value: function(value, context) {
14309    return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14310  },
14311  identifier: function(name, expensiveChecks, context, create, expression) {
14312    return function(scope, locals, assign, inputs) {
14313      var base = locals && (name in locals) ? locals : scope;
14314      if (create && create !== 1 && base && !(base[name])) {
14315        base[name] = {};
14316      }
14317      var value = base ? base[name] : undefined;
14318      if (expensiveChecks) {
14319        ensureSafeObject(value, expression);
14320      }
14321      if (context) {
14322        return {context: base, name: name, value: value};
14323      } else {
14324        return value;
14325      }
14326    };
14327  },
14328  computedMember: function(left, right, context, create, expression) {
14329    return function(scope, locals, assign, inputs) {
14330      var lhs = left(scope, locals, assign, inputs);
14331      var rhs;
14332      var value;
14333      if (lhs != null) {
14334        rhs = right(scope, locals, assign, inputs);
14335        rhs = getStringValue(rhs);
14336        ensureSafeMemberName(rhs, expression);
14337        if (create && create !== 1 && lhs && !(lhs[rhs])) {
14338          lhs[rhs] = {};
14339        }
14340        value = lhs[rhs];
14341        ensureSafeObject(value, expression);
14342      }
14343      if (context) {
14344        return {context: lhs, name: rhs, value: value};
14345      } else {
14346        return value;
14347      }
14348    };
14349  },
14350  nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14351    return function(scope, locals, assign, inputs) {
14352      var lhs = left(scope, locals, assign, inputs);
14353      if (create && create !== 1 && lhs && !(lhs[right])) {
14354        lhs[right] = {};
14355      }
14356      var value = lhs != null ? lhs[right] : undefined;
14357      if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14358        ensureSafeObject(value, expression);
14359      }
14360      if (context) {
14361        return {context: lhs, name: right, value: value};
14362      } else {
14363        return value;
14364      }
14365    };
14366  },
14367  inputs: function(input, watchId) {
14368    return function(scope, value, locals, inputs) {
14369      if (inputs) return inputs[watchId];
14370      return input(scope, value, locals);
14371    };
14372  }
14373};
14374
14375/**
14376 * @constructor
14377 */
14378var Parser = function(lexer, $filter, options) {
14379  this.lexer = lexer;
14380  this.$filter = $filter;
14381  this.options = options;
14382  this.ast = new AST(this.lexer);
14383  this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14384                                   new ASTCompiler(this.ast, $filter);
14385};
14386
14387Parser.prototype = {
14388  constructor: Parser,
14389
14390  parse: function(text) {
14391    return this.astCompiler.compile(text, this.options.expensiveChecks);
14392  }
14393};
14394
14395function isPossiblyDangerousMemberName(name) {
14396  return name == 'constructor';
14397}
14398
14399var objectValueOf = Object.prototype.valueOf;
14400
14401function getValueOf(value) {
14402  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14403}
14404
14405///////////////////////////////////
14406
14407/**
14408 * @ngdoc service
14409 * @name $parse
14410 * @kind function
14411 *
14412 * @description
14413 *
14414 * Converts Angular {@link guide/expression expression} into a function.
14415 *
14416 * ```js
14417 *   var getter = $parse('user.name');
14418 *   var setter = getter.assign;
14419 *   var context = {user:{name:'angular'}};
14420 *   var locals = {user:{name:'local'}};
14421 *
14422 *   expect(getter(context)).toEqual('angular');
14423 *   setter(context, 'newValue');
14424 *   expect(context.user.name).toEqual('newValue');
14425 *   expect(getter(context, locals)).toEqual('local');
14426 * ```
14427 *
14428 *
14429 * @param {string} expression String expression to compile.
14430 * @returns {function(context, locals)} a function which represents the compiled expression:
14431 *
14432 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
14433 *      are evaluated against (typically a scope object).
14434 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
14435 *      `context`.
14436 *
14437 *    The returned function also has the following properties:
14438 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14439 *        literal.
14440 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14441 *        constant literals.
14442 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14443 *        set to a function to change its value on the given context.
14444 *
14445 */
14446
14447
14448/**
14449 * @ngdoc provider
14450 * @name $parseProvider
14451 *
14452 * @description
14453 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14454 *  service.
14455 */
14456function $ParseProvider() {
14457  var cacheDefault = createMap();
14458  var cacheExpensive = createMap();
14459
14460  this.$get = ['$filter', function($filter) {
14461    var noUnsafeEval = csp().noUnsafeEval;
14462    var $parseOptions = {
14463          csp: noUnsafeEval,
14464          expensiveChecks: false
14465        },
14466        $parseOptionsExpensive = {
14467          csp: noUnsafeEval,
14468          expensiveChecks: true
14469        };
14470
14471    return function $parse(exp, interceptorFn, expensiveChecks) {
14472      var parsedExpression, oneTime, cacheKey;
14473
14474      switch (typeof exp) {
14475        case 'string':
14476          exp = exp.trim();
14477          cacheKey = exp;
14478
14479          var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14480          parsedExpression = cache[cacheKey];
14481
14482          if (!parsedExpression) {
14483            if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14484              oneTime = true;
14485              exp = exp.substring(2);
14486            }
14487            var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14488            var lexer = new Lexer(parseOptions);
14489            var parser = new Parser(lexer, $filter, parseOptions);
14490            parsedExpression = parser.parse(exp);
14491            if (parsedExpression.constant) {
14492              parsedExpression.$$watchDelegate = constantWatchDelegate;
14493            } else if (oneTime) {
14494              parsedExpression.$$watchDelegate = parsedExpression.literal ?
14495                  oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14496            } else if (parsedExpression.inputs) {
14497              parsedExpression.$$watchDelegate = inputsWatchDelegate;
14498            }
14499            cache[cacheKey] = parsedExpression;
14500          }
14501          return addInterceptor(parsedExpression, interceptorFn);
14502
14503        case 'function':
14504          return addInterceptor(exp, interceptorFn);
14505
14506        default:
14507          return addInterceptor(noop, interceptorFn);
14508      }
14509    };
14510
14511    function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14512
14513      if (newValue == null || oldValueOfValue == null) { // null/undefined
14514        return newValue === oldValueOfValue;
14515      }
14516
14517      if (typeof newValue === 'object') {
14518
14519        // attempt to convert the value to a primitive type
14520        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14521        //             be cheaply dirty-checked
14522        newValue = getValueOf(newValue);
14523
14524        if (typeof newValue === 'object') {
14525          // objects/arrays are not supported - deep-watching them would be too expensive
14526          return false;
14527        }
14528
14529        // fall-through to the primitive equality check
14530      }
14531
14532      //Primitive or NaN
14533      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14534    }
14535
14536    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14537      var inputExpressions = parsedExpression.inputs;
14538      var lastResult;
14539
14540      if (inputExpressions.length === 1) {
14541        var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14542        inputExpressions = inputExpressions[0];
14543        return scope.$watch(function expressionInputWatch(scope) {
14544          var newInputValue = inputExpressions(scope);
14545          if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14546            lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14547            oldInputValueOf = newInputValue && getValueOf(newInputValue);
14548          }
14549          return lastResult;
14550        }, listener, objectEquality, prettyPrintExpression);
14551      }
14552
14553      var oldInputValueOfValues = [];
14554      var oldInputValues = [];
14555      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14556        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14557        oldInputValues[i] = null;
14558      }
14559
14560      return scope.$watch(function expressionInputsWatch(scope) {
14561        var changed = false;
14562
14563        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14564          var newInputValue = inputExpressions[i](scope);
14565          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14566            oldInputValues[i] = newInputValue;
14567            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14568          }
14569        }
14570
14571        if (changed) {
14572          lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14573        }
14574
14575        return lastResult;
14576      }, listener, objectEquality, prettyPrintExpression);
14577    }
14578
14579    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14580      var unwatch, lastValue;
14581      return unwatch = scope.$watch(function oneTimeWatch(scope) {
14582        return parsedExpression(scope);
14583      }, function oneTimeListener(value, old, scope) {
14584        lastValue = value;
14585        if (isFunction(listener)) {
14586          listener.apply(this, arguments);
14587        }
14588        if (isDefined(value)) {
14589          scope.$$postDigest(function() {
14590            if (isDefined(lastValue)) {
14591              unwatch();
14592            }
14593          });
14594        }
14595      }, objectEquality);
14596    }
14597
14598    function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14599      var unwatch, lastValue;
14600      return unwatch = scope.$watch(function oneTimeWatch(scope) {
14601        return parsedExpression(scope);
14602      }, function oneTimeListener(value, old, scope) {
14603        lastValue = value;
14604        if (isFunction(listener)) {
14605          listener.call(this, value, old, scope);
14606        }
14607        if (isAllDefined(value)) {
14608          scope.$$postDigest(function() {
14609            if (isAllDefined(lastValue)) unwatch();
14610          });
14611        }
14612      }, objectEquality);
14613
14614      function isAllDefined(value) {
14615        var allDefined = true;
14616        forEach(value, function(val) {
14617          if (!isDefined(val)) allDefined = false;
14618        });
14619        return allDefined;
14620      }
14621    }
14622
14623    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14624      var unwatch;
14625      return unwatch = scope.$watch(function constantWatch(scope) {
14626        return parsedExpression(scope);
14627      }, function constantListener(value, old, scope) {
14628        if (isFunction(listener)) {
14629          listener.apply(this, arguments);
14630        }
14631        unwatch();
14632      }, objectEquality);
14633    }
14634
14635    function addInterceptor(parsedExpression, interceptorFn) {
14636      if (!interceptorFn) return parsedExpression;
14637      var watchDelegate = parsedExpression.$$watchDelegate;
14638      var useInputs = false;
14639
14640      var regularWatch =
14641          watchDelegate !== oneTimeLiteralWatchDelegate &&
14642          watchDelegate !== oneTimeWatchDelegate;
14643
14644      var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14645        var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
14646        return interceptorFn(value, scope, locals);
14647      } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14648        var value = parsedExpression(scope, locals, assign, inputs);
14649        var result = interceptorFn(value, scope, locals);
14650        // we only return the interceptor's result if the
14651        // initial value is defined (for bind-once)
14652        return isDefined(value) ? result : value;
14653      };
14654
14655      // Propagate $$watchDelegates other then inputsWatchDelegate
14656      if (parsedExpression.$$watchDelegate &&
14657          parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14658        fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14659      } else if (!interceptorFn.$stateful) {
14660        // If there is an interceptor, but no watchDelegate then treat the interceptor like
14661        // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14662        fn.$$watchDelegate = inputsWatchDelegate;
14663        useInputs = !parsedExpression.inputs;
14664        fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14665      }
14666
14667      return fn;
14668    }
14669  }];
14670}
14671
14672/**
14673 * @ngdoc service
14674 * @name $q
14675 * @requires $rootScope
14676 *
14677 * @description
14678 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14679 * when they are done processing.
14680 *
14681 * This is an implementation of promises/deferred objects inspired by
14682 * [Kris Kowal's Q](https://github.com/kriskowal/q).
14683 *
14684 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14685 * implementations, and the other which resembles ES6 promises to some degree.
14686 *
14687 * # $q constructor
14688 *
14689 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14690 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14691 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14692 *
14693 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14694 * available yet.
14695 *
14696 * It can be used like so:
14697 *
14698 * ```js
14699 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14700 *   // are available in the current lexical scope (they could have been injected or passed in).
14701 *
14702 *   function asyncGreet(name) {
14703 *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
14704 *     return $q(function(resolve, reject) {
14705 *       setTimeout(function() {
14706 *         if (okToGreet(name)) {
14707 *           resolve('Hello, ' + name + '!');
14708 *         } else {
14709 *           reject('Greeting ' + name + ' is not allowed.');
14710 *         }
14711 *       }, 1000);
14712 *     });
14713 *   }
14714 *
14715 *   var promise = asyncGreet('Robin Hood');
14716 *   promise.then(function(greeting) {
14717 *     alert('Success: ' + greeting);
14718 *   }, function(reason) {
14719 *     alert('Failed: ' + reason);
14720 *   });
14721 * ```
14722 *
14723 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14724 *
14725 * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
14726 *
14727 * However, the more traditional CommonJS-style usage is still available, and documented below.
14728 *
14729 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14730 * interface for interacting with an object that represents the result of an action that is
14731 * performed asynchronously, and may or may not be finished at any given point in time.
14732 *
14733 * From the perspective of dealing with error handling, deferred and promise APIs are to
14734 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14735 *
14736 * ```js
14737 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14738 *   // are available in the current lexical scope (they could have been injected or passed in).
14739 *
14740 *   function asyncGreet(name) {
14741 *     var deferred = $q.defer();
14742 *
14743 *     setTimeout(function() {
14744 *       deferred.notify('About to greet ' + name + '.');
14745 *
14746 *       if (okToGreet(name)) {
14747 *         deferred.resolve('Hello, ' + name + '!');
14748 *       } else {
14749 *         deferred.reject('Greeting ' + name + ' is not allowed.');
14750 *       }
14751 *     }, 1000);
14752 *
14753 *     return deferred.promise;
14754 *   }
14755 *
14756 *   var promise = asyncGreet('Robin Hood');
14757 *   promise.then(function(greeting) {
14758 *     alert('Success: ' + greeting);
14759 *   }, function(reason) {
14760 *     alert('Failed: ' + reason);
14761 *   }, function(update) {
14762 *     alert('Got notification: ' + update);
14763 *   });
14764 * ```
14765 *
14766 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14767 * comes in the way of guarantees that promise and deferred APIs make, see
14768 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14769 *
14770 * Additionally the promise api allows for composition that is very hard to do with the
14771 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14772 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14773 * section on serial or parallel joining of promises.
14774 *
14775 * # The Deferred API
14776 *
14777 * A new instance of deferred is constructed by calling `$q.defer()`.
14778 *
14779 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14780 * that can be used for signaling the successful or unsuccessful completion, as well as the status
14781 * of the task.
14782 *
14783 * **Methods**
14784 *
14785 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14786 *   constructed via `$q.reject`, the promise will be rejected instead.
14787 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14788 *   resolving it with a rejection constructed via `$q.reject`.
14789 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14790 *   multiple times before the promise is either resolved or rejected.
14791 *
14792 * **Properties**
14793 *
14794 * - promise – `{Promise}` – promise object associated with this deferred.
14795 *
14796 *
14797 * # The Promise API
14798 *
14799 * A new promise instance is created when a deferred instance is created and can be retrieved by
14800 * calling `deferred.promise`.
14801 *
14802 * The purpose of the promise object is to allow for interested parties to get access to the result
14803 * of the deferred task when it completes.
14804 *
14805 * **Methods**
14806 *
14807 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14808 *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14809 *   as soon as the result is available. The callbacks are called with a single argument: the result
14810 *   or rejection reason. Additionally, the notify callback may be called zero or more times to
14811 *   provide a progress indication, before the promise is resolved or rejected.
14812 *
14813 *   This method *returns a new promise* which is resolved or rejected via the return value of the
14814 *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14815 *   with the value which is resolved in that promise using
14816 *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14817 *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14818 *   resolved or rejected from the notifyCallback method.
14819 *
14820 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14821 *
14822 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14823 *   but to do so without modifying the final value. This is useful to release resources or do some
14824 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14825 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14826 *   more information.
14827 *
14828 * # Chaining promises
14829 *
14830 * Because calling the `then` method of a promise returns a new derived promise, it is easily
14831 * possible to create a chain of promises:
14832 *
14833 * ```js
14834 *   promiseB = promiseA.then(function(result) {
14835 *     return result + 1;
14836 *   });
14837 *
14838 *   // promiseB will be resolved immediately after promiseA is resolved and its value
14839 *   // will be the result of promiseA incremented by 1
14840 * ```
14841 *
14842 * It is possible to create chains of any length and since a promise can be resolved with another
14843 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14844 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14845 * $http's response interceptors.
14846 *
14847 *
14848 * # Differences between Kris Kowal's Q and $q
14849 *
14850 *  There are two main differences:
14851 *
14852 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14853 *   mechanism in angular, which means faster propagation of resolution or rejection into your
14854 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
14855 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14856 *   all the important functionality needed for common async tasks.
14857 *
14858 *  # Testing
14859 *
14860 *  ```js
14861 *    it('should simulate promise', inject(function($q, $rootScope) {
14862 *      var deferred = $q.defer();
14863 *      var promise = deferred.promise;
14864 *      var resolvedValue;
14865 *
14866 *      promise.then(function(value) { resolvedValue = value; });
14867 *      expect(resolvedValue).toBeUndefined();
14868 *
14869 *      // Simulate resolving of promise
14870 *      deferred.resolve(123);
14871 *      // Note that the 'then' function does not get called synchronously.
14872 *      // This is because we want the promise API to always be async, whether or not
14873 *      // it got called synchronously or asynchronously.
14874 *      expect(resolvedValue).toBeUndefined();
14875 *
14876 *      // Propagate promise resolution to 'then' functions using $apply().
14877 *      $rootScope.$apply();
14878 *      expect(resolvedValue).toEqual(123);
14879 *    }));
14880 *  ```
14881 *
14882 * @param {function(function, function)} resolver Function which is responsible for resolving or
14883 *   rejecting the newly created promise. The first parameter is a function which resolves the
14884 *   promise, the second parameter is a function which rejects the promise.
14885 *
14886 * @returns {Promise} The newly created promise.
14887 */
14888function $QProvider() {
14889
14890  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14891    return qFactory(function(callback) {
14892      $rootScope.$evalAsync(callback);
14893    }, $exceptionHandler);
14894  }];
14895}
14896
14897function $$QProvider() {
14898  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
14899    return qFactory(function(callback) {
14900      $browser.defer(callback);
14901    }, $exceptionHandler);
14902  }];
14903}
14904
14905/**
14906 * Constructs a promise manager.
14907 *
14908 * @param {function(function)} nextTick Function for executing functions in the next turn.
14909 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
14910 *     debugging purposes.
14911 * @returns {object} Promise manager.
14912 */
14913function qFactory(nextTick, exceptionHandler) {
14914  var $qMinErr = minErr('$q', TypeError);
14915  function callOnce(self, resolveFn, rejectFn) {
14916    var called = false;
14917    function wrap(fn) {
14918      return function(value) {
14919        if (called) return;
14920        called = true;
14921        fn.call(self, value);
14922      };
14923    }
14924
14925    return [wrap(resolveFn), wrap(rejectFn)];
14926  }
14927
14928  /**
14929   * @ngdoc method
14930   * @name ng.$q#defer
14931   * @kind function
14932   *
14933   * @description
14934   * Creates a `Deferred` object which represents a task which will finish in the future.
14935   *
14936   * @returns {Deferred} Returns a new instance of deferred.
14937   */
14938  var defer = function() {
14939    return new Deferred();
14940  };
14941
14942  function Promise() {
14943    this.$$state = { status: 0 };
14944  }
14945
14946  extend(Promise.prototype, {
14947    then: function(onFulfilled, onRejected, progressBack) {
14948      if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
14949        return this;
14950      }
14951      var result = new Deferred();
14952
14953      this.$$state.pending = this.$$state.pending || [];
14954      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
14955      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
14956
14957      return result.promise;
14958    },
14959
14960    "catch": function(callback) {
14961      return this.then(null, callback);
14962    },
14963
14964    "finally": function(callback, progressBack) {
14965      return this.then(function(value) {
14966        return handleCallback(value, true, callback);
14967      }, function(error) {
14968        return handleCallback(error, false, callback);
14969      }, progressBack);
14970    }
14971  });
14972
14973  //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
14974  function simpleBind(context, fn) {
14975    return function(value) {
14976      fn.call(context, value);
14977    };
14978  }
14979
14980  function processQueue(state) {
14981    var fn, deferred, pending;
14982
14983    pending = state.pending;
14984    state.processScheduled = false;
14985    state.pending = undefined;
14986    for (var i = 0, ii = pending.length; i < ii; ++i) {
14987      deferred = pending[i][0];
14988      fn = pending[i][state.status];
14989      try {
14990        if (isFunction(fn)) {
14991          deferred.resolve(fn(state.value));
14992        } else if (state.status === 1) {
14993          deferred.resolve(state.value);
14994        } else {
14995          deferred.reject(state.value);
14996        }
14997      } catch (e) {
14998        deferred.reject(e);
14999        exceptionHandler(e);
15000      }
15001    }
15002  }
15003
15004  function scheduleProcessQueue(state) {
15005    if (state.processScheduled || !state.pending) return;
15006    state.processScheduled = true;
15007    nextTick(function() { processQueue(state); });
15008  }
15009
15010  function Deferred() {
15011    this.promise = new Promise();
15012    //Necessary to support unbound execution :/
15013    this.resolve = simpleBind(this, this.resolve);
15014    this.reject = simpleBind(this, this.reject);
15015    this.notify = simpleBind(this, this.notify);
15016  }
15017
15018  extend(Deferred.prototype, {
15019    resolve: function(val) {
15020      if (this.promise.$$state.status) return;
15021      if (val === this.promise) {
15022        this.$$reject($qMinErr(
15023          'qcycle',
15024          "Expected promise to be resolved with value other than 
15024itself '{0}'",
15025          val));
15026      } else {
15027        this.$$resolve(val);
15028      }
15029
15030    },
15031
15032    $$resolve: function(val) {
15033      var then, fns;
15034
15035      fns = callOnce(this, this.$$resolve, this.$$reject);
15036      try {
15037        if ((isObject(val) || isFunction(val))) then = val && val.then;
15038        if (isFunction(then)) {
15039          this.promise.$$state.status = -1;
15040          then.call(val, fns[0], fns[1], this.notify);
15041        } else {
15042          this.promise.$$state.value = val;
15043          this.promise.$$state.status = 1;
15044          scheduleProcessQueue(this.promise.$$state);
15045        }
15046      } catch (e) {
15047        fns[1](e);
15048        exceptionHandler(e);
15049      }
15050    },
15051
15052    reject: function(reason) {
15053      if (this.promise.$$state.status) return;
15054      this.$$reject(reason);
15055    },
15056
15057    $$reject: function(reason) {
15058      this.promise.$$state.value = reason;
15059      this.promise.$$state.status = 2;
15060      scheduleProcessQueue(this.promise.$$state);
15061    },
15062
15063    notify: function(progress) {
15064      var callbacks = this.promise.$$state.pending;
15065
15066      if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
15067        nextTick(function() {
15068          var callback, result;
15069          for (var i = 0, ii = callbacks.length; i < ii; i++) {
15070            result = callbacks[i][0];
15071            callback = callbacks[i][3];
15072            try {
15073              result.notify(isFunction(callback) ? callback(progress) : progress);
15074            } catch (e) {
15075              exceptionHandler(e);
15076            }
15077          }
15078        });
15079      }
15080    }
15081  });
15082
15083  /**
15084   * @ngdoc method
15085   * @name $q#reject
15086   * @kind function
15087   *
15088   * @description
15089   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
15090   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
15091   * a promise chain, you don't need to worry about it.
15092   *
15093   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
15094   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
15095   * a promise error callback and you want to forward the error to the promise derived from the
15096   * current promise, you have to "rethrow" the error by returning a rejection constructed via
15097   * `reject`.
15098   *
15099   * ```js
15100   *   promiseB = promiseA.then(function(result) {
15101   *     // success: do something and resolve promiseB
15102   *     //          with the old or a new result
15103   *     return result;
15104   *   }, function(reason) {
15105   *     // error: handle the error if possible and
15106   *     //        resolve promiseB with newPromiseOrValue,
15107   *     //        otherwise forward the rejection to promiseB
15108   *     if (canHandle(reason)) {
15109   *      // handle the error and recover
15110   *      return newPromiseOrValue;
15111   *     }
15112   *     return $q.reject(reason);
15113   *   });
15114   * ```
15115   *
15116   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
15117   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
15118   */
15119  var reject = function(reason) {
15120    var result = new Deferred();
15121    result.reject(reason);
15122    return result.promise;
15123  };
15124
15125  var makePromise = function makePromise(value, resolved) {
15126    var result = new Deferred();
15127    if (resolved) {
15128      result.resolve(value);
15129    } else {
15130      result.reject(value);
15131    }
15132    return result.promise;
15133  };
15134
15135  var handleCallback = function handleCallback(value, isResolved, callback) {
15136    var callbackOutput = null;
15137    try {
15138      if (isFunction(callback)) callbackOutput = callback();
15139    } catch (e) {
15140      return makePromise(e, false);
15141    }
15142    if (isPromiseLike(callbackOutput)) {
15143      return callbackOutput.then(function() {
15144        return makePromise(value, isResolved);
15145      }, function(error) {
15146        return makePromise(error, false);
15147      });
15148    } else {
15149      return makePromise(value, isResolved);
15150    }
15151  };
15152
15153  /**
15154   * @ngdoc method
15155   * @name $q#when
15156   * @kind function
15157   *
15158   * @description
15159   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
15160   * This is useful when you are dealing with an object that might or might not be a promise, or if
15161   * the promise comes from a source that can't be trusted.
15162   *
15163   * @param {*} value Value or a promise
15164   * @param {Function=} successCallback
15165   * @param {Function=} errorCallback
15166   * @param {Function=} progressCallback
15167   * @returns {Promise} Returns a promise of the passed value or promise
15168   */
15169
15170
15171  var when = function(value, callback, errback, progressBack) {
15172    var result = new Deferred();
15173    result.resolve(value);
15174    return result.promise.then(callback, errback, progressBack);
15175  };
15176
15177  /**
15178   * @ngdoc method
15179   * @name $q#resolve
15180   * @kind function
15181   *
15182   * @description
15183   * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
15184   *
15185   * @param {*} value Value or a promise
15186   * @param {Function=} successCallback
15187   * @param {Function=} errorCallback
15188   * @param {Function=} progressCallback
15189   * @returns {Promise} Returns a promise of the passed value or promise
15190   */
15191  var resolve = when;
15192
15193  /**
15194   * @ngdoc method
15195   * @name $q#all
15196   * @kind function
15197   *
15198   * @description
15199   * Combines multiple promises into a single promise that is resolved when all of the input
15200   * promises are resolved.
15201   *
15202   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15203   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15204   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
15205   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
15206   *   with the same rejection value.
15207   */
15208
15209  function all(promises) {
15210    var deferred = new Deferred(),
15211        counter = 0,
15212        results = isArray(promises) ? [] : {};
15213
15214    forEach(promises, function(promise, key) {
15215      counter++;
15216      when(promise).then(function(value) {
15217        if (results.hasOwnProperty(key)) return;
15218        results[key] = value;
15219        if (!(--counter)) deferred.resolve(results);
15220      }, function(reason) {
15221        if (results.hasOwnProperty(key)) return;
15222        deferred.reject(reason);
15223      });
15224    });
15225
15226    if (counter === 0) {
15227      deferred.resolve(results);
15228    }
15229
15230    return deferred.promise;
15231  }
15232
15233  var $Q = function Q(resolver) {
15234    if (!isFunction(resolver)) {
15235      throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
15236    }
15237
15238    if (!(this instanceof Q)) {
15239      // More useful when $Q is the Promise itself.
15240      return new Q(resolver);
15241    }
15242
15243    var deferred = new Deferred();
15244
15245    function resolveFn(value) {
15246      deferred.resolve(value);
15247    }
15248
15249    function rejectFn(reason) {
15250      deferred.reject(reason);
15251    }
15252
15253    resolver(resolveFn, rejectFn);
15254
15255    return deferred.promise;
15256  };
15257
15258  $Q.defer = defer;
15259  $Q.reject = reject;
15260  $Q.when = when;
15261  $Q.resolve = resolve;
15262  $Q.all = all;
15263
15264  return $Q;
15265}
15266
15267function $$RAFProvider() { //rAF
15268  this.$get = ['$window', '$timeout', function($window, $timeout) {
15269    var requestAnimationFrame = $window.requestAnimationFrame ||
15270                                $window.webkitRequestAnimationFrame;
15271
15272    var cancelAnimationFrame = $window.cancelAnimationFrame ||
15273                               $window.webkitCancelAnimationFrame ||
15274                               $window.webkitCancelRequestAnimationFrame;
15275
15276    var rafSupported = !!requestAnimationFrame;
15277    var raf = rafSupported
15278      ? function(fn) {
15279          var id = requestAnimationFrame(fn);
15280          return function() {
15281            cancelAnimationFrame(id);
15282          };
15283        }
15284      : function(fn) {
15285          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15286          return function() {
15287            $timeout.cancel(timer);
15288          };
15289        };
15290
15291    raf.supported = rafSupported;
15292
15293    return raf;
15294  }];
15295}
15296
15297/**
15298 * DESIGN NOTES
15299 *
15300 * The design decisions behind the scope are heavily favored for speed and memory consumption.
15301 *
15302 * The typical use of scope is to watch the expressions, which most of the time return the same
15303 * value as last time so we optimize the operation.
15304 *
15305 * Closures construction is expensive in terms of speed as well as memory:
15306 *   - No closures, instead use prototypical inheritance for API
15307 *   - Internal state needs to be stored on scope directly, which means that private state is
15308 *     exposed as $$____ properties
15309 *
15310 * Loop operations are optimized by using while(count--) { ... }
15311 *   - This means that in order to keep the same order of execution as addition we have to add
15312 *     items to the array at the beginning (unshift) instead of at the end (push)
15313 *
15314 * Child scopes are created and removed often
15315 *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
15316 *
15317 * There are fewer watches than observers. This is why you don't want the observer to be implemented
15318 * in the same way as watch. Watch requires return of the initialization function which is expensive
15319 * to construct.
15320 */
15321
15322
15323/**
15324 * @ngdoc provider
15325 * @name $rootScopeProvider
15326 * @description
15327 *
15328 * Provider for the $rootScope service.
15329 */
15330
15331/**
15332 * @ngdoc method
15333 * @name $rootScopeProvider#digestTtl
15334 * @description
15335 *
15336 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15337 * assuming that the model is unstable.
15338 *
15339 * The current default is 10 iterations.
15340 *
15341 * In complex applications it's possible that the dependencies between `$watch`s will result in
15342 * several digest iterations. However if an application needs more than the default 10 digest
15343 * iterations for its model to stabilize then you should investigate what is causing the model to
15344 * continuously change during the digest.
15345 *
15346 * Increasing the TTL could have performance implications, so you should not change it without
15347 * proper justification.
15348 *
15349 * @param {number} limit The number of digest iterations.
15350 */
15351
15352
15353/**
15354 * @ngdoc service
15355 * @name $rootScope
15356 * @description
15357 *
15358 * Every application has a single root {@link ng.$rootScope.Scope scope}.
15359 * All other scopes are descendant scopes of the root scope. Scopes provide separation
15360 * between the model and the view, via a mechanism for watching the model for changes.
15361 * They also provide event emission/broadcast and subscription facility. See the
15362 * {@link guide/scope developer guide on scopes}.
15363 */
15364function $RootScopeProvider() {
15365  var TTL = 10;
15366  var $rootScopeMinErr = minErr('$rootScope');
15367  var lastDirtyWatch = null;
15368  var applyAsyncId = null;
15369
15370  this.digestTtl = function(value) {
15371    if (arguments.length) {
15372      TTL = value;
15373    }
15374    return TTL;
15375  };
15376
15377  function createChildScopeClass(parent) {
15378    function ChildScope() {
15379      this.$$watchers = this.$$nextSibling =
15380          this.$$childHead = this.$$childTail = null;
15381      this.$$listeners = {};
15382      this.$$listenerCount = {};
15383      this.$$watchersCount = 0;
15384      this.$id = nextUid();
15385      this.$$ChildScope = null;
15386    }
15387    ChildScope.prototype = parent;
15388    return ChildScope;
15389  }
15390
15391  this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
15392      function($injector, $exceptionHandler, $parse, $browser) {
15393
15394    function destroyChildScope($event) {
15395        $event.currentScope.$$destroyed = true;
15396    }
15397
15398    function cleanUpScope($scope) {
15399
15400      if (msie === 9) {
15401        // There is a memory leak in IE9 if all child scopes are not disconnected
15402        // completely when a scope is destroyed. So this code will recurse up through
15403        // all this scopes children
15404        //
15405        // See issue https://github.com/angular/angular.js/issues/10706
15406        $scope.$$childHead && cleanUpScope($scope.$$childHead);
15407        $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
15408      }
15409
15410      // The code below works around IE9 and V8's memory leaks
15411      //
15412      // See:
15413      // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15414      // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15415      // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15416
15417      $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
15418          $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
15419    }
15420
15421    /**
15422     * @ngdoc type
15423     * @name $rootScope.Scope
15424     *
15425     * @description
15426     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15427     * {@link auto.$injector $injector}. Child scopes are created using the
15428     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15429     * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15430     * an in-depth introduction and usage examples.
15431     *
15432     *
15433     * # Inheritance
15434     * A scope can inherit from a parent scope, as in this example:
15435     * ```js
15436         var parent = $rootScope;
15437         var child = parent.$new();
15438
15439         parent.salutation = "Hello";
15440         expect(child.salutation).toEqual('Hello');
15441
15442         child.salutation = "Welcome";
15443         expect(child.salutation).toEqual('Welcome');
15444         expect(parent.salutation).toEqual('Hello');
15445     * ```
15446     *
15447     * When interacting with `Scope` in tests, additional helper methods are available on the
15448     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15449     * details.
15450     *
15451     *
15452     * @param {Object.<string, function()>=} providers Map of service factory which need to be
15453     *                                       provided for the current scope. Defaults to {@link ng}.
15454     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15455     *                              append/override services provided by `providers`. This is handy
15456     *                              when unit-testing and having the need to override a default
15457     *                              service.
15458     * @returns {Object} Newly created scope.
15459     *
15460     */
15461    function Scope() {
15462      this.$id = nextUid();
15463      this.$$phase = this.$parent = this.$$watchers =
15464                     this.$$nextSibling = this.$$prevSibling =
15465                     this.$$childHead = this.$$childTail = null;
15466      this.$root = this;
15467      this.$$destroyed = false;
15468      this.$$listeners = {};
15469      this.$$listenerCount = {};
15470      this.$$watchersCount = 0;
15471      this.$$isolateBindings = null;
15472    }
15473
15474    /**
15475     * @ngdoc property
15476     * @name $rootScope.Scope#$id
15477     *
15478     * @description
15479     * Unique scope ID (monotonically increasing) useful for debugging.
15480     */
15481
15482     /**
15483      * @ngdoc property
15484      * @name $rootScope.Scope#$parent
15485      *
15486      * @description
15487      * Reference to the parent scope.
15488      */
15489
15490      /**
15491       * @ngdoc property
15492       * @name $rootScope.Scope#$root
15493       *
15494       * @description
15495       * Reference to the root scope.
15496       */
15497
15498    Scope.prototype = {
15499      constructor: Scope,
15500      /**
15501       * @ngdoc method
15502       * @name $rootScope.Scope#$new
15503       * @kind function
15504       *
15505       * @description
15506       * Creates a new child {@link ng.$rootScope.Scope scope}.
15507       *
15508       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15509       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15510       *
15511       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15512       * desired for the scope and its child scopes to be permanently detached from the parent and
15513       * thus stop participating in model change detection and listener notification by invoking.
15514       *
15515       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15516       *         parent scope. The scope is isolated, as it can not see parent scope properties.
15517       *         When creating widgets, it is useful for the widget to not accidentally read parent
15518       *         state.
15519       *
15520       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15521       *                              of the newly created scope. Defaults to `this` scope if not provided.
15522       *                              This is used when creating a transclude scope to correctly place it
15523       *                              in the scope hierarchy while maintaining the correct prototypical
15524       *                              inheritance.
15525       *
15526       * @returns {Object} The newly created child scope.
15527       *
15528       */
15529      $new: function(isolate, parent) {
15530        var child;
15531
15532        parent = parent || this;
15533
15534        if (isolate) {
15535          child = new Scope();
15536          child.$root = this.$root;
15537        } else {
15538          // Only create a child scope class if somebody asks for one,
15539          // but cache it to allow the VM to optimize lookups.
15540          if (!this.$$ChildScope) {
15541            this.$$ChildScope = createChildScopeClass(this);
15542          }
15543          child = new this.$$ChildScope();
15544        }
15545        child.$parent = parent;
15546        child.$$prevSibling = parent.$$childTail;
15547        if (parent.$$childHead) {
15548          parent.$$childTail.$$nextSibling = child;
15549          parent.$$childTail = child;
15550        } else {
15551          parent.$$childHead = parent.$$childTail = child;
15552        }
15553
15554        // When the new scope is not isolated or we inherit from `this`, and
15555        // the parent scope is destroyed, the property `$$destroyed` is inherited
15556        // prototypically. In all other cases, this property needs to be set
15557        // when the parent scope is destroyed.
15558        // The listener needs to be added after the parent is set
15559        if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15560
15561        return child;
15562      },
15563
15564      /**
15565       * @ngdoc method
15566       * @name $rootScope.Scope#$watch
15567       * @kind function
15568       *
15569       * @description
15570       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15571       *
15572       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15573       *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
15574       *   its value when executed multiple times with the same input because it may be executed multiple
15575       *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15576       *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15577       * - The `listener` is called only when the value from the current `watchExpression` and the
15578       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
15579       *   see below). Inequality is determined according to reference inequality,
15580       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15581       *    via the `!==` Javascript operator, unless `objectEquality == true`
15582       *   (see next point)
15583       * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15584       *   according to the {@link angular.equals} function. To save the value of the object for
15585       *   later comparison, the {@link angular.copy} function is used. This therefore means that
15586       *   watching complex objects will have adverse memory and performance implications.
15587       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15588       *   This is achieved by rerunning the watchers until no changes are detected. The rerun
15589       *   iteration limit is 10 to prevent an infinite loop deadlock.
15590       *
15591       *
15592       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15593       * you can register a `watchExpression` function with no `listener`. (Be prepared for
15594       * multiple calls to your `watchExpression` because it will execute multiple times in a
15595       * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
15596       *
15597       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15598       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15599       * watcher. In rare cases, this is undesirable because the listener is called when the result
15600       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15601       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15602       * listener was called due to initialization.
15603       *
15604       *
15605       *
15606       * # Example
15607       * ```js
15608           // let's assume that scope was dependency injected as the $rootScope
15609           var scope = $rootScope;
15610           scope.name = 'misko';
15611           scope.counter = 0;
15612
15613           expect(scope.counter).toEqual(0);
15614           scope.$watch('name', function(newValue, oldValue) {
15615             scope.counter = scope.counter + 1;
15616           });
15617           expect(scope.counter).toEqual(0);
15618
15619           scope.$digest();
15620           // the listener is always called during the first $digest loop after it was registered
15621           expect(scope.counter).toEqual(1);
15622
15623           scope.$digest();
15624           // but now it will not be called unless the value changes
15625           expect(scope.counter).toEqual(1);
15626
15627           scope.name = 'adam';
15628           scope.$digest();
15629           expect(scope.counter).toEqual(2);
15630
15631
15632
15633           // Using a function as a watchExpression
15634           var food;
15635           scope.foodCounter = 0;
15636           expect(scope.foodCounter).toEqual(0);
15637           scope.$watch(
15638             // This function returns the value being watched. It is called for each turn of the $digest loop
15639             function() { return food; },
15640             // This is the change listener, called when the value returned from the above function changes
15641             function(newValue, oldValue) {
15642               if ( newValue !== oldValue ) {
15643                 // Only increment the counter if the value changed
15644                 scope.foodCounter = scope.foodCounter + 1;
15645               }
15646             }
15647           );
15648           // No digest has been run so the counter will be zero
15649           expect(scope.foodCounter).toEqual(0);
15650
15651           // Run the digest but since food has not changed count will still be zero
15652           scope.$digest();
15653           expect(scope.foodCounter).toEqual(0);
15654
15655           // Update food and run digest.  Now the counter will increment
15656           food = 'cheeseburger';
15657           scope.$digest();
15658           expect(scope.foodCounter).toEqual(1);
15659
15660       * ```
15661       *
15662       *
15663       *
15664       * @param {(function()|string)} watchExpression Expression that is evaluated on each
15665       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15666       *    a call to the `listener`.
15667       *
15668       *    - `string`: Evaluated as {@link guide/expression expression}
15669       *    - `function(scope)`: called with current `scope` as a parameter.
15670       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15671       *    of `watchExpression` changes.
15672       *
15673       *    - `newVal` contains the current value of the `watchExpression`
15674       *    - `oldVal` contains the previous value of the `watchExpression`
15675       *    - `scope` refers to the current scope
15676       * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15677       *     comparing for reference equality.
15678       * @returns {function()} Returns a deregistration function for this listener.
15679       */
15680      $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15681        var get = $parse(watchExp);
15682
15683        if (get.$$watchDelegate) {
15684          return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15685        }
15686        var scope = this,
15687            array = scope.$$watchers,
15688            watcher = {
15689              fn: listener,
15690              last: initWatchVal,
15691              get: get,
15692              exp: prettyPrintExpression || watchExp,
15693              eq: !!objectEquality
15694            };
15695
15696        lastDirtyWatch = null;
15697
15698        if (!isFunction(listener)) {
15699          watcher.fn = noop;
15700        }
15701
15702        if (!array) {
15703          array = scope.$$watchers = [];
15704        }
15705        // we use unshift since we use a while loop in $digest for speed.
15706        // the while loop reads in reverse order.
15707        array.unshift(watcher);
15708        incrementWatchersCount(this, 1);
15709
15710        return function deregisterWatch() {
15711          if (arrayRemove(array, watcher) >= 0) {
15712            incrementWatchersCount(scope, -1);
15713          }
15714          lastDirtyWatch = null;
15715        };
15716      },
15717
15718      /**
15719       * @ngdoc method
15720       * @name $rootScope.Scope#$watchGroup
15721       * @kind function
15722       *
15723       * @description
15724       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15725       * If any one expression in the collection changes the `listener` is executed.
15726       *
15727       * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15728       *   call to $digest() to see if any items changes.
15729       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15730       *
15731       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15732       * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15733       *
15734       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15735       *    expression in `watchExpressions` changes
15736       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15737       *    those of `watchExpression`
15738       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15739       *    those of `watchExpression`
15740       *    The `scope` refers to the current scope.
15741       * @returns {function()} Returns a de-registration function for all listeners.
15742       */
15743      $watchGroup: function(watchExpressions, listener) {
15744        var oldValues = new Array(watchExpressions.length);
15745        var newValues = new Array(watchExpressions.length);
15746        var deregisterFns = [];
15747        var self = this;
15748        var changeReactionScheduled = false;
vendor: 10,192 bytes, lines 15749-16011
15749        var firstRun = true;
15750
15751        if (!watchExpressions.length) {
15752          // No expressions means we call the listener ASAP
15753          var shouldCall = true;
15754          self.$evalAsync(function() {
15755            if (shouldCall) listener(newValues, newValues, self);
15756          });
15757          return function deregisterWatchGroup() {
15758            shouldCall = false;
15759          };
15760        }
15761
15762        if (watchExpressions.length === 1) {
15763          // Special case size of one
15764          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15765            newValues[0] = value;
15766            oldValues[0] = oldValue;
15767            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15768          });
15769        }
15770
15771        forEach(watchExpressions, function(expr, i) {
15772          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15773            newValues[i] = value;
15774            oldValues[i] = oldValue;
15775            if (!changeReactionScheduled) {
15776              changeReactionScheduled = true;
15777              self.$evalAsync(watchGroupAction);
15778            }
15779          });
15780          deregisterFns.push(unwatchFn);
15781        });
15782
15783        function watchGroupAction() {
15784          changeReactionScheduled = false;
15785
15786          if (firstRun) {
15787            firstRun = false;
15788            listener(newValues, newValues, self);
15789          } else {
15790            listener(newValues, oldValues, self);
15791          }
15792        }
15793
15794        return function deregisterWatchGroup() {
15795          while (deregisterFns.length) {
15796            deregisterFns.shift()();
15797          }
15798        };
15799      },
15800
15801
15802      /**
15803       * @ngdoc method
15804       * @name $rootScope.Scope#$watchCollection
15805       * @kind function
15806       *
15807       * @description
15808       * Shallow watches the properties of an object and fires whenever any of the properties change
15809       * (for arrays, this implies watching the array items; for object maps, this implies watching
15810       * the properties). If a change is detected, the `listener` callback is fired.
15811       *
15812       * - The `obj` collection is observed via standard $watch operation and is examined on every
15813       *   call to $digest() to see if any items have been added, removed, or moved.
15814       * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15815       *   adding, removing, and moving items belonging to an object or array.
15816       *
15817       *
15818       * # Example
15819       * ```js
15820          $scope.names = ['igor', 'matias', 'misko', 'james'];
15821          $scope.dataCount = 4;
15822
15823          $scope.$watchCollection('names', function(newNames, oldNames) {
15824            $scope.dataCount = newNames.length;
15825          });
15826
15827          expect($scope.dataCount).toEqual(4);
15828          $scope.$digest();
15829
15830          //still at 4 ... no changes
15831          expect($scope.dataCount).toEqual(4);
15832
15833          $scope.names.pop();
15834          $scope.$digest();
15835
15836          //now there's been a change
15837          expect($scope.dataCount).toEqual(3);
15838       * ```
15839       *
15840       *
15841       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15842       *    expression value should evaluate to an object or an array which is observed on each
15843       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15844       *    collection will trigger a call to the `listener`.
15845       *
15846       * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15847       *    when a change is detected.
15848       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
15849       *    - The `oldCollection` object is a copy of the former collection data.
15850       *      Due to performance considerations, the`oldCollection` value is computed only if the
15851       *      `listener` function declares two or more arguments.
15852       *    - The `scope` argument refers to the current scope.
15853       *
15854       * @returns {function()} Returns a de-registration function for this listener. When the
15855       *    de-registration function is executed, the internal watch operation is terminated.
15856       */
15857      $watchCollection: function(obj, listener) {
15858        $watchCollectionInterceptor.$stateful = true;
15859
15860        var self = this;
15861        // the current value, updated on each dirty-check run
15862        var newValue;
15863        // a shallow copy of the newValue from the last dirty-check run,
15864        // updated to match newValue during dirty-check run
15865        var oldValue;
15866        // a shallow copy of the newValue from when the last change happened
15867        var veryOldValue;
15868        // only track veryOldValue if the listener is asking for it
15869        var trackVeryOldValue = (listener.length > 1);
15870        var changeDetected = 0;
15871        var changeDetector = $parse(obj, $watchCollectionInterceptor);
15872        var internalArray = [];
15873        var internalObject = {};
15874        var initRun = true;
15875        var oldLength = 0;
15876
15877        function $watchCollectionInterceptor(_value) {
15878          newValue = _value;
15879          var newLength, key, bothNaN, newItem, oldItem;
15880
15881          // If the new value is undefined, then return undefined as the watch may be a one-time watch
15882          if (isUndefined(newValue)) return;
15883
15884          if (!isObject(newValue)) { // if primitive
15885            if (oldValue !== newValue) {
15886              oldValue = newValue;
15887              changeDetected++;
15888            }
15889          } else if (isArrayLike(newValue)) {
15890            if (oldValue !== internalArray) {
15891              // we are transitioning from something which was not an array into array.
15892              oldValue = internalArray;
15893              oldLength = oldValue.length = 0;
15894              changeDetected++;
15895            }
15896
15897            newLength = newValue.length;
15898
15899            if (oldLength !== newLength) {
15900              // if lengths do not match we need to trigger change notification
15901              changeDetected++;
15902              oldValue.length = oldLength = newLength;
15903            }
15904            // copy the items to oldValue and look for changes.
15905            for (var i = 0; i < newLength; i++) {
15906              oldItem = oldValue[i];
15907              newItem = newValue[i];
15908
15909              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15910              if (!bothNaN && (oldItem !== newItem)) {
15911                changeDetected++;
15912                oldValue[i] = newItem;
15913              }
15914            }
15915          } else {
15916            if (oldValue !== internalObject) {
15917              // we are transitioning from something which was not an object into object.
15918              oldValue = internalObject = {};
15919              oldLength = 0;
15920              changeDetected++;
15921            }
15922            // copy the items to oldValue and look for changes.
15923            newLength = 0;
15924            for (key in newValue) {
15925              if (hasOwnProperty.call(newValue, key)) {
15926                newLength++;
15927                newItem = newValue[key];
15928                oldItem = oldValue[key];
15929
15930                if (key in oldValue) {
15931                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15932                  if (!bothNaN && (oldItem !== newItem)) {
15933                    changeDetected++;
15934                    oldValue[key] = newItem;
15935                  }
15936                } else {
15937                  oldLength++;
15938                  oldValue[key] = newItem;
15939                  changeDetected++;
15940                }
15941              }
15942            }
15943            if (oldLength > newLength) {
15944              // we used to have more keys, need to find them and destroy them.
15945              changeDetected++;
15946              for (key in oldValue) {
15947                if (!hasOwnProperty.call(newValue, key)) {
15948                  oldLength--;
15949                  delete oldValue[key];
15950                }
15951              }
15952            }
15953          }
15954          return changeDetected;
15955        }
15956
15957        function $watchCollectionAction() {
15958          if (initRun) {
15959            initRun = false;
15960            listener(newValue, newValue, self);
15961          } else {
15962            listener(newValue, veryOldValue, self);
15963          }
15964
15965          // make a copy for the next time a collection is changed
15966          if (trackVeryOldValue) {
15967            if (!isObject(newValue)) {
15968              //primitive
15969              veryOldValue = newValue;
15970            } else if (isArrayLike(newValue)) {
15971              veryOldValue = new Array(newValue.length);
15972              for (var i = 0; i < newValue.length; i++) {
15973                veryOldValue[i] = newValue[i];
15974              }
15975            } else { // if object
15976              veryOldValue = {};
15977              for (var key in newValue) {
15978                if (hasOwnProperty.call(newValue, key)) {
15979                  veryOldValue[key] = newValue[key];
15980                }
15981              }
15982            }
15983          }
15984        }
15985
15986        return this.$watch(changeDetector, $watchCollectionAction);
15987      },
15988
15989      /**
15990       * @ngdoc method
15991       * @name $rootScope.Scope#$digest
15992       * @kind function
15993       *
15994       * @description
15995       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
15996       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
15997       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
15998       * until no more listeners are firing. This means that it is possible to get into an infinite
15999       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
16000       * iterations exceeds 10.
16001       *
16002       * Usually, you don't call `$digest()` directly in
16003       * {@link ng.directive:ngController controllers} or in
16004       * {@link ng.$compileProvider#directive directives}.
16005       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
16006       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
16007       *
16008       * If you want to be notified whenever `$digest()` is called,
16009       * you can register a `watchExpression` function with
16010       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
16011       *
16012       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
16013       *
16014       * # Example
16015       * ```js
16016           var scope = ...;
16017           scope.name = 'misko';
16018           scope.counter = 0;
16019
16020           expect(scope.counter).toEqual(0);
16021           scope.$watch('name', function(newValue, oldValue) {
16022             scope.counter = scope.counter + 1;
16023           });
16024           expect(scope.counter).toEqual(0);
16025
16026           scope.$digest();
16027           // the listener is always called during the first $digest loop after it was registered
16028           expect(scope.counter).toEqual(1);
16029
16030           scope.$digest();
16031           // but now it will not be called unless the value changes
16032           expect(scope.counter).toEqual(1);
16033
16034           scope.name = 'adam';
16035           scope.$digest();
16036           expect(scope.counter).toEqual(2);
16037       * ```
16038       *
16039       */
16040      $digest: function() {
16041        var watch, value, last,
16042            watchers,
16043            length,
16044            dirty, ttl = TTL,
16045            next, current, target = this,
16046            watchLog = [],
16047            logIdx, logMsg, asyncTask;
16048
16049        beginPhase('$digest');
16050        // Check for changes to browser url that happened in sync before the call to $digest
16051        $browser.$$checkUrlChange();
16052
16053        if (this === $rootScope && applyAsyncId !== null) {
16054          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
16055          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
16056          $browser.defer.cancel(applyAsyncId);
16057          flushApplyAsync();
16058        }
16059
16060        lastDirtyWatch = null;
16061
16062        do { // "while dirty" loop
16063          dirty = false;
16064          current = target;
16065
16066          while (asyncQueue.length) {
16067            try {
16068              asyncTask = asyncQueue.shift();
16069              asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
16070            } catch (e) {
16071              $exceptionHandler(e);
16072            }
16073            lastDirtyWatch = null;
16074          }
16075
16076          traverseScopesLoop:
16077          do { // "traverse the scopes" loop
16078            if ((watchers = current.$$watchers)) {
16079              // process our watches
16080              length = watchers.length;
16081              while (length--) {
16082                try {
16083                  watch = watchers[length];
16084                  // Most common watches are on primitives, in which case we can short
16085                  // circuit it with === operator, only when === fails do we use .equals
16086                  if (watch) {
16087                    if ((value = watch.get(current)) !== (last = watch.last) &&
16088                        !(watch.eq
16089                            ? equals(value, last)
16090                            : (typeof value === 'number' && typeof last === 'number'
16091                               && isNaN(value) && isNaN(last)))) {
16092                      dirty = true;
16093                      lastDirtyWatch = watch;
16094                      watch.last = watch.eq ? copy(value, null) : value;
16095                      watch.fn(value, ((last === initWatchVal) ? value : last), current);
16096                      if (ttl < 5) {
16097                        logIdx = 4 - ttl;
16098                        if (!watchLog[logIdx]) watchLog[logIdx] = [];
16099                        watchLog[logIdx].push({
16100                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
16101                          newVal: value,
16102                          oldVal: last
16103                        });
16104                      }
16105                    } else if (watch === lastDirtyWatch) {
16106                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
16107                      // have already been tested.
16108                      dirty = false;
16109                      break traverseScopesLoop;
16110                    }
16111                  }
16112                } catch (e) {
16113                  $exceptionHandler(e);
16114                }
16115              }
16116            }
16117
16118            // Insanity Warning: scope depth-first traversal
16119            // yes, this code is a bit crazy, but it works and we have tests to prove it!
16120            // this piece should be kept in sync with the traversal in $broadcast
16121            if (!(next = ((current.$$watchersCount && current.$$childHead) ||
16122                (current !== target && current.$$nextSibling)))) {
16123              while (current !== target && !(next = current.$$nextSibling)) {
16124                current = current.$parent;
16125              }
16126            }
16127          } while ((current = next));
16128
16129          // `break traverseScopesLoop;` takes us to here
16130
16131          if ((dirty || asyncQueue.length) && !(ttl--)) {
16132            clearPhase();
16133            throw $rootScopeMinErr('infdig',
16134                '{0} $digest() iterations reached. Aborting!\n' +
16135                'Watchers fired in the last 5 iterations: {1}',
16136                TTL, watchLog);
16137          }
16138
16139        }
vendor: 4,632 bytes, lines 16139-16258
16139 while (dirty || asyncQueue.length);
16140
16141        clearPhase();
16142
16143        while (postDigestQueue.length) {
16144          try {
16145            postDigestQueue.shift()();
16146          } catch (e) {
16147            $exceptionHandler(e);
16148          }
16149        }
16150      },
16151
16152
16153      /**
16154       * @ngdoc event
16155       * @name $rootScope.Scope#$destroy
16156       * @eventType broadcast on scope being destroyed
16157       *
16158       * @description
16159       * Broadcasted when a scope and its children are being destroyed.
16160       *
16161       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16162       * clean up DOM bindings before an element is removed from the DOM.
16163       */
16164
16165      /**
16166       * @ngdoc method
16167       * @name $rootScope.Scope#$destroy
16168       * @kind function
16169       *
16170       * @description
16171       * Removes the current scope (and all of its children) from the parent scope. Removal implies
16172       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
16173       * propagate to the current scope and its children. Removal also implies that the current
16174       * scope is eligible for garbage collection.
16175       *
16176       * The `$destroy()` is usually used by directives such as
16177       * {@link ng.directive:ngRepeat ngRepeat} for managing the
16178       * unrolling of the loop.
16179       *
16180       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
16181       * Application code can register a `$destroy` event handler that will give it a chance to
16182       * perform any necessary cleanup.
16183       *
16184       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16185       * clean up DOM bindings before an element is removed from the DOM.
16186       */
16187      $destroy: function() {
16188        // We can't destroy a scope that has been already destroyed.
16189        if (this.$$destroyed) return;
16190        var parent = this.$parent;
16191
16192        this.$broadcast('$destroy');
16193        this.$$destroyed = true;
16194
16195        if (this === $rootScope) {
16196          //Remove handlers attached to window when $rootScope is removed
16197          $browser.$$applicationDestroyed();
16198        }
16199
16200        incrementWatchersCount(this, -this.$$watchersCount);
16201        for (var eventName in this.$$listenerCount) {
16202          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16203        }
16204
16205        // sever all the references to parent scopes (after this cleanup, the current scope should
16206        // not be retained by any of our references and should be eligible for garbage collection)
16207        if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16208        if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16209        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16210        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16211
16212        // Disable listeners, watchers and apply/digest methods
16213        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16214        this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16215        this.$$listeners = {};
16216
16217        // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16218        this.$$nextSibling = null;
16219        cleanUpScope(this);
16220      },
16221
16222      /**
16223       * @ngdoc method
16224       * @name $rootScope.Scope#$eval
16225       * @kind function
16226       *
16227       * @description
16228       * Executes the `expression` on the current scope and returns the result. Any exceptions in
16229       * the expression are propagated (uncaught). This is useful when evaluating Angular
16230       * expressions.
16231       *
16232       * # Example
16233       * ```js
16234           var scope = ng.$rootScope.Scope();
16235           scope.a = 1;
16236           scope.b = 2;
16237
16238           expect(scope.$eval('a+b')).toEqual(3);
16239           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
16240       * ```
16241       *
16242       * @param {(string|function())=} expression An angular expression to be executed.
16243       *
16244       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
16245       *    - `function(scope)`: execute the function with the current `scope` parameter.
16246       *
16247       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16248       * @returns {*} The result of evaluating the expression.
16249       */
16250      $eval: function(expr, locals) {
16251        return $parse(expr)(this, locals);
16252      },
16253
16254      /**
16255       * @ngdoc method
16256       * @name $rootScope.Scope#$evalAsync
16257       * @kind function
16258       *
16259       * @description
16260       * Executes the expression on the current scope at a later point in time.
16261       *
16262       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16263       * that:
16264       *
16265       *   - it will execute after the function that scheduled the evaluation (preferably before DOM
16266       *     rendering).
16267       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16268       *     `expression` execution.
16269       *
16270       * Any exceptions from the execution of the expression are forwarded to the
16271       * {@link ng.$exceptionHandler $exceptionHandler} service.
16272       *
16273       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16274       * will be scheduled. However, it is encouraged to always call code that changes the model
16275       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16276       *
16277       * @param {(string|function())=} expression An angular expression to be executed.
16278       *
16279       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16280       *    - `function(scope)`: execute the function with the current `scope` parameter.
16281       *
16282       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16283       */
16284      $evalAsync: function(expr, locals) {
16285        // if we are outside of an $digest loop and this is the first time we are scheduling async
16286        // task also schedule async auto-flush
16287        if (!$rootScope.$$phase && !asyncQueue.length) {
16288          $browser.defer(function() {
16289            if (asyncQueue.length) {
16290              $rootScope.$digest();
16291            }
16292          });
16293        }
16294
16295        asyncQueue.push({scope: this, expression: expr, locals: locals});
16296      },
16297
16298      $$postDigest: function(fn) {
16299        postDigestQueue.push(fn);
16300      },
16301
16302      /**
16303       * @ngdoc method
16304       * @name $rootScope.Scope#$apply
16305       * @kind function
16306       *
16307       * @description
16308       * `$apply()` is used to execute an expression in angular from outside of the angular
16309       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16310       * Because we are calling into the angular framework we need to perform proper scope life
16311       * cycle of {@link ng.$exceptionHandler exception handling},
16312       * {@link ng.$rootScope.Scope#$digest executing watches}.
16313       *
16314       * ## Life cycle
16315       *
16316       * # Pseudo-Code of `$apply()`
16317       * ```js
16318           function $apply(expr) {
16319             try {
16320               return $eval(expr);
16321             } catch (e) {
16322               $exceptionHandler(e);
16323             } finally {
16324               $root.$digest();
16325             }
16326           }
16327       * ```
16328       *
16329       *
16330       * Scope's `$apply()` method transitions through the following stages:
16331       *
16332       * 1. The {@link guide/expression expression} is executed using the
16333       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
16334       * 2. Any exceptions from the execution of the expression are forwarded to the
16335       *    {@link ng.$exceptionHandler $exceptionHandler} service.
16336       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16337       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16338       *
16339       *
16340       * @param {(string|function())=} exp An angular expression to be executed.
16341       *
16342       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16343       *    - `function(scope)`: execute the function with current `scope` parameter.
16344       *
16345       * @returns {*} The result of evaluating the expression.
16346       */
16347      $apply: function(expr) {
16348        try {
16349          beginPhase('$apply');
16350          try {
16351            return this.$eval(expr);
16352          } finally {
16353            clearPhase();
16354          }
16355        } catch (e) {
16356          $exceptionHandler(e);
16357        } finally {
16358          try {
16359            $rootScope.$digest();
16360          } catch (e) {
16361            $exceptionHandler(e);
16362            throw e;
16363          }
16364        }
16365      },
16366
16367      /**
16368       * @ngdoc method
16369       * @name $rootScope.Scope#$applyAsync
16370       * @kind function
16371       *
16372       * @description
16373       * Schedule the invocation of $apply to occur at a later time. The actual time difference
16374       * varies across browsers, but is typically around ~10 milliseconds.
16375       *
16376       * This can be used to queue up multiple expressions which need to be evaluated in the same
16377       * digest.
16378       *
16379       * @param {(string|function())=} exp An angular expression to be executed.
16380       *
16381       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16382       *    - `function(scope)`: execute the function with current `scope` parameter.
16383       */
16384      $applyAsync: function(expr) {
16385        var scope = this;
16386        expr && applyAsyncQueue.push($applyAsyncExpression);
16387        scheduleApplyAsync();
16388
16389        function $applyAsyncExpression() {
16390          scope.$eval(expr);
16391        }
16392      },
16393
16394      /**
16395       * @ngdoc method
16396       * @name $rootScope.Scope#$on
16397       * @kind function
16398       *
16399       * @description
16400       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16401       * discussion of event life cycle.
16402       *
16403       * The event listener function format is: `function(event, args...)`. The `event` object
16404       * passed into the listener has the following attributes:
16405       *
16406       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16407       *     `$broadcast`-ed.
16408       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16409       *     event propagates through the scope hierarchy, this property is set to null.
16410       *   - `name` - `{string}`: name of the event.
16411       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16412       *     further event propagation (available only for events that were `$emit`-ed).
16413       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16414       *     to true.
16415       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16416       *
16417       * @param {string} name Event name to listen on.
16418       * @param {function(event, ...args)} listener Function to call when the event is emitted.
16419       * @returns {function()} Returns a deregistration function for this listener.
16420       */
16421      $on: function(name, listener) {
16422        var namedListeners = this.$$listeners[name];
16423        if (!namedListeners) {
16424          this.$$listeners[name] = namedListeners = [];
16425        }
16426        namedListeners.push(listener);
16427
16428        var current = this;
16429        do {
16430          if (!current.$$listenerCount[name]) {
16431            current.$$listenerCount[name] = 0;
16432          }
16433          current.$$listenerCount[name]++;
16434        } while ((current = current.$parent));
16435
16436        var self = this;
16437        return function() {
16438          var indexOfListener = namedListeners.indexOf(listener);
16439          if (indexOfListener !== -1) {
16440            namedListeners[indexOfListener] = null;
16441            decrementListenerCount(self, 1, name);
16442          }
16443        };
16444      },
16445
16446
16447      /**
16448       * @ngdoc method
16449       * @name $rootScope.Scope#$emit
16450       * @kind function
16451       *
16452       * @description
16453       * Dispatches an event `name` upwards through the scope hierarchy notifying the
16454       * registered {@link ng.$rootScope.Scope#$on} listeners.
16455       *
16456       * The event life cycle starts at the scope on which `$emit` was called. All
16457       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16458       * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16459       * registered listeners along the way. The event will stop propagating if one of the listeners
16460       * cancels it.
16461       *
16462       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16463       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16464       *
16465       * @param {string} name Event name to emit.
16466       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16467       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16468       */
16469      $emit: function(name, args) {
16470        var empty = [],
16471            namedListeners,
16472            scope = this,
16473            stopPropagation = false,
16474            event = {
16475              name: name,
16476              targetScope: scope,
16477              stopPropagation: function() {stopPropagation = true;},
16478              preventDefault: function() {
16479                event.defaultPrevented = true;
16480              },
16481              defaultPrevented: false
16482            },
16483            listenerArgs = concat([event], arguments, 1),
16484            i, length;
16485
16486        do {
16487          namedListeners = scope.$$listeners[name] || empty;
16488          event.currentScope = scope;
16489          for (i = 0, length = namedListeners.length; i < length; i++) {
16490
16491            // if listeners were deregistered, defragment the array
16492            if (!namedListeners[i]) {
16493              namedListeners.splice(i, 1);
16494              i--;
16495              length--;
16496              continue;
16497            }
16498            try {
16499              //allow all listeners attached to the current scope to run
16500              namedListeners[i].apply(null, listenerArgs);
16501            } catch (e) {
16502              $exceptionHandler(e);
16503            }
16504          }
16505          //if any listener on the current scope stops propagation, prevent bubbling
16506          if (stopPropagation) {
16507            event.currentScope = null;
16508            return event;
16509          }
16510          //traverse upwards
16511          scope = scope.$parent;
16512        } while (scope);
16513
16514        event.currentScope = null;
16515
16516        return event;
16517      },
16518
16519
16520      /**
16521       * @ngdoc method
16522       * @name $rootScope.Scope#$broadcast
16523       * @kind function
16524       *
16525       * @description
16526       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16527       * registered {@link ng.$rootScope.Scope#$on} listeners.
16528       *
16529       * The event life cycle starts at the scope on which `$broadcast` was called. All
16530       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16531       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16532       * scope and calls all registered listeners along the way. The event cannot be canceled.
16533       *
16534       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16535       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16536       *
16537       * @param {string} name Event name to broadcast.
16538       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16539       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16540       */
16541      $broadcast: function(name, args) {
16542        var target = this,
16543            current = target,
16544            next = target,
16545            event = {
16546              name: name,
16547              targetScope: target,
16548              preventDefault: function() {
16549                event.defaultPrevented = true;
16550              },
16551              defaultPrevented: false
16552            };
16553
16554        if (!target.$$listenerCount[name]) return event;
16555
16556        var listenerArgs = concat([event], arguments, 1),
16557            listeners, i, length;
16558
16559        //down while you can, then up and next sibling or up and next sibling until back at root
16560        while ((current = next)) {
16561          event.currentScope = current;
16562          listeners = current.$$listeners[name] || [];
16563          for (i = 0, length = listeners.length; i < length; i++) {
16564            // if listeners were deregistered, defragment the array
16565            if (!listeners[i]) {
16566              listeners.splice(i, 1);
16567              i--;
16568              length--;
16569              continue;
16570            }
16571
16572            try {
16573              listeners[i].apply(null, listenerArgs);
16574            } catch (e) {
16575              $exceptionHandler(e);
16576            }
16577          }
16578
16579          // Insanity Warning: scope depth-first traversal
16580          // yes, this code is a bit crazy, but it works and we have tests to prove it!
16581          // this piece should be kept in sync with the traversal in $digest
16582          // (though it differs due to having the extra check for $$listenerCount)
16583          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16584              (current !== target && current.$$nextSibling)))) {
16585            while (current !== target && !(next = current.$$nextSibling)) {
16586              current = current.$parent;
16587            }
16588          }
16589        }
16590
16591        event.currentScope = null;
16592        return event;
16593      }
16594    };
16595
16596    var $rootScope = new Scope();
16597
16598    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16599    var asyncQueue = $rootScope.$$asyncQueue = [];
16600    var postDigestQueue = $rootScope.$$postDigestQueue = [];
16601    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16602
16603    return $rootScope;
16604
16605
16606    function beginPhase(phase) {
16607      if ($rootScope.$$phase) {
16608        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16609      }
16610
16611      $rootScope.$$phase = phase;
16612    }
16613
16614    function clearPhase() {
16615      $rootScope.$$phase = null;
16616    }
16617
16618    function incrementWatchersCount(current, count) {
16619      do {
16620        current.$$watchersCount += count;
16621      } while ((current = current.$parent));
16622    }
16623
16624    function decrementListenerCount(current, count, name) {
16625      do {
16626        current.$$listenerCount[name] -= count;
16627
16628        if (current.$$listenerCount[name] === 0) {
16629          delete current.$$listenerCount[name];
16630        }
16631      } while ((current = current.$parent));
16632    }
16633
16634    /**
16635     * function used as an initial value for watchers.
16636     * because it's unique we can easily tell it apart from other values
16637     */
16638    function initWatchVal() {}
16639
16640    function flushApplyAsync() {
vendor: 5,358 bytes, lines 16641-16783
16641      while (applyAsyncQueue.length) {
16642        try {
16643          applyAsyncQueue.shift()();
16644        } catch (e) {
16645          $exceptionHandler(e);
16646        }
16647      }
16648      applyAsyncId = null;
16649    }
16650
16651    function scheduleApplyAsync() {
16652      if (applyAsyncId === null) {
16653        applyAsyncId = $browser.defer(function() {
16654          $rootScope.$apply(flushApplyAsync);
16655        });
16656      }
16657    }
16658  }];
16659}
16660
16661/**
16662 * @ngdoc service
16663 * @name $rootElement
16664 *
16665 * @description
16666 * The root element of Angular application. This is either the element where {@link
16667 * ng.directive:ngApp ngApp} was declared or the element passed into
16668 * {@link angular.bootstrap}. The element represents the root element of application. It is also the
16669 * location where the application's {@link auto.$injector $injector} service gets
16670 * published, and can be retrieved using `$rootElement.injector()`.
16671 */
16672
16673
16674// the implementation is in angular.bootstrap
16675
16676/**
16677 * @description
16678 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16679 */
16680function $$SanitizeUriProvider() {
16681  var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16682    imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16683
16684  /**
16685   * @description
16686   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16687   * urls during a[href] sanitization.
16688   *
16689   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16690   *
16691   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16692   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16693   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16694   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16695   *
16696   * @param {RegExp=} regexp New regexp to whitelist urls with.
16697   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16698   *    chaining otherwise.
16699   */
16700  this.aHrefSanitizationWhitelist = function(regexp) {
16701    if (isDefined(regexp)) {
16702      aHrefSanitizationWhitelist = regexp;
16703      return this;
16704    }
16705    return aHrefSanitizationWhitelist;
16706  };
16707
16708
16709  /**
16710   * @description
16711   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16712   * urls during img[src] sanitization.
16713   *
16714   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16715   *
16716   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16717   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16718   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16719   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16720   *
16721   * @param {RegExp=} regexp New regexp to whitelist urls with.
16722   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16723   *    chaining otherwise.
16724   */
16725  this.imgSrcSanitizationWhitelist = function(regexp) {
16726    if (isDefined(regexp)) {
16727      imgSrcSanitizationWhitelist = regexp;
16728      return this;
16729    }
16730    return imgSrcSanitizationWhitelist;
16731  };
16732
16733  this.$get = function() {
16734    return function sanitizeUri(uri, isImage) {
16735      var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16736      var normalizedVal;
16737      normalizedVal = urlResolve(uri).href;
16738      if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16739        return 'unsafe:' + normalizedVal;
16740      }
16741      return uri;
16742    };
16743  };
16744}
16745
16746/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16747 *     Any commits to this file should be reviewed with security in mind.  *
16748 *   Changes to this file can potentially create security vulnerabilities. *
16749 *          An approval from 2 Core members with history of modifying      *
16750 *                         this file is required.                          *
16751 *                                                                         *
16752 *  Does the change somehow allow for arbitrary javascript to be executed? *
16753 *    Or allows for someone to change the prototype of built-in objects?   *
16754 *     Or gives undesired access to variables likes document or window?    *
16755 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16756
16757var $sceMinErr = minErr('$sce');
16758
16759var SCE_CONTEXTS = {
16760  HTML: 'html',
16761  CSS: 'css',
16762  URL: 'url',
16763  // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16764  // url.  (e.g. ng-include, script src, templateUrl)
16765  RESOURCE_URL: 'resourceUrl',
16766  JS: 'js'
16767};
16768
16769// Helper functions follow.
16770
16771function adjustMatcher(matcher) {
16772  if (matcher === 'self') {
16773    return matcher;
16774  } else if (isString(matcher)) {
16775    // Strings match exactly except for 2 wildcards - '*' and '**'.
16776    // '*' matches any character except those from the set ':/.?&'.
16777    // '**' matches any character (like .* in a RegExp).
16778    // More than 2 *'s raises an error as it's ill defined.
16779    if (matcher.indexOf('***') > -1) {
16780      throw $sceMinErr('iwcard',
16781          'Illegal sequence *** in string matcher.  String: {0}', matcher);
16782    }
16783    matcher = escapeForRegexp(matcher).
16784                  replace('\\*\\*', '.*').
16785                  replace('\\*', '[^:/.?&;]*');
16786    return new RegExp('^' + matcher + '$');
16787  } else if (isRegExp(matcher)) {
16788    // The only other type of matcher allowed is a Regexp.
16789    // Match entire URL / disallow partial matches.
16790    // Flags are reset (i.e. no global, ignoreCase or multiline)
16791    return new RegExp('^' + matcher.source + '$');
16792  } else {
16793    throw $sceMinErr('imatcher',
16794        'Matchers may only be "self", string patterns or RegExp objects');
16795  }
16796}
16797
16798
16799function adjustMatchers(matchers) {
16800  var adjustedMatchers = [];
16801  if (isDefined(matchers)) {
16802    forEach(matchers, function(matcher) {
16803      adjustedMatchers.push(adjustMatcher(matcher));
16804    });
16805  }
16806  return adjustedMatchers;
16807}
16808
16809
16810/**
16811 * @ngdoc service
16812 * @name $sceDelegate
16813 * @kind function
16814 *
16815 * @description
16816 *
16817 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16818 * Contextual Escaping (SCE)} services to AngularJS.
16819 *
16820 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16821 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
16822 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16823 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16824 * work because `$sce` delegates to `$sceDelegate` for these operations.
16825 *
16826 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16827 *
16828 * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
16829 * can override it completely to change the behavior of `$sce`, the common case would
16830 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16831 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16832 * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16833 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16834 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16835 */
16836
16837/**
16838 * @ngdoc provider
16839 * @name $sceDelegateProvider
16840 * @description
16841 *
16842 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16843 * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
16844 * that the URLs used for sourcing Angular templates are safe.  Refer {@link
16845 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16846 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16847 *
16848 * For the general details about this service in Angular, read the main page for {@link ng.$sce
16849 * Strict Contextual Escaping (SCE)}.
16850 *
16851 * **Example**:  Consider the following case. <a name="example"></a>
16852 *
16853 * - your app is hosted at url `http://myapp.example.com/`
16854 * - but some of your templates are hosted on other domains you control such as
16855 *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
16856 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16857 *
16858 * Here is what a secure configuration for this scenario might look like:
16859 *
16860 * ```
16861 *  angular.module('myApp', []).config(function($sceDelegateProvider) {
16862 *    $sceDelegateProvider.resourceUrlWhitelist([
16863 *      // Allow same origin resource loads.
16864 *      'self',
16865 *      // Allow loading from our assets domain.  Notice the difference between * and **.
16866 *      'http://srv*.assets.example.com/**'
16867 *    ]);
16868 *
16869 *    // The blacklist overrides the whitelist so the open redirect here is blocked.
16870 *    $sceDelegateProvider.resourceUrlBlacklist([
16871 *      'http://myapp.example.com/clickThru**'
16872 *    ]);
16873 *  });
16874 * ```
16875 */
16876
16877function $SceDelegateProvider() {
16878  this.SCE_CONTEXTS = SCE_CONTEXTS;
16879
16880  // Resource URLs can also be trusted by policy.
16881  var resourceUrlWhitelist = ['self'],
16882      resourceUrlBlacklist = [];
16883
16884  /**
16885   * @ngdoc method
16886   * @name $sceDelegateProvider#resourceUrlWhitelist
16887   * @kind function
16888   *
16889   * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16890   *     provided.  This must be an array or null.  A snapshot of this array is used so further
16891   *     changes to the array are ignored.
16892   *
16893   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16894   *     allowed in this array.
16895   *
16896   *     Note: **an empty whitelist array will block all URLs**!
16897   *
16898   * @return {Array} the currently set whitelist array.
16899   *
16900   * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
16901   * same origin resource requests.
16902   *
16903   * @description
16904   * Sets/Gets the whitelist of trusted resource URLs.
16905   */
16906  this.resourceUrlWhitelist = function(value) {
16907    if (arguments.length) {
16908      resourceUrlWhitelist = adjustMatchers(value);
16909    }
16910    return resourceUrlWhitelist;
16911  };
16912
16913  /**
16914   * @ngdoc method
16915   * @name $sceDelegateProvider#resourceUrlBlacklist
16916   * @kind function
16917   *
16918   * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
16919   *     provided.  This must be an array or null.  A snapshot of this array is used so further
16920   *     changes to the array are ignored.
16921   *
16922   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16923   *     allowed in this array.
16924   *
16925   *     The typical usage for the blacklist is to **block
16926   *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
16927   *     these would otherwise be trusted but actually return content from the redirected domain.
16928   *
16929   *     Finally, **the blacklist overrides the whitelist** and has the final say.
16930   *
16931   * @return {Array} the currently set blacklist array.
16932   *
16933   * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
16934   * is no blacklist.)
16935   *
16936   * @description
16937   * Sets/Gets the blacklist of trusted resource URLs.
16938   */
16939
16940  this.resourceUrlBlacklist = function(value) {
16941    if (arguments.length) {
16942      resourceUrlBlacklist = adjustMatchers(value);
16943    }
16944    return resourceUrlBlacklist;
16945  };
16946
16947  this.$get = ['$injector', function($injector) {
16948
16949    var htmlSanitizer = function htmlSanitizer(html) {
16950      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16951    };
16952
16953    if ($injector.has('$sanitize')) {
16954      htmlSanitizer = $injector.get('$sanitize');
16955    }
16956
16957
16958    function matchUrl(matcher, parsedUrl) {
16959      if (matcher === 'self') {
16960        return urlIsSameOrigin(parsedUrl);
16961      } else {
16962        // definitely a regex.  See adjustMatchers()
16963        return !!matcher.exec(parsedUrl.href);
16964      }
16965    }
16966
16967    function isResourceUrlAllowedByPolicy(url) {
16968      var parsedUrl = urlResolve(url.toString());
16969      var i, n, allowed = false;
16970      // Ensure that at least one item from the whitelist allows this url.
16971      for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
16972        if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
16973          allowed = true;
16974          break;
16975        }
16976      }
16977      if (allowed) {
16978        // Ensure that no item from the blacklist blocked this url.
16979        for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
16980          if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
16981            allowed = false;
16982            break;
16983          }
16984        }
16985      }
16986      return allowed;
16987    }
16988
16989    function generateHolderType(Base) {
16990      var holderType = function TrustedValueHolderType(trustedValue) {
16991        this.$$unwrapTrustedValue = function() {
16992          return trustedValue;
16993        };
16994      };
16995      if (Base) {
16996        holderType.prototype = new Base();
16997      }
16998      holderType.prototype.valueOf = function sceValueOf() {
16999        return this.$$unwrapTrustedValue();
17000      };
17001      holderType.prototype.toString = function sceToString() {
17002        return this.$$unwrapTrustedValue().toString();
17003      };
17004      return holderType;
17005    }
17006
17007    var trustedValueHolderBase = generateHolderType(),
17008        byType = {};
17009
17010    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
17011    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
17012    byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
17013    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
17014    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
17015
17016    /**
17017     * @ngdoc method
17018     * @name $sceDelegate#trustAs
17019     *
17020     * @description
17021     * Returns an object that is trusted by angular for use in specified strict
17022     * contextual escaping contexts (such as ng-bind-html, ng-include, any src
17023     * attribute interpolation, any dom event binding attribute interpolation
17024     * such as for onclick,  etc.) that uses the provided value.
17025     * See {@link ng.$sce $sce} for enabling strict contextual escaping.
17026     *
17027     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17028     *   resourceUrl, html, js and css.
17029     * @param {*} value The value that that should be considered trusted/safe.
17030     * @returns {*} A value that can be used to stand in for the provided `value` in places
17031     * where Angular expects a $sce.trustAs() return value.
17032     */
17033    function trustAs(type, trustedValue) {
17034      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
17035      if (!Constructor) {
17036        throw $sceMinErr('icontext',
17037            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
17038            type, trustedValue);
17039      }
17040      if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
17041        return trustedValue;
17042      }
17043      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
17044      // mutable objects, we ensure here that the value passed in is actually a string.
17045      if (typeof trustedValue !== 'string') {
17046        throw $sceMinErr('itype',
17047            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
17048            type);
17049      }
17050      return new Constructor(trustedValue);
17051    }
17052
17053    /**
17054     * @ngdoc method
17055     * @name $sceDelegate#valueOf
17056     *
17057     * @description
17058     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
17059     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
17060     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
17061     *
17062     * If the passed parameter is not a value that had been returned by {@link
17063     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
17064     *
17065     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
17066     *      call or anything else.
17067     * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
17068     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
17069     *     `value` unchanged.
17070     */
17071    function valueOf(maybeTrusted) {
17072      if (maybeTrusted instanceof trustedValueHolderBase) {
17073        return maybeTrusted.$$unwrapTrustedValue();
17074      } else {
17075        return maybeTrusted;
17076      }
17077    }
17078
17079    /**
17080     * @ngdoc method
17081     * @name $sceDelegate#getTrusted
17082     *
17083     * @description
17084     * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
17085     * returns the originally supplied value if the queried context type is a supertype of the
17086     * created type.  If this condition isn't satisfied, throws an exception.
17087     *
17088     * @param {string} type The kind of context in which this value is to be used.
17089     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
17090     *     `$sceDelegate.trustAs`} call.
17091     * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
17092     *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
17093     */
17094    function getTrusted(type, maybeTrusted) {
17095      if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
17096        return maybeTrusted;
17097      }
17098      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
17099      if (constructor && maybeTrusted instanceof constructor) {
17100        return maybeTrusted.$$unwrapTrustedValue();
17101      }
17102      // If we get here, then we may only take one of two actions.
17103      // 1. sanitize the value for the requested type, or
17104      // 2. throw an exception.
17105      if (type === SCE_CONTEXTS.RESOURCE_URL) {
17106        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
17107          return maybeTrusted;
17108        } else {
17109          throw $sceMinErr('insecurl',
17110              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
17111              maybeTrusted.toString());
17112        }
17113      } else if (type === SCE_CONTEXTS.HTML) {
17114        return htmlSanitizer(maybeTrusted);
17115      }
17116      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
17117    }
17118
17119    return { trustAs: trustAs,
17120             getTrusted: getTrusted,
17121             valueOf: valueOf };
17122  }];
17123}
17124
17125
17126/**
17127 * @ngdoc provider
17128 * @name $sceProvider
17129 * @description
17130 *
17131 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
17132 * -   enable/disable Strict Contextual Escaping (SCE) in a module
17133 * -   override the default implementation with a custom delegate
17134 *
17135 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
17136 */
17137
17138/* jshint maxlen: false*/
17139
17140/**
17141 * @ngdoc service
17142 * @name $sce
17143 * @kind function
17144 *
17145 * @description
17146 *
17147 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
17148 *
17149 * # Strict Contextual Escaping
17150 *
17151 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
17152 * contexts to result in a value that is marked as safe to use for that context.  One example of
17153 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
17154 * to these contexts as privileged or SCE contexts.
17155 *
17156 * As of version 1.2, Angular ships with SCE enabled by default.
17157 *
17158 * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
17159 * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
17160 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
17161 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
17162 * to the top of your HTML document.
17163 *
17164 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
17165 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
17166 *
17167 * Here's an example of a binding in a privileged context:
17168 *
17169 * ```
17170 * <input ng-model="userHtml" aria-label="User input">
17171 * <div ng-bind-html="userHtml"></div>
17172 * ```
17173 *
17174 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
17175 * disabled, this application allows the user to render arbitrary HTML into the DIV.
17176 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
17177 * bindings.  (HTML is just one example of a context where rendering user controlled input creates
17178 * security vulnerabilities.)
17179 *
17180 * For the case of HTML, you might use a library, either on the client side, or on the server side,
17181 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
17182 *
17183 * How would you ensure that every place that used these types of bindings was bound to a value that
17184 * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
17185 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
17186 * properties/fields and forgot to update the binding to the sanitized value?
17187 *
17188 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
17189 * determine that something explicitly says it's safe to use a value for binding in that
17190 * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
17191 * for those values that you can easily tell are safe - because they were received from your server,
17192 * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
17193 * allowing only the files in a specific directory to do this.  Ensuring that the internal API
17194 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
17195 *
17196 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
17197 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
17198 * obtain values that will be accepted by SCE / privileged contexts.
17199 *
17200 *
17201 * ## How does it work?
17202 *
17203 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
17204 * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
17205 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
17206 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
17207 *
17208 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
17209 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
17210 * simplified):
17211 *
17212 * ```
17213 * var ngBindHtmlDirective = ['$sce', function($sce) {
17214 *   return function(scope, element, attr) {
17215 *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17216 *       element.html(value || '');
17217 *     });
17218 *   };
17219 * }];
17220 * ```
17221 *
17222 * ## Impact on loading templates
17223 *
17224 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
17225 * `templateUrl`'s specified by {@link guide/directive directives}.
17226 *
17227 * By default, Angular only loads templates from the same domain and protocol as the application
17228 * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
17229 * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
17230 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17231 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
17232 *
17233 * *Please note*:
17234 * The browser's
17235 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
17236 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
17237 * policy apply in addition to this and may further restrict whether the template is successfully
17238 * loaded.  This means that without the right CORS policy, loading templates from a different domain
17239 * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
17240 * browsers.
17241 *
17242 * ## This feels like too much overhead
17243 *
17244 * It's important to remember that SCE only applies to interpolation expressions.
17245 *
17246 * If your expressions are constant literals, they're automatically trusted and you don't need to
17247 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
17248 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
17249 *
17250 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
17251 * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
17252 *
17253 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
17254 * templates in `ng-include` from your application's domain without having to even know about SCE.
17255 * It blocks loading templates from other domains or loading templates over http from an https
17256 * served document.  You can change these by setting your own custom {@link
17257 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
17258 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
17259 *
17260 * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
17261 * application that's secure and can be audited to verify that with much more ease than bolting
17262 * security onto an application later.
17263 *
17264 * <a name="contexts"></a>
17265 * ## What trusted context types are supported?
17266 *
17267 * | Context             | Notes          |
17268 * |---------------------|----------------|
17269 * | `$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. |
17270 * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
17271 * | `$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. |
17272 * | `$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. |
17273 * | `$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. |
17274 *
17275 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
17276 *
17277 *  Each element in these arrays must be one of the following:
17278 *
17279 *  - **'self'**
17280 *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
17281 *      domain** as the application document using the **same protocol**.
17282 *  - **String** (except the special value `'self'`)
17283 *    - The string is matched against the full *normalized / absolute URL* of the resource
17284 *      being tested (substring matches are not good enough.)
17285 *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
17286 *      match themselves.
17287 *    - `*`: matches zero or more occurrences of any character other than one of the following 6
17288 *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
17289 *      in a whitelist.
17290 *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
17291 *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
17292 *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17293 *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
17294 *      http://foo.example.com/templates/**).
17295 *  - **RegExp** (*see caveat below*)
17296 *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
17297 *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
17298 *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17299 *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
17300 *      small number of cases.  A `.` character in the regex used when matching the scheme or a
17301 *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
17302 *      is highly recommended to use the string patterns and only fall back to regular expressions
17303 *      as a last resort.
17304 *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
17305 *      matched against the **entire** *normalized / absolute URL* of the resource being tested
17306 *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
17307 *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17308 *    - If you are generating your JavaScript from some other templating engine (not
17309 *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17310 *      remember to escape your regular expression (and be aware that you might need more than
17311 *      one level of escaping depending on your templating engine and the way you interpolated
17312 *      the value.)  Do make use of your platform's escaping mechanism as it might be good
17313 *      enough before coding your own.  E.g. Ruby has
17314 *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17315 *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17316 *      Javascript lacks a similar built in function for escaping.  Take a look at Google
17317 *      Closure library's [goog.string.regExpEscape(s)](
17318 *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17319 *
17320 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17321 *
17322 * ## Show me an example using SCE.
17323 *
17324 * <example module="mySceApp" deps="angular-sanitize.js">
17325 * <file name="index.html">
17326 *   <div ng-controller="AppController as myCtrl">
17327 *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17328 *     <b>User comments</b><br>
17329 *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17330 *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
17331 *     exploit.
17332 *     <div class="well">
17333 *       <div ng-repeat="userComment in myCtrl.userComments">
17334 *         <b>{{userComment.name}}</b>:
17335 *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17336 *         <br>
17337 *       </div>
17338 *     </div>
17339 *   </div>
17340 * </file>
17341 *
17342 * <file name="script.js">
17343 *   angular.module('mySceApp', ['ngSanitize'])
17344 *     .controller('AppController', ['$http', '$templateCache', '$sce',
17345 *       function($http, $templateCache, $sce) {
17346 *         var self = this;
17347 *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17348 *           self.userComments = userComments;
17349 *         });
17350 *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
17351 *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17352 *             'sanitization.&quot;">Hover over this text.</span>');
17353 *       }]);
17354 * </file>
17355 *
17356 * <file name="test_data.json">
17357 * [
17358 *   { "name": "Alice",
17359 *     "htmlComment":
17360 *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17361 *   },
17362 *   { "name": "Bob",
17363 *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
17364 *   }
17365 * ]
17366 * </file>
17367 *
17368 * <file name="protractor.js" type="protractor">
17369 *   describe('SCE doc demo', function() {
17370 *     it('should sanitize untrusted values', function() {
17371 *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17372 *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
17373 *     });
17374 *
17375 *     it('should NOT sanitize explicitly trusted values', function() {
17376 *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17377 *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17378 *           'sanitization.&quot;">Hover over this text.</span>');
17379 *     });
17380 *   });
17381 * </file>
17382 * </example>
17383 *
17384 *
17385 *
17386 * ## Can I disable SCE completely?
17387 *
17388 * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
17389 * for little coding overhead.  It will be much harder to take an SCE disabled application and
17390 * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
17391 * for cases where you have a lot of existing code that was written before SCE was introduced and
17392 * you're migrating them a module at a time.
17393 *
17394 * That said, here's how you can completely disable SCE:
17395 *
17396 * ```
17397 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17398 *   // Completely disable SCE.  For demonstration purposes only!
17399 *   // Do not use in new projects.
17400 *   $sceProvider.enabled(false);
17401 * });
17402 * ```
17403 *
17404 */
17405/* jshint maxlen: 100 */
17406
17407function $SceProvider() {
17408  var enabled = true;
17409
17410  /**
17411   * @ngdoc method
17412   * @name $sceProvider#enabled
17413   * @kind function
17414   *
17415   * @param {boolean=} value If provided, then enables/disables SCE.
17416   * @return {boolean} true if SCE is enabled, false otherwise.
17417   *
17418   * @description
17419   * Enables/disables SCE and returns the current value.
17420   */
17421  this.enabled = function(value) {
17422    if (arguments.length) {
17423      enabled = !!value;
17424    }
17425    return enabled;
17426  };
17427
17428
17429  /* Design notes on the default implementation for SCE.
17430   *
17431   * The API contract for the SCE delegate
17432   * -------------------------------------
17433   * The SCE delegate object must provide the following 3 methods:
17434   *
17435   * - trustAs(contextEnum, value)
17436   *     This method is used to tell the SCE service that the provided value is OK to use in the
17437   *     contexts specified by contextEnum.  It must return an object that will be accepted by
17438   *     getTrusted() for a compatible contextEnum and return this value.
17439   *
17440   * - valueOf(value)
17441   *     For values that were not produced by trustAs(), return them as is.  For values that were
17442   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
17443   *     trustAs is wrapping the given values into some type, this operation unwraps it when given
17444   *     such a value.
17445   *
17446   * - getTrusted(contextEnum, value)
17447   *     This function should return the a value that is safe to use in the context specified by
17448   *     contextEnum or throw and exception otherwise.
17449   *
17450   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17451   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
17452   * instance, an implementation could maintain a registry of all trusted objects by context.  In
17453   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
17454   * return the same object passed in if it was found in the registry under a compatible context or
17455   * throw an exception otherwise.  An implementation might only wrap values some of the time based
17456   * on some criteria.  getTrusted() might return a value and not throw an exception for special
17457   * constants or objects even if not wrapped.  All such implementations fulfill this contract.
17458   *
17459   *
17460   * A note on the inheritance model for SCE contexts
17461   * ------------------------------------------------
17462   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
17463   * is purely an implementation details.
17464   *
17465   * The contract is simply this:
17466   *
17467   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17468   *     will also succeed.
17469   *
17470   * Inheritance happens to capture this in a natural way.  In some future, we
17471   * may not use inheritance anymore.  That is OK because no code outside of
17472   * sce.js and sceSpecs.js would need to be aware of this detail.
17473   */
17474
17475  this.$get = ['$parse', '$sceDelegate', function(
17476                $parse,   $sceDelegate) {
17477    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
17478    // the "expression(javascript expression)" syntax which is insecure.
17479    if (enabled && msie < 8) {
17480      throw $sceMinErr('iequirks',
17481        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17482        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17483        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
17484    }
17485
17486    var sce = shallowCopy(SCE_CONTEXTS);
17487
17488    /**
17489     * @ngdoc method
17490     * @name $sce#isEnabled
17491     * @kind function
17492     *
17493     * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
17494     * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17495     *
17496     * @description
17497     * Returns a boolean indicating if SCE is enabled.
17498     */
17499    sce.isEnabled = function() {
17500      return enabled;
17501    };
17502    sce.trustAs = $sceDelegate.trustAs;
17503    sce.getTrusted = $sceDelegate.getTrusted;
17504    sce.valueOf = $sceDelegate.valueOf;
17505
17506    if (!enabled) {
17507      sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17508      sce.valueOf = identity;
17509    }
17510
17511    /**
17512     * @ngdoc method
17513     * @name $sce#parseAs
17514     *
17515     * @description
17516     * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
17517     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
17518     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17519     * *result*)}
17520     *
17521     * @param {string} type The kind of SCE context in which this result will be used.
17522     * @param {string} expression String expression to compile.
17523     * @returns {function(context, locals)} a function which represents the compiled expression:
17524     *
17525     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17526     *      are evaluated against (typically a scope object).
17527     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17528     *      `context`.
17529     */
17530    sce.parseAs = function sceParseAs(type, expr) {
17531      var parsed = $parse(expr);
17532      if (parsed.literal && parsed.constant) {
17533        return parsed;
17534      } else {
17535        return $parse(expr, function(value) {
17536          return sce.getTrusted(type, value);
17537        });
17538      }
17539    };
17540
17541    /**
17542     * @ngdoc method
17543     * @name $sce#trustAs
17544     *
17545     * @description
17546     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
17547     * returns an object that is trusted by angular for use in specified strict contextual
17548     * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17549     * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
17550     * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
17551     * escaping.
17552     *
17553     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17554     *   resourceUrl, html, js and css.
17555     * @param {*} value The value that that should be considered trusted/safe.
17556     * @returns {*} A value that can be used to stand in for the provided `value` in places
17557     * where Angular expects a $sce.trustAs() return value.
17558     */
17559
17560    /**
17561     * @ngdoc method
17562     * @name $sce#trustAsHtml
17563     *
17564     * @description
17565     * Shorthand method.  `$sce.trustAsHtml(value)` →
17566     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17567     *
17568     * @param {*} value The value to trustAs.
17569     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17570     *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
17571     *     only accept expressions that are either literal constants or are the
17572     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17573     */
17574
17575    /**
17576     * @ngdoc method
17577     * @name $sce#trustAsUrl
17578     *
17579     * @description
17580     * Shorthand method.  `$sce.trustAsUrl(value)` →
17581     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17582     *
17583     * @param {*} value The value to trustAs.
17584     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17585     *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
17586     *     only accept expressions that are either literal constants or are the
17587     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17588     */
17589
17590    /**
17591     * @ngdoc method
17592     * @name $sce#trustAsResourceUrl
17593     *
17594     * @description
17595     * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
17596     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17597     *
17598     * @param {*} value The value to trustAs.
17599     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17600     *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
17601     *     only accept expressions that are either literal constants or are the return
17602     *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
17603     */
17604
17605    /**
17606     * @ngdoc method
17607     * @name $sce#trustAsJs
17608     *
17609     * @description
17610     * Shorthand method.  `$sce.trustAsJs(value)` →
17611     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17612     *
17613     * @param {*} value The value to trustAs.
17614     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17615     *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
17616     *     only accept expressions that are either literal constants or are the
17617     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17618     */
17619
17620    /**
17621     * @ngdoc method
17622     * @name $sce#getTrusted
17623     *
17624     * @description
17625     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
17626     * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17627     * originally supplied value if the queried context type is a supertype of the created type.
17628     * If this condition isn't satisfied, throws an exception.
17629     *
17630     * @param {string} type The kind of context in which this value is to be used.
17631     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17632     *                         call.
17633     * @returns {*} The value the was originally provided to
17634     *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17635     *              Otherwise, throws an exception.
17636     */
17637
17638    /**
17639     * @ngdoc method
17640     * @name $sce#getTrustedHtml
17641     *
17642     * @description
17643     * Shorthand method.  `$sce.getTrustedHtml(value)` →
17644     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17645     *
17646     * @param {*} value The value to pass to `$sce.getTrusted`.
17647     * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17648     */
17649
17650    /**
17651     * @ngdoc method
17652     * @name $sce#getTrustedCss
17653     *
17654     * @description
17655     * Shorthand method.  `$sce.getTrustedCss(value)` →
17656     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17657     *
17658     * @param {*} value The value to pass to `$sce.getTrusted`.
17659     * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17660     */
17661
17662    /**
17663     * @ngdoc method
17664     * @name $sce#getTrustedUrl
17665     *
17666     * @description
17667     * Shorthand method.  `$sce.getTrustedUrl(value)` →
17668     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17669     *
17670     * @param {*} value The value to pass to `$sce.getTrusted`.
17671     * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17672     */
17673
17674    /**
17675     * @ngdoc method
17676     * @name $sce#getTrustedResourceUrl
17677     *
17678     * @description
17679     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
17680     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17681     *
17682     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17683     * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17684     */
17685
17686    /**
17687     * @ngdoc method
17688     * @name $sce#getTrustedJs
17689     *
17690     * @description
17691     * Shorthand method.  `$sce.getTrustedJs(value)` →
17692     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17693     *
17694     * @param {*} value The value to pass to `$sce.getTrusted`.
17695     * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17696     */
17697
17698    /**
17699     * @ngdoc method
17700     * @name $sce#parseAsHtml
17701     *
17702     * @description
17703     * Shorthand method.  `$sce.parseAsHtml(expression string)` →
17704     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17705     *
17706     * @param {string} expression String expression to compile.
17707     * @returns {function(context, locals)} a function which represents the compiled expression:
17708     *
17709     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17710     *      are evaluated against (typically a scope object).
17711     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17712     *      `context`.
17713     */
17714
17715    /**
17716     * @ngdoc method
17717     * @name $sce#parseAsCss
17718     *
17719     * @description
17720     * Shorthand method.  `$sce.parseAsCss(value)` →
17721     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17722     *
17723     * @param {string} expression String expression to compile.
17724     * @returns {function(context, locals)} a function which represents the compiled expression:
17725     *
17726     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17727     *      are evaluated against (typically a scope object).
17728     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17729     *      `context`.
17730     */
17731
17732    /**
17733     * @ngdoc method
17734     * @name $sce#parseAsUrl
17735     *
17736     * @description
17737     * Shorthand method.  `$sce.parseAsUrl(value)` →
17738     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17739     *
17740     * @param {string} expression String expression to compile.
17741     * @returns {function(context, locals)} a function which represents the compiled expression:
17742     *
17743     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17744     *      are evaluated against (typically a scope object).
17745     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17746     *      `context`.
17747     */
17748
17749    /**
17750     * @ngdoc method
17751     * @name $sce#parseAsResourceUrl
17752     *
17753     * @description
17754     * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
17755     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17756     *
17757     * @param {string} expression String expression to compile.
17758     * @returns {function(context, locals)} a function which represents the compiled expression:
17759     *
17760     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17761     *      are evaluated against (typically a scope object).
17762     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17763     *      `context`.
17764     */
17765
17766    /**
17767     * @ngdoc method
17768     * @name $sce#parseAsJs
17769     *
17770     * @description
17771     * Shorthand method.  `$sce.parseAsJs(value)` →
17772     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17773     *
17774     * @param {string} expression String expression to compile.
17775     * @returns {function(context, locals)} a function which represents the compiled expression:
17776     *
17777     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17778     *      are evaluated against (typically a scope object).
17779     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17780     *      `context`.
17781     */
17782
17783    // Shorthand delegations.
17784    var parse = sce.parseAs,
17785        getTrusted = sce.getTrusted,
17786        trustAs = sce.trustAs;
17787
17788    forEach(SCE_CONTEXTS, function(enumValue, name) {
17789      var lName = lowercase(name);
17790      sce[camelCase("parse_as_" + lName)] = function(expr) {
17791        return parse(enumValue, expr);
17792      };
17793      sce[camelCase("get_trusted_" + lName)] = function(value) {
17794        return getTrusted(enumValue, value);
17795      };
17796      sce[camelCase("trust_as_" + lName)] = function(value) {
17797        return trustAs(enumValue, value);
17798      };
17799    });
17800
17801    return sce;
17802  }];
17803}
17804
17805/**
17806 * !!! This is an undocumented "private" service !!!
17807 *
17808 * @name $sniffer
17809 * @requires $window
17810 * @requires $document
17811 *
17812 * @property {boolean} history Does the browser support html5 history api ?
17813 * @property {boolean} transitions Does the browser support CSS transition events ?
17814 * @property {boolean} animations Does the browser support CSS animation events ?
17815 *
17816 * @description
17817 * This is very simple implementation of testing browser's features.
17818 */
17819function $SnifferProvider() {
17820  this.$get = ['$window', '$document', function($window, $document) {
17821    var eventSupport = {},
17822        android =
17823          toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17824        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17825        document = $document[0] || {},
17826        vendorPrefix,
17827        vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17828        bodyStyle = document.body && document.body.style,
17829        transitions = false,
17830        animations = false,
17831        match;
17832
17833    if (bodyStyle) {
17834      for (var prop in bodyStyle) {
17835        if (match = vendorRegex.exec(prop)) {
17836          vendorPrefix = match[0];
17837          vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17838          break;
17839        }
17840      }
17841
17842      if (!vendorPrefix) {
17843        vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17844      }
17845
17846      transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17847      animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17848
17849      if (android && (!transitions ||  !animations)) {
17850        transitions = isString(bodyStyle.webkitTransition);
17851        animations = isString(bodyStyle.webkitAnimation);
17852      }
17853    }
17854
17855
17856    return {
17857      // Android has history.pushState, but it does not update location correctly
17858      // so let's not use the history API at all.
17859      // http://code.google.com/p/android/issues/detail?id=17471
17860      // https://github.com/angular/angular.js/issues/904
17861
17862      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17863      // so let's not use the history API also
17864      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17865      // jshint -W018
17866      history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17867      // jshint +W018
17868      hasEvent: function(event) {
17869        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17870        // it. In particular the event is not fired when backspace or delete key are pressed or
17871        // when cut operation is performed.
17872        // IE10+ implements 'input' event but it erroneously fires under various situations,
17873        // e.g. when placeholder changes, or a form is focused.
17874        if (event === 'input' && msie <= 11) return false;
17875
17876        if (isUndefined(eventSupport[event])) {
17877          var divElm = document.createElement('div');
17878          eventSupport[event] = 'on' + event in divElm;
17879        }
17880
17881        return eventSupport[event];
17882      },
17883      csp: csp(),
17884      vendorPrefix: vendorPrefix,
17885      transitions: transitions,
17886      animations: animations,
17887      android: android
17888    };
17889  }];
17890}
17891
17892var $compileMinErr = minErr('$compile');
17893
17894/**
17895 * @ngdoc service
17896 * @name $templateRequest
17897 *
17898 * @description
17899 * The `$templateRequest` service runs security checks then downloads the provided template using
17900 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
17901 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
17902 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
17903 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
17904 * when `tpl` is of type string and `$templateCache` has the matching entry.
17905 *
17906 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
17907 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
17908 *
17909 * @return {Promise} a promise for the HTTP response data of the given URL.
17910 *
17911 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
17912 */
17913function $TemplateRequestProvider() {
17914  this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
17915    function handleRequestFn(tpl, ignoreRequestError) {
17916      handleRequestFn.totalPendingRequests++;
17917
17918      // We consider the template cache holds only trusted templates, so
17919      // there's no need to go through whitelisting again for keys that already
17920      // are included in there. This also makes Angular accept any script
17921      // directive, no matter its name. However, we still need to unwrap trusted
17922      // types.
17923      if (!isString(tpl) || !$templateCache.get(tpl)) {
17924        tpl = $sce.getTrustedResourceUrl(tpl);
17925      }
17926
17927      var transformResponse = $http.defaults && $http.defaults.transformResponse;
17928
17929      if (isArray(transformResponse)) {
17930        transformResponse = transformResponse.filter(function(transformer) {
17931          return transformer !== defaultHttpResponseTransform;
17932        });
17933      } else if (transformResponse === defaultHttpResponseTransform) {
17934        transformResponse = null;
17935      }
17936
17937      var httpOptions = {
17938        cache: $templateCache,
17939        transformResponse: transformResponse
17940      };
17941
17942      return $http.get(tpl, httpOptions)
17943        ['finally'](function() {
17944          handleRequestFn.totalPendingRequests--;
17945        })
17946        .then(function(response) {
17947          $templateCache.put(tpl, response.data);
17948          return response.data;
17949        }, handleError);
17950
17951      function handleError(resp) {
17952        if (!ignoreRequestError) {
17953          throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
17954            tpl, resp.status, resp.statusText);
17955        }
17956        return $q.reject(resp);
17957      }
17958    }
17959
17960    handleRequestFn.totalPendingRequests = 0;
17961
17962    return handleRequestFn;
17963  }];
17964}
17965
vendor: 5,426 bytes, lines 17966-18119
17966function $$TestabilityProvider() {
17967  this.$get = ['$rootScope', '$browser', '$location',
17968       function($rootScope,   $browser,   $location) {
17969
17970    /**
17971     * @name $testability
17972     *
17973     * @description
17974     * The private $$testability service provides a collection of methods for use when debugging
17975     * or by automated test and debugging tools.
17976     */
17977    var testability = {};
17978
17979    /**
17980     * @name $$testability#findBindings
17981     *
17982     * @description
17983     * Returns an array of elements that are bound (via ng-bind or {{}})
17984     * to expressions matching the input.
17985     *
17986     * @param {Element} element The element root to search from.
17987     * @param {string} expression The binding expression to match.
17988     * @param {boolean} opt_exactMatch If true, only returns exact matches
17989     *     for the expression. Filters and whitespace are ignored.
17990     */
17991    testability.findBindings = function(element, expression, opt_exactMatch) {
17992      var bindings = element.getElementsByClassName('ng-binding');
17993      var matches = [];
17994      forEach(bindings, function(binding) {
17995        var dataBinding = angular.element(binding).data('$binding');
17996        if (dataBinding) {
17997          forEach(dataBinding, function(bindingName) {
17998            if (opt_exactMatch) {
17999              var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
18000              if (matcher.test(bindingName)) {
18001                matches.push(binding);
18002              }
18003            } else {
18004              if (bindingName.indexOf(expression) != -1) {
18005                matches.push(binding);
18006              }
18007            }
18008          });
18009        }
18010      });
18011      return matches;
18012    };
18013
18014    /**
18015     * @name $$testability#findModels
18016     *
18017     * @description
18018     * Returns an array of elements that are two-way found via ng-model to
18019     * expressions matching the input.
18020     *
18021     * @param {Element} element The element root to search from.
18022     * @param {string} expression The model expression to match.
18023     * @param {boolean} opt_exactMatch If true, only returns exact matches
18024     *     for the expression.
18025     */
18026    testability.findModels = function(element, expression, opt_exactMatch) {
18027      var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
18028      for (var p = 0; p < prefixes.length; ++p) {
18029        var attributeEquals = opt_exactMatch ? '=' : '*=';
18030        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
18031        var elements = element.querySelectorAll(selector);
18032        if (elements.length) {
18033          return elements;
18034        }
18035      }
18036    };
18037
18038    /**
18039     * @name $$testability#getLocation
18040     *
18041     * @description
18042     * Shortcut for getting the location in a browser agnostic way. Returns
18043     *     the path, search, and hash. (e.g. /path?a=b#hash)
18044     */
18045    testability.getLocation = function() {
18046      return $location.url();
18047    };
18048
18049    /**
18050     * @name $$testability#setLocation
18051     *
18052     * @description
18053     * Shortcut for navigating to a location without doing a full page reload.
18054     *
18055     * @param {string} url The location url (path, search and hash,
18056     *     e.g. /path?a=b#hash) to go to.
18057     */
18058    testability.setLocation = function(url) {
18059      if (url !== $location.url()) {
18060        $location.url(url);
18061        $rootScope.$digest();
18062      }
18063    };
18064
18065    /**
18066     * @name $$testability#whenStable
18067     *
18068     * @description
18069     * Calls the callback when $timeout and $http requests are completed.
18070     *
18071     * @param {function} callback
18072     */
18073    testability.whenStable = function(callback) {
18074      $browser.notifyWhenNoOutstandingRequests(callback);
18075    };
18076
18077    return testability;
18078  }];
18079}
18080
18081function $TimeoutProvider() {
18082  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
18083       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
18084
18085    var deferreds = {};
18086
18087
18088     /**
18089      * @ngdoc service
18090      * @name $timeout
18091      *
18092      * @description
18093      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
18094      * block and delegates any exceptions to
18095      * {@link ng.$exceptionHandler $exceptionHandler} service.
18096      *
18097      * The return value of calling `$timeout` is a promise, which will be resolved when
18098      * the delay has passed and the timeout function, if provided, is executed.
18099      *
18100      * To cancel a timeout request, call `$timeout.cancel(promise)`.
18101      *
18102      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
18103      * synchronously flush the queue of deferred functions.
18104      *
18105      * If you only want a promise that will be resolved after some specified delay
18106      * then you can call `$timeout` without the `fn` function.
18107      *
18108      * @param {function()=} fn A function, whose execution should be delayed.
18109      * @param {number=} [delay=0] Delay in milliseconds.
18110      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
18111      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
18112      * @param {...*=} Pass additional parameters to the executed function.
18113      * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
18114      *   will be resolved with the return value of the `fn` function.
18115      *
18116      */
18117    function timeout(fn, delay, invokeApply) {
18118      if (!isFunction(fn)) {
18119        invokeApply = delay;
18120        delay = fn;
18121        fn = noop;
18122      }
18123
18124      var args = sliceArgs(arguments, 3),
18125          skipApply = (isDefined(invokeApply) && !invokeApply),
18126          deferred = (skipApply ? $$q : $q).defer(),
18127          promise = deferred.promise,
18128          timeoutId;
18129
18130      timeoutId = $browser.defer(function() {
18131        try {
18132          deferred.resolve(fn.apply(null, args));
18133        } catch (e) {
18134          deferred.reject(e);
18135          $exceptionHandler(e);
18136        }
18137        finally {
18138          delete deferreds[promise.$$timeoutId];
18139        }
18140
18141        if (!skipApply) $rootScope.$apply();
18142      }, delay);
18143
18144      promise.$$timeoutId = timeoutId;
18145      deferreds[timeoutId] = deferred;
18146
18147      return promise;
18148    }
18149
18150
18151     /**
18152      * @ngdoc method
18153      * @name $timeout#cancel
18154      *
18155      * @description
18156      * Cancels a task associated with the `promise`. As a result of this, the promise will be
18157      * resolved with a rejection.
18158      *
18159      * @param {Promise=} promise Promise returned by the `$timeout` function.
18160      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
18161      *   canceled.
18162      */
18163    timeout.cancel = function(promise) {
18164      if (promise && promise.$$timeoutId in deferreds) {
18165        deferreds[promise.$$timeoutId].reject('canceled');
18166        delete deferreds[promise.$$timeoutId];
18167        return $browser.defer.cancel(promise.$$timeoutId);
18168      }
18169      return false;
18170    };
18171
18172    return timeout;
18173  }];
18174}
18175
18176// NOTE:  The usage of window and document instead of $window and $document here is
18177// deliberate.  This service depends on the specific behavior of anchor nodes created by the
18178// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
18179// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
18180// doesn't know about mocked locations and resolves URLs to the real document - which is
18181// exactly the behavior needed here.  There is little value is mocking these out for this
18182// service.
18183var urlParsingNode = document.createElement("a");
18184var originUrl = urlResolve(window.location.href);
18185
18186
18187/**
18188 *
18189 * Implementation Notes for non-IE browsers
18190 * ----------------------------------------
18191 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
18192 * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
18193 * URL will be resolved into an absolute URL in the context of the application document.
18194 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
18195 * properties are all populated to reflect the normalized URL.  This approach has wide
18196 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
18197 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18198 *
18199 * Implementation Notes for IE
18200 * ---------------------------
18201 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
18202 * browsers.  However, the parsed components will not be set if the URL assigned did not specify
18203 * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
18204 * work around that by performing the parsing in a 2nd step by taking a previously normalized
18205 * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
18206 * properties such as protocol, hostname, port, etc.
18207 *
18208 * References:
18209 *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
18210 *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18211 *   http://url.spec.whatwg.org/#urlutils
18212 *   https://github.com/angular/angular.js/pull/2902
18213 *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
18214 *
18215 * @kind function
18216 * @param {string} url The URL to be parsed.
18217 * @description Normalizes and parses a URL.
18218 * @returns {object} Returns the normalized URL as a dictionary.
18219 *
18220 *   | member name   | Description    |
18221 *   |---------------|----------------|
18222 *   | href          | A normalized version of the provided URL if it was not an absolute URL |
18223 *   | protocol      | The protocol including the trailing colon                              |
18224 *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
18225 *   | search        | The search params, minus the question mark                             |
18226 *   | hash          | The hash string, minus the hash symbol
18227 *   | hostname      | The hostname
18228 *   | port          | The port, without ":"
18229 *   | pathname      | The pathname, beginning with "/"
18230 *
18231 */
18232function urlResolve(url) {
18233  var href = url;
18234
18235  if (msie) {
18236    // Normalize before parse.  Refer Implementation Notes on why this is
18237    // done in two steps on IE.
18238    urlParsingNode.setAttribute("href", href);
18239    href = urlParsingNode.href;
18240  }
18241
18242  urlParsingNode.setAttribute('href', href);
18243
18244  // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
18245  return {
18246    href: urlParsingNode.href,
18247    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
18248    host: urlParsingNode.host,
18249    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
18250    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
18251    hostname: urlParsingNode.hostname,
18252    port: urlParsingNode.port,
18253    pathname: (urlParsingNode.pathname.charAt(0) === '/')
18254      ? urlParsingNode.pathname
18255      : '/' + urlParsingNode.pathname
18256  };
18257}
18258
18259/**
18260 * Parse a request URL and determine whether this is a same-origin request as the application document.
18261 *
18262 * @param {string|object} requestUrl The url of the request as a string that will be resolved
18263 * or a parsed URL object.
18264 * @returns {boolean} Whether the request is for the same origin as the application document.
18265 */
18266function urlIsSameOrigin(requestUrl) {
18267  var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
18268  return (parsed.protocol === originUrl.protocol &&
18269          parsed.host === originUrl.host);
18270}
18271
18272/**
18273 * @ngdoc service
18274 * @name $window
18275 *
18276 * @description
18277 * A reference to the browser's `window` object. While `window`
18278 * is globally available in JavaScript, it causes testability problems, because
18279 * it is a global variable. In angular we always refer to it through the
18280 * `$window` service, so it may be overridden, removed or mocked for testing.
18281 *
18282 * Expressions, like the one defined for the `ngClick` directive in the example
18283 * below, are evaluated with respect to the current scope.  Therefore, there is
18284 * no risk of inadvertently coding in a dependency on a global value in such an
18285 * expression.
18286 *
18287 * @example
18288   <example module="windowExample">
18289     <file name="index.html">
18290       <script>
18291         angular.module('windowExample', [])
18292           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18293             $scope.greeting = 'Hello, World!';
18294             $scope.doGreeting = function(greeting) {
18295               $window.alert(greeting);
18296             };
18297           }]);
18298       </script>
18299       <div ng-controller="ExampleController">
18300         <input type="text" ng-model="greeting" aria-label="greeting" />
18301         <button ng-click="doGreeting(greeting)">ALERT</button>
18302       </div>
18303     </file>
18304     <file name="protractor.js" type="protractor">
18305      it('should display the greeting in the input box', function() {
18306       element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18307       // If we click the button it will block the test runner
18308       // element(':button').click();
18309      });
18310     </file>
18311   </example>
18312 */
18313function $WindowProvider() {
18314  this.$get = valueFn(window);
18315}
18316
18317/**
18318 * @name $$cookieReader
18319 * @requires $document
18320 *
18321 * @description
18322 * This is a private service for reading cookies used by $http and ngCookies
18323 *
18324 * @return {Object} a key/value map of the current cookies
18325 */
18326function $$CookieReader($document) {
18327  var rawDocument = $document[0] || {};
18328  var lastCookies = {};
18329  var lastCookieString = '';
18330
18331  function safeDecodeURIComponent(str) {
18332    try {
18333      return decodeURIComponent(str);
18334    } catch (e) {
18335      return str;
18336    }
18337  }
18338
18339  return function() {
18340    var cookieArray, cookie, i, index, name;
18341    var currentCookieString = rawDocument.cookie || '';
18342
18343    if (currentCookieString !== lastCookieString) {
18344      lastCookieString = currentCookieString;
18345      cookieArray = lastCookieString.split('; ');
18346      lastCookies = {};
18347
18348      for (i = 0; i < cookieArray.length; i++) {
18349        cookie = cookieArray[i];
18350        index = cookie.indexOf('=');
18351        if (index > 0) { //ignore nameless cookies
18352          name = safeDecodeURIComponent(cookie.substring(0, index));
18353          // the first value that is seen for a cookie is the most
18354          // specific one.  values for the same cookie name that
18355          // follow are for less specific paths.
18356          if (isUndefined(lastCookies[name])) {
18357            lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18358          }
18359        }
18360      }
18361    }
18362    return lastCookies;
18363  };
18364}
18365
18366$$CookieReader.$inject = ['$document'];
18367
18368function $$CookieReaderProvider() {
18369  this.$get = $$CookieReader;
18370}
18371
18372/* global currencyFilter: true,
18373 dateFilter: true,
18374 filterFilter: true,
18375 jsonFilter: true,
18376 limitToFilter: true,
18377 lowercaseFilter: true,
18378 numberFilter: true,
18379 orderByFilter: true,
18380 uppercaseFilter: true,
18381 */
18382
18383/**
18384 * @ngdoc provider
18385 * @name $filterProvider
18386 * @description
18387 *
18388 * Filters are just functions which transform input to an output. However filters need to be
18389 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18390 * annotated with dependencies and is responsible for creating a filter function.
18391 *
18392 * <div class="alert alert-warning">
18393 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18394 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18395 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18396 * (`myapp_subsection_filterx`).
18397 * </div>
18398 *
18399 * ```js
18400 *   // Filter registration
18401 *   function MyModule($provide, $filterProvider) {
18402 *     // create a service to demonstrate injection (not always needed)
18403 *     $provide.value('greet', function(name){
18404 *       return 'Hello ' + name + '!';
18405 *     });
18406 *
18407 *     // register a filter factory which uses the
18408 *     // greet service to demonstrate DI.
18409 *     $filterProvider.register('greet', function(greet){
18410 *       // return the filter function which uses the greet service
18411 *       // to generate salutation
18412 *       return function(text) {
18413 *         // filters need to be forgiving so check input validity
18414 *         return text && greet(text) || text;
18415 *       };
18416 *     });
18417 *   }
18418 * ```
18419 *
18420 * The filter function is registered with the `$injector` under the filter name suffix with
18421 * `Filter`.
18422 *
18423 * ```js
18424 *   it('should be the same instance', inject(
18425 *     function($filterProvider) {
18426 *       $filterProvider.register('reverse', function(){
18427 *         return ...;
18428 *       });
18429 *     },
18430 *     function($filter, reverseFilter) {
18431 *       expect($filter('reverse')).toBe(reverseFilter);
18432 *     });
18433 * ```
18434 *
18435 *
18436 * For more information about how angular filters work, and how to create your own filters, see
18437 * {@link guide/filter Filters} in the Angular Developer Guide.
18438 */
18439
18440/**
18441 * @ngdoc service
18442 * @name $filter
18443 * @kind function
18444 * @description
18445 * Filters are used for formatting data displayed to the user.
18446 *
18447 * The general syntax in templates is as follows:
18448 *
18449 *         {{ expression [| filter_name[:parameter_value] ... ] }}
18450 *
18451 * @param {String} name Name of the filter function to retrieve
18452 * @return {Function} the filter function
18453 * @example
18454   <example name="$filter" module="filterExample">
18455     <file name="index.html">
18456       <div ng-controller="MainCtrl">
18457        <h3>{{ originalText }}</h3>
18458        <h3>{{ filteredText }}</h3>
18459       </div>
18460     </file>
18461
18462     <file name="script.js">
18463      angular.module('filterExample', [])
18464      .controller('MainCtrl', function($scope, $filter) {
18465        $scope.originalText = 'hello';
18466        $scope.filteredText = $filter('uppercase')($scope.originalText);
18467      });
18468     </file>
18469   </example>
18470  */
18471$FilterProvider.$inject = ['$provide'];
18472function $FilterProvider($provide) {
18473  var suffix = 'Filter';
18474
18475  /**
18476   * @ngdoc method
18477   * @name $filterProvider#register
18478   * @param {string|Object} name Name of the filter function, or an object map of filters where
18479   *    the keys are the filter names and the values are the filter factories.
18480   *
18481   *    <div class="alert alert-warning">
18482   *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18483   *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18484   *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18485   *    (`myapp_subsection_filterx`).
18486   *    </div>
18487    * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18488   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18489   *    of the registered filter instances.
18490   */
18491  function register(name, factory) {
18492    if (isObject(name)) {
18493      var filters = {};
18494      forEach(name, function(filter, key) {
18495        filters[key] = register(key, filter);
18496      });
18497      return filters;
18498    } else {
18499      return $provide.factory(name + suffix, factory);
18500    }
18501  }
18502  this.register = register;
18503
18504  this.$get = ['$injector', function($injector) {
18505    return function(name) {
18506      return $injector.get(name + suffix);
18507    };
18508  }];
18509
18510  ////////////////////////////////////////
18511
18512  /* global
18513    currencyFilter: false,
18514    dateFilter: false,
18515    filterFilter: false,
18516    jsonFilter: false,
18517    limitToFilter: false,
18518    lowercaseFilter: false,
18519    numberFilter: false,
18520    orderByFilter: false,
18521    uppercaseFilter: false,
18522  */
18523
18524  register('currency', currencyFilter);
18525  register('date', dateFilter);
18526  register('filter', filterFilter);
18527  register('json', jsonFilter);
18528  register('limitTo', limitToFilter);
18529  register('lowercase', lowercaseFilter);
18530  register('number', numberFilter);
18531  register('orderBy', orderByFilter);
18532  register('uppercase', uppercaseFilter);
18533}
18534
18535/**
18536 * @ngdoc filter
18537 * @name filter
18538 * @kind function
18539 *
18540 * @description
18541 * Selects a subset of items from `array` and returns it as a new array.
18542 *
18543 * @param {Array} array The source array.
18544 * @param {string|Object|function()} expression The predicate to be used for selecting items from
18545 *   `array`.
18546 *
18547 *   Can be one of:
18548 *
18549 *   - `string`: The string is used for matching against the contents of the `array`. All strings or
18550 *     objects with string properties in `array` that match this string will be returned. This also
18551 *     applies to nested object properties.
18552 *     The predicate can be negated by prefixing the string with `!`.
18553 *
18554 *   - `Object`: A pattern object can be used to filter specific properties on objects contained
18555 *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18556 *     which have property `name` containing "M" and property `phone` containing "1". A special
18557 *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18558 *     property of the object or its nested object properties. That's equivalent to the simple
18559 *     substring match with a `string` as described above. The predicate can be negated by prefixing
18560 *     the string with `!`.
18561 *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
18562 *     not containing "M".
18563 *
18564 *     Note that a named property will match properties on the same level only, while the special
18565 *     `$` property will match properties on the same level or deeper. E.g. an array item like
18566 *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18567 *     **will** be matched by `{$: 'John'}`.
18568 *
18569 *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18570 *     The function is called for each element of the array, with the element, its index, and
18571 *     the entire array itself as arguments.
18572 *
18573 *     The final result is an array of those elements that the predicate returned true for.
18574 *
18575 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18576 *     determining if the expected value (from the filter expression) and actual value (from
18577 *     the object in the array) should be considered a match.
18578 *
18579 *   Can be one of:
18580 *
18581 *   - `function(actual, expected)`:
18582 *     The function will be given the object value and the predicate value to compare and
18583 *     should return true if both values should be considered equal.
18584 *
18585 *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18586 *     This is essentially strict comparison of expected and actual.
18587 *
18588 *   - `false|undefined`: A short hand for a function which will look for a substring match in case
18589 *     insensitive way.
18590 *
18591 *     Primitive values are converted to strings. Objects are not compared against primitives,
18592 *     unless they have a custom `toString` method (e.g. `Date` objects).
18593 *
18594 * @example
18595   <example>
18596     <file name="index.html">
18597       <div ng-init="friends = [{name:'John', phone:'555-1276'},
18598                                {name:'Mary', phone:'800-BIG-MARY'},
18599                                {name:'Mike', phone:'555-4321'},
18600                                {name:'Adam', phone:'555-5678'},
18601                                {name:'Julie', phone:'555-8765'},
18602                                {name:'Juliette', phone:'555-5678'}]"></div>
18603
18604       <label>Search: <input ng-model="searchText"></label>
18605       <table id="searchTextResults">
18606         <tr><th>
18606Name</th><th>Phone</th></tr>
18607         <tr ng-repeat="friend in friends | filter:searchText">
18608           <td>{{friend.name}}</td>
18609           <td>{{friend.phone}}</td>
18610         </tr>
18611       </table>
18612       <hr>
18613       <label>Any: <input ng-model="search.$"></label> <br>
18614       <label>Name only <input ng-model="search.name"></label><br>
18615       <label>Phone only <input ng-model="search.phone"></label><br>
18616       <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18617       <table id="searchObjResults">
18618         <tr><th>Name</th><th>Phone</th></tr>
18619         <tr ng-repeat="friendObj in friends | filter:search:strict">
18620           <td>{{friendObj.name}}</td>
18621           <td>{{friendObj.phone}}</td>
18622         </tr>
18623       </table>
18624     </file>
18625     <file name="protractor.js" type="protractor">
18626       var expectFriendNames = function(expectedNames, key) {
18627         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18628           arr.forEach(function(wd, i) {
18629             expect(wd.getText()).toMatch(expectedNames[i]);
18630           });
18631         });
18632       };
18633
18634       it('should search across all fields when filtering with a string', function() {
18635         var searchText = element(by.model('searchText'));
18636         searchText.clear();
18637         searchText.sendKeys('m');
18638         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18639
18640         searchText.clear();
18641         searchText.sendKeys('76');
18642         expectFriendNames(['John', 'Julie'], 'friend');
18643       });
18644
18645       it('should search in specific fields when filtering with a predicate object', function() {
18646         var searchAny = element(by.model('search.$'));
18647         searchAny.clear();
18648         searchAny.sendKeys('i');
18649         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18650       });
18651       it('should use a equal comparison when comparator is true', function() {
18652         var searchName = element(by.model('search.name'));
18653         var strict = element(by.model('strict'));
18654         searchName.clear();
18655         searchName.sendKeys('Julie');
18656         strict.click();
18657         expectFriendNames(['Julie'], 'friendObj');
18658       });
18659     </file>
18660   </example>
18661 */
18662function filterFilter() {
18663  return function(array, expression, comparator) {
18664    if (!isArrayLike(array)) {
18665      if (array == null) {
18666        return array;
18667      } else {
18668        throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18669      }
18670    }
18671
18672    var expressionType = getTypeForFilter(expression);
18673    var predicateFn;
18674    var matchAgainstAnyProp;
18675
18676    switch (expressionType) {
18677      case 'function':
18678        predicateFn = expression;
18679        break;
18680      case 'boolean':
18681      case 'null':
18682      case 'number':
18683      case 'string':
18684        matchAgainstAnyProp = true;
18685        //jshint -W086
18686      case 'object':
18687        //jshint +W086
18688        predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18689        break;
18690      default:
18691        return array;
18692    }
18693
18694    return Array.prototype.filter.call(array, predicateFn);
18695  };
18696}
18697
18698// Helper functions for `filterFilter`
18699function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18700  var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18701  var predicateFn;
18702
18703  if (comparator === true) {
18704    comparator = equals;
18705  } else if (!isFunction(comparator)) {
18706    comparator = function(actual, expected) {
18707      if (isUndefined(actual)) {
18708        // No substring matching against `undefined`
18709        return false;
18710      }
18711      if ((actual === null) || (expected === null)) {
18712        // No substring matching against `null`; only match against `null`
18713        return actual === expected;
18714      }
18715      if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18716        // Should not compare primitives against objects, unless they have custom `toString` method
18717        return false;
18718      }
18719
18720      actual = lowercase('' + actual);
18721      expected = lowercase('' + expected);
18722      return actual.indexOf(expected) !== -1;
18723    };
18724  }
18725
18726  predicateFn = function(item) {
18727    if (shouldMatchPrimitives && !isObject(item)) {
18728      return deepCompare(item, expression.$, comparator, false);
18729    }
18730    return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18731  };
18732
18733  return predicateFn;
18734}
18735
18736function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18737  var actualType = getTypeForFilter(actual);
18738  var expectedType = getTypeForFilter(expected);
18739
18740  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18741    return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18742  } else if (isArray(actual)) {
18743    // In case `actual` is an array, consider it a match
18744    // if ANY of it's items matches `expected`
18745    return actual.some(function(item) {
18746      return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18747    });
18748  }
18749
18750  switch (actualType) {
18751    case 'object':
18752      var key;
18753      if (matchAgainstAnyProp) {
18754        for (key in actual) {
18755          if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18756            return true;
18757          }
18758        }
18759        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18760      } else if (expectedType === 'object') {
18761        for (key in expected) {
18762          var expectedVal = expected[key];
18763          if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18764            continue;
18765          }
18766
18767          var matchAnyProperty = key === '$';
18768          var actualVal = matchAnyProperty ? actual : actual[key];
18769          if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18770            return false;
18771          }
18772        }
18773        return true;
18774      } else {
18775        return comparator(actual, expected);
18776      }
18777      break;
18778    case 'function':
18779      return false;
18780    default:
18781      return comparator(actual, expected);
18782  }
18783}
18784
18785// Used for easily differentiating between `null` and actual `object`
18786function getTypeForFilter(val) {
18787  return (val === null) ? 'null' : typeof val;
18788}
18789
18790var MAX_DIGITS = 22;
18791var DECIMAL_SEP = '.';
18792var ZERO_CHAR = '0';
18793
18794/**
18795 * @ngdoc filter
18796 * @name currency
18797 * @kind function
18798 *
18799 * @description
18800 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18801 * symbol for current locale is used.
18802 *
18803 * @param {number} amount Input to filter.
18804 * @param {string=} symbol Currency symbol or identifier to be displayed.
18805 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18806 * @returns {string} Formatted number.
18807 *
18808 *
18809 * @example
18810   <example module="currencyExample">
18811     <file name="index.html">
18812       <script>
18813         angular.module('currencyExample', [])
18814           .controller('ExampleController', ['$scope', function($scope) {
18815             $scope.amount = 1234.56;
18816           }]);
18817       </script>
18818       <div ng-controller="ExampleController">
18819         <input type="number" ng-model="amount" aria-label="amount"> <br>
18820         default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18821         custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18822         no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18823       </div>
18824     </file>
18825     <file name="protractor.js" type="protractor">
18826       it('should init with 1234.56', function() {
18827         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18828         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18829         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18830       });
18831       it('should update', function() {
18832         if (browser.params.browser == 'safari') {
18833           // Safari does not understand the minus key. See
18834           // https://github.com/angular/protractor/issues/481
18835           return;
18836         }
18837         element(by.model('amount')).clear();
18838         element(by.model('amount')).sendKeys('-1234');
18839         expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
18840         expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
18841         expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
18842       });
18843     </file>
18844   </example>
18845 */
18846currencyFilter.$inject = ['$locale'];
18847function currencyFilter($locale) {
18848  var formats = $locale.NUMBER_FORMATS;
18849  return function(amount, currencySymbol, fractionSize) {
18850    if (isUndefined(currencySymbol)) {
18851      currencySymbol = formats.CURRENCY_SYM;
18852    }
18853
18854    if (isUndefined(fractionSize)) {
18855      fractionSize = formats.PATTERNS[1].maxFrac;
18856    }
18857
18858    // if null or undefined pass it through
18859    return (amount == null)
18860        ? amount
18861        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18862            replace(/\u00A4/g, currencySymbol);
18863  };
18864}
18865
18866/**
18867 * @ngdoc filter
18868 * @name number
18869 * @kind function
18870 *
18871 * @description
18872 * Formats a number as text.
18873 *
18874 * If the input is null or undefined, it will just be returned.
18875 * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
18876 * If the input is not a number an empty string is returned.
18877 *
18878 *
18879 * @param {number|string} number Number to format.
18880 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18881 * If this is not provided then the fraction size is computed from the current locale's number
18882 * formatting pattern. In the case of the default locale, it will be 3.
18883 * @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
18884 *
18885 * @example
18886   <example module="numberFilterExample
vendor: 3,034 bytes, lines 18886-18965
18886">
18887     <file name="index.html">
18888       <script>
18889         angular.module('numberFilterExample', [])
18890           .controller('ExampleController', ['$scope', function($scope) {
18891             $scope.val = 1234.56789;
18892           }]);
18893       </script>
18894       <div ng-controller="ExampleController">
18895         <label>Enter number: <input ng-model='val'></label><br>
18896         Default formatting: <span id='number-default'>{{val | number}}</span><br>
18897         No fractions: <span>{{val | number:0}}</span><br>
18898         Negative number: <span>{{-val | number:4}}</span>
18899       </div>
18900     </file>
18901     <file name="protractor.js" type="protractor">
18902       it('should format numbers', function() {
18903         expect(element(by.id('number-default')).getText()).toBe('1,234.568');
18904         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
18905         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
18906       });
18907
18908       it('should update', function() {
18909         element(by.model('val')).clear();
18910         element(by.model('val')).sendKeys('3374.333');
18911         expect(element(by.id('number-default')).getText()).toBe('3,374.333');
18912         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
18913         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
18914      });
18915     </file>
18916   </example>
18917 */
18918numberFilter.$inject = ['$locale'];
18919function numberFilter($locale) {
18920  var formats = $locale.NUMBER_FORMATS;
18921  return function(number, fractionSize) {
18922
18923    // if null or undefined pass it through
18924    return (number == null)
18925        ? number
18926        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
18927                       fractionSize);
18928  };
18929}
18930
18931/**
18932 * Parse a number (as a string) into three components that can be used
18933 * for formatting the number.
18934 *
18935 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
18936 *
18937 * @param  {string} numStr The number to parse
18938 * @return {object} An object describing this number, containing the following keys:
18939 *  - d : an array of digits containing leading zeros as necessary
18940 *  - i : the number of the digits in `d` that are to the left of the decimal point
18941 *  - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
18942 *
18943 */
18944function parse(numStr) {
18945  var exponent = 0, digits, numberOfIntegerDigits;
18946  var i, j, zeros;
18947
18948  // Decimal point?
18949  if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
18950    numStr = numStr.replace(DECIMAL_SEP, '');
18951  }
18952
18953  // Exponential form?
18954  if ((i = numStr.search(/e/i)) > 0) {
18955    // Work out the exponent.
18956    if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
18957    numberOfIntegerDigits += +numStr.slice(i + 1);
18958    numStr = numStr.substring(0, i);
18959  } else if (numberOfIntegerDigits < 0) {
18960    // There was no decimal point or exponent so it is an integer.
18961    numberOfIntegerDigits = numStr.length;
18962  }
18963
18964  // Count the number of leading zeros.
18965  for (i = 0; numStr.charAt(i) ==
18965 ZERO_CHAR; i++);
18966
18967  if (i == (zeros = numStr.length)) {
18968    // The digits are all zero.
18969    digits = [0];
18970    numberOfIntegerDigits = 1;
18971  } else {
18972    // Count the number of trailing zeros
18973    zeros--;
18974    while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
18975
18976    // Trailing zeros are insignificant so ignore them
18977    numberOfIntegerDigits -= i;
18978    digits = [];
18979    // Convert string to array of digits without leading/trailing zeros.
18980    for (j = 0; i <= zeros; i++, j++) {
18981      digits[j] = +numStr.charAt(i);
18982    }
18983  }
18984
18985  // If the number overflows the maximum allowed digits then use an exponent.
18986  if (numberOfIntegerDigits > MAX_DIGITS) {
18987    digits = digits.splice(0, MAX_DIGITS - 1);
18988    exponent = numberOfIntegerDigits - 1;
18989    numberOfIntegerDigits = 1;
18990  }
18991
18992  return { d: digits, e: exponent, i: numberOfIntegerDigits };
18993}
18994
18995/**
18996 * Round the parsed number to the specified number of decimal places
18997 * This function changed the parsedNumber in-place
18998 */
18999function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
19000    var digits = parsedNumber.d;
19001    var fractionLen = digits.length - parsedNumber.i;
19002
19003    // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
19004    fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
19005
19006    // The index of the digit to where rounding is to occur
19007    var roundAt = fractionSize + parsedNumber.i;
19008    var digit = digits[roundAt];
19009
19010    if (roundAt > 0) {
19011      digits.splice(roundAt);
19012    } else {
19013      // We rounded to zero so reset the parsedNumber
19014      parsedNumber.i = 1;
19015      digits.length = roundAt = fractionSize + 1;
19016      for (var i=0; i < roundAt; i++) digits[i] = 0;
19017    }
19018
19019    if (digit >= 5) digits[roundAt - 1]++;
19020
19021    // Pad out with zeros to get the required fraction length
19022    for (; fractionLen < fractionSize; fractionLen++) digits.push(0);
19023
19024
19025    // Do any carrying, e.g. a digit was rounded up to 10
19026    var carry = digits.reduceRight(function(carry, d, i, digits) {
19027      d = d + carry;
19028      digits[i] = d % 10;
19029      return Math.floor(d / 10);
19030    }, 0);
19031    if (carry) {
19032      digits.unshift(carry);
19033      parsedNumber.i++;
19034    }
19035}
19036
19037/**
19038 * Format a number into a string
19039 * @param  {number} number       The number to format
19040 * @param  {{
19041 *           minFrac, // the minimum number of digits required in the fraction part of the number
19042 *           maxFrac, // the maximum number of digits required in the fraction part of the number
19043 *           gSize,   // number of digits in each group of separated digits
19044 *           lgSize,  // number of digits in the last group of digits before the decimal separator
19045 *           negPre,  // the string to go in front of a negative number (e.g. `-` or `(`))
19046 *           posPre,  // the string to go in front of a positive number
19047 *           negSuf,  // the string to go after a negative number (e.g. `)`)
19048 *           posSuf   // the string to go after a positive number
19049 *         }} pattern
19050 * @param  {string} groupSep     The string to separate groups of number (e.g. `,`)
19051 * @param  {string} decimalSep   The string to act as the decimal separator (e.g. `.`)
19052 * @param  {[type]} fractionSize The size of the fractional part of the number
19053 * @return {string}              The number formatted as a string
19054 */
19055function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
19056
19057  if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
19058
19059  var isInfinity = !isFinite(number);
19060  var isZero = false;
19061  var numStr = Math.abs(number) + '',
19062      formattedText = '',
19063      parsedNumber;
19064
19065  if (isInfinity) {
19066    formattedText = '\u221e';
19067  } else {
19068    parsedNumber = parse(numStr);
19069
19070    roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
19071
19072    var digits = parsedNumber.d;
19073    var integerLen = parsedNumber.i;
19074    var exponent = parsedNumber.e;
19075    var decimals = [];
19076    isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
19077
19078    // pad zeros for small numbers
19079    while (integerLen < 0) {
19080      digits.unshift(0);
19081      integerLen++;
19082    }
19083
19084    // extract decimals digits
19085    if (integerLen > 0) {
19086      decimals = digits.splice(integerLen);
19087    } else {
19088      decimals = digits;
19089      digits = [0];
19090    }
19091
19092    // format the integer digits with grouping separators
19093    var groups = [];
19094    if (digits.length > pattern.lgSize) {
vendor: 5,630 bytes, lines 19095-19274
19095      groups.unshift(digits.splice(-pattern.lgSize).join(''));
19096    }
19097    while (digits.length > pattern.gSize) {
19098      groups.unshift(digits.splice(-pattern.gSize).join(''));
19099    }
19100    if (digits.length) {
19101      groups.unshift(digits.join(''));
19102    }
19103    formattedText = groups.join(groupSep);
19104
19105    // append the decimal digits
19106    if (decimals.length) {
19107      formattedText += decimalSep + decimals.join('');
19108    }
19109
19110    if (exponent) {
19111      formattedText += 'e+' + exponent;
19112    }
19113  }
19114  if (number < 0 && !isZero) {
19115    return pattern.negPre + formattedText + pattern.negSuf;
19116  } else {
19117    return pattern.posPre + formattedText + pattern.posSuf;
19118  }
19119}
19120
19121function padNumber(num, digits, trim) {
19122  var neg = '';
19123  if (num < 0) {
19124    neg =  '-';
19125    num = -num;
19126  }
19127  num = '' + num;
19128  while (num.length < digits) num = ZERO_CHAR + num;
19129  if (trim) {
19130    num = num.substr(num.length - digits);
19131  }
19132  return neg + num;
19133}
19134
19135
19136function dateGetter(name, size, offset, trim) {
19137  offset = offset || 0;
19138  return function(date) {
19139    var value = date['get' + name]();
19140    if (offset > 0 || value > -offset) {
19141      value += offset;
19142    }
19143    if (value === 0 && offset == -12) value = 12;
19144    return padNumber(value, size, trim);
19145  };
19146}
19147
19148function dateStrGetter(name, shortForm) {
19149  return function(date, formats) {
19150    var value = date['get' + name]();
19151    var get = uppercase(shortForm ? ('SHORT' + name) : name);
19152
19153    return formats[get][value];
19154  };
19155}
19156
19157function timeZoneGetter(date, formats, offset) {
19158  var zone = -1 * offset;
19159  var paddedZone = (zone >= 0) ? "+" : "";
19160
19161  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
19162                padNumber(Math.abs(zone % 60), 2);
19163
19164  return paddedZone;
19165}
19166
19167function getFirstThursdayOfYear(year) {
19168    // 0 = index of January
19169    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
19170    // 4 = index of Thursday (+1 to account for 1st = 5)
19171    // 11 = index of *next* Thursday (+1 account for 1st = 12)
19172    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
19173}
19174
19175function getThursdayThisWeek(datetime) {
19176    return new Date(datetime.getFullYear(), datetime.getMonth(),
19177      // 4 = index of Thursday
19178      datetime.getDate() + (4 - datetime.getDay()));
19179}
19180
19181function weekGetter(size) {
19182   return function(date) {
19183      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
19184         thisThurs = getThursdayThisWeek(date);
19185
19186      var diff = +thisThurs - +firstThurs,
19187         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
19188
19189      return padNumber(result, size);
19190   };
19191}
19192
19193function ampmGetter(date, formats) {
19194  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
19195}
19196
19197function eraGetter(date, formats) {
19198  return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
19199}
19200
19201function longEraGetter(date, formats) {
19202  return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
19203}
19204
19205var DATE_FORMATS = {
19206  yyyy: dateGetter('FullYear', 4),
19207    yy: dateGetter('FullYear', 2, 0, true),
19208     y: dateGetter('FullYear', 1),
19209  MMMM: dateStrGetter('Month'),
19210   MMM: dateStrGetter('Month', true),
19211    MM: dateGetter('Month', 2, 1),
19212     M: dateGetter('Month', 1, 1),
19213    dd: dateGetter('Date', 2),
19214     d: dateGetter('Date', 1),
19215    HH: dateGetter('Hours', 2),
19216     H: dateGetter('Hours', 1),
19217    hh: dateGetter('Hours', 2, -12),
19218     h: dateGetter('Hours', 1, -12),
19219    mm: dateGetter('Minutes', 2),
19220     m: dateGetter('Minutes', 1),
19221    ss: dateGetter('Seconds', 2),
19222     s: dateGetter('Seconds', 1),
19223     // while ISO 8601 requires fractions to be prefixed with `.` or `,`
19224     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
19225   sss: dateGetter('Milliseconds', 3),
19226  EEEE: dateStrGetter('Day'),
19227   EEE: dateStrGetter('Day', true),
19228     a: ampmGetter,
19229     Z: timeZoneGetter,
19230    ww: weekGetter(2),
19231     w: weekGetter(1),
19232     G: eraGetter,
19233     GG: eraGetter,
19234     GGG: eraGetter,
19235     GGGG: longEraGetter
19236};
19237
19238var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
19239    NUMBER_STRING = /^\-?\d+$/;
19240
19241/**
19242 * @ngdoc filter
19243 * @name date
19244 * @kind function
19245 *
19246 * @description
19247 *   Formats `date` to a string based on the requested `format`.
19248 *
19249 *   `format` string can be composed of the following elements:
19250 *
19251 *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
19252 *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
19253 *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
19254 *   * `'MMMM'`: Month in year (January-December)
19255 *   * `'MMM'`: Month in year (Jan-Dec)
19256 *   * `'MM'`: Month in year, padded (01-12)
19257 *   * `'M'`: Month in year (1-12)
19258 *   * `'dd'`: Day in month, padded (01-31)
19259 *   * `'d'`: Day in month (1-31)
19260 *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
19261 *   * `'EEE'`: Day in Week, (Sun-Sat)
19262 *   * `'HH'`: Hour in day, padded (00-23)
19263 *   * `'H'`: Hour in day (0-23)
19264 *   * `'hh'`: Hour in AM/PM, padded (01-12)
19265 *   * `'h'`: Hour in AM/PM, (1-12)
19266 *   * `'mm'`: Minute in hour, padded (00-59)
19267 *   * `'m'`: Minute in hour (0-59)
19268 *   * `'ss'`: Second in minute, padded (00-59)
19269 *   * `'s'`: Second in minute (0-59)
19270 *   * `'sss'`: Millisecond in second, padded (000-999)
19271 *   * `'a'`: AM/PM marker
19272 *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
19273 *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
19274 *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
19275 *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
19276 *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
19277 *
19278 *   `format` string can also be one of the following predefined
19279 *   {@link guide/i18n localizable formats}:
19280 *
19281 *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
19282 *     (e.g. Sep 3, 2010 12:05:08 PM)
19283 *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
19284 *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
19285 *     (e.g. Friday, September 3, 2010)
19286 *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
19287 *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
19288 *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
19289 *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
19290 *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
19291 *
19292 *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
19293 *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
19294 *   (e.g. `"h 'o''clock'"`).
19295 *
19296 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
19297 *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
19298 *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
19299 *    specified in the string input, the time is considered to be in the local timezone.
19300 * @param {string=} format Formatting rules (see Description). If not specified,
19301 *    `mediumDate` is used.
19302 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
19303 *    continental US time zone abbreviations, but for general use, use a time zone offset, for
19304 *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
19305 *    If not specified, the timezone of the browser will be used.
19306 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
19307 *
19308 * @example
19309   <example>
19310     <file name="index.html">
19311       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
19312           <span>{{1288323623006 | date:'medium'}}</span><br>
19313       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
19314          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
19315       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
19316          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
19317       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
19318          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
19319     </file>
19320     <file name="protractor.js" type="protractor">
19321       it('should format date', function() {
19322         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
19323            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19324         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
19325            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19326         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
19327            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19328         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
19329            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
19330       });
19331     </file>
19332   </example>
19333 */
19334dateFilter.$inject = ['$locale'];
19335function dateFilter($locale) {
19336
19337
19338  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
19339                     // 1        2       3         4          5          6          7          8  9     10      11
19340  function jsonStringToDate(string) {
19341    var match;
19342    if (match = string.match(R_ISO8601_STR)) {
19343      var date = new Date(0),
19344          tzHour = 0,
19345          tzMin  = 0,
19346          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
19347          timeSetter = match[8] ? date.setUTCHours : date.setHours;
19348
19349      if (match[9]) {
19350        tzHour = toInt(match[9] + match[10]);
19351        tzMin = toInt(match[9] + match[11]);
19352      }
19353      dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
19354      var h = toInt(match[4] || 0) - tzHour;
19355      var m = toInt(match[5] || 0) - tzMin;
19356      var s = toInt(match[6] || 0);
19357      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
19358      timeSetter.call(date, h, m, s, ms);
19359      return date;
19360    }
19361    return string;
19362  }
19363
19364
19365  return function(date, format, timezone) {
19366    var text = '',
19367        parts = [],
19368        fn, match;
19369
19370    format = format || 'mediumDate';
19371    format = $locale.DATETIME_FORMATS[format] || format;
19372    if (isString(date)) {
19373      date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
19374    }
19375
19376    if (isNumber(date)) {
19377      date = new Date(date);
19378    }
19379
19380    if (!isDate(date) || !isFinite(date.getTime())) {
19381      return date;
19382    }
19383
19384    while (format) {
19385      match = DATE_FORMATS_SPLIT.exec(format);
19386      if (match) {
19387        parts = concat(parts, match, 1);
19388        format = parts.pop();
19389      } else {
19390        parts.push(format);
19391        format = null;
19392      }
19393    }
19394
19395    var dateTimezoneOffset = date.getTimezoneOffset();
19396    if (timezone) {
19397      dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
19398      date = convertTimezoneToLocal(date, timezone, true);
19399    }
19400    forEach(parts, function(value) {
19401      fn = DATE_FORMATS[value];
19402      text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19403                 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19404    });
19405
19406    return text;
19407  };
19408}
19409
19410
19411/**
19412 * @ngdoc filter
19413 * @name json
19414 * @kind function
19415 *
19416 * @description
19417 *   Allows you to convert a JavaScript object into JSON string.
19418 *
19419 *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
19420 *   the binding is automatically converted to JSON.
19421 *
19422 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19423 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
19424 * @returns {string} JSON string.
19425 *
19426 *
19427 * @example
19428   <example>
19429     <file name="index.html">
19430       <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19431       <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19432     </file>
19433     <file name="protractor.js" type="protractor">
19434       it('should jsonify filtered objects', function() {
19435         expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
19436         expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
19437       });
19438     </file>
19439   </example>
19440 *
19441 */
19442function jsonFilter() {
19443  return function(object, spacing) {
19444    if (isUndefined(spacing)) {
19445        spacing = 2;
19446    }
19447    return toJson(object, spacing);
19448  };
19449}
19450
19451
19452/**
19453 * @ngdoc filter
19454 * @name lowercase
19455 * @kind function
19456 * @description
19457 * Converts string to lowercase.
19458 * @see angular.lowercase
19459 */
19460var lowercaseFilter = valueFn(lowercase);
19461
19462
19463/**
19464 * @ngdoc filter
19465 * @name uppercase
19466 * @kind function
19467 * @description
19468 * Converts string to uppercase.
19469 * @see angular.uppercase
19470 */
19471var uppercaseFilter = valueFn(uppercase);
19472
19473/**
19474 * @ngdoc filter
19475 * @name limitTo
19476 * @kind function
19477 *
19478 * @description
19479 * Creates a new array or string containing only a specified number of elements. The elements
19480 * are taken from either the beginning or the end of the source array, string or number, as specified by
19481 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19482 * converted to a string.
19483 *
19484 * @param {Array|string|number} input Source array, string or number to be limited.
19485 * @param {string|number} limit The length of the returned array or string. If the `limit` number
19486 *     is positive, `limit` number of items from the beginning of the source array/string are copied.
19487 *     If the number is negative, `limit` number  of items from the end of the source array/string
19488 *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19489 *     the input will be returned unchanged.
19490 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19491 *     indicates an offset from the end of `input`. Defaults to `0`.
19492 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19493 *     had less than `limit` elements.
19494 *
19495 * @example
19496   <example module="limitToExample">
19497     <file name="index.html">
19498       <script>
19499         angular.module('limitToExample', [])
19500           .controller('ExampleController', ['$scope', function($scope) {
19501             $scope.numbers = [1,2,3,4,5,6,7,8,9];
19502             $scope.letters = "abcdefghi";
19503             $scope.longNumber = 2345432342;
19504             $scope.numLimit = 3;
19505             $scope.letterLimit = 3;
19506             $scope.longNumberLimit = 3;
19507           }]);
19508       </script>
19509       <div ng-controller="ExampleController">
19510         <label>
19511            Limit {{numbers}} to:
19512            <input type="number" step="1" ng-model="numLimit">
19513         </label>
19514         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
19515         <label>
19516            Limit {{letters}} to:
19517            <input type="number" step="1" ng-model="letterLimit">
19518         </label>
19519         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
19520         <label>
19521            Limit {{longNumber}} to:
19522            <input type="number" step="1" ng-model="longNumberLimit">
19523         </label>
19524         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
19525       </div>
19526     </file>
19527     <file name="protractor.js" type="protractor">
19528       var numLimitInput = element(by.model('numLimit'));
19529       var letterLimitInput = element(by.model('letterLimit'));
19530       var longNumberLimitInput = element(by.model('longNumberLimit'));
19531       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
19532       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
19533       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
19534
19535       it('should limit the number array to first three items', function() {
19536         expect(numLimitInput.getAttribute('value')).toBe('3');
19537         expect(letterLimitInput.getAttribute('value')).toBe('3');
19538         expect(longNumberLimitInput.getAttribute('value')).toBe('3');
19539         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
19540         expect(limitedLetters.getText()).toEqual('Output letters: abc');
19541         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
19542       });
19543
19544       // There is a bug in safari and protractor that doesn't like the minus key
19545       // it('should update the output when -3 is entered', function() {
19546       //   numLimitInput.clear();
19547       //   numLimitInput.sendKeys('-3');
19548       //   letterLimitInput.clear();
19549       //   letterLimitInput.sendKeys('-3');
19550       //   longNumberLimitInput.clear();
19551       //   longNumberLimitInput.sendKeys('-3');
19552       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19553       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19554       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19555       // });
19556
19557       it('should not exceed the maximum size of input array', function() {
19558         numLimitInput.clear();
19559         numLimitInput.sendKeys('100');
19560         letterLimitInput.clear();
19561         letterLimitInput.sendKeys('100');
19562         longNumberLimitInput.clear();
19563         longNumberLimitInput.sendKeys('100');
19564         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19565         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19566         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19567       });
19568     </file>
19569   </example>
19570*/
19571function limitToFilter() {
19572  return function(input, limit, begin) {
19573    if (Math.abs(Number(limit)) === Infinity) {
19574      limit = Number(limit);
19575    } else {
19576      limit = toInt(limit);
19577    }
19578    if (isNaN(limit)) return input;
19579
19580    if (isNumber(input)) input = input.toString();
19581    if (!isArray(input) && !isString(input)) return input;
19582
19583    begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19584    begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
19585
19586    if (limit >= 0) {
19587      return input.slice(begin, begin + limit);
19588    } else {
19589      if (begin === 0) {
19590        return input.slice(limit, input.length);
19591      } else {
19592        return input.slice(Math.max(0, begin + limit), begin);
19593      }
19594    }
19595  };
19596}
19597
19598/**
19599 * @ngdoc filter
19600 * @name orderBy
19601 * @kind function
19602 *
19603 * @description
19604 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19605 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19606 * as expected, make sure they are actually being saved as numbers and not strings.
19607 *
19608 * @param {Array} array The array to sort.
19609 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19610 *    used by the comparator to determine the order of elements.
19611 *
19612 *    Can be one of:
19613 *
19614 *    - `function`: Getter function. The result of this function will be sorted using the
19615 *      `<`, `===`, `>` operator.
19616 *    - `string`: An Angular expression. The result of this expression is used to compare elements
19617 *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19618 *      3 first characters of a property called `name`). The result of a constant expression
19619 *      is interpreted as a property name to be used in comparisons (for example `"special name"`
19620 *      to sort object by the value of their `special name` property). An expression can be
19621 *      optionally prefixed with `+` or `-` to control ascending or descending sort order
19622 *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19623 *      element itself is used to compare where sorting.
19624 *    - `Array`: An array of function or string predicates. The first predicate in the array
19625 *      is used for sorting, but when two items are equivalent, the next predicate is used.
19626 *
19627 *    If the predicate is missing or empty then it defaults to `'+'`.
19628 *
19629 * @param {boolean=} reverse Reverse the order of the array.
19630 * @returns {Array} Sorted copy of the source array.
19631 *
19632 *
19633 * @example
19634 * The example below demonstrates a simple ngRepeat, where the data is sorted
19635 * by age in descending order (predicate is set to `'-age'`).
19636 * `reverse` is not set, which means it defaults to `false`.
19637   <example module="orderByExample">
19638     <file name="index.html">
19639       <div ng-controller="ExampleController">
19640         <table class="friend">
19641           <tr>
19642             <th>Name</th>
19643             <th>Phone Number</th>
19644             <th>Age</th>
19645           </tr>
19646           <tr ng-repeat="friend in friends | orderBy:'-age'">
19647             <td>{{friend.name}}</td>
19648             <td>{{friend.phone}}</td>
19649             <td>{{friend.age}}</td>
19650           </tr>
19651         </table>
19652       </div>
19653     </file>
19654     <file name="script.js">
19655       angular.module('orderByExample', [])
19656         .controller('ExampleController', ['$scope', function($scope) {
19657           $scope.friends =
19658               [{name:'John', phone:'555-1212', age:10},
19659                {name:'Mary', phone:'555-9876', age:19},
19660                {name:'Mike', phone:'555-4321', age:21},
19661                {name:'Adam', phone:'555-5678', age:35},
19662                {name:'Julie', phone:'555-8765', age:29}];
19663         }]);
19664     </file>
19665   </example>
19666 *
19667 * The predicate and reverse parameters can be controlled dynamically through scope properties,
19668 * as shown in the next example.
19669 * @example
19670   <example module="orderByExample">
19671     <file name="index.html">
19672       <div ng-controller="ExampleController">
19673         <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19674         <hr/>
19675         <button ng-click="predicate=''">Set to unsorted</button>
19676         <table class="friend">
19677           <tr>
19678            <th>
19679                <button ng-click="order('name')">Name</button>
19680                <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19681            </th>
19682            <th>
19683                <button ng-click="order('phone')">Phone Number</button>
19684                <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19685            </th>
19686            <th>
19687                <button ng-click="order('age')">Age</button>
19688                <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19689            </th>
19690           </tr>
19691           <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19692             <td>{{friend.name}}</td>
19693             <td>{{friend.phone}}</td>
19694             <td>{{friend.age}}</td>
19695           </tr>
19696         </table>
19697       </div>
19698     </file>
19699     <file name="script.js">
19700       angular.module('orderByExample', [])
19701         .controller('ExampleController', ['$scope', function($scope) {
19702           $scope.friends =
19703               [{name:'John', phone:'555-1212', age:10},
19704                {name:'Mary', phone:'555-9876', age:19},
19705                {name:'Mike', phone:'555-4321', age:21},
19706                {name:'Adam', phone:'555-5678', age:35},
19707                {name:'Julie', phone:'555-8765', age:29}];
19708           $scope.predicate = 'age';
19709           $scope.reverse = true;
19710           $scope.order = function(predicate) {
19711             $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19712             $scope.predicate = predicate;
19713           };
19714         }]);
19715      </file>
19716     <file name="style.css">
19717       .sortorder:after {
19718         content: '\25b2';
19719       }
19720       .sortorder.reverse:after {
19721         content: '\25bc';
19722       }
19723     </file>
19724   </example>
19725 *
19726 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19727 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19728 * desired parameters.
19729 *
19730 * Example:
19731 *
19732 * @example
19733  <example module="orderByExample">
19734    <file name="index.html">
19735    <div ng-controller="ExampleController">
19736      <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19737      <table class="friend">
19738        <tr>
19739          <th>
19740              <button ng-click="order('name')">Name</button>
19741              <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19742          </th>
19743          <th>
19744              <button ng-click="order('phone')">Phone Number</button>
19745              <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19746          </th>
19747          <th>
19748              <button ng-click="order('age')">Age</button>
19749              <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19750          </th>
19751        </tr>
19752        <tr ng-repeat="friend in friends">
19753          <td>{{friend.name}}</td>
19754          <td>{{friend.phone}}</td>
19755          <td>{{friend.age}}</td>
19756        </tr>
19757      </table>
19758    </div>
19759    </file>
19760
19761    <file name="script.js">
19762      angular.module('orderByExample', [])
19763        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19764          var orderBy = $filter('orderBy');
19765          $scope.friends = [
19766            { name: 'John',    phone: '555-1212',    age: 10 },
19767            { name: 'Mary',    phone: '555-9876',    age: 19 },
19768            { name: 'Mike',    phone: '555-4321',    age: 21 },
19769            { name: 'Adam',    phone: '555-5678',    age: 35 },
19770            { name: 'Julie',   phone: '555-8765',    age: 29 }
19771          ];
19772          $scope.order = function(predicate) {
19773            $scope.predicate = predicate;
19774            $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19775            $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
19776          };
19777          $scope.order('age', true);
19778        }]);
19779    </file>
19780
19781    <file name="style.css">
19782       .sortorder:after {
19783         content: '\25b2';
19784       }
19785       .sortorder.reverse:after {
19786         content: '\25bc';
19787       }
19788    </file>
19789</example>
19790 */
19791orderByFilter.$inject = ['$parse'];
19792function orderByFilter($parse) {
19793  return function(array, sortPredicate, reverseOrder) {
19794
19795    if (!(isArrayLike(array))) return array;
19796
19797    if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
19798    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19799
19800    var predicates = processPredicates(sortPredicate, reverseOrder);
19801    // Add a predicate at the end that evaluates to the element index. This makes the
19802    // sort stable as it works as a tie-breaker when all the input predicates cannot
19803    // distinguish between two elements.
19804    predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
19805
19806    // The next three lines are a version of a Swartzian Transform idiom from Perl
19807    // (sometimes called the Decorate-Sort-Undecorate idiom)
19808    // See https://en.wikipedia.org/wiki/Schwartzian_transform
19809    var compareValues = Array.prototype.map.call(array, getComparisonObject);
19810    compareValues.sort(doComparison);
19811    array = compareValues.map(function(item) { return item.value; });
19812
19813    return array;
19814
19815    function getComparisonObject(value, index) {
19816      return {
19817        value: value,
19818        predicateValues: predicates.map(function(predicate) {
19819          return getPredicateValue(predicate.get(value), index);
19820        })
19821      };
19822    }
19823
19824    function doComparison(v1, v2) {
19825      var result = 0;
19826      for (var index=0, length = predicates.length; index < length; ++index) {
19827        result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
19828        if (result) break;
19829      }
19830      return result;
19831    }
19832  };
19833
19834  function processPredicates(sortPredicate, reverseOrder) {
19835    reverseOrder = reverseOrder ? -1 : 1;
19836    return sortPredicate.map(function(predicate) {
19837      var descending = 1, get = identity;
19838
19839      if (isFunction(predicate)) {
19840        get = predicate;
19841      } else if (isString(predicate)) {
19842        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19843          descending = predicate.charAt(0) == '-' ? -1 : 1;
19844          predicate = predicate.substring(1);
19845        }
19846        if (predicate !== '') {
19847          get = $parse(predicate);
19848          if (get.constant) {
19849            var key = get();
19850            get = function(value) { return value[key]; };
19851          }
19852        }
19853      }
19854      return { get: get, descending: descending * reverseOrder };
19855    });
19856  }
19857
19858  function isPrimitive(value) {
19859    switch (typeof value) {
19860      case 'number': /* falls through */
19861      case 'boolean': /* falls through */
19862      case 'string':
19863        return true;
19864      default:
19865        return false;
19866    }
19867  }
19868
19869  function objectValue(value, index) {
19870    // If `valueOf` is a valid function use that
19871    if (typeof value.valueOf === 'function') {
19872      value = value.valueOf();
19873      if (isPrimitive(value)) return value;
19874    }
19875    // If `toString` is a valid function and not the one from `Object.prototype` use that
19876    if (hasCustomToString(value)) {
19877      value = value.toString();
19878      if (isPrimitive(value)) return value;
19879    }
19880    // We have a basic object so we use the position of the object in the collection
19881    return index;
19882  }
19883
19884  function getPredicateValue(value, index) {
19885    var type = typeof value;
19886    if (value === null) {
19887      type = 'string';
19888      value = 'null';
19889    } else if (type === 'string') {
19890      value = value.toLowerCase();
19891    } else if (type === 'object') {
19892      value = objectValue(value, index);
19893    }
19894    return { value: value, type: type };
19895  }
19896
19897  function compare(v1, v2) {
19898    var result = 0;
19899    if (v1.type === v2.type) {
19900      if (v1.value !== v2.value) {
19901        result = v1.value < v2.value ? -1 : 1;
19902      }
19903    } else {
19904      result = v1.type < v2.type ? -1 : 1;
19905    }
19906    return result;
19907  }
19908}
19909
19910function ngDirective(directive) {
19911  if (isFunction(directive)) {
19912    directive = {
19913      link: directive
19914    };
19915  }
19916  directive.restrict = directive.restrict || 'AC';
19917  return valueFn(directive);
19918}
19919
19920/**
19921 * @ngdoc directive
19922 * @name a
19923 * @restrict E
19924 *
19925 * @description
19926 * Modifies the default behavior of the html A tag so that the default action is prevented when
19927 * the href attribute is empty.
19928 *
19929 * This change permits the easy creation of action links with the `ngClick` directive
19930 * without changing the location or causing page reloads, e.g.:
19931 * `<a href="" ng-click="list.addItem()">Add Item</a>`
19932 */
19933var htmlAnchorDirective = valueFn({
19934  restrict: 'E',
19935  compile: function(element, attr) {
19936    if (!attr.href && !attr.xlinkHref) {
19937      return function(scope, element) {
19938        // If the linked element is not an anchor tag anymore, do nothing
19939        if (element[0].nodeName.toLowerCase() !== 'a') return;
19940
19941        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
19942        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
19943                   'xlink:href' : 'href';
19944        element.on('click', function(event) {
19945          // if we have no href url, then don't navigate anywhere.
19946          if (!element.attr(href)) {
19947            event.preventDefault();
19948          }
19949        });
19950      };
19951    }
19952  }
19953});
19954
19955/**
19956 * @ngdoc directive
19957 * @name ngHref
19958 * @restrict A
19959 * @priority 99
19960 *
19961 * @description
19962 * Using Angular markup like `{{hash}}` in an href attribute will
19963 * make the link go to the wrong URL if the user clicks it before
19964 * Angular has a chance to replace the `{{hash}}` markup with its
19965 * value. Until Angular replaces the markup the link will be broken
19966 * and will most likely return a 404 error. The `ngHref` directive
19967 * solves this problem.
19968 *
19969 * The wrong way to write it:
19970 * ```html
19971 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19972 * ```
19973 *
19974 * The correct way to write it:
19975 * ```html
19976 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19977 * ```
19978 *
19979 * @element A
19980 * @param {template} ngHref any string which can contain `{{}}` markup.
19981 *
19982 * @example
19983 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
19984 * in links and their different behaviors:
19985    <example>
19986      <file name="index.html">
19987        <input ng-model="value" /><br />
19988        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
19989        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
19990        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
19991        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
19992        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
19993        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
19994      </file>
19995      <file name="protractor.js" type="protractor">
19996        it('should execute ng-click but not reload when href without value', function() {
19997          element(by.id('link-1')).click();
19998          expect(element(by.model('value')).getAttribute('value')).toEqual('1');
19999          expect(element(by.id('link-1')).getAttribute('href')).toBe('');
20000        });
20001
20002        it('should execute ng-click but not reload when href empty string', function() {
20003          element(by.id('link-2')).click();
20004          expect(element(by.model('value')).getAttribute('value')).toEqual('2');
20005          expect(element(by.id('link-2')).getAttribute('href')).toBe('');
20006        });
20007
20008        it('should execute ng-click and change url when ng-href specified', function() {
20009          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
20010
20011          element(by.id('link-3')).click();
20012
20013          // At this point, we navigate away from an Angular page, so we need
20014          // to use browser.driver to get the base webdriver.
20015
20016          browser.wait(function() {
20017            return browser.driver.getCurrentUrl().then(function(url) {
20018              return url.match(/\/123$/);
20019            });
20020          }, 5000, 'page should navigate to /123');
20021        });
20022
20023        it('should execute ng-click but not reload when href empty string and name specified', function() {
20024          element(by.id('link-4')).click();
20025          expect(element(by.model('value')).getAttribute('value')).toEqual('4');
20026          expect(element(by.id('link-4')).getAttribute('href')).toBe('');
20027        });
20028
20029        it('should execute ng-click but not reload when no href but name specified', function() {
20030          element(by.id('link-5')).click();
20031          expect(element(by.model('value')).getAttribute('value')).toEqual('5');
20032          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
20033        });
20034
20035        it('should only change url when only ng-href', function() {
20036          element(by.model('value')).clear();
20037          element(by.model('value')).sendKeys('6');
20038          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
20039
20040          element(by.id('link-6')).click();
20041
20042          // At this point, we navigate away from an Angular page, so we need
20043          // to use browser.driver to get the base webdriver.
20044          browser.wait(function() {
20045            return browser.driver.getCurrentUrl().then(function(url) {
20046              return url.match(/\/6$/);
20047            });
20048          }, 5000, 'page should navigate to /6');
20049        });
20050      </file>
20051    </example>
20052 */
20053
20054/**
20055 * @ngdoc directive
20056 * @name ngSrc
20057 * @restrict A
20058 * @priority 99
20059 *
20060 * @description
20061 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
20062 * work right: The browser will fetch from the URL with the literal
20063 * text `{{hash}}` until Angular replaces the expression inside
20064 * `{{hash}}`. The `ngSrc` directive solves this problem.
20065 *
20066 * The buggy way to write it:
20067 * ```html
20068 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
20069 * ```
20070 *
20071 * The correct way to write it:
20072 * ```html
20073 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
20074 * ```
20075 *
20076 * @element IMG
20077 * @param {template} ngSrc any string which can contain `{{}}` markup.
20078 */
20079
20080/**
20081 * @ngdoc directive
20082 * @name ngSrcset
20083 * @restrict A
20084 * @priority 99
20085 *
20086 * @description
20087 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
20088 * work right: The browser will fetch from the URL with the literal
20089 * text `{{hash}}` until Angular replaces the expression inside
20090 * `{{hash}}`. The `ngSrcset` directive solves this problem.
20091 *
20092 * The buggy way to write it:
20093 * ```html
20094 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
20095 * ```
20096 *
20097 * The correct way to write it:
20098 * ```html
20099 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
20100 * ```
20101 *
20102 * @element IMG
20103 * @param {template} ngSrcset any string which can contain `{{}}` markup.
20104 */
20105
20106/**
20107 * @ngdoc directive
20108 * @name ngDisabled
20109 * @restrict A
20110 * @priority 100
20111 *
20112 * @description
20113 *
20114 * This directive sets the `disabled` attribute on the element if the
20115 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
20116 *
20117 * A special directive is necessary because we cannot use interpolation inside the `disabled`
20118 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20119 *
20120 * @example
20121    <example>
20122      <file name="index.html">
20123        <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
20124        <button ng-model="button" ng-disabled="checked">Button</button>
20125      </file>
20126      <file name="protractor.js" type="protractor">
20127        it('should toggle button', function() {
20128          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
20129          element(by.model('checked')).click();
20130          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
20131        });
20132      </file>
20133    </example>
20134 *
20135 * @element INPUT
20136 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
20137 *     then the `disabled` attribute will be set on the element
20138 */
20139
20140
20141/**
20142 * @ngdoc directive
20143 * @name ngChecked
20144 * @restrict A
20145 * @priority 100
20146 *
20147 * @description
20148 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
20149 *
20150 * Note that this directive should not be used together with {@link ngModel `ngModel`},
20151 * as this can lead to unexpected behavior.
20152 *
20153 * A special directive is necessary because we cannot use interpolation inside the `checked`
20154 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20155 *
20156 * @example
20157    <example>
20158      <file name="index.html">
20159        <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
20160        <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
20161      </file>
20162      <file name="protractor.js" type="protractor">
20163        it('should check both checkBoxes', function() {
20164          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
20165          element(by.model('master')).click();
20166          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
20167        });
20168      </file>
20169    </example>
20170 *
20171 * @element INPUT
20172 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
20173 *     then the `checked` attribute will be set on the element
20174 */
20175
20176
20177/**
20178 * @ngdoc directive
20179 * @name ngReadonly
20180 * @restrict A
20181 * @priority 100
20182 *
20183 * @description
20184 *
20185 * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
20186 *
20187 * A special directive is necessary because we cannot use interpolation inside the `readOnly`
20188 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20189 *
20190 * @example
20191    <example>
20192      <file name="index.html">
20193        <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
20194        <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
20195      </file>
20196      <file name="protractor.js" type="protractor">
20197        it('should toggle readonly attr', function() {
20198          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
20199          element(by.model('checked')).click();
20200          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
20201        });
20202      </file>
20203    </example>
20204 *
20205 * @element INPUT
20206 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
20207 *     then special attribute "readonly" will be set on the element
20208 */
20209
20210
20211/**
20212 * @ngdoc directive
20213 * @name ngSelected
20214 * @restrict A
20215 * @priority 100
20216 *
20217 * @description
20218 *
20219 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
20220 *
20221 * A special directive is necessary because we cannot use interpolation inside the `selected`
20222 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20223 *
20224 * @example
20225    <example>
20226      <file name="index.html">
20227        <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
20228        <select aria-label="ngSelected demo">
20229          <option>Hello!</option>
20230          <option id="greet" ng-selected="selected">Greetings!</option>
20231        </select>
20232      </file>
20233      <file name="protractor.js" type="protractor">
20234        it('should select Greetings!', function() {
20235          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
20236          element(by.model('selected')).click();
20237          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
20238        });
20239      </file>
20240    </example>
20241 *
20242 * @element OPTION
20243 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
20244 *     then special attribute "selected" will be set on the element
20245 */
20246
20247/**
20248 * @ngdoc directive
20249 * @name ngOpen
20250 * @restrict A
20251 * @priority 100
20252 *
20253 * @description
20254 *
20255 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
20256 *
20257 * A special directive is necessary because we cannot use interpolation inside the `open`
20258 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20259 *
20260 * @example
20261     <example>
20262       <file name="index.html">
20263         <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
20264         <details id="details" ng-open="open">
20265            <summary>Show/Hide me</summary>
20266         </details>
20267       </file>
20268       <file name="protractor.js" type="protractor">
20269         it('should toggle open', function() {
20270           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
20271           element(by.model('open')).click();
20272           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
20273         });
20274       </file>
20275     </example>
20276 *
20277 * @element DETAILS
20278 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
20279 *     then special attribute "open" will be set on the element
20280 */
20281
20282var ngAttributeAliasDirectives = {};
20283
20284// boolean attrs are evaluated
20285forEach(BOOLEAN_ATTR, function(propName, attrName) {
20286  // binding to multiple is not supported
20287  if (propName == "multiple") return;
20288
20289  function defaultLinkFn(scope, element, attr) {
20290    scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
20291      attr.$set(attrName, !!value);
20292    });
20293  }
20294
20295  var normalized = directiveNormalize('ng-' + attrName);
20296  var linkFn = defaultLinkFn;
20297
20298  if (propName === 'checked') {
20299    linkFn = function(scope, element, attr) {
20300      // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
20301      if (attr.ngModel !== attr[normalized]) {
20302        defaultLinkFn(scope, element, attr);
20303      }
20304    };
20305  }
20306
20307  ngAttributeAliasDirectives[normalized] = function() {
20308    return {
20309      restrict: 'A',
20310      priority: 100,
20311      link: linkFn
20312    };
20313  };
20314});
20315
20316// aliased input attrs are evaluated
20317forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
20318  ngAttributeAliasDirectives[ngAttr] = function() {
20319    return {
20320      priority: 100,
20321      link: function(scope, element, attr) {
20322        //special case ngPattern when a literal regular expression value
20323        //is used as the expression (this way we don't have to watch anything).
20324        if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
20325          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
20326          if (match) {
20327            attr.$set("ngPattern", new RegExp(match[1], match[2]));
20328            return;
20329          }
20330        }
20331
20332        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
20333          attr.$set(ngAttr, value);
20334        });
20335      }
20336    };
20337  };
20338});
20339
20340// ng-src, ng-srcset, ng-href are interpolated
20341forEach(['src', 'srcset', 'href'], function(attrName) {
20342  var normalized = directiveNormalize('ng-' + attrName);
20343  ngAttributeAliasDirectives[normalized] = function() {
20344    return {
20345      priority: 99, // it needs to run after the attributes are interpolated
20346      link: function(scope, element, attr) {
20347        var propName = attrName,
20348            name = attrName;
20349
20350        if (attrName === 'href' &&
20351            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
20352          name = 'xlinkHref';
20353          attr.$attr[name] = 'xlink:href';
20354          propName = null;
20355        }
20356
20357        attr.$observe(normalized, function(value) {
20358          if (!value) {
20359            if (attrName === 'href') {
20360              attr.$set(name, null);
20361            }
20362            return;
20363          }
20364
20365          attr.$set(name, value);
20366
20367          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
20368          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
20369          // to set the property as well to achieve the desired effect.
20370          // we use attr[attrName] value since $set can sanitize the url.
20371          if (msie && propName) element.prop(propName, attr[name]);
20372        });
20373      }
20374    };
20375  };
20376});
20377
20378/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20379 */
20380var nullFormCtrl = {
20381  $addControl: noop,
20382  $$renameControl: nullFormRenameControl,
20383  $removeControl: noop,
20384  $setValidity: noop,
20385  $setDirty: noop,
20386  $setPristine: noop,
20387  $setSubmitted: noop
20388},
20389SUBMITTED_CLASS = 'ng-submitted';
20390
20391function nullFormRenameControl(control, name) {
20392  control.$name = name;
20393}
20394
20395/**
20396 * @ngdoc type
20397 * @name form.FormController
20398 *
20399 * @property {boolean} $pristine True if user has not interacted with the form yet.
20400 * @property {boolean} $dirty True if user has already interacted with the form.
20401 * @property {boolean} $valid True if all of the containing forms and controls are valid.
20402 * @property {boolean} $invalid True if at least one containing control or form is invali
20402d.
20403 * @property {boolean} $pending True if at least one containing control or form is pending.
20404 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20405 *
20406 * @property {Object} $error Is an object hash, containing references to controls or
20407 *  forms with failing validators, where:
20408 *
20409 *  - keys are validation tokens (error names),
20410 *  - values are arrays of controls or forms that have a failing validator for given error name.
20411 *
20412 *  Built-in validation tokens:
20413 *
20414 *  - `email`
20415 *  - `max`
20416 *  - `maxlength`
20417 *  - `min`
20418 *  - `minlength`
20419 *  - `number`
20420 *  - `pattern`
20421 *  - `required`
20422 *  - `url`
20423 *  - `date`
20424 *  - `datetimelocal`
20425 *  - `time`
20426 *  - `week`
20427 *  - `month`
20428 *
20429 * @description
20430 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20431 * such as being valid/invalid or dirty/pristine.
20432 *
20433 * Each {@link ng.directive:form form} directive creates an instance
20434 * of `FormController`.
20435 *
20436 */
20437//asks for $scope to fool the BC controller module
20438FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20439function FormController(element, attrs, $scope, $animate, $interpolate) {
20440  var form = this,
20441      controls = [];
20442
20443  // init state
20444  form.$error = {};
20445  form.$$success = {};
20446  form.$pending = undefined;
20447  form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20448  form.$dirty = false;
20449  form.$pristine = true;
20450  form.$valid = true;
20451  form.$invalid = false;
20452  form.$submitted = false;
20453  form.$$parentForm = nullFormCtrl;
20454
20455  /**
20456   * @ngdoc method
20457   * @name form.FormController#$rollbackViewValue
20458   *
20459   * @description
20460   * Rollback all form controls pending updates to the `$modelValue`.
20461   *
20462   * Updates may be pending by a debounced event or because the input is waiting for a some future
20463   * event defined in `ng-model-options`. This method is typically needed by the reset button of
20464   * a form that uses `ng-model-options` to pend updates.
20465   */
20466  form.$rollbackViewValue = function() {
20467    forEach(controls, function(control) {
20468      control.$rollbackViewValue();
20469    });
20470  };
20471
20472  /**
20473   * @ngdoc method
20474   * @name form.FormController#$commitViewValue
20475   *
20476   * @description
20477   * Commit all form controls pending updates to the `$modelValue`.
20478   *
20479   * Updates may be pending by a debounced event or because the input is waiting for a some future
20480   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20481   * usually handles calling this in response to input events.
20482   */
20483  form.$commitViewValue = function() {
20484    forEach(controls, function(control) {
20485      control.$commitViewValue();
20486    });
20487  };
20488
20489  /**
20490   * @ngdoc method
20491   * @name form.FormController#$addControl
20492   * @param {object} control control object, either a {@link form.FormController} or an
20493   * {@link ngModel.NgModelController}
20494   *
20495   * @description
20496   * Register a control with the form. Input elements using ngModelController do this automatically
20497   * when they are linked.
20498   *
20499   * Note that the current state of the control will not be reflected on the new parent form. This
20500   * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20501   * state.
20502   *
20503   * However, if the method is used programmatically, for example by adding dynamically created controls,
20504   * or controls that have been previously removed without destroying their corresponding DOM element,
20505   * it's the developers responsiblity to make sure the current state propagates to the parent form.
20506   *
20507   * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20508   * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20509   */
20510  form.$addControl = function(control) {
20511    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
20512    // and not added to the scope.  Now we throw an error.
20513    assertNotHasOwnProperty(control.$name, 'input');
20514    controls.push(control);
20515
20516    if (control.$name) {
20517      form[control.$name] = control;
20518    }
20519
20520    control.$$parentForm = form;
20521  };
20522
20523  // Private API: rename a form control
20524  form.$$renameControl = function(control, newName) {
20525    var oldName = control.$name;
20526
20527    if (form[oldName] === control) {
20528      delete form[oldName];
20529    }
20530    form[newName] = control;
20531    control.$name = newName;
20532  };
20533
20534  /**
20535   * @ngdoc method
20536   * @name form.FormController#$removeControl
20537   * @param {object} control control object, either a {@link form.FormController} or an
20538   * {@link ngModel.NgModelController}
20539   *
20540   * @description
20541   * Deregister a control from the form.
20542   *
20543   * Input elements using ngModelController do this automatically when they are destroyed.
20544   *
20545   * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20546   * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20547   * different from case to case. For example, removing the only `$dirty` control from a form may or
20548   * may not mean that the form is still `$dirty`.
20549   */
20550  form.$removeControl = function(control) {
20551    if (control.$name && form[control.$name] === control) {
20552      delete form[control.$name];
20553    }
20554    forEach(form.$pending, function(value, name) {
20555      form.$setValidity(name, null, control);
20556    });
20557    forEach(form.$error, function(value, name) {
20558      form.$setValidity(name, null, control);
20559    });
20560    forEach(form.$$success, function(value, name) {
20561      form.$setValidity(name, null, control);
20562    });
20563
20564    arrayRemove(controls, control);
20565    control.$$parentForm = nullFormCtrl;
20566  };
20567
20568
20569  /**
20570   * @ngdoc method
20571   * @name form.FormController#$setValidity
20572   *
20573   * @description
20574   * Sets the validity of a form control.
20575   *
20576   * This method will also propagate to parent forms.
20577   */
20578  addSetValidityMethod({
20579    ctrl: this,
20580    $element: element,
20581    set: function(object, property, controller) {
20582      var list = object[property];
20583      if (!list) {
20584        object[property] = [controller];
20585      } else {
20586        var index = list.indexOf(controller);
20587        if (index === -1) {
20588          list.push(controller);
20589        }
20590      }
20591    },
20592    unset: function(object, property, controller) {
20593      var list = object[property];
20594      if (!list) {
20595        return;
20596      }
20597      arrayRemove(list, controller);
20598      if (list.length === 0) {
20599        delete object[property];
20600      }
20601    },
20602    $animate: $animate
20603  });
20604
20605  /**
20606   * @ngdoc method
20607   * @name form.FormController#$setDirty
20608   *
20609   * @description
20610   * Sets the form to a dirty state.
20611   *
20612   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20613   * state (ng-dirty class). This method will also propagate to parent forms.
20614   */
20615  form.$setDirty = function() {
20616    $animate.removeClass(element, PRISTINE_CLASS);
20617    $animate.addClass(element, DIRTY_CLASS);
20618    form.$dirty = true;
20619    form.$pristine = false;
20620    form.$$parentForm.$setDirty();
20621  };
20622
20623  /**
20624   * @ngdoc method
20625   * @name form.FormController#$setPristine
20626   *
20627   * @description
20628   * Sets the form to its pristine state.
20629   *
20630   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20631   * state (ng-pristine class). This method will also propagate to all the controls contained
20632   * in this form.
20633   *
20634   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20635   * saving or resetting it.
20636   */
20637  form.$setPristine = function() {
20638    $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20639    form.$dirty = false;
20640    form.$pristine = true;
20641    form.$submitted = false;
20642    forEach(controls, function(control) {
20643      control.$setPristine();
20644    });
20645  };
20646
20647  /**
20648   * @ngdoc method
20649   * @name form.FormController#$setUntouched
20650   *
20651   * @description
20652   * Sets the form to its untouched state.
20653   *
20654   * This method can be called to remove the 'ng-touched' class and set the form controls to their
20655   * untouched state (ng-untouched class).
20656   *
20657   * Setting a form controls back to their untouched state is often useful when setting the form
20658   * back to its pristine state.
20659   */
20660  form.$setUntouched = function() {
20661    forEach(controls, function(control) {
20662      control.$setUntouched();
20663    });
20664  };
20665
20666  /**
20667   * @ngdoc method
20668   * @name form.FormController#$setSubmitted
20669   *
20670   * @description
20671   * Sets the form to its submitted state.
20672   */
20673  form.$setSubmitted = function() {
20674    $animate.addClass(element, SUBMITTED_CLASS);
20675    form.$submitted = true;
20676    form.$$parentForm.$setSubmitted();
20677  };
20678}
20679
20680/**
20681 * @ngdoc directive
20682 * @name ngForm
20683 * @restrict EAC
20684 *
20685 * @description
20686 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20687 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20688 * sub-group of controls needs to be determined.
20689 *
20690 * Note: the purpose of `ngForm` is to group controls,
20691 * but not to be a replacement for the `<form>` tag with all of its capabilities
20692 * (e.g. posting to the server, ...).
20693 *
20694 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20695 *                       related scope, under this name.
20696 *
20697 */
20698
20699 /**
20700 * @ngdoc directive
20701 * @name form
20702 * @restrict E
20703 *
20704 * @description
20705 * Directive that instantiates
20706 * {@link form.FormController FormController}.
20707 *
20708 * If the `name` attribute is specified, the form controller is published onto the current scope under
20709 * this name.
20710 *
20711 * # Alias: {@link ng.directive:ngForm `ngForm`}
20712 *
20713 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20714 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20715 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
20716 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
20717 * of controls needs to be determined.
20718 *
20719 * # CSS classes
20720 *  - `ng-valid` is set if the form is valid.
20721 *  - `ng-invalid` is set if the form is invalid.
20722 *  - `ng-pending` is set if the form is pending.
20723 *  - `ng-pristine` is set if the form is pristine.
20724 *  - `ng-dirty` is set if the form is dirty.
20725 *  - `ng-submitted` is set if the form was submitted.
20726 *
20727 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20728 *
20729 *
20730 * # Submitting a form and preventing the default action
20731 *
20732 * Since the role of forms in client-side Angular applications is different than in classical
20733 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20734 * page reload that sends the data to the server. Instead some javascript logic should be triggered
20735 * to handle the form submission in an application-specific way.
20736 *
20737 * For this reason, Angular prevents the default action (form submission to the server) unless the
20738 * `<form>` element has an `action` attribute specified.
20739 *
20740 * You can use one of the following two ways to specify what javascript method should be called when
20741 * a form is submitted:
20742 *
20743 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20744 * - {@link ng.directive:ngClick ngClick} directive on the first
20745  *  button or input field of type submit (input[type=submit])
20746 *
20747 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20748 * or {@link ng.directive:ngClick ngClick} directives.
20749 * This is because of the following form submission rules in the HTML specification:
20750 *
20751 * - If a form has only one input field then hitting enter in this field triggers form submit
20752 * (`ngSubmit`)
20753 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20754 * doesn't trigger submit
20755 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20756 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20757 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20758 *
20759 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20760 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20761 * to have access to the updated model.
20762 *
20763 * ## Animation Hooks
20764 *
20765 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20766 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20767 * other validations that are performed within the form. Animations in ngForm are similar to how
20768 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20769 * as JS animations.
20770 *
20771 * The following example shows a simple way to utilize CSS transitions to style a form element
20772 * that has been rendered as invalid after it has been validated:
20773 *
20774 * <pre>
20775 * //be sure to include ngAnimate as a module to hook into more
20776 * //advanced animations
20777 * .my-form {
20778 *   transition:0.5s linear all;
20779 *   background: white;
20780 * }
20781 * .my-form.ng-invalid {
20782 *   background: red;
20783 *   color:white;
20784 * }
20785 * </pre>
20786 *
20787 * @example
20788    <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20789      <file name="index.html">
20790       <script>
20791         angular.module('formExample', [])
20792           .controller('FormController', ['$scope', function($scope) {
20793             $scope.userType = 'guest';
20794           }]);
20795       </script>
20796       <style>
20797        .my-form {
20798          transition:all linear 0.5s;
20799          background: transparent;
20800        }
20801        .my-form.ng-invalid {
20802          background: red;
20803        }
20804       </style>
20805       <form name="myForm" ng-controller="FormController" class="my-form">
20806         userType: <input name="input" ng-model="userType" required>
20807         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20808         <code>userType = {{userType}}</code><br>
20809         <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20810         <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20811         <code>myForm.$valid = {{myForm.$valid}}</code><br>
20812         <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20813        </form>
20814      </file>
20815      <file name="protractor.js" type="protractor">
20816        it('should initialize to model', function() {
20817          var userType = element(by.binding('userType'));
20818          var valid = element(by.binding('myForm.input.$valid'));
20819
20820          expect(userType.getText()).toContain('guest');
20821          expect(valid.getText()).toContain('true');
20822        });
20823
20824        it('should be invalid if empty', function() {
20825          var userType = element(by.binding('userType'));
20826          var valid = element(by.binding('myForm.input.$valid'));
20827          var userInput = element(by.model('userType'));
20828
20829          userInput.clear();
20830          userInput.sendKeys('');
20831
20832          expect(userType.getText()).toEqual('userType =');
20833          expect(valid.getText()).toContain('false');
20834        });
20835      </file>
20836    </example>
20837 *
20838 * @param {string=} name Name of the form. If specified, the form controller will be published into
20839 *                       related scope, under this name.
20840 */
20841var formDirectiveFactory = function(isNgForm) {
20842  return ['$timeout', '$parse', function($timeout, $parse) {
20843    var formDirective = {
20844      name: 'form',
20845      restrict: isNgForm ? 'EAC' : 'E',
20846      require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20847      controller: FormController,
20848      compile: function ngFormCompile(formElement, attr) {
20849        // Setup initial state of the control
20850        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20851
20852        var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20853
20854        return {
20855          pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20856            var controller = ctrls[0];
20857
20858            // if `action` attr is not present on the form, prevent the default action (submission)
20859            if (!('action' in attr)) {
20860              // we can't use jq events because if a form is destroyed during submission the default
20861              // action is not prevented. see #1238
20862              //
20863              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20864              // page reload if the form was destroyed by submission of the form via a click handler
20865              // on a button in the form. Looks like an IE9 specific bug.
20866              var handleFormSubmission = function(event) {
20867                scope.$apply(function() {
20868                  controller.$commitViewValue();
20869                  controller.$setSubmitted();
20870                });
20871
20872                event.preventDefault();
20873              };
20874
20875              addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20876
20877              // unregister the preventDefault listener so that we don't not leak memory but in a
20878              // way that will achieve the prevention of the default action.
20879              formElement.on('$destroy', function() {
20880                $timeout(function() {
20881                  removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20882                }, 0, false);
20883              });
20884            }
20885
20886            var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20887            parentFormCtrl.$addControl(controller);
20888
20889            var setter = nameAttr ? getSetter(controller.$name) : noop;
20890
20891            if (nameAttr) {
20892              setter(scope, controller);
20893              attr.$observe(nameAttr, function(newValue) {
20894                if (controller.$name === newValue) return;
20895                setter(scope, undefined);
20896                controller.$$parentForm.$$renameControl(controller, newValue);
20897                setter = getSetter(controller.$name);
20898                setter(scope, controller);
20899              });
20900            }
20901            formElement.on('$destroy', function() {
20902              controller.$$parentForm.$removeControl(controller);
20903              setter(scope, undefined);
20904              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
20905            });
20906          }
20907        };
20908      }
20909    };
20910
20911    return formDirective;
20912
20913    function getSetter(expression) {
20914      if (expression === '') {
20915        //create an assignable expression, so forms with an empty name can be renamed later
20916        return $parse('this[""]').assign;
20917      }
20918      return $parse(expression).assign || noop;
20919    }
20920  }];
20921};
20922
20923var formDirective = formDirectiveFactory();
20924var ngFormDirective = formDirectiveFactory(true);
20925
20926/* global VALID_CLASS: false,
20927  INVALID_CLASS: false,
20928  PRISTINE_CLASS: false,
20929  DIRTY_CLASS: false,
20930  UNTOUCHED_CLASS: false,
20931  TOUCHED_CLASS: false,
20932  ngModelMinErr: false,
20933*/
20934
20935// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
20936var 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)/;
20937// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
20938// Note: We are being more lenient, because browsers are too.
20939//   1. Scheme
20940//   2. Slashes
20941//   3. Username
20942//   4. Password
20943//   5. Hostname
20944//   6. Port
20945//   7. Path
20946//   8. Query
20947//   9. Fragment
20948//                 1111111111111111 222   333333    44444        555555555555555555555555    666     77777777     8888888     999
20949var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
20950var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
20951var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
20952var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
20953var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20954var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
20955var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
20956var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20957
20958var inputType = {
20959
20960  /**
20961   * @ngdoc input
20962   * @name input[text]
20963   *
20964   * @description
20965   * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
20966   *
20967   *
20968   * @param {string} ngModel Assignable angular expression to data-bind to.
20969   * @param {string=} name Property name of the form under which the control is published.
20970   * @param {string=} required Adds `required` validation error key if the value is not entered.
20971   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20972   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20973   *    `required` when you want to data-bind to the `required` attribute.
20974   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20975   *    minlength.
20976   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20977   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
20978   *    any length.
20979   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
20980   *    that contains the regular expression body that will be converted to a regular expression
20981   *    as in the ngPattern directive.
20982   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
20983   *    a RegExp found by evaluating the Angular expression given in the attribute value.
20984   *    If the expression evaluates to a RegExp object, then this is used directly.
20985   *    If the expression evaluates to a string, then it will be converted to a RegExp
20986   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
20987   *    `new RegExp('^abc$')`.<br />
20988   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
20989   *    start at the index of the last search's match, thus not taking the whole input value into
20990   *    account.
20991   * @param {string=} ngChange Angular expression to be executed when input changes due to user
20992   *    interaction with the input element.
20993   * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20994   *    This parameter is ignored for input[type=password] controls, which will never trim the
20995   *    input.
20996   *
20997   * @example
20998      <example name="text-input-directive" module="textInputExample">
20999        <file name="index.html">
21000         <script>
21001           angular.module('textInputExample', [])
21002             .controller('ExampleController', ['$scope', function($scope) {
21003               $scope.example = {
21004                 text: 'guest',
21005                 word: /^\s*\w*\s*$/
21006               };
21007             }]);
21008         </script>
21009         <form name="myForm" ng-controller="ExampleController">
21010           <label>Single word:
21011             <input type="text" name="input" ng-model="example.text"
21012                    ng-pattern="example.word" required ng-trim="false">
21013           </label>
21014           <div role="alert">
21015             <span class="error" ng-show="myForm.input.$error.required">
21016               Required!</span>
21017             <span class="error" ng-show="myForm.input.$error.pattern">
21018               Single word only!</span>
21019           </div>
21020           <tt>text = {{example.text}}</tt><br/>
21021           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21022           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21023           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21024           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21025          </form>
21026        </file>
21027        <file name="protractor.js" type="protractor">
21028          var text = element(by.binding('example.text'));
21029          var valid = element(by.binding('myForm.input.$valid'));
21030          var input = element(by.model('example.text'));
21031
21032          it('should initialize to model', function() {
21033            expect(text.getText()).toContain('guest');
21034            expect(valid.getText()).toContain('true');
21035          });
21036
21037          it('should be invalid if empty', function() {
21038            input.clear();
21039            input.sendKeys('');
21040
21041            expect(text.getText()).toEqual('text =');
21042            expect(valid.getText()).toContain('false');
21043          });
21044
21045          it('should be invalid if multi word', function() {
21046            input.clear();
21047            input.sendKeys('hello world');
21048
21049            expect(valid.getText()).toContain('false');
21050          });
21051        </file>
21052      </example>
21053   */
21054  'text': textInputType,
21055
21056    /**
21057     * @ngdoc input
21058     * @name input[date]
21059     *
21060     * @description
21061     * Input with date validation and transformation. In browsers that do not yet support
21062     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
21063     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
21064     * modern browsers do not yet support this input type, it is important to provide cues to users on the
21065     * expected input format via a placeholder or label.
21066     *
21067     * The model must always be a Date object, otherwise Angular will throw an error.
21068     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21069     *
21070     * The timezone to be used to read/write the `Date` instance in the model can be defined using
21071     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21072     *
21073     * @param {string} ngModel Assignable angular expression to data-bind to.
21074     * @param {string=} name Property name of the form under which the control is published.
21075     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
21076     *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
21077     *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
21078     *   constraint validation.
21079     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
21080     *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
21081     *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
21082     *   constraint validation.
21083     * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
21084     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21085     * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
21086     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21087     * @param {string=} required Sets `required` validation error key if the value is not entered.
21088     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21089     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21090     *    `required` when you want to data-bind to the `required` attribute.
21091     * @param {string=} ngChange Angular expression to be executed when input changes due to user
21092     *    interaction with the input element.
21093     *
21094     * @example
21095     <example name="date-input-directive" module="dateInputExample">
21096     <file name="index.html">
21097       <script>
21098          angular.module('dateInputExample', [])
21099            .controller('DateController', ['$scope', function($scope) {
21100              $scope.example = {
21101                value: new Date(2013, 9, 22)
21102              };
21103            }]);
21104       </script>
21105       <form name="myForm" ng-controller="DateController as dateCtrl">
21106          <label for="exampleInput">Pick a date in 2013:</label>
21107          <input type="date" id="exampleInput" name="input" ng-model="example.value"
21108              placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
21109          <div role="alert">
21110            <span class="error" ng-show="myForm.input.$error.required">
21111                Required!</span>
21112            <span class="error" ng-show="myForm.input.$error.date">
21113                Not a valid date!</span>
21114           </div>
21115           <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
21116           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21117           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21118           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21119           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21120       </form>
21121     </file>
21122     <file name="protractor.js" type="protractor">
21123        var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
21124        var valid = element(by.binding('myForm.input.$valid'));
21125        var input = element(by.model('example.value'));
21126
21127        // currently protractor/webdriver does not support
21128        // sending keys to all known HTML5 input controls
21129        // for various browsers (see https://github.com/angular/protractor/issues/562).
21130        function setInput(val) {
21131          // set the value of the element and force validation.
21132          var scr = "var ipt = document.getElementById('exampleInput'); " +
21133          "ipt.value = '" + val + "';" +
21134          "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21135          browser.executeScript(scr);
21136        }
21137
21138        it('should initialize to model', function() {
21139          expect(value.getText()).toContain('2013-10-22');
21140          expect(valid.getText()).toContain('myForm.input.$valid = true');
21141        });
21142
21143        it('should be invalid if empty', function() {
21144          setInput('');
21145          expect(value.getText()).toEqual('value =');
21146          expect(valid.getText()).toContain('myForm.input.$valid = false');
21147        });
21148
21149        it('should be invalid if over max', function() {
21150          setInput('2015-01-01');
21151          expect(value.getText()).toContain('');
21152          expect(valid.getText()).toContain('myForm.input.$valid = false');
21153        });
21154     </file>
21155     </example>
21156     */
21157  'date': createDateInputType('date', DATE_REGEXP,
21158         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
21159         'yyyy-MM-dd'),
21160
21161   /**
21162    * @ngdoc input
21163    * @name input[datetime-local]
21164    *
21165    * @description
21166    * Input with datetime validation and transformation. In browsers that do not yet support
21167    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21168    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
21169    *
21170    * The model must always be a Date object, otherwise Angular will throw an error.
21171    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21172    *
21173    * The timezone to be used to read/write the `Date` instance in the model can be defined using
21174    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21175    *
21176    * @param {string} ngModel Assignable angular expression to data-bind to.
21177    * @param {string=} name Property name of the form under which the control is published.
21178    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21179    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
21180    *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
21181    *   Note that `min` will also add native HTML5 constraint validation.
21182    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21183    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
21184    *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
21185    *   Note that `max` will also add native HTML5 constraint validation.
21186    * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
21187    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21188    * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
21189    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21190    * @param {string=} required Sets `required` validation error key if the value is not entered.
21191    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21192    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21193    *    `required` when you want to data-bind to the `required` attribute.
21194    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21195    *    interaction with the input element.
21196    *
21197    * @example
21198    <example name="datetimelocal-input-directive" module="dateExample">
21199    <file name="index.html">
21200      <script>
21201        angular.module('dateExample', [])
21202          .controller('DateController', ['$scope', function($scope) {
21203            $scope.example = {
21204              value: new Date(2010, 11, 28, 14, 57)
21205            };
21206          }]);
21207      </script>
21208      <form name="myForm" ng-controller="DateController as dateCtrl">
21209        <label for="exampleInput">Pick a date between in 2013:</label>
21210        <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
21211            placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
21212        <div role="alert">
21213          <span class="error" ng-show="myForm.input.$error.required">
21214              Required!</span>
21215          <span class="error" ng-show="myForm.input.$error.datetimelocal">
21216              Not a valid date!</span>
21217        </div>
21218        <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
21219        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21220        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21221        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21222        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21223      </form>
21224    </file>
21225    <file name="protractor.js" type="protractor">
21226      var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
21227      var valid = element(by.binding('myForm.input.$valid'));
21228      var input = element(by.model('example.value'));
21229
21230      // currently protractor/webdriver does not support
21231      // sending keys to all known HTML5 input controls
21232      // for various browsers (https://github.com/angular/protractor/issues/562).
21233      function setInput(val) {
21234        // set the value of the element and force validation.
21235        var scr = "var ipt = document.getElementById('exampleInput'); " +
21236        "ipt.value = '" + val + "';" +
21237        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21238        browser.executeScript(scr);
21239      }
21240
21241      it('should initialize to model', function() {
21242        expect(value.getText()).toContain('2010-12-28T14:57:00');
21243        expect(valid.getText()).toContain('myForm.input.$valid = true');
21244      });
21245
21246      it('should be invalid if empty', function() {
21247        setInput('');
21248        expect(value.getText()).toEqual('value =');
21249        expect(valid.getText()).toContain('myForm.input.$valid = false');
21250      });
21251
21252      it('should be invalid if over max', function() {
21253        setInput('2015-01-01T23:59:00');
21254        expect(value.getText()).toContain('');
21255        expect(valid.getText()).toContain('myForm.input.$valid = false');
21256      });
21257    </file>
21258    </example>
21259    */
21260  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
21261      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
21262      'yyyy-MM-ddTHH:mm:ss.sss'),
21263
21264  /**
21265   * @ngdoc input
21266   * @name input[time]
21267   *
21268   * @description
21269   * Input with time validation and transformation. In browsers that do not yet support
21270   * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21271   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
21272   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
21273   *
21274   * The model must always be a Date object, otherwise Angular will throw an error.
21275   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21276   *
21277   * The timezone to be used to read/write the `Date` instance in the model can be defined using
21278   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21279   *
21280   * @param {string} ngModel Assignable angular expression to data-bind to.
21281   * @param {string=} name Property name of the form under which the control is published.
21282   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21283   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21284   *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
21285   *   native HTML5 constraint validation.
21286   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21287   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21288   *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
21289   *   native HTML5 constraint validation.
21290   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
21291   *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21292   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
21293   *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21294   * @param {string=} required Sets `required` validation error key if the value is not entered.
21295   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21296   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21297   *    `required` when you want to data-bind to the `required` attribute.
21298   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21299   *    interaction with the input element.
21300   *
21301   * @example
21302   <example name="time-input-directive" module="timeExample">
21303   <file name="index.html">
21304     <script>
21305      angular.module('timeExample', [])
21306        .controller('DateController', ['$scope', function($scope) {
21307          $scope.example = {
21308            value: new Date(1970, 0, 1, 14, 57, 0)
21309          };
21310        }]);
21311     </script>
21312     <form name="myForm" ng-controller="DateController as dateCtrl">
21313        <label for="exampleInput">Pick a between 8am and 5pm:</label>
21314        <input type="time" id="exampleInput" name="input" ng-model="example.value"
21315            placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
21316        <div role="alert">
21317          <span class="error" ng-show="myForm.input.$error.required">
21318              Required!</span>
21319          <span class="error" ng-show="myForm.input.$error.time">
21320              Not a valid date!</span>
21321        </div>
21322        <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
21323        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21324        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21325        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21326        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21327     </form>
21328   </file>
21329   <file name="protractor.js" type="protractor">
21330      var value = element(by.binding('example.value | date: "HH:mm:ss"'));
21331      var valid = element(by.binding('myForm.input.$valid'));
21332      var input = element(by.model('example.value'));
21333
21334      // currently protractor/webdriver does not support
21335      // sending keys to all known HTML5 input controls
21336      // for various browsers (https://github.com/angular/protractor/issues/562).
21337      function setInput(val) {
21338        // set the value of the element and force validation.
21339        var scr = "var ipt = document.getElementById('exampleInput'); " +
21340        "ipt.value = '" + val + "';" +
21341        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21342        browser.executeScript(scr);
21343      }
21344
21345      it('should initialize to model', function() {
21346        expect(value.getText()).toContain('14:57:00');
21347        expect(valid.getText()).toContain('myForm.input.$valid = true');
21348      });
21349
21350      it('should be invalid if empty', function() {
21351        setInput('');
21352        expect(value.getText()).toEqual('value =');
21353        expect(valid.getText()).toContain('myForm.input.$valid = false');
21354      });
21355
21356      it('should be invalid if over max', function() {
21357        setInput('23:59:00');
21358        expect(value.getText()).toContain('');
21359        expect(valid.getText()).toContain('myForm.input.$valid = false');
21360      });
21361   </file>
21362   </example>
21363   */
21364  'time': createDateInputType('time', TIME_REGEXP,
21365      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
21366     'HH:mm:ss.sss'),
21367
21368   /**
21369    * @ngdoc input
21370    * @name input[week]
21371    *
21372    * @description
21373    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
21374    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21375    * week format (yyyy-W##), for example: `2013-W02`.
21376    *
21377    * The model must always be a Date object, otherwise Angular will throw an error.
21378    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21379    *
21380    * The timezone to be used to read/write the `Date` instance in the model can be defined using
21381    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21382    *
21383    * @param {string} ngModel Assignable angular expression to data-bind to.
21384    * @param {string=} name Property name of the form under which the control is published.
21385    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21386    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21387    *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21388    *   native HTML5 constraint validation.
21389    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21390    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21391    *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21392    *   native HTML5 constraint validation.
21393    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21394    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21395    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21396    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21397    * @param {string=} required Sets `required` validation error key if the value is not entered.
21398    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21399    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21400    *    `required` when you want to data-bind to the `required` attribute.
21401    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21402    *    interaction with the input element.
21403    *
21404    * @example
21405    <example name="week-input-directive" module="weekExample">
21406    <file name="index.html">
21407      <script>
21408      angular.module('weekExample', [])
21409        .controller('DateController', ['$scope', function($scope) {
21410          $scope.example = {
21411            value: new Date(2013, 0, 3)
21412          };
21413        }]);
21414      </script>
21415      <form name="myForm" ng-controller="DateController as dateCtrl">
21416        <label>Pick a date between in 2013:
21417          <input id="exampleInput" type="week" name="input" ng-model="example.value"
21418                 placeholder="YYYY-W##" min="2012-W32"
21419                 max="2013-W52" required />
21420        </label>
21421        <div role="alert">
21422          <span class="error" ng-show="myForm.input.$error.required">
21423              Required!</span>
21424          <span class="error" ng-show="myForm.input.$error.week">
21425              Not a valid date!</span>
21426        </div>
21427        <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21428        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21429        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21430        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21431        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21432      </form>
21433    </file>
21434    <file name="protractor.js" type="protractor">
21435      var value = element(by.binding('example.value | date: "yyyy-Www"'));
21436      var valid = element(by.binding('myForm.input.$valid'));
21437      var input = element(by.model('example.value'));
21438
21439      // currently protractor/webdriver does not support
21440      // sending keys to all known HTML5 input controls
21441      // for various browsers (https://github.com/angular/protractor/issues/562).
21442      function setInput(val) {
21443        // set the value of the element and force validation.
21444        var scr = "var ipt = document.getElementById('exampleInput'); " +
21445        "ipt.value = '" + val + "';" +
21446        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21447        browser.executeScript(scr);
21448      }
21449
21450      it('should initialize to model', function() {
21451        expect(value.getText()).toContain('2013-W01');
21452        expect(valid.getText()).toContain('myForm.input.$valid = true');
21453      });
21454
21455      it('should be invalid if empty', function() {
21456        setInput('');
21457        expect(value.getText()).toEqual('value =');
21458        expect(valid.getText()).toContain('myForm.input.$valid = false');
21459      });
21460
21461      it('should be invalid if over max', function() {
21462        setInput('2015-W01');
21463        expect(value.getText()).toContain('');
21464        expect(valid.getText()).toContain('myForm.input.$valid = false');
21465      });
21466    </file>
21467    </example>
21468    */
21469  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21470
21471  /**
21472   * @ngdoc input
21473   * @name input[month]
21474   *
21475   * @description
21476   * Input with month validation and transformation. In browsers that do not yet support
21477   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21478   * month format (yyyy-MM), for example: `2009-01`.
21479   *
21480   * The model must always be a Date object, otherwise Angular will throw an error.
21481   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21482   * If the model is not set to the first of the month, the next view to model update will set it
21483   * to the first of the month.
21484   *
21485   * The timezone to be used to read/write the `Date` instance in the model can be defined using
21486   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21487   *
21488   * @param {string} ngModel Assignable angular expression to data-bind to.
21489   * @param {string=} name Property name of the form under which the control is published.
21490   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21491   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21492   *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21493   *   native HTML5 constraint validation.
21494   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21495   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21496   *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21497   *   native HTML5 constraint validation.
21498   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21499   *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21500   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21501   *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21502
21503   * @param {string=} required Sets `required` validation error key if the value is not entered.
21504   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21505   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21506   *    `required` when you want to data-bind to the `required` attribute.
21507   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21508   *    interaction with the input element.
21509   *
21510   * @example
21511   <example name="month-input-directive" module="monthExample">
21512   <file name="index.html">
21513     <script>
21514      angular.module('monthExample', [])
21515        .controller('DateController', ['$scope', function($scope) {
21516          $scope.example = {
21517            value: new Date(2013, 9, 1)
21518          };
21519        }]);
21520     </script>
21521     <form name="myForm" ng-controller="DateController as dateCtrl">
21522       <label for="exampleInput">Pick a month in 2013:</label>
21523       <input id="exampleInput" type="month" name="input" ng-model="example.value"
21524          placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
21525       <div role="alert">
21526         <span class="error" ng-show="myForm.input.$error.required">
21527            Required!</span>
21528         <span class="error" ng-show="myForm.input.$error.month">
21529            Not a valid month!</span>
21530       </div>
21531       <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
21532       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21533       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21534       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21535       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21536     </form>
21537   </file>
21538   <file name="protractor.js" type="protractor">
21539      var value = element(by.binding('example.value | date: "yyyy-MM"'));
21540      var valid = element(by.binding('myForm.input.$valid'));
21541      var input = element(by.model('example.value'));
21542
21543      // currently protractor/webdriver does not support
21544      // sending keys to all known HTML5 input controls
21545      // for various browsers (https://github.com/angular/protractor/issues/562).
21546      function setInput(val) {
21547        // set the value of the element and force validation.
21548        var scr = "var ipt = document.getElementById('exampleInput'); " +
21549        "ipt.value = '" + val + "';" +
21550        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21551        browser.executeScript(scr);
21552      }
21553
21554      it('should initialize to model', function() {
21555        expect(value.getText()).toContain('2013-10');
21556        expect(valid.getText()).toContain('myForm.input.$valid = true');
21557      });
21558
21559      it('should be invalid if empty', function() {
21560        setInput('');
21561        expect(value.getText()).toEqual('value =');
21562        expect(valid.getText()).toContain('myForm.input.$valid = false');
21563      });
21564
21565      it('should be invalid if over max', function() {
21566        setInput('2015-01');
21567        expect(value.getText()).toContain('');
21568        expect(valid.getText()).toContain('myForm.input.$valid = false');
21569      });
21570   </file>
21571   </example>
21572   */
21573  'month': createDateInputType('month', MONTH_REGEXP,
21574     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
21575     'yyyy-MM'),
21576
21577  /**
21578   * @ngdoc input
21579   * @name input[number]
21580   *
21581   * @description
21582   * Text input with number validation and transformation. Sets the `number` validation
21583   * error if not a valid number.
21584   *
21585   * <div class="alert alert-warning">
21586   * The model must always be of type `number` otherwise Angular will throw an error.
21587   * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
21588   * error docs for more information and an example of how to convert your model if necessary.
21589   * </div>
21590   *
21591   * ## Issues with HTML5 constraint validation
21592   *
21593   * In browsers that follow the
21594   * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
21595   * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
21596   * If a non-number is entered in the input, the browser will report the value as an empty string,
21597   * which means the view / model values in `ngModel` and subsequently the scope value
21598   * will also be an empty string.
21599   *
21600   *
21601   * @param {string} ngModel Assignable angular expression to data-bind to.
21602   * @param {string=} name Property name of the form under which the control is published.
21603   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21604   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21605   * @param {string=} required Sets `required` validation error key if the value is not entered.
21606   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21607   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21608   *    `required` when you want to data-bind to the `required` attribute.
21609   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21610   *    minlength.
21611   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21612   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21613   *    any length.
21614   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21615   *    that contains the regular expression body that will be converted to a regular expression
21616   *    as in the ngPattern directive.
21617   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21618   *    a RegExp found by evaluating the Angular expression given in the attribute value.
21619   *    If the expression evaluates to a RegExp object, then this is used directly.
21620   *    If the expression evaluates to a string, then it will be converted to a RegExp
21621   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21622   *    `new RegExp('^abc$')`.<br />
21623   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21624   *    start at the index of the last search's match, thus not taking the whole input value into
21625   *    account.
21626   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21627   *    interaction with the input element.
21628   *
21629   * @example
21630      <example name="number-input-directive" module="numberExample">
21631        <file name="index.html">
21632         <script>
21633           angular.module('numberExample', [])
21634             .controller('ExampleController', ['$scope', function($scope) {
21635               $scope.example = {
21636                 value: 12
21637               };
21638             }]);
21639         </script>
21640         <form name="myForm" ng-controller="ExampleController">
21641           <label>Number:
21642             <input type="number" name="input" ng-model="example.value"
21643                    min="0" max="99" required>
21644          </label>
21645           <div role="alert">
21646             <span class="error" ng-show="myForm.input.$error.required">
21647               Required!</span>
21648             <span class="error" ng-show="myForm.input.$error.number">
21649               Not valid number!</span>
21650           </div>
21651           <tt>value = {{example.value}}</tt><br/>
21652           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21653           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21654           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21655           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21656          </form>
21657        </file>
21658        <file name="protractor.js" type="protractor">
21659          var value = element(by.binding('example.value'));
21660          var valid = element(by.binding('myForm.input.$valid'));
21661          var input = element(by.model('example.value'));
21662
21663          it('should initialize to model', function() {
21664            expect(value.getText()).toContain('12');
21665            expect(valid.getText()).toContain('true');
21666          });
21667
21668          it('should be invalid if empty', function() {
21669            input.clear();
21670            input.sendKeys('');
21671            expect(value.getText()).toEqual('value =');
21672            expect(valid.getText()).toContain('false');
21673          });
21674
21675          it('should be invalid if over max', function() {
21676            input.clear();
21677            input.sendKeys('123');
21678            expect(value.getText()).toEqual('value =');
21679            expect(valid.getText()).toContain('false');
21680          });
21681        </file>
21682      </example>
21683   */
21684  'number': numberInputType,
21685
21686
21687  /**
21688   * @ngdoc input
21689   * @name input[url]
21690   *
21691   * @description
21692   * Text input with URL validation. Sets the `url` validation error key if the content is not a
21693   * valid URL.
21694   *
21695   * <div class="alert alert-warning">
21696   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21697   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21698   * the built-in validators (see the {@link guide/forms Forms guide})
21699   * </div>
21700   *
21701   * @param {string} ngModel Assignable angular expression to data-bind to.
21702   * @param {string=} name Property name of the form under which the control is published.
21703   * @param {string=} required Sets `required` validation error key if the value is not entered.
21704   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21705   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21706   *    `required` when you want to data-bind to the `required` attribute.
21707   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21708   *    minlength.
21709   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21710   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21711   *    any length.
21712   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21713   *    that contains the regular expression body that will be converted to a regular expression
21714   *    as in the ngPattern directive.
21715   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21716   *    a RegExp found by evaluating the Angular expression given in the attribute value.
21717   *    If the expression evaluates to a RegExp object, then this is used directly.
21718   *    If the expression evaluates to a string, then it will be converted to a RegExp
21719   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21720   *    `new RegExp('^abc$')`.<br />
21721   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21722   *    start at the index of the last search's match, thus not taking the whole input value into
21723   *    account.
21724   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21725   *    interaction with the input element.
21726   *
21727   * @example
21728      <example name="url-input-directive" module="urlExample">
21729        <file name="index.html">
21730         <script>
21731           angular.module('urlExample', [])
21732             .controller('ExampleController', ['$scope', function($scope) {
21733               $scope.url = {
21734                 text: 'http://google.com'
21735               };
21736             }]);
21737         </script>
21738         <form name="myForm" ng-controller="ExampleController">
21739           <label>URL:
21740             <input type="url" name="input" ng-model="url.text" required>
21741           <label>
21742           <div role="alert">
21743             <span class="error" ng-show="myForm.input.$error.required">
21744               Required!</span>
21745             <span class="error" ng-show="myForm.input.$error.url">
21746               Not valid url!</span>
21747           </div>
21748           <tt>text = {{url.text}}</tt><br/>
21749           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21750           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21751           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21752           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21753           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21754          </form>
21755        </file>
21756        <file name="protractor.js" type="protractor">
21757          var text = element(by.binding('url.text'));
21758          var valid = element(by.binding('myForm.input.$valid'));
21759          var input = element(by.model('url.text'));
21760
21761          it('should initialize to model', function() {
21762            expect(text.getText()).toContain('http://google.com');
21763            expect(valid.getText()).toContain('true');
21764          });
21765
21766          it('should be invalid if empty', function() {
21767            input.clear();
21768            input.sendKeys('');
21769
21770            expect(text.getText()).toEqual('text =');
21771            expect(valid.getText()).toContain('false');
21772          });
21773
21774          it('should be invalid if not url', function() {
21775            input.clear();
21776            input.sendKeys('box');
21777
21778            expect(valid.getText()).toContain('false');
21779          });
21780        </file>
21781      </example>
21782   */
21783  'url': urlInputType,
21784
21785
21786  /**
21787   * @ngdoc input
21788   * @name input[email]
21789   *
21790   * @description
21791   * Text input with email validation. Sets the `email` validation error key if not a valid email
21792   * address.
21793   *
21794   * <div class="alert alert-warning">
21795   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21796   * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21797   * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21798   * </div>
21799   *
21800   * @param {string} ngModel Assignable angular expression to data-bind to.
21801   * @param {string=} name Property name of the form under which the control is published.
21802   * @param {string=} required Sets `required` validation error key if the value is not entered.
21803   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21804   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21805   *    `required` when you want to data-bind to the `required` attribute.
21806   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21807   *    minlength.
21808   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21809   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21810   *    any length.
21811   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21812   *    that contains the regular expression body that will be converted to a regular expression
21813   *    as in the ngPattern directive.
21814   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21815   *    a RegExp found by evaluating the Angular expression given in the attribute value.
21816   *    If the expression evaluates to a RegExp object, then this is used directly.
21817   *    If the expression evaluates to a string, then it will be converted to a RegExp
21818   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21819   *    `new RegExp('^abc$')`.<br />
21820   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21821   *    start at the index of the last search's match, thus not taking the whole input value into
21822   *    account.
21823   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21824   *    interaction with the input element.
21825   *
21826   * @example
21827      <example name="email-input-directive" module="emailExample">
21828        <file name="index.html">
21829         <script>
21830           angular.module('emailExample', [])
21831             .controller('ExampleController', ['$scope', function($scope) {
21832               $scope.email = {
21833                 text: '[email protected]'
21834               };
21835             }]);
21836         </script>
21837           <form name="myForm" ng-controller="ExampleController">
21838             <label>Email:
21839               <input type="email" name="input" ng-model="email.text" required>
21840             </label>
21841             <div role="alert">
21842               <span class="error" ng-show="myForm.input.$error.required">
21843                 Required!</span>
21844               <span class="error" ng-show="myForm.input.$error.email">
21845                 Not valid email!</span>
21846             </div>
21847             <tt>text = {{email.text}}</tt><br/>
21848             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21849             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21850             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21851             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21852             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21853           </form>
21854         </file>
21855        <file name="protractor.js" type="protractor">
21856          var text = element(by.binding('email.text'));
21857          var valid = element(by.binding('myForm.input.$valid'));
21858          var input = element(by.model('email.text'));
21859
21860          it('should initialize to model', function() {
21861            expect(text.getText()).toContain('[email protected]');
21862            expect(valid.getText()).toContain('true');
21863          });
21864
21865          it('should be invalid if empty', function() {
21866            input.clear();
21867            input.sendKeys('');
21868            expect(text.getText()).toEqual('text =');
21869            expect(valid.getText()).toContain('false');
21870          });
21871
21872          it('should be invalid if not email', function() {
21873            input.clear();
21874            input.sendKeys('xxx');
21875
21876            expect(valid.getText()).toContain('false');
21877          });
21878        </file>
21879      </example>
21880   */
21881  'email': emailInputType,
21882
21883
21884  /**
21885   * @ngdoc input
21886   * @name input[radio]
21887   *
21888   * @description
21889   * HTML radio button.
21890   *
21891   * @param {string} ngModel Assignable angular expression to data-bind to.
21892   * @param {string} value The value to which the `ngModel` expression should be set when selected.
21893   *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
21894   *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
21895   * @param {string=} name Property name of the form under which the control is published.
21896   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21897   *    interaction with the input element.
21898   * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
21899   *    is selected. Should be used instead of the `value` attribute if you need
21900   *    a non-string `ngModel` (`boolean`, `array`, ...).
21901   *
21902   * @example
21903      <example name="radio-input-directive" module="radioExample">
21904        <file name="index.html">
21905         <script>
21906           angular.module('radioExample', [])
21907             .controller('ExampleController', ['$scope', function($scope) {
21908               $scope.color = {
21909                 name: 'blue'
21910               };
21911               $scope.specialValue = {
21912                 "id": "12345",
21913                 "value": "green"
21914               };
21915             }]);
21916         </script>
21917         <form name="myForm" ng-controller="ExampleController">
21918           <label>
21919             <input type="radio" ng-model="color.name" value="red">
21920             Red
21921           </label><br/>
21922           <label>
21923             <input type="radio" ng-model="color.name" ng-value="specialValue">
21924             Green
21925           </label><br/>
21926           <label>
21927             <input type="radio" ng-model="color.name" value="blue">
21928             Blue
21929           </label><br/>
21930           <tt>color = {{color.name | json}}</tt><br/>
21931          </form>
21932          Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
21933        </file>
21934        <file name="protractor.js" type="protractor">
21935          it('should change state', function() {
21936            var color = element(by.binding('color.name'));
21937
21938            expect(color.getText()).toContain('blue');
21939
21940            element.all(by.model('color.name')).get(0).click();
21941
21942            expect(color.getText()).toContain('red');
21943          });
21944        </file>
21945      </example>
21946   */
21947  'radio': radioInputType,
21948
21949
21950  /**
21951   * @ngdoc input
21952   * @name input[checkbox]
21953   *
21954   * @description
21955   * HTML checkbox.
21956   *
21957   * @param {string} ngModel Assignable angular expression to data-bind to.
21958   * @param {string=} name Property name of the form under which the control is published.
21959   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
21960   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
21961   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21962   *    interaction with the input element.
21963   *
21964   * @example
21965      <example name="checkbox-input-directive" module="checkboxExample">
21966        <file name="index.html">
21967         <script>
21968           angular.module('checkboxExample', [])
21969             .controller('ExampleController', ['$scope', function($scope) {
21970               $scope.checkboxModel = {
21971                value1 : true,
21972                value2 : 'YES'
21973              };
21974             }]);
21975         </script>
21976         <form name="myForm" ng-controller="ExampleController">
21977           <label>Value1:
21978             <input type="checkbox" ng-model="checkboxModel.value1">
21979           </label><br/>
21980           <label>Value2:
21981             <input type="checkbox" ng-model="checkboxModel.value2"
21982                    ng-true-value="'YES'" ng-false-value="'NO'">
21983            </label><br/>
21984           <tt>value1 = {{checkboxModel.value1}}</tt><br/>
21985           <tt>value2 = {{checkboxModel.value2}}</tt><br/>
21986          </form>
21987        </file>
21988        <file name="protractor.js" type="protractor">
21989          it('should change state', function() {
21990            var value1 = element(by.binding('checkboxModel.value1'));
21991            var value2 = element(by.binding('checkboxModel.value2'));
21992
21993            expect(value1.getText()).toContain('true');
21994            expect(value2.getText()).toContain('YES');
21995
21996            element(by.model('checkboxModel.value1')).click();
21997            element(by.model('checkboxModel.value2')).click();
21998
21999            expect(value1.getText()).toContain('false');
22000            expect(value2.getText()).toContain('NO');
22001          });
22002        </file>
22003      </example>
22004   */
22005  'checkbox': checkboxInputType,
22006
22007  'hidden': noop,
22008  'button': noop,
22009  'submit': noop,
22010  'reset': noop,
22011  'file': noop
22012};
22013
22014function stringBasedInputType(ctrl) {
22015  ctrl.$formatters.push(function(value) {
22016    return ctrl.$isEmpty(value) ? value : value.toString();
22017  });
22018}
22019
22020function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22021  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22022  stringBasedInputType(ctrl);
22023}
22024
22025function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22026  var type = lowercase(element[0].type);
22027
22028  // In composition mode, users are still inputing intermediate text buffer,
22029  // hold the listener until composition is done.
22030  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
22031  if (!$sniffer.android) {
22032    var composing = false;
22033
22034    element.on('compositionstart', function(data) {
22035      composing = true;
22036    });
22037
22038    element.on('compositionend', function() {
22039      composing = false;
22040      listener();
22041    });
22042  }
22043
22044  var listener = function(ev) {
22045    if (timeout) {
22046      $browser.defer.cancel(timeout);
22047      timeout = null;
22048    }
22049    if (composing) return;
22050    var value = element.val(),
22051        event = ev && ev.type;
22052
22053    // By default we will trim the value
22054    // If the attribute ng-trim exists we will avoid trimming
22055    // If input type is 'password', the value is never trimmed
22056    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
22057      value = trim(value);
22058    }
22059
22060    // If a control is suffering from bad input (due to native validators), browsers discard its
22061    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
22062    // control's value is the same empty value twice in a row.
22063    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
22064      ctrl.$setViewValue(value, event);
22065    }
22066  };
22067
22068  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
22069  // input event on backspace, delete or cut
22070  if ($sniffer.hasEvent('input')) {
22071    element.on('input', listener);
22072  } else {
22073    var timeout;
22074
22075    var deferListener = function(ev, input, origValue) {
22076      if (!timeout) {
22077        timeout = $browser.defer(function() {
22078          timeout = null;
22079          if (!input || input.value !== origValue) {
22080            listener(ev);
22081          }
22082        });
22083      }
22084    };
22085
22086    element.on('keydown', function(event) {
22087      var key = event.keyCode;
22088
22089      // ignore
22090      //    command            modifiers                   arrows
22091      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
22092
22093      deferListener(event, this, this.value);
22094    });
22095
22096    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
22097    if ($sniffer.hasEvent('paste')) {
22098      element.on('paste cut', deferListener);
22099    }
22100  }
22101
22102  // if user paste into input using mouse on older browser
22103  // or form autocomplete on newer browser, we need "change" event to catch it
22104  element.on('change', listener);
22105
22106  ctrl.$render = function() {
22107    // Workaround for Firefox validation #12102.
22108    var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
22109    if (element.val() !== value) {
22110      element.val(value);
22111    }
22112  };
22113}
22114
22115function weekParser(isoWeek, existingDate) {
22116  if (isDate(isoWeek)) {
22117    return isoWeek;
22118  }
22119
22120  if (isString(isoWeek)) {
22121    WEEK_REGEXP.lastIndex = 0;
22122    var parts = WEEK_REGEXP.exec(isoWeek);
22123    if (parts) {
22124      var year = +parts[1],
22125          week = +parts[2],
22126          hours = 0,
22127          minutes = 0,
22128          seconds = 0,
22129          milliseconds = 0,
22130          firstThurs = getFirstThursdayOfYear(year),
22131          addDays = (week - 1) * 7;
22132
22133      if (existingDate) {
22134        hours = existingDate.getHours();
22135        minutes = existingDate.getMinutes();
22136        seconds = existingDate.getSeconds();
22137        milliseconds = existingDate.getMilliseconds();
22138      }
22139
22140      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
22141    }
22142  }
22143
22144  return NaN;
22145}
22146
22147function createDateParser(regexp, mapping) {
22148  return function(iso, date) {
22149    var parts, map;
22150
22151    if (isDate(iso)) {
22152      return iso;
22153    }
22154
22155    if (isString(iso)) {
22156      // When a date is JSON'ified to wraps itself inside of an extra
22157      // set of double quotes. This makes the date parsing code unable
22158      // to match the date string and parse it as a date.
22159      if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
22160        iso = iso.substring(1, iso.length - 1);
22161      }
22162      if (ISO_DATE_REGEXP.test(iso)) {
22163        return new Date(iso);
22164      }
22165      regexp.lastIndex = 0;
22166      parts = regexp.exec(iso);
22167
22168      if (parts) {
22169        parts.shift();
22170        if (date) {
22171          map = {
22172            yyyy: date.getFullYear(),
22173            MM: date.getMonth() + 1,
22174            dd: date.getDate(),
22175            HH: date.getHours(),
22176            mm: date.getMinutes(),
22177            ss: date.getSeconds(),
22178            sss: date.getMilliseconds() / 1000
22179          };
22180        } else {
22181          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
22182        }
22183
22184        forEach(parts, function(part, index) {
22185          if (index < mapping.length) {
22186            map[mapping[index]] = +part;
22187          }
22188        });
22189        return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
22190      }
22191    }
22192
22193    return NaN;
22194  };
22195}
22196
22197function createDateInputType(type, regexp, parseDate, format) {
22198  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
22199    badInputChecker(scope, element, attr, ctrl);
22200    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22201    var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
22202    var previousDate;
22203
22204    ctrl.$$parserName = type;
22205    ctrl.$parsers.push(function(value) {
22206      if (ctrl.$isEmpty(value)) return null;
22207      if (regexp.test(value)) {
22208        // Note: We cannot read ctrl.$modelValue, as there might be a different
22209        // parser/formatter in the processing chain so that the model
22210        // contains some different data format!
22211        var parsedDate = parseDate(value, previousDate);
22212        if (timezone) {
22213          parsedDate = convertTimezoneToLocal(parsedDate, timezone);
22214        }
22215        return parsedDate;
22216      }
22217      return undefined;
22218    });
22219
22220    ctrl.$formatters.push(function(value) {
22221      if (value && !isDate(value)) {
22222        throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
22223      }
22224      if (isValidDate(value)) {
22225        previousDate = value;
22226        if (previousDate && timezone) {
22227          previousDate = convertTimezoneToLocal(previousDate, timezone, true);
22228        }
22229        return $filter('date')(value, format, timezone);
22230      } else {
22231        previousDate = null;
22232        return '';
22233      }
22234    });
22235
22236    if (isDefined(attr.min) || attr.ngMin) {
22237      var minVal;
22238      ctrl.$validators.min = function(value) {
22239        return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
22240      };
22241      attr.$observe('min', function(val) {
22242        minVal = parseObservedDateValue(val);
22243        ctrl.$validate();
22244      });
22245    }
22246
22247    if (isDefined(attr.max) || attr.ngMax) {
22248      var maxVal;
22249      ctrl.$validators.max = function(value) {
22250        return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
22251      };
22252      attr.$observe('max', function(val) {
22253        maxVal = parseObservedDateValue(val);
22254        ctrl.$validate();
22255      });
22256    }
22257
22258    function isValidDate(value) {
22259      // Invalid Date: getTime() returns NaN
22260      return value && !(value.getTime && value.getTime() !== value.getTime());
22261    }
22262
22263    function parseObservedDateValue(val) {
22264      return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
22265    }
22266  };
22267}
22268
22269function badInputChecker(scope, element, attr, ctrl) {
22270  var node = element[0];
22271  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
22272  if (nativeValidation) {
22273    ctrl.$parsers.push(function(value) {
22274      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
22275      // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
22276      // - also sets validity.badInput (should only be validity.typeMismatch).
22277      // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
22278      // - can ignore this case as we can still read out the erroneous email...
22279      return validity.badInput && !validity.typeMismatch ? undefined : value;
22280    });
22281  }
22282}
22283
22284function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22285  badInputChecker(scope, element, attr, ctrl);
22286  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22287
22288  ctrl.$$parserName = 'number';
22289  ctrl.$parsers.push(function(value) {
22290    if (ctrl.$isEmpty(value))      return null;
22291    if (NUMBER_REGEXP.test(value)) return parseFloat(value);
22292    return undefined;
22293  });
22294
22295  ctrl.$formatters.push(function(value) {
22296    if (!ctrl.$isEmpty(value)) {
22297      if (!isNumber(value)) {
22298        throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
22299      }
22300      value = value.toString();
22301    }
22302    return value;
22303  });
22304
22305  if (isDefined(attr.min) || attr.ngMin) {
22306    var minVal;
22307    ctrl.$validators.min = function(value) {
22308      return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
22309    };
22310
22311    attr.$observe('min', function(val) {
22312      if (isDefined(val) && !isNumber(val)) {
22313        val = parseFloat(val, 10);
22314      }
22315      minVal = isNumber(val) && !isNaN(val) ? val : undefined;
22316      // TODO(matsko): implement validateLater to reduce number of validations
22317      ctrl.$validate();
22318    });
22319  }
22320
22321  if (isDefined(attr.max) || attr.ngMax) {
22322    var maxVal;
22323    ctrl.$validators.max = function(value) {
22324      return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
22325    };
22326
22327    attr.$observe('max', function(val) {
22328      if (isDefined(val) && !isNumber(val)) {
22329        val = parseFloat(val, 10);
22330      }
22331      maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
22332      // TODO(matsko): implement validateLater to reduce number of validations
22333      ctrl.$validate();
22334    });
22335  }
22336}
22337
22338function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22339  // Note: no badInputChecker here by purpose as `url` is only a validation
22340  // in browsers, i.e. we can always read out input.value even if it is not val
22340id!
22341  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22342  stringBasedInputType(ctrl);
22343
22344  ctrl.$$parserName = 'url';
22345  ctrl.$validators.url = function(modelValue, viewValue) {
22346    var value = modelValue || viewValue;
22347    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
22348  };
22349}
22350
22351function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22352  // Note: no badInputChecker here by purpose as `url` is only a validation
22353  // in browsers, i.e. we can always read out input.value even if it is not valid!
22354  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22355  stringBasedInputType(ctrl);
22356
22357  ctrl.$$parserName = 'email';
22358  ctrl.$validators.email = function(modelValue, viewValue) {
22359    var value = modelValue || viewValue;
22360    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
22361  };
22362}
22363
22364function radioInputType(scope, element, attr, ctrl) {
22365  // make the name unique, if not defined
22366  if (isUndefined(attr.name)) {
22367    element.attr('name', nextUid());
22368  }
22369
22370  var listener = function(ev) {
22371    if (element[0].checked) {
22372      ctrl.$setViewValue(attr.value, ev && ev.type);
22373    }
22374  };
22375
22376  element.on('click', listener);
22377
22378  ctrl.$render = function() {
22379    var value = attr.value;
22380    element[0].checked = (value == ctrl.$viewValue);
22381  };
22382
22383  attr.$observe('value', ctrl.$render);
22384}
22385
22386function parseConstantExpr($parse, context, name, expression, fallback) {
22387  var parseFn;
22388  if (isDefined(expression)) {
22389    parseFn = $parse(expression);
22390    if (!parseFn.constant) {
22391      throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22392                                   '`{1}`.', name, expression);
22393    }
22394    return parseFn(context);
22395  }
22396  return fallback;
22397}
22398
22399function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22400  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22401  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22402
22403  var listener = function(ev) {
22404    ctrl.$setViewValue(element[0].checked, ev && ev.type);
22405  };
22406
22407  element.on('click', listener);
22408
22409  ctrl.$render = function() {
22410    element[0].checked = ctrl.$viewValue;
22411  };
22412
22413  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22414  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22415  // it to a boolean.
22416  ctrl.$isEmpty = function(value) {
22417    return value === false;
22418  };
22419
22420  ctrl.$formatters.push(function(value) {
22421    return equals(value, trueValue);
22422  });
22423
22424  ctrl.$parsers.push(function(value) {
22425    return value ? trueValue : falseValue;
22426  });
22427}
22428
22429
22430/**
22431 * @ngdoc directive
22432 * @name textarea
22433 * @restrict E
22434 *
22435 * @description
22436 * HTML textarea element control with angular data-binding. The data-binding and validation
22437 * properties of this element are exactly the same as those of the
22438 * {@link ng.directive:input input element}.
22439 *
22440 * @param {string} ngModel Assignable angular expression to data-bind to.
22441 * @param {string=} name Property name of the form under which the control is published.
22442 * @param {string=} required Sets `required` validation error key if the value is not entered.
22443 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22444 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22445 *    `required` when you want to data-bind to the `required` attribute.
22446 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22447 *    minlength.
22448 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22449 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22450 *    length.
22451 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22452 *    a RegExp found by evaluating the Angular expression given in the attribute value.
22453 *    If the expression evaluates to a RegExp object, then this is used directly.
22454 *    If the expression evaluates to a string, then it will be converted to a RegExp
22455 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22456 *    `new RegExp('^abc$')`.<br />
22457 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22458 *    start at the index of the last search's match, thus not taking the whole input value into
22459 *    account.
22460 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22461 *    interaction with the input element.
22462 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22463 */
22464
22465
22466/**
22467 * @ngdoc directive
22468 * @name input
22469 * @restrict E
22470 *
22471 * @description
22472 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22473 * input state control, and validation.
22474 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22475 *
22476 * <div class="alert alert-warning">
22477 * **Note:** Not every feature offered is available for all input types.
22478 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22479 * </div>
22480 *
22481 * @param {string} ngModel Assignable angular expression to data-bind to.
22482 * @param {string=} name Property name of the form under which the control is published.
22483 * @param {string=} required Sets `required` validation error key if the value is not entered.
22484 * @param {boolean=} ngRequired Sets `required` attribute if set to true
22485 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22486 *    minlength.
22487 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22488 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22489 *    length.
22490 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22491 *    a RegExp found by evaluating the Angular expression given in the attribute value.
22492 *    If the expression evaluates to a RegExp object, then this is used directly.
22493 *    If the expression evaluates to a string, then it will be converted to a RegExp
22494 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22495 *    `new RegExp('^abc$')`.<br />
22496 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22497 *    start at the index of the last search's match, thus not taking the whole input value into
22498 *    account.
22499 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22500 *    interaction with the input element.
22501 * @param {boolean=} [ngTrim=true] If set to false Angular
vendor: 4,842 bytes, lines 22501-22619
22501 will not automatically trim the input.
22502 *    This parameter is ignored for input[type=password] controls, which will never trim the
22503 *    input.
22504 *
22505 * @example
22506    <example name="input-directive" module="inputExample">
22507      <file name="index.html">
22508       <script>
22509          angular.module('inputExample', [])
22510            .controller('ExampleController', ['$scope', function($scope) {
22511              $scope.user = {name: 'guest', last: 'visitor'};
22512            }]);
22513       </script>
22514       <div ng-controller="ExampleController">
22515         <form name="myForm">
22516           <label>
22517              User name:
22518              <input type="text" name="userName" ng-model="user.name" required>
22519           </label>
22520           <div role="alert">
22521             <span class="error" ng-show="myForm.userName.$error.required">
22522              Required!</span>
22523           </div>
22524           <label>
22525              Last name:
22526              <input type="text" name="lastName" ng-model="user.last"
22527              ng-minlength="3" ng-maxlength="10">
22528           </label>
22529           <div role="alert">
22530             <span class="error" ng-show="myForm.lastName.$error.minlength">
22531               Too short!</span>
22532             <span class="error" ng-show="myForm.lastName.$error.maxlength">
22533               Too long!</span>
22534           </div>
22535         </form>
22536         <hr>
22537         <tt>user = {{user}}</tt><br/>
22538         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
22539         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
22540         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
22541         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
22542         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22543         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22544         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
22545         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
22546       </div>
22547      </file>
22548      <file name="protractor.js" type="protractor">
22549        var user = element(by.exactBinding('user'));
22550        var userNameValid = element(by.binding('myForm.userName.$valid'));
22551        var lastNameValid = element(by.binding('myForm.lastName.$valid'));
22552        var lastNameError = element(by.binding('myForm.lastName.$error'));
22553        var formValid = element(by.binding('myForm.$valid'));
22554        var userNameInput = element(by.model('user.name'));
22555        var userLastInput = element(by.model('user.last'));
22556
22557        it('should initialize to model', function() {
22558          expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
22559          expect(userNameValid.getText()).toContain('true');
22560          expect(formValid.getText()).toContain('true');
22561        });
22562
22563        it('should be invalid if empty when required', function() {
22564          userNameInput.clear();
22565          userNameInput.sendKeys('');
22566
22567          expect(user.getText()).toContain('{"last":"visitor"}');
22568          expect(userNameValid.getText()).toContain('false');
22569          expect(formValid.getText()).toContain('false');
22570        });
22571
22572        it('should be valid if empty when min length is set', function() {
22573          userLastInput.clear();
22574          userLastInput.sendKeys('');
22575
22576          expect(user.getText()).toContain('{"name":"guest","last":""}');
22577          expect(lastNameValid.getText()).toContain('true');
22578          expect(formValid.getText()).toContain('true');
22579        });
22580
22581        it('should be invalid if less than required min length', function() {
22582          userLastInput.clear();
22583          userLastInput.sendKeys('xx');
22584
22585          expect(user.getText()).toContain('{"name":"guest"}');
22586          expect(lastNameValid.getText()).toContain('false');
22587          expect(lastNameError.getText()).toContain('minlength');
22588          expect(formValid.getText()).toContain('false');
22589        });
22590
22591        it('should be invalid if longer than max length', function() {
22592          userLastInput.clear();
22593          userLastInput.sendKeys('some ridiculously long name');
22594
22595          expect(user.getText()).toContain('{"name":"guest"}');
22596          expect(lastNameValid.getText()).toContain('false');
22597          expect(lastNameError.getText()).toContain('maxlength');
22598          expect(formValid.getText()).toContain('false');
22599        });
22600      </file>
22601    </example>
22602 */
22603var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
22604    function($browser, $sniffer, $filter, $parse) {
22605  return {
22606    restrict: 'E',
22607    require: ['?ngModel'],
22608    link: {
22609      pre: function(scope, element, attr, ctrls) {
22610        if (ctrls[0]) {
22611          (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
22612                                                              $browser, $filter, $parse);
22613        }
22614      }
22615    }
22616  };
22617}];
22618
22619
22620
22621var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
22622/**
22623 * @ngdoc directive
22624 * @name ngValue
22625 *
22626 * @description
22627 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
22628 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
22629 * the bound value.
22630 *
22631 * `ngValue` is useful when dynamically generating lists of radio buttons using
22632 * {@link ngRepeat `ngRepeat`}, as shown below.
22633 *
22634 * Likewise, `ngValue` can be used to generate `<option>` elements for
22635 * the {@link select `select`} element. In that case however, only strings are supported
22636 * for the `value `attribute, so the resulting `ngModel` will always be a string.
22637 * Support for `select` models with non-string values is available via `ngOptions`.
22638 *
22639 * @element input
22640 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
22641 *   of the `input` element
22642 *
22643 * @example
22644    <example name="ngValue-directive" module="valueExample">
22645      <file name="index.html">
22646       <script>
22647          angular.module('valueExample', [])
22648            .controller('ExampleController', ['$scope', function($scope) {
22649              $scope.names = ['pizza', 'unicorns', 'robots'];
22650              $scope.my = { favorite: 'unicorns' };
22651            }]);
22652       </script>
22653        <form ng-controller="ExampleController">
22654          <h2>Which is your favorite?</h2>
22655            <label ng-repeat="name in names" for="{{name}}">
22656              {{name}}
22657              <input type="radio"
22658                     ng-model="my.favorite"
22659                     ng-value="name"
22660                     id="{{name}}"
22661                     name="favorite">
22662            </label>
22663          <div>You chose {{my.favorite}}</div>
22664        </form>
22665      </file>
22666      <file name="protractor.js" type="protractor">
22667        var favorite = element(by.binding('my.favorite'));
22668
22669        it('should initialize to model', function() {
22670          expect(favorite.getText()).toContain('unicorns');
22671        });
22672        it('should bind the values to the inputs', function() {
22673          element.all(by.model('my.favorite')).get(0).click();
22674          expect(favorite.getText()).toContain('pizza');
22675        });
22676      </file>
22677    </example>
22678 */
22679var ngValueDirective = function() {
22680  return {
22681    restrict: 'A',
22682    priority: 100,
22683    compile: function(tpl, tplAttr) {
22684      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22685        return function ngValueConstantLink(scope, elm, attr) {
22686          attr.$set('value', scope.$eval(attr.ngValue));
22687        };
22688      } else {
22689        return function ngValueLink(scope, elm, attr) {
22690          scope.$watch(attr.ngValue, function valueWatchAction(value) {
22691            attr.$set('value', value);
22692          });
22693        };
22694      }
22695    }
22696  };
22697};
22698
22699/**
22700 * @ngdoc directive
22701 * @name ngBind
22702 * @restrict AC
22703 *
22704 * @description
22705 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22706 * with the value of a given expression, and to update the text content when the value of that
22707 * expression changes.
22708 *
22709 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22710 * `{{ expression }}` which is similar but less verbose.
22711 *
22712 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22713 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22714 * element attribute, it makes the bindings invisible to the user while the page is loading.
22715 *
22716 * An alternative solution to this problem would be using the
22717 * {@link ng.directive:ngCloak ngCloak} directive.
22718 *
22719 *
22720 * @element ANY
22721 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22722 *
22723 * @example
22724 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22725   <example module="bindExample">
22726     <file name="index.html">
22727       <script>
22728         angular.module('bindExample', [])
22729           .controller('ExampleController', ['$scope', function($scope) {
22730             $scope.name = 'Whirled';
22731           }]);
22732       </script>
22733       <div ng-controller="ExampleController">
22734         <label>Enter name: <input type="text" ng-model="name"></label><br>
22735         Hello <span ng-bind="name"></span>!
22736       </div>
22737     </file>
22738     <file name="protractor.js" type="protractor">
22739       it('should check ng-bind', function() {
22740         var nameInput = element(by.model('name'));
22741
22742         expect(element(by.binding('name')).getText()).toBe('Whirled');
22743         nameInput.clear();
22744         nameInput.sendKeys('world');
22745         expect(element(by.binding('name')).getText()).toBe('world');
22746       });
22747     </file>
22748   </example>
22749 */
22750var ngBindDirective = ['$compile', function($compile) {
22751  return {
22752    restrict: 'AC',
22753    compile: function ngBindCompile(templateElement) {
22754      $compile.$$addBindingClass(templateElement);
22755      return function ngBindLink(scope, element, attr) {
22756        $compile.$$addBindingInfo(element, attr.ngBind);
22757        element = element[0];
22758        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22759          element.textContent = isUndefined(value) ? '' : value;
22760        });
22761      };
22762    }
22763  };
22764}];
22765
22766
22767/**
22768 * @ngdoc directive
22769 * @name ngBindTemplate
22770 *
22771 * @description
22772 * The `ngBindTemplate` directive specifies that the element
22773 * text content should be replaced with the interpolation of the template
22774 * in the `ngBindTemplate` attribute.
22775 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22776 * expressions. This directive is needed since some HTML elements
22777 * (such as TITLE and OPTION) cannot contain SPAN elements.
22778 *
22779 * @element ANY
22780 * @param {string} ngBindTemplate template of form
22781 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22782 *
22783 * @example
22784 * Try it here: enter text in text box and watch the greeting change.
22785   <example module="bindExample">
22786     <file name="index.html">
22787       <script>
22788         angular.module('bindExample', [])
22789           .controller('ExampleController', ['$scope', function($scope) {
22790             $scope.salutation = 'Hello';
22791             $scope.name = 'World';
22792           }]);
22793       </script>
22794       <div ng-controller="ExampleController">
22795        <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22796        <label>Name: <input type="text" ng-model="name"></label><br>
22797        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22798       </div>
22799     </file>
22800     <file name="protractor.js" type="protractor">
22801       it('should check ng-bind', function() {
22802         var salutationElem = element(by.binding('salutation'));
22803         var salutationInput = element(by.model('salutation'));
22804         var nameInput = element(by.model('name'));
22805
22806         expect(salutationElem.getText()).toBe('Hello World!');
22807
22808         salutationInput.clear();
22809         salutationInput.sendKeys('Greetings');
22810         nameInput.clear();
22811         nameInput.sendKeys('user');
22812
22813         expect(salutationElem.getText()).toBe('Greetings user!');
22814       });
22815     </file>
22816   </example>
22817 */
22818var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22819  return {
22820    compile: function ngBindTemplateCompile(templateElement) {
22821      $compile.$$addBindingClass(templateElement);
22822      return function ngBindTemplateLink(scope, element, attr) {
22823        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22824        $compile.$$addBindingInfo(element, interpolateFn.expressions);
22825        element = element[0];
22826        attr.$observe('ngBindTemplate', function(value) {
22827          element.textContent = isUndefined(value) ? '' : value;
22828        });
22829      };
22830    }
22831  };
22832}];
22833
22834
22835/**
22836 * @ngdoc directive
22837 * @name ngBindHtml
22838 *
22839 * @description
22840 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22841 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22842 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22843 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22844 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22845 *
22846 * You may also bypass sanitization for values you know are safe. To do so, bind to
22847 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
22848 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22849 *
22850 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22851 * will have an exception (instead of an exploit.)
22852 *
22853 * @element ANY
22854 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22855 *
22856 * @example
22857
22858   <example module="bindHtmlExample" deps="angular-sanitize.js">
22859     <file name="index.html">
22860       <div ng-controller="ExampleController">
22861        <p ng-bind-html="myHTML"></p>
22862       </div>
22863     </file>
22864
22865     <file name="script.js">
22866       angular.module('bindHtmlExample', ['ngSanitize'])
22867         .controller('ExampleController', ['$scope', function($scope) {
22868           $scope.myHTML =
22869              'I am an <code>HTML</code>string with ' +
22870              '<a href="#">links!</a> and other <em>stuff</em>';
22871         }]);
22872     </file>
22873
22874     <file name="protractor.js" type="protractor">
22875       it('should check ng-bind-html', function() {
22876         expect(element(by.binding('myHTML')).getText()).toBe(
22877             'I am an HTMLstring with links! and other stuff');
22878       });
22879     </file>
22880   </example>
22881 */
22882var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
22883  return {
22884    restrict: 'A',
22885    compile: function ngBindHtmlCompile(tElement, tAttrs) {
22886      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
22887      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
22888        return (value || '').toString();
22889      });
22890      $compile.$$addBindingClass(tElement);
22891
22892      return function ngBindHtmlLink(scope, element, attr) {
22893        $compile.$$addBindingInfo(element, attr.ngBindHtml);
22894
22895        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
22896          // we re-evaluate the expr because we want a TrustedValueHolderType
22897          // for $sce, not a string
22898          element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
22899        });
22900      };
22901    }
22902  };
22903}];
22904
22905/**
22906 * @ngdoc directive
22907 * @name ngChange
22908 *
22909 * @description
22910 * Evaluate the given expression when the user changes the input.
22911 * The expression is evaluated immediately, unlike the JavaScript onchange event
22912 * which only triggers at the end of a change (usually, when the user leaves the
22913 * form element or presses the return key).
22914 *
22915 * The `ngChange` expression is only evaluated when a change in the input value causes
22916 * a new value to be committed to the model.
22917 *
22918 * It will not be evaluated:
22919 * * if the value returned from the `$parsers` transformation pipeline has not changed
22920 * * if the input has continued to be invalid since the model will stay `null`
22921 * * if the model is changed programmatically and not by a change to the input value
22922 *
22923 *
22924 * Note, this directive requires `ngModel` to be present.
22925 *
22926 * @element input
22927 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
22928 * in input value.
22929 *
22930 * @example
22931 * <example name="ngChange-directive" module="changeExample">
22932 *   <file name="index.html">
22933 *     <script>
22934 *       angular.module('changeExample', [])
22935 *         .controller('ExampleController', ['$scope', function($scope) {
22936 *           $scope.counter = 0;
22937 *           $scope.change = function() {
22938 *             $scope.counter++;
22939 *           };
22940 *         }]);
22941 *     </script>
22942 *     <div ng-controller="ExampleController">
22943 *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
22944 *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
22945 *       <label for="ng-change-example2">Confirmed</label><br />
22946 *       <tt>debug = {{confirmed}}</tt><br/>
22947 *       <tt>counter = {{counter}}</tt><br/>
22948 *     </div>
22949 *   </file>
22950 *   <file name="protractor.js" type="protractor">
22951 *     var counter = element(by.binding('counter'));
22952 *     var debug = element(by.binding('confirmed'));
22953 *
22954 *     it('should evaluate the expression if changing from view', function() {
22955 *       expect(counter.getText()).toContain('0');
22956 *
22957 *       element(by.id('ng-change-example1')).click();
22958 *
22959 *       expect(counter.getText()).toContain('1');
22960 *       expect(debug.getText()).toContain('true');
22961 *     });
22962 *
22963 *     it('should not evaluate the expression if changing from model', function() {
22964 *       element(by.id('ng-change-example2')).click();
22965
22966 *       expect(counter.getText()).toContain('0');
22967 *       expect(debug.getText()).toContain('true');
22968 *     });
22969 *   </file>
22970 * </example>
22971 */
22972var ngChangeDirective = valueFn({
22973  restrict: 'A',
22974  require: 'ngModel',
22975  link: function(scope, element, attr, ctrl) {
22976    ctrl.$viewChangeListeners.push(function() {
22977      scope.$eval(attr.ngChange);
22978    });
22979  }
22980});
22981
22982function classDirective(name, selector) {
22983  name = 'ngClass' + name;
22984  return ['$animate', function($animate) {
22985    return {
22986      restrict: 'AC',
22987      link: function(scope, element, attr) {
22988        var oldVal;
22989
22990        scope.$watch(attr[name], ngClassWatchAction, true);
22991
22992        attr.$observe('class', function(value) {
22993          ngClassWatchAction(scope.$eval(attr[name]));
22994        });
22995
22996
22997        if (name !== 'ngClass') {
22998          scope.$watch('$index', function($index, old$index) {
22999            // jshint bitwise: false
23000            var mod = $index & 1;
23001            if (mod !== (old$index & 1)) {
23002              var classes = arrayClasses(scope.$eval(attr[name]));
23003              mod === selector ?
23004                addClasses(classes) :
23005                removeClasses(classes);
23006            }
23007          });
23008        }
23009
23010        function addClasses(classes) {
23011          var newClasses = digestClassCounts(classes, 1);
23012          attr.$addClass(newClasses);
23013        }
23014
23015        function removeClasses(classes) {
23016          var newClasses = digestClassCounts(classes, -1);
23017          attr.$removeClass(newClasses);
23018        }
23019
23020        function digestClassCounts(classes, count) {
23021          // Use createMap() to prevent class assumptions involving property
23022          // names in Object.prototype
23023          var classCounts = element.data('$classCounts') || createMap();
23024          var classesToUpdate = [];
23025          forEach(classes, function(className) {
23026            if (count > 0 || classCounts[className]) {
23027              classCounts[className] = (classCounts[className] || 0) + count;
23028              if (classCounts[className] === +(count > 0)) {
23029                classesToUpdate.push(className);
23030              }
23031            }
23032          });
23033          element.data('$classCounts', classCounts);
23034          return classesToUpdate.join(' ');
23035        }
23036
23037        function updateClasses(oldClasses, newClasses) {
23038          var toAdd = arrayDifference(newClasses, oldClasses);
23039          var toRemove = arrayDifference(oldClasses, newClasses);
23040          toAdd = digestClassCounts(toAdd, 1);
23041          toRemove = digestClassCounts(toRemove, -1);
23042          if (toAdd && toAdd.length) {
23043            $animate.addClass(element, toAdd);
23044          }
23045          if (toRemove && toRemove.length) {
23046            $animate.removeClass(element, toRemove);
23047          }
23048        }
23049
23050        function ngClassWatchAction(newVal) {
23051          if (selector === true || scope.$index % 2 === selector) {
23052            var newClasses = arrayClasses(newVal || []);
23053            if (!oldVal) {
23054              addClasses(newClasses);
23055            } else if (!equals(newVal,oldVal)) {
23056              var oldClasses = arrayClasses(oldVal);
23057              updateClasses(oldClasses, newClasses);
23058            }
23059          }
23060          oldVal = shallowCopy(newVal);
23061        }
23062      }
23063    };
23064
23065    function arrayDifference(tokens1, tokens2) {
23066      var values = [];
23067
23068      outer:
23069      for (var i = 0; i < tokens1.length; i++) {
23070        var token = tokens1[i];
23071        for (var j = 0; j < tokens2.length; j++) {
23072          if (token == tokens2[j]) continue outer;
23073        }
23074        values.push(token);
23075      }
23076      return values;
23077    }
23078
23079    function arrayClasses(classVal) {
23080      var classes = [];
23081      if (isArray(classVal)) {
23082        forEach(classVal, function(v) {
23083          classes = classes.concat(arrayClasses(v));
23084        });
23085        return classes;
23086      } else if (isString(classVal)) {
23087        return classVal.split(' ');
23088      } else if (isObject(classVal)) {
23089        forEach(classVal, function(v, k) {
23090          if (v) {
23091            classes = classes.concat(k.split(' '));
23092          }
23093        });
23094        return classes;
23095      }
23096      return classVal;
23097    }
23098  }];
23099}
23100
23101/**
23102 * @ngdoc directive
23103 * @name ngClass
23104 * @restrict AC
23105 *
23106 * @description
23107 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
23108 * an expression that represents all classes to be added.
23109 *
23110 * The directive operates in three different ways, depending on which of three types the expression
23111 * evaluates to:
23112 *
23113 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
23114 * names.
23115 *
23116 * 2. If the expression evaluates to an object, then for each key-value pair of the
23117 * object with a truthy value the corresponding key is used as a class name.
23118 *
23119 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
23120 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
23121 * to give you more control over what CSS classes appear. See the code below for an example of this.
23122 *
23123 *
23124 * The directive won't add duplicate classes if a particular class was already set.
23125 *
23126 * When the expression changes, the previously added classes are removed and only then are the
23127 * new classes added.
23128 *
23129 * @animations
23130 * **add** - happens just before the class is applied to the elements
23131 *
23132 * **remove** - happens just before the class is removed from the element
23133 *
23134 * @element ANY
23135 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
23136 *   of the evaluation can be a string representing space delimited class
23137 *   names, an array, or a map of class names to boolean values. In the case of a map, the
23138 *   names of the properties whose values are truthy will be added as css classes to the
23139 *   element.
23140 *
23141 * @example Example that demonstrates basic bindings via ngClass directive.
23142   <example>
23143     <file name="index.html">
23144       <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
23145       <label>
23146          <input type="checkbox" ng-model="deleted">
23147          deleted (apply "strike" class)
23148       </label><br>
23149       <label>
23150          <input type="checkbox" ng-model="important">
23151          important (apply "bold" class)
23152       </label><br>
23153       <label>
23154          <input type="checkbox" ng-model="error">
23155          error (apply "has-error" class)
23156       </label>
23157       <hr>
23158       <p ng-class="style">Using String Syntax</p>
23159       <input type="text" ng-model="style"
23160              placeholder="Type: bold strike red" aria-label="Type: bold strike red">
23161       <hr>
23162       <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
23163       <input ng-model="style1"
23164              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
23165       <input ng-model="style2"
23166              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
23167       <input ng-model="style3"
23168              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
23169       <hr>
23170       <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
23171       <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
23172       <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
23173     </file>
23174     <file name="style.css">
23175       .strike {
23176           text-decoration: line-through;
23177       }
23178       .bold {
23179           font-weight: bold;
23180       }
23181       .red {
23182           color: red;
23183       }
23184       .has-error {
23185           color: red;
23186           background-color: yellow;
23187       }
23188       .orange {
23189           color: orange;
23190       }
23191     </file>
23192     <file name="protractor.js" type="protractor">
23193       var ps = element.all(by.css('p'));
23194
23195       it('should let you toggle the class', function() {
23196
23197         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
23198         expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
23199
23200         element(by.model('important')).click();
23201         expect(ps.first().getAttribute('class')).toMatch(/bold/);
23202
23203         element(by.model('error')).click();
23204         expect(ps.first().getAttribute('class')).toMatch(/has-error/);
23205       });
23206
23207       it('should let you toggle string example', function() {
23208         expect(ps.get(1).getAttribute('class')).toBe('');
23209         element(by.model('style')).clear();
23210         element(by.model('style')).sendKeys('red');
23211         expect(ps.get(1).getAttribute('class')).toBe('red');
23212       });
23213
23214       it('array example should have 3 classes', function() {
23215         expect(ps.get(2).getAttribute('class')).toBe('');
23216         element(by.model('style1')).sendKeys('bold');
23217         element(by.model('style2')).sendKeys('strike');
23218         element(by.model('style3')).sendKeys('red');
23219         expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
23220       });
23221
23222       it('array with map example should have 2 classes', function() {
23223         expect(ps.last().getAttribute('class')).toBe('');
23224         element(by.model('style4')).sendKeys('bold');
23225         element(by.model('warning')).click();
23226         expect(ps.last().getAttribute('class')).toBe('bold orange');
23227       });
23228     </file>
23229   </example>
23230
23231   ## Animations
23232
23233   The example below demonstrates how to perform animations using ngClass.
23234
23235   <example module="ngAnimate" deps="angular-animate.js" animations="true">
23236     <file name="index.html">
23237      <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
23238      <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
23239      <br>
23240      <span class="base-class" ng-class="myVar">Sample Text</span>
23241     </file>
23242     <file name="style.css">
23243       .base-class {
23244         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23245       }
23246
23247       .base-class.my-class {
23248         color: red;
23249         font-size:3em;
23250       }
23251     </file>
23252     <file name="protractor.js" type="protractor">
23253       it('should check ng-class', function() {
23254         expect(element(by.css('.base-class')).getAttribute('class')).not.
23255           toMatch(/my-class/);
23256
23257         element(by.id('setbtn')).click();
23258
23259         expect(element(by.css('.base-class')).getAttribute('class')).
23260           toMatch(/my-class/);
23261
23262         element(by.id('clearbtn')).click();
23263
23264         expect(element(by.css('.base-class')).getAttribute('class')).not.
23265           toMatch(/my-class/);
23266       });
23267     </file>
23268   </example>
23269
23270
23271   ## ngClass and pre-existing CSS3 Transitions/Animations
23272   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
23273   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
23274   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
23275   to view the step by step details of {@link $animate#addClass $animate.addClass} and
23276   {@link $animate#removeClass $animate.removeClass}.
23277 */
23278var ngClassDirective = classDirective('', true);
23279
23280/**
23281 * @ngdoc directive
23282 * @name ngClassOdd
23283 * @restrict AC
23284 *
23285 * @description
23286 * The `ngClassOdd` and `ngClassEven` directives work exactly as
23287 * {@link ng.directive:ngClass ngClass}, except they work in
23288 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23289 *
23290 * This directive can be applied only within the scope of an
23291 * {@link ng.directive:ngRepeat ngRepeat}.
23292 *
23293 * @element ANY
23294 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
23295 *   of the evaluation can be a string representing space delimited class names or an array.
23296 *
23297 * @example
23298   <example>
23299     <file name="index.html">
23300        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23301          <li ng-repeat="name in names">
23302           <span ng-class-odd="'odd'" ng-class-even="'even'">
23303             {{name}}
23304           </span>
23305          </li>
23306        </ol>
23307     </file>
23308     <file name="style.css">
23309       .odd {
23310         color: red;
23311       }
23312       .even {
23313         color: blue;
23314       }
23315     </file>
23316     <file name="protractor.js" type="protractor">
23317       it('should check ng-class-odd and ng-class-even', function() {
23318         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23319           toMatch(/odd/);
23320         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23321           toMatch(/even/);
23322       });
23323     </file>
23324   </example>
23325 */
23326var ngClassOddDirective = classDirective('Odd', 0);
23327
23328/**
23329 * @ngdoc directive
23330 * @name ngClassEven
23331 * @restrict AC
23332 *
23333 * @description
23334 * The `ngClassOdd` and `ngClassEven` directives work exactly as
23335 * {@link ng.directive:ngClass ngClass}, except they work in
23336 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23337 *
23338 * This directive can be applied only within the scope of an
23339 * {@link ng.directive:ngRepeat ngRepeat}.
23340 *
23341 * @element ANY
23342 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
23343 *   result of the evaluation can be a string representing space delimited class names or an array.
23344 *
23345 * @example
23346   <example>
23347     <file name="index.html">
23348        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23349          <li ng-repeat="name in names">
23350           <span ng-class-odd="'odd'" ng-class-even="'even'">
23351             {{name}} &nbsp; &nbsp; &nbsp;
23352           </span>
23353          </li>
23354        </ol>
23355     </file>
23356     <file name="style.css">
23357       .odd {
23358         color: red;
23359       }
23360       .even {
23361         color: blue;
23362       }
23363     </file>
23364     <file name="protractor.js" type="protractor">
23365       it('should check ng-class-odd and ng-class-even', function() {
23366         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23367           toMatch(/odd/);
23368         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23369           toMatch(/even/);
23370       });
23371     </file>
23372   </example>
23373 */
23374var ngClassEvenDirective = classDirective('Even', 1);
23375
23376/**
23377 * @ngdoc directive
23378 * @name ngCloak
23379 * @restrict AC
23380 *
23381 * @description
23382 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
23383 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
23384 * directive to avoid the undesirable flicker effect caused by the html template display.
23385 *
23386 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23387 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23388 * of the browser view.
23389 *
23390 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23391 * `angular.min.js`.
23392 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23393 *
23394 * ```css
23395 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23396 *   display: none !important;
23397 * }
23398 * ```
23399 *
23400 * When this css rule is loaded by the browser, all html elements (including their children) that
23401 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23402 * during the compilation of the template it deletes the `ngCloak` element attribute, making
23403 * the compiled element visible.
23404 *
23405 * For the best result, the `angular.js` script must be loaded in the head section of the html
23406 * document; alternatively, the css rule above must be included in the external stylesheet of the
23407 * application.
23408 *
23409 * @element ANY
23410 *
23411 * @example
23412   <example>
23413     <file name="index.html">
23414        <div id="template1" ng-cloak>{{ 'hello' }}</div>
23415        <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23416     </file>
23417     <file name="protractor.js" type="protractor">
23418       it('should remove the template directive and css class', function() {
23419         expect($('#template1').getAttribute('ng-cloak')).
23420           toBeNull();
23421         expect($('#template2').getAttribute('ng-cloak')).
23422           toBeNull();
23423       });
23424     </file>
23425   </example>
23426 *
23427 */
23428var ngCloakDirective = ngDirective({
23429  compile: function(element, attr) {
23430    attr.$set('ngCloak', undefined);
23431    element.removeClass('ng-cloak');
23432  }
23433});
23434
23435/**
23436 * @ngdoc directive
23437 * @name ngController
23438 *
23439 * @description
23440 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23441 * supports the principles behind the Model-View-Controller design pattern.
23442 *
23443 * MVC components in angular:
23444 *
23445 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23446 *   are accessed through bindings.
23447 * * View — The template (HTML with data bindings) that is rendered into the View.
23448 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23449 *   logic behind the application to decorate the scope with functions and values
23450 *
23451 * Note that you can also attach controllers to the DOM by declaring it in a route definition
23452 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23453 * again using `ng-controller` in the template itself.  This will cause the controller to be attached
23454 * and executed twice.
23455 *
23456 * @element ANY
23457 * @scope
23458 * @priority 500
23459 * @param {expression} ngController Name of a constructor function registered with the current
23460 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23461 * that on the current scope evaluates to a constructor function.
23462 *
23463 * The controller instance can be published into a scope property by specifying
23464 * `ng-controller="as propertyName"`.
23465 *
23466 * If the current `$controllerProvider` is configured to use globals (via
23467 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23468 * also be the name of a globally accessible constructor function (not recommended).
23469 *
23470 * @example
23471 * Here is a simple form for editing user contact information. Adding, remov
23471ing, clearing, and
23472 * greeting are methods declared on the controller (see source tab). These methods can
23473 * easily be called from the angular markup. Any changes to the data are automatically reflected
23474 * in the View without the need for a manual update.
23475 *
23476 * Two different declaration styles are included below:
23477 *
23478 * * one binds methods and properties directly onto the controller using `this`:
23479 * `ng-controller="SettingsController1 as settings"`
23480 * * one injects `$scope` into the controller:
23481 * `ng-controller="SettingsController2"`
23482 *
23483 * The second option is more common in the Angular community, and is generally used in boilerplates
23484 * and in this guide. However, there are advantages to binding properties directly to the controller
23485 * and avoiding scope.
23486 *
23487 * * Using `controller as` makes it obvious which controller you are accessing in the template when
23488 * multiple controllers apply to an element.
23489 * * If you are writing your controllers as classes you have easier access to the properties and
23490 * methods, which will appear on the scope, from inside the controller code.
23491 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23492 * inheritance masking primitives.
23493 *
23494 * This example demonstrates the `controller as` syntax.
23495 *
23496 * <example name="ngControllerAs" module="controllerAsExample">
23497 *   <file name="index.html">
23498 *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23499 *      <label>Name: <input type="text" ng-model="settings.name"/></label>
23500 *      <button ng-click="settings.greet()">greet</button><br/>
23501 *      Contact:
23502 *      <ul>
23503 *        <li ng-repeat="contact in settings.contacts">
23504 *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23505 *             <option>phone</option>
23506 *             <option>email</option>
23507 *          </select>
23508 *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23509 *          <button ng-click="settings.clearContact(contact)">clear</button>
23510 *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
23511 *        </li>
23512 *        <li><button ng-click="settings.addContact()">add</button></li>
23513 *     </ul>
23514 *    </div>
23515 *   </file>
23516 *   <file name="app.js">
23517 *    angular.module('controllerAsExample', [])
23518 *      .controller('SettingsController1', SettingsController1);
23519 *
23520 *    function SettingsController1() {
23521 *      this.name = "John Smith";
23522 *      this.contacts = [
23523 *        {type: 'phone', value: '408 555 1212'},
23524 *        {type: 'email', value: '[email protected]'} ];
23525 *    }
23526 *
23527 *    SettingsController1.prototype.greet = function() {
23528 *      alert(this.name);
23529 *    };
23530 *
23531 *    SettingsController1.prototype.addContact = function() {
23532 *      this.contacts.push({type: 'email', value: '[email protected]'});
23533 *    };
23534 *
23535 *    SettingsController1.prototype.removeContact = function(contactToRemove) {
23536 *     var index = this.contacts.indexOf(contactToRemove);
23537 *      this.contacts.splice(index, 1);
23538 *    };
23539 *
23540 *    SettingsController1.prototype.clearContact = function(contact) {
23541 *      contact.type = 'phone';
23542 *      contact.value = '';
23543 *    };
23544 *   </file>
23545 *   <file name="protractor.js" type="protractor">
23546 *     it('should check controller as', function() {
23547 *       var container = element(by.id('ctrl-as-exmpl'));
23548 *         expect(container.element(by.model('settings.name'))
23549 *           .getAttribute('value')).toBe('John Smith');
23550 *
23551 *       var firstRepeat =
23552 *           container.element(by.repeater('contact in settings.contacts').row(0));
23553 *       var secondRepeat =
23554 *           container.element(by.repeater('contact in settings.contacts').row(1));
23555 *
23556 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23557 *           .toBe('408 555 1212');
23558 *
23559 *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23560 *           .toBe('[email protected]');
23561 *
23562 *       firstRepeat.element(by.buttonText('clear')).click();
23563 *
23564 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23565 *           .toBe('');
23566 *
23567 *       container.element(by.buttonText('add')).click();
23568 *
23569 *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
23570 *           .element(by.model('contact.value'))
23571 *           .getAttribute('value'))
23572 *           .toBe('[email protected]');
23573 *     });
23574 *   </file>
23575 * </example>
23576 *
23577 * This example demonstrates the "attach to `$scope`" style of controller.
23578 *
23579 * <example name="ngController" module="controllerExample">
23580 *  <file name="index.html">
23581 *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
23582 *     <label>Name: <input type="text" ng-model="name"/></label>
23583 *     <button ng-click="greet()">greet</button><br/>
23584 *     Contact:
23585 *     <ul>
23586 *       <li ng-repeat="contact in contacts">
23587 *         <select ng-model="contact.type" id="select_{{$index}}">
23588 *            <option>phone</option>
23589 *            <option>email</option>
23590 *         </select>
23591 *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23592 *         <button ng-click="clearContact(contact)">clear</button>
23593 *         <button ng-click="removeContact(contact)">X</button>
23594 *       </li>
23595 *       <li>[ <button ng-click="addContact()">add</button> ]</li>
23596 *    </ul>
23597 *   </div>
23598 *  </file>
23599 *  <file name="app.js">
23600 *   angular.module('controllerExample', [])
23601 *     .controller('SettingsController2', ['$scope', SettingsController2]);
23602 *
23603 *   function SettingsController2($scope) {
23604 *     $scope.name = "John Smith";
23605 *     $scope.contacts = [
23606 *       {type:'phone', value:'408 555 1212'},
23607 *       {type:'email', value:'[email protected]'} ];
23608 *
23609 *     $scope.greet = function() {
23610 *       alert($scope.name);
23611 *     };
23612 *
23613 *     $scope.addContact = function() {
23614 *       $scope.contacts.push({type:'email', value:'[email protected]'});
23615 *     };
23616 *
23617 *     $scope.removeContact = function(contactToRemove) {
23618 *       var index = $scope.contacts.indexOf(contactToRemove);
23619 *       $scope.contacts.splice(index, 1);
23620 *     };
23621 *
23622 *     $scope.clearContact = function(contact) {
23623 *       contact.type = 'phone';
23624 *       contact.value = '';
23625 *     };
23626 *   }
23627 *  </file>
23628 *  <file name="protractor.js" type="protractor">
23629 *    it('should check controller', function() {
23630 *      var container = element(by.id('ctrl-exmpl'));
23631 *
23632 *      expect(container.element(by.model('name'))
23633 *          .getAttribute('value')).toBe('John Smith');
23634 *
23635 *      var firstRepeat =
23636 *          container.element(by.repeater('contact in contacts').row(0));
23637 *      var secondRepeat =
23638 *          container.element(by.repeater('contact in contacts').row(1));
23639 *
23640 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23641 *          .toBe('408 555 1212');
23642 *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23643 *          .toBe('[email protected]');
23644 *
23645 *      firstRepeat.element(by.buttonText('clear')).click();
23646 *
23647 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23648 *          .toBe('');
23649 *
23650 *      container.element(by.buttonText('add')).click();
23651 *
23652 *      expect(container.element(by.repeater('contact in contacts').row(2))
23653 *          .element(by.model('contact.value'))
23654 *          .getAttribute('value'))
23655 *          .toBe('[email protected]');
23656 *    });
23657 *  </file>
23658 *</example>
23659
23660 */
23661var ngControllerDirective = [function() {
23662  return {
23663    restrict: 'A',
23664    scope: true,
23665    controller: '@',
23666    priority: 500
23667  };
23668}];
23669
23670/**
23671 * @ngdoc directive
23672 * @name ngCsp
23673 *
23674 * @element html
23675 * @description
23676 *
23677 * Angular has some features that can break certain
23678 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
23679 *
23680 * If you intend to implement these rules then you must tell Angular not to use these features.
23681 *
23682 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23683 *
23684 *
23685 * The following rules affect Angular:
23686 *
23687 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
23688 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
23689 * increase in the speed of evaluating Angular expressions.
23690 *
23691 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
23692 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
23693 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
23694 * `angular-csp.css` in your HTML manually.
23695 *
23696 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
23697 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
23698 * however, triggers a CSP error to be logged in the console:
23699 *
23700 * ```
23701 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23702 * script in the following Content Security Policy directive: "default-src 'self'". Note that
23703 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23704 * ```
23705 *
23706 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23707 * directive on an element of the HTML document that appears before the `<script>` tag that loads
23708 * the `angular.js` file.
23709 *
23710 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23711 *
23712 * You can specify which of the CSP related Angular features should be deactivated by providing
23713 * a value for the `ng-csp` attribute. The options are as follows:
23714 *
23715 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
23716 *
23717 * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
23718 *
23719 * You can use these values in the following combinations:
23720 *
23721 *
23722 * * No declaration means that Angular will assume that you can do inline styles, but it will do
23723 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
23724 * of Angular.
23725 *
23726 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
23727 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
23728 * of Angular.
23729 *
23730 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
23731 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
23732 *
23733 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
23734 * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
23735 *
23736 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
23737 * styles nor use eval, which is the same as an empty: ng-csp.
23738 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
23739 *
23740 * @example
23741 * This example shows how to apply the `ngCsp` directive to the `html` tag.
23742   ```html
23743     <!doctype html>
23744     <html ng-app ng-csp>
23745     ...
23746     ...
23747     </html>
23748   ```
23749  * @example
23750      // Note: the suffix `.csp` in the example name triggers
23751      // csp mode in our http server!
23752      <example name="example.csp" module="cspExample" ng-csp="true">
23753        <file name="index.html">
23754          <div ng-controller="MainController as ctrl">
23755            <div>
23756              <button ng-click="ctrl.inc()" id="inc">Increment</button>
23757              <span id="counter">
23758                {{ctrl.counter}}
23759              </span>
23760            </div>
23761
23762            <div>
23763              <button ng-click="ctrl.evil()" id="evil">Evil</button>
23764              <span id="evilError">
23765                {{ctrl.evilError}}
23766              </span>
23767            </div>
23768          </div>
23769        </file>
23770        <file name="script.js">
23771           angular.module('cspExample', [])
23772             .controller('MainController', function() {
23773                this.counter = 0;
23774                this.inc = function() {
23775                  this.counter++;
23776                };
23777                this.evil = function() {
23778                  // jshint evil:true
23779                  try {
23780                    eval('1+2');
23781                  } catch (e) {
23782                    this.evilError = e.message;
23783                  }
23784                };
23785              });
23786        </file>
23787        <file name="protractor.js" type="protractor">
23788          var util, webdriver;
23789
23790          var incBtn = element(by.id('inc'));
23791          var counter = element(by.id('counter'));
23792          var evilBtn = element(by.id('evil'));
23793          var evilError = element(by.id('evilError'));
23794
23795          function getAndClearSevereErrors() {
23796            return browser.manage().logs().get('browser').then(function(browserLog) {
23797              return browserLog.filter(function(logEntry) {
23798                return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23799              });
23800            });
23801          }
23802
23803          function clearErrors() {
23804            getAndClearSevereErrors();
23805          }
23806
23807          function expectNoErrors() {
23808            getAndClearSevereErrors().then(function(filteredLog) {
23809              expect(filteredLog.length).toEqual(0);
23810              if (filteredLog.length) {
23811                console.log('browser console errors: ' + util.inspect(filteredLog));
23812              }
23813            });
23814          }
23815
23816          function expectError(regex) {
23817            getAndClearSevereErrors().then(function(filteredLog) {
23818              var found = false;
23819              filteredLog.forEach(function(log) {
23820                if (log.message.match(regex)) {
23821                  found = true;
23822                }
23823              });
23824              if (!found) {
23825                throw new Error('expected an error that matches ' + regex);
23826              }
23827            });
23828          }
23829
23830          beforeEach(function() {
23831            util = require('util');
23832            webdriver = require('protractor/node_modules/selenium-webdriver');
23833          });
23834
23835          // For now, we only test on Chrome,
23836          // as Safari does not load the page with Protractor's injected scripts,
23837          // and Firefox webdriver always disables content security policy (#6358)
23838          if (browser.params.browser !== 'chrome') {
23839            return;
23840          }
23841
23842          it('should not report errors when the page is loaded', function() {
23843            // clear errors so we are not dependent on previous tests
23844            clearErrors();
23845            // Need to reload the page as the page is already loaded when
23846            // we come here
23847            browser.driver.getCurrentUrl().then(function(url) {
23848              browser.get(url);
23849            });
23850            expectNoErrors();
23851          });
23852
23853          it('should evaluate expressions', function() {
23854            expect(counter.getText()).toEqual('0');
23855            incBtn.click();
23856            expect(counter.getText()).toEqual('1');
23857            expectNoErrors();
23858          });
23859
23860          it('should throw and report an error when using "eval"', function() {
23861            evilBtn.click();
23862            expect(evilError.getText()).toMatch(/Content Security Policy/);
23863            expectError(/Content Security Policy/);
23864          });
23865        </file>
23866      </example>
23867  */
23868
23869// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
23870// bootstrap the system (before $parse is instantiated), for this reason we just have
23871// the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
23872
23873/**
23874 * @ngdoc directive
23875 * @name ngClick
23876 *
23877 * @description
23878 * The ngClick directive allows you to specify custom behavior when
23879 * an element is clicked.
23880 *
23881 * @element ANY
23882 * @priority 0
23883 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
23884 * click. ({@link guide/expression#-event- Event object is available as `$event`})
23885 *
23886 * @example
23887   <example>
23888     <file name="index.html">
23889      <button ng-click="count = count + 1" ng-init="count=0">
23890        Increment
23891      </button>
23892      <span>
23893        count: {{count}}
23894      </span>
23895     </file>
23896     <file name="protractor.js" type="protractor">
23897       it('should check ng-click', function() {
23898         expect(element(by.binding('count')).getText()).toMatch('0');
23899         element(by.css('button')).click();
23900         expect(element(by.binding('count')).getText()).toMatch('1');
23901       });
23902     </file>
23903   </example>
23904 */
23905/*
23906 * A collection of directives that allows creation of custom event handlers that are defined as
23907 * angular expressions and are compiled and executed within the current scope.
23908 */
23909var ngEventDirectives = {};
23910
23911// For events that might fire synchronously during DOM manipulation
23912// we need to execute their event handlers asynchronously using $evalAsync,
23913// so that they are not executed in an inconsistent state.
23914var forceAsyncEvents = {
23915  'blur': true,
23916  'focus': true
23917};
23918forEach(
23919  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
23920  function(eventName) {
23921    var directiveName = directiveNormalize('ng-' + eventName);
23922    ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
23923      return {
23924        restrict: 'A',
23925        compile: function($element, attr) {
23926          // We expose the powerful $event object on the scope that provides access to the Window,
23927          // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
23928          // checks at the cost of speed since event handler expressions are not executed as
23929          // frequently as regular change detection.
23930          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
23931          return function ngEventHandler(scope, element) {
23932            element.on(eventName, function(event) {
23933              var callback = function() {
23934                fn(scope, {$event:event});
23935              };
23936              if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
23937                scope.$evalAsync(callback);
23938              } else {
23939                scope.$apply(callback);
23940              }
23941            });
23942          };
23943        }
23944      };
23945    }];
23946  }
23947);
23948
23949/**
23950 * @ngdoc directive
23951 * @name ngDblclick
23952 *
23953 * @description
23954 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
23955 *
23956 * @element ANY
23957 * @priority 0
23958 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
23959 * a dblclick. (The Event object is available as `$event`)
23960 *
23961 * @example
23962   <example>
23963     <file name="index.html">
23964      <button ng-dblclick="count = count + 1" ng-init="count=0">
23965        Increment (on double click)
23966      </button>
23967      count: {{count}}
23968     </file>
23969   </example>
23970 */
23971
23972
23973/**
23974 * @ngdoc directive
23975 * @name ngMousedown
23976 *
23977 * @description
23978 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
23979 *
23980 * @element ANY
23981 * @priority 0
23982 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
23983 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
23984 *
23985 * @example
23986   <example>
23987     <file name="index.html">
23988      <button ng-mousedown="count = count + 1" ng-init="count=0">
23989        Increment (on mouse down)
23990      </button>
23991      count: {{count}}
23992     </file>
23993   </example>
23994 */
23995
23996
23997/**
23998 * @ngdoc directive
23999 * @name ngMouseup
24000 *
24001 * @description
24002 * Specify custom behavior on mouseup event.
24003 *
24004 * @element ANY
24005 * @priority 0
24006 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
24007 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
24008 *
24009 * @example
24010   <example>
24011     <file name="index.html">
24012      <button ng-mouseup="count = count + 1" ng-init="count=0">
24013        Increment (on mouse up)
24014      </button>
24015      count: {{count}}
24016     </file>
24017   </example>
24018 */
24019
24020/**
24021 * @ngdoc directive
24022 * @name ngMouseover
24023 *
24024 * @description
24025 * Specify custom behavior on mouseover event.
24026 *
24027 * @element ANY
24028 * @priority 0
24029 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
24030 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
24031 *
24032 * @example
24033   <example>
24034     <file name="index.html">
24035      <button ng-mouseover="count = count + 1" ng-init="count=0">
24036        Increment (when mouse is over)
24037      </button>
24038      count: {{count}}
24039     </file>
24040   </example>
24041 */
24042
24043
24044/**
24045 * @ngdoc directive
24046 * @name ngMouseenter
24047 *
24048 * @description
24049 * Specify custom behavior on mouseenter event.
24050 *
24051 * @element ANY
24052 * @priority 0
24053 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
24054 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
24055 *
24056 * @example
24057   <example>
24058     <file name="index.html">
24059      <button ng-mouseenter="count = count + 1" ng-init="count=0">
24060        Increment (when mouse enters)
24061      </button>
24062      count: {{count}}
24063     </file>
24064   </example>
24065 */
24066
24067
24068/**
24069 * @ngdoc directive
24070 * @name ngMouseleave
24071 *
24072 * @description
24073 * Specify custom behavior on mouseleave event.
24074 *
24075 * @element ANY
24076 * @priority 0
24077 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
24078 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
24079 *
24080 * @example
24081   <example>
24082     <file name="index.html">
24083      <button ng-mouseleave="count = count + 1" ng-init="count=0">
24084        Increment (when mouse leaves)
24085      </button>
24086      count: {{count}}
24087     </file>
24088   </example>
24089 */
24090
24091
24092/**
24093 * @ngdoc directive
24094 * @name ngMousemove
24095 *
24096 * @description
24097 * Specify custom behavior on mousemove event.
24098 *
24099 * @element ANY
24100 * @priority 0
24101 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
24102 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
24103 *
24104 * @example
24105   <example>
24106     <file name="index.html">
24107      <button ng-mousemove="count = count + 1" ng-init="count=0">
24108        Increment (when mouse moves)
24109      </button>
24110      count: {{count}}
24111     </file>
24112   </example>
24113 */
24114
24115
24116/**
24117 * @ngdoc directive
24118 * @name ngKeydown
24119 *
24120 * @description
24121 * Specify custom behavior on keydown event.
24122 *
24123 * @element ANY
24124 * @priority 0
24125 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
24126 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
24127 *
24128 * @example
24129   <example>
24130     <file name="index.html">
24131      <input ng-keydown="count = count + 1" ng-init="count=0">
24132      key down count: {{count}}
24133     </file>
24134   </example>
24135 */
24136
24137
24138/**
24139 * @ngdoc directive
24140 * @name ngKeyup
24141 *
24142 * @description
24143 * Specify custom behavior on keyup event.
24144 *
24145 * @element ANY
24146 * @priority 0
24147 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
24148 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
24149 *
24150 * @example
24151   <example>
24152     <file name="index.html">
24153       <p>Typing in the input box below updates the key count</p>
24154       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
24155
24156       <p>Typing in the input box below updates the keycode</p>
24157       <input ng-keyup="event=$event">
24158       <p>event keyCode: {{ event.keyCode }}</p>
24159       <p>event altKey: {{ event.altKey }}</p>
24160     </file>
24161   </example>
24162 */
24163
24164
24165/**
24166 * @ngdoc directive
24167 * @name ngKeypress
24168 *
24169 * @description
24170 * Specify custom behavior on keypress event.
24171 *
24172 * @element ANY
24173 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
24174 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
24175 * and can be interrogated for keyCode, altKey, etc.)
24176 *
24177 * @example
24178   <example>
24179     <file name="index.html">
24180      <input ng-keypress="count = count + 1" ng-init="count=0">
24181      key press count: {{count}}
24182     </file>
24183   </example>
24184 */
24185
24186
24187/**
24188 * @ngdoc directive
24189 * @name ngSubmit
24190 *
24191 * @description
24192 * Enables binding angular expressions to onsubmit events.
24193 *
24194 * Additionally it prevents the default action (which for form means 
24194sending the request to the
24195 * server and reloading the current page), but only if the form does not contain `action`,
24196 * `data-action`, or `x-action` attributes.
24197 *
24198 * <div class="alert alert-warning">
24199 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
24200 * `ngSubmit` handlers together. See the
24201 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
24202 * for a detailed discussion of when `ngSubmit` may be triggered.
24203 * </div>
24204 *
24205 * @element form
24206 * @priority 0
24207 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
24208 * ({@link guide/expression#-event- Event object is available as `$event`})
24209 *
24210 * @example
24211   <example module="submitExample">
24212     <file name="index.html">
24213      <script>
24214        angular.module('submitExample', [])
24215          .controller('ExampleController', ['$scope', function($scope) {
24216            $scope.list = [];
24217            $scope.text = 'hello';
24218            $scope.submit = function() {
24219              if ($scope.text) {
24220                $scope.list.push(this.text);
24221                $scope.text = '';
24222              }
24223            };
24224          }]);
24225      </script>
24226      <form ng-submit="submit()" ng-controller="ExampleController">
24227        Enter text and hit enter:
24228        <input type="text" ng-model="text" name="text" />
24229        <input type="submit" id="submit" value="Submit" />
24230        <pre>list={{list}}</pre>
24231      </form>
24232     </file>
24233     <file name="protractor.js" type="protractor">
24234       it('should check ng-submit', function() {
24235         expect(element(by.binding('list')).getText()).toBe('list=[]');
24236         element(by.css('#submit')).click();
24237         expect(element(by.binding('list')).getText()).toContain('hello');
24238         expect(element(by.model('text')).getAttribute('value')).toBe('');
24239       });
24240       it('should ignore empty strings', function() {
24241         expect(element(by.binding('list')).getText()).toBe('list=[]');
24242         element(by.css('#submit')).click();
24243         element(by.css('#submit')).click();
24244         expect(element(by.binding('list')).getText()).toContain('hello');
24245        });
24246     </file>
24247   </example>
24248 */
24249
24250/**
24251 * @ngdoc directive
24252 * @name ngFocus
24253 *
24254 * @description
24255 * Specify custom behavior on focus event.
24256 *
24257 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
24258 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24259 * during an `$apply` to ensure a consistent state.
24260 *
24261 * @element window, input, select, textarea, a
24262 * @priority 0
24263 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
24264 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
24265 *
24266 * @example
24267 * See {@link ng.directive:ngClick ngClick}
24268 */
24269
24270/**
24271 * @ngdoc directive
24272 * @name ngBlur
24273 *
24274 * @description
24275 * Specify custom behavior on blur event.
24276 *
24277 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
24278 * an element has lost focus.
24279 *
24280 * Note: As the `blur` event is executed synchronously also during DOM manipulations
24281 * (e.g. removing a focussed input),
24282 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24283 * during an `$apply` to ensure a consistent state.
24284 *
24285 * @element window, input, select, textarea, a
24286 * @priority 0
24287 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
24288 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
24289 *
24290 * @example
24291 * See {@link ng.directive:ngClick ngClick}
24292 */
24293
24294/**
24295 * @ngdoc directive
24296 * @name ngCopy
24297 *
24298 * @description
24299 * Specify custom behavior on copy event.
24300 *
24301 * @element window, input, select, textarea, a
24302 * @priority 0
24303 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
24304 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
24305 *
24306 * @example
24307   <example>
24308     <file name="index.html">
24309      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
24310      copied: {{copied}}
24311     </file>
24312   </example>
24313 */
24314
24315/**
24316 * @ngdoc directive
24317 * @name ngCut
24318 *
24319 * @description
24320 * Specify custom behavior on cut event.
24321 *
24322 * @element window, input, select, textarea, a
24323 * @priority 0
24324 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
24325 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
24326 *
24327 * @example
24328   <example>
24329     <file name="index.html">
24330      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
24331      cut: {{cut}}
24332     </file>
24333   </example>
24334 */
24335
24336/**
24337 * @ngdoc directive
24338 * @name ngPaste
24339 *
24340 * @description
24341 * Specify custom behavior on paste event.
24342 *
24343 * @element window, input, select, textarea, a
24344 * @priority 0
24345 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
24346 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
24347 *
24348 * @example
24349   <example>
24350     <file name="index.html">
24351      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
24352      pasted: {{paste}}
24353     </file>
24354   </example>
24355 */
24356
24357/**
24358 * @ngdoc directive
24359 * @name ngIf
24360 * @restrict A
24361 * @multiElement
24362 *
24363 * @description
24364 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
24365 * {expression}. If the expression assigned to `ngIf` evaluates to a false
24366 * value then the element is removed from the DOM, otherwise a clone of the
24367 * element is reinserted into the DOM.
24368 *
24369 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24370 * element in the DOM rather than changing its visibility via the `display` css property.  A common
24371 * case when this difference is significant is when using css selectors that rely on an element's
24372 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classe
24372s.
24373 *
24374 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
24375 * is created when the element is restored.  The scope created within `ngIf` inherits from
24376 * its parent scope using
24377 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
24378 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24379 * a javascript primitive defined in the parent scope. In this case any modifications made to the
24380 * variable within the child scope will override (hide) the value in the parent scope.
24381 *
24382 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
24383 * is if an element's class attribute is directly modified after it's compiled, using something like
24384 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24385 * the added class will be lost because the original compiled state is used to regenerate the element.
24386 *
24387 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24388 * and `leave` effects.
24389 *
24390 * @animations
24391 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24392 * leave - happens just before the `ngIf` contents are removed from the DOM
24393 *
24394 * @element ANY
24395 * @scope
24396 * @priority 600
24397 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24398 *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
24399 *     element is added to the DOM tree.
24400 *
24401 * @example
24402  <example module="ngAnimate" deps="angular-animate.js" animations="true">
24403    <file name="index.html">
24404      <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24405      Show when checked:
24406      <span ng-if="checked" class="animate-if">
24407        This is removed when the checkbox is unchecked.
24408      </span>
24409    </file>
24410    <file name="animations.css">
24411      .animate-if {
24412        background:white;
24413        border:1px solid black;
24414        padding:10px;
24415      }
24416
24417      .animate-if.ng-enter, .animate-if.ng-leave {
24418        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24419      }
24420
24421      .animate-if.ng-enter,
24422      .animate-if.ng-leave.ng-leave-active {
24423        opacity:0;
24424      }
24425
24426      .animate-if.ng-leave,
24427      .animate-if.ng-enter.ng-enter-active {
24428        opacity:1;
24429      }
24430    </file>
24431  </example>
24432 */
24433var ngIfDirective = ['$animate', function($animate) {
24434  return {
24435    multiElement: true,
24436    transclude: 'element',
24437    priority: 600,
24438    terminal: true,
24439    restrict: 'A',
24440    $$tlb: true,
24441    link: function($scope, $element, $attr, ctrl, $transclude) {
24442        var block, childScope, previousElements;
24443        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24444
24445          if (value) {
24446            if (!childScope) {
24447              $transclude(function(clone, newScope) {
24448                childScope = newScope;
24449                clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24450                // Note: We only need the first/last node of the cloned nodes.
24451                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24452                // by a directive with templateUrl when its template arrives.
24453                block = {
24454                  clone: clone
24455                };
24456                $animate.enter(clone, $element.parent(), $element);
24457              });
24458            }
24459          } else {
24460            if (previousElements) {
24461              previousElements.remove();
24462              previousElements = null;
24463            }
24464            if (childScope) {
24465              childScope.$destroy();
24466              childScope = null;
24467            }
24468            if (block) {
24469              previousElements = getBlockNodes(block.clone);
24470              $animate.leave(previousElements).then(function() {
24471                previousElements = null;
24472              });
24473              block = null;
24474            }
24475          }
24476        });
24477    }
24478  };
24479}];
24480
24481/**
24482 * @ngdoc directive
24483 * @name ngInclude
24484 * @restrict ECA
24485 *
24486 * @description
24487 * Fetches, compiles and includes an external HTML fragment.
24488 *
24489 * By default, the template URL is restricted to the same domain and protocol as the
24490 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24491 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24492 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24493 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24494 * ng.$sce Strict Contextual Escaping}.
24495 *
24496 * In addition, the browser's
24497 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24498 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24499 * policy may further restrict whether the template is successfully loaded.
24500 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24501 * access on some browsers.
24502 *
24503 * @animations
24504 * enter - animation is used to bring new content into the browser.
24505 * leave - animation is used to animate existing content away.
24506 *
24507 * The enter and leave animation occur concurrently.
24508 *
24509 * @scope
24510 * @priority 400
24511 *
24512 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24513 *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24514 * @param {string=} onload Expression to evaluate when a new partial is loaded.
24515 *                  <div class="alert alert-warning">
24516 *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
24517 *                  a function with the name on the window element, which will usually throw a
24518 *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
24519 *                  different form that {@link guide/directive#normalization matches} `onload`.
24520 *                  </div>
24521   *
24522 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24523 *                  $anchorScroll} to scroll the viewport after the content is loaded.
24524 *
24525 *                  - If the attribute is not set, disable scrolling.
24526 *                  - If the attribute is set without value, enable scrolling.
24527 *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
24528 *
24529 * @example
24530  <example module="includeExample" deps="angular-animate.js" animations="true">
24531    <file name="index.html">
24532     <div ng-controller="ExampleController">
24533       <select ng-model="template" ng-options="t.name for t in templates">
24534        <option value="">(blank)</option>
24535       </select>
24536       url of the template: <code>{{template.url}}</code>
24537       <hr/>
24538       <div class="slide-animate-container">
24539         <div class="slide-animate" ng-include="template.url"></div>
24540       </div>
24541     </div>
24542    </file>
24543    <file name="script.js">
24544      angular.module('includeExample', ['ngAnimate'])
24545        .controller('ExampleController', ['$scope', function($scope) {
24546          $scope.templates =
24547            [ { name: 'template1.html', url: 'template1.html'},
24548              { name: 'template2.html', url: 'template2.html'} ];
24549          $scope.template = $scope.templates[0];
24550        }]);
24551     </file>
24552    <file name="template1.html">
24553      Content of template1.html
24554    </file>
24555    <file name="template2.html">
24556      Content of template2.html
24557    </file>
24558    <file name="animations.css">
24559      .slide-animate-container {
24560        position:relative;
24561        background:white;
24562        border:1px solid black;
24563        height:40px;
24564        overflow:hidden;
24565      }
24566
24567      .slide-animate {
24568        padding:10px;
24569      }
24570
24571      .slide-animate.ng-enter, .slide-animate.ng-leave {
24572        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24573
24574        position:absolute;
24575        top:0;
24576        left:0;
24577        right:0;
24578        bottom:0;
24579        display:block;
24580        padding:10px;
24581      }
24582
24583      .slide-animate.ng-enter {
24584        top:-50px;
24585      }
24586      .slide-animate.ng-enter.ng-enter-active {
24587        top:0;
24588      }
24589
24590      .slide-animate.ng-leave {
24591        top:0;
24592      }
24593      .slide-animate.ng-leave.ng-leave-active {
24594        top:50px;
24595      }
24596    </file>
24597    <file name="protractor.js" type="protractor">
24598      var templateSelect = element(by.model('template'));
24599      var includeElem = element(by.css('[ng-include]'));
24600
24601      it('should load template1.html', function() {
24602        expect(includeElem.getText()).toMatch(/Content of template1.html/);
24603      });
24604
24605      it('should load template2.html', function() {
24606        if (browser.params.browser == 'firefox') {
24607          // Firefox can't handle using selects
24608          // See https://github.com/angular/protractor/issues/480
24609          return;
24610        }
24611        templateSelect.click();
24612        templateSelect.all(by.css('option')).get(2).click();
24613        expect(includeElem.getText()).toMatch(/Content of template2.html/);
24614      });
24615
24616      it('should change to blank', function() {
24617        if (browser.params.browser == 'firefox') {
24618          // Firefox can't handle using selects
24619          return;
24620        }
24621        templateSelect.click();
24622        templateSelect.all(by.css('option')).get(0).click();
24623        expect(includeElem.isPresent()).toBe(false);
24624      });
24625    </file>
24626  </example>
24627 */
24628
24629
24630/**
24631 * @ngdoc event
24632 * @name ngInclude#$includeContentRequested
24633 * @eventType emit on the scope ngInclude was declared in
24634 * @description
24635 * Emitted every time the ngInclude content is requested.
24636 *
24637 * @param {Object} angularEvent Synthetic event object.
24638 * @param {String} src URL of content to load.
24639 */
24640
24641
24642/**
24643 * @ngdoc event
24644 * @name ngInclude#$includeContentLoaded
24645 * @eventType emit on the current ngInclude scope
24646 * @description
24647 * Emitted every time the ngInclude content is reloaded.
24648 *
24649 * @param {Object} angularEvent Synthetic event object.
24650 * @param {String} src URL of content to load.
24651 */
24652
24653
24654/**
24655 * @ngdoc event
24656 * @name ngInclude#$includeContentError
24657 * @eventType emit on the scope ngInclude was declared in
24658 * @description
24659 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
24660 *
24661 * @param {Object} angularEvent Synthetic event object.
24662 * @param {String} src URL of content to load.
24663 */
24664var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
24665                  function($templateRequest,   $anchorScroll,   $animate) {
24666  return {
24667    restrict: 'ECA',
24668    priority: 400,
24669    terminal: true,
24670    transclude: 'element',
24671    controller: angular.noop,
24672    compile: function(element, attr) {
24673      var srcExp = attr.ngInclude || attr.src,
24674          onloadExp = attr.onload || '',
24675          autoScrollExp = attr.autoscroll;
24676
24677      return function(scope, $element, $attr, ctrl, $transclude) {
24678        var changeCounter = 0,
24679            currentScope,
24680            previousElement,
24681            currentElement;
24682
24683        var cleanupLastIncludeContent = function() {
24684          if (previousElement) {
24685            previousElement.remove();
24686            previousElement = null;
24687          }
24688          if (currentScope) {
24689            currentScope.$destroy();
24690            currentScope = null;
24691          }
24692          if (currentElement) {
24693            $animate.leave(currentElement).then(function() {
24694              previousElement = null;
24695            });
24696            previousElement = currentElement;
24697            currentElement = null;
24698          }
24699        };
24700
24701        scope.$watch(srcExp, function ngIncludeWatchAction(src) {
24702          var afterAnimation = function() {
24703            if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24704              $anchorScroll();
24705            }
24706          };
24707          var thisChangeId = ++changeCounter;
24708
24709          if (src) {
24710            //set the 2nd param to true to ignore the template request error so that the inner
24711            //contents and scope can be cleaned up.
24712            $templateRequest(src, true).then(function(response) {
24713              if (scope.$$destroyed) return;
24714
24715              if (thisChangeId !== changeCounter) return;
24716              var newScope = scope.$new();
24717              ctrl.template = response;
24718
24719              // Note: This will also link all children of ng-include that were contained in the original
24720              // html. If that content contains controllers, ... they could pollute/change the scope.
24721              // However, using ng-include on an element with additional content does not make sense...
24722              // Note: We can't remove them in the cloneAttchFn of $transclude as that
24723              // function is called before linking the content, which would apply child
24724              // directives to non existing elements.
24725              var clone = $transclude(newScope, function(clone) {
24726                cleanupLastIncludeContent();
24727                $animate.enter(clone, null, $element).then(afterAnimation);
24728              });
24729
24730              currentScope = newScope;
24731              currentElement = clone;
24732
24733              currentScope.$emit('$includeContentLoaded', src);
24734              scope.$eval(onloadExp);
24735            }, function() {
24736              if (scope.$$destroyed) return;
24737
24738              if (thisChangeId === changeCounter) {
24739                cleanupLastIncludeContent();
24740                scope.$emit('$includeContentError', src);
24741              }
24742            });
24743            scope.$emit('$includeContentRequested', src);
24744          } else {
24745            cleanupLastIncludeContent();
24746            ctrl.template = null;
24747          }
24748        });
24749      };
24750    }
24751  };
24752}];
24753
24754// This directive is called during the $transclude call of the first `ngInclude` directive.
24755// It will replace and compile the content of the element with the loaded template.
24756// We need this directive so that the element content is already filled when
24757// the link function of another directive on the same element as ngInclude
24758// is called.
24759var ngIncludeFillContentDirective = ['$compile',
24760  function($compile) {
24761    return {
24762      restrict: 'ECA',
24763      priority: -400,
24764      require: 'ngInclude',
24765      link: function(scope, $element, $attr, ctrl) {
24766        if (/SVG/.test($element[0].toString())) {
24767          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24768          // support innerHTML, so detect this here and try to generate the contents
24769          // specially.
24770          $element.empty();
24771          $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24772              function namespaceAdaptedClone(clone) {
24773            $element.append(clone);
24774          }, {futureParentElement: $element});
24775          return;
24776        }
24777
24778        $element.html(ctrl.template);
24779        $compile($element.contents())(scope);
24780      }
24781    };
24782  }];
24783
24784/**
24785 * @ngdoc directive
24786 * @name ngInit
24787 * @restrict AC
24788 *
24789 * @description
24790 * The `ngInit` directive allows you to evaluate an expression in the
24791 * current scope.
24792 *
24793 * <div class="alert alert-danger">
24794 * This directive can be abused to add unnecessary amounts of logic into your templates.
24795 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24796 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24797 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24798 * rather than `ngInit` to initialize values on a scope.
24799 * </div>
24800 *
24801 * <div class="alert alert-warning">
24802 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24803 * sure you have parentheses to ensure correct operator precedence:
24804 * <pre class="prettyprint">
24805 * `<div ng-init="test1 = ($index | toString)"></div>`
24806 * </pre>
24807 * </div>
24808 *
24809 * @priority 450
24810 *
24811 * @element ANY
24812 * @param {expression} ngInit {@link guide/expression Expression} to eval.
24813 *
24814 * @example
24815   <example module="initExample">
24816     <file name="index.html">
24817   <script>
24818     angular.module('initExample', [])
24819       .controller('ExampleController', ['$scope', function($scope) {
24820         $scope.list = [['a', 'b'], ['c', 'd']];
24821       }]);
24822   </script>
24823   <div ng-controller="ExampleController">
24824     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24825       <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24826          <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24827       </div>
24828     </div>
24829   </div>
24830     </file>
24831     <file name="protractor.js" type="protractor">
24832       it('should alias index positions', function() {
24833         var elements = element.all(by.css('.example-init'));
24834         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24835         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24836         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24837         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24838       });
24839     </file>
24840   </example>
24841 */
24842var ngInitDirective = ngDirective({
24843  priority: 450,
24844  compile: function() {
24845    return {
24846      pre: function(scope, element, attrs) {
24847        scope.$eval(attrs.ngInit);
24848      }
24849    };
24850  }
24851});
24852
24853/**
24854 * @ngdoc directive
24855 * @name ngList
24856 *
24857 * @description
24858 * Text input that converts between a delimited string and an array of strings. The default
24859 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24860 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24861 *
24862 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
24863 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
24864 *   list item is respected. This implies that the user of the directive is responsible for
24865 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
24866 *   tab or newline character.
24867 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
24868 *   when joining the list items back together) and whitespace around each list item is stripped
24869 *   before it is added to the model.
24870 *
24871 * ### Example with Validation
24872 *
24873 * <example name="ngList-directive" module="listExample">
24874 *   <file name="app.js">
24875 *      angular.module('listExample', [])
24876 *        .controller('ExampleController', ['$scope', function($scope) {
24877 *          $scope.names = ['morpheus', 'neo', 'trinity'];
24878 *        }]);
24879 *   </file>
24880 *   <file name="index.html">
24881 *    <form name="myForm" ng-controller="ExampleController">
24882 *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
24883 *      <span role="alert">
24884 *        <span class="error" ng-show="myForm.namesInput.$error.required">
24885 *        Required!</span>
24886 *      </span>
24887 *      <br>
24888 *      <tt>names = {{names}}</tt><br/>
24889 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
24890 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
24891 *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24892 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24893 *     </form>
24894 *   </file>
24895 *   <file name="protractor.js" type="protractor">
24896 *     var listInput = element(by.model('names'));
24897 *     var names = element(by.exactBinding('names'));
24898 *     var valid = element(by.binding('myForm.namesInput.$valid'));
24899 *     var error = element(by.css('span.error'));
24900 *
24901 *     it('should initialize to model', function() {
24902 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
24903 *       expect(valid.getText()).toContain('true');
24904 *       expect(error.getCssValue('display')).toBe('none');
24905 *     });
24906 *
24907 *     it('should be invalid if empty', function() {
24908 *       listInput.clear();
24909 *       listInput.sendKeys('');
24910 *
24911 *       expect(names.getText()).toContain('');
24912 *       expect(valid.getText()).toContain('false');
24913 *       expect(error.getCssValue('display')).not.toBe('none');
24914 *     });
24915 *   </file>
24916 * </example>
24917 *
24918 * ### Example - splitting on newline
24919 * <example name="ngList-directive-newlines">
24920 *   <file name="index.html">
24921 *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
24922 *    <pre>{{ list | json }}</pre>
24923 *   </file>
24924 *   <file name="protractor.js" type="protractor">
24925 *     it("should split the text by newlines", function() {
24926 *       var listInput = element(by.model('list'));
24927 *       var output = element(by.binding('list | json'));
24928 *       listInput.sendKeys('abc\ndef\nghi');
24929 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
24930 *     });
24931 *   </file>
24932 * </example>
24933 *
24934 * @element input
24935 * @param {string=} ngList optional delimiter that should be used to split the value.
24936 */
24937var ngListDirective = function() {
24938  return {
24939    restrict: 'A',
24940    priority: 100,
24941    require: 'ngModel',
24942    link: function(scope, element, attr, ctrl) {
24943      // We want to control whitespace trimming so we use this convoluted approach
24944      // to access the ngList attribute, which doesn't pre-trim the attribute
24945      var ngList = element.attr(attr.$attr.ngList) || ', ';
24946      var trimValues = attr.ngTrim !== 'false';
24947      var separator = trimValues ? trim(ngList) : ngList;
24948
24949      var parse = function(viewValue) {
24950        // If the viewValue is invalid (say required but empty) it will be `undefined`
24951        if (isUndefined(viewValue)) return;
24952
24953        var list = [];
24954
24955        if (viewValue) {
24956          forEach(viewValue.split(separator), function(value) {
24957            if (value) list.push(trimValues ? trim(value) : value);
24958          });
24959        }
24960
24961        return list;
24962      };
24963
24964      ctrl.$parsers.push(parse);
24965      ctrl.$formatters.push(function(value) {
24966        if (isArray(value)) {
24967          return value.join(ngList);
24968        }
24969
24970        return undefined;
24971      });
24972
24973      // Override the standard $isEmpty because an empty array means the input is empty.
24974      ctrl.$isEmpty = function(value) {
24975        return !value || !value.length;
24976      };
24977    }
24978  };
24979};
24980
24981/* global VALID_CLASS: true,
24982  INVALID_CLASS: true,
24983  PRISTINE_CLASS: true,
24984  DIRTY_CLASS: true,
24985  UNTOUCHED_CLASS: true,
24986  TOUCHED_CLASS: true,
24987*/
24988
24989var VALID_CLASS = 'ng-valid',
24990    INVALID_CLASS = 'ng-invalid',
24991    PRISTINE_CLASS = 'ng-pristine',
24992    DIRTY_CLASS = 'ng-dirty',
24993    UNTOUCHED_CLASS = 'ng-untouched',
24994    TOUCHED_CLASS = 'ng-touched',
24995    PENDING_CLASS = 'ng-pending';
24996
24997var ngModelMinErr = minErr('ngModel');
24998
24999/**
25000 * @ngdoc type
25001 * @name ngModel.NgModelController
25002 *
25003 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
25004 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
25005 * is set.
25006 * @property {*} $modelValue The value in the model that the control is bound to.
25007 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
25008       the control reads value from the DOM. The functions are called in array order, each passing
25009       its return value through to the next. The last return value is forwarded to the
25010       {@link ngModel.NgModelController#$validators `$validators`} collection.
25011
25012Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
25013`$viewValue`}.
25014
25015Returning `undefined` from a parser means a parse error occurred. In that case,
25016no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
25017will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
25018is set to `true`. The parse error is stored in `ngModel.$error.parse`.
25019
25020 *
25021 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
25022       the model value changes. The functions are called in reverse array order, each passing the value through to the
25023       next. The last return value is used as the actual DOM value.
25024       Used to format / convert values for display in the control.
25025 * ```js
25026 * function formatter(value) {
25027 *   if (value) {
25028 *     return value.toUpperCase();
25029 *   }
25030 * }
25031 * ngModel.$formatters.push(formatter);
25032 * ```
25033 *
25034 * @property {Object.<string, function>} $validators A collection of validators that are applied
25035 *      whenever the model value changes. The key value within the object refers to the name of the
25036 *      validator while the function refers to the validation operation. The validation operation is
25037 *      provided with the model value as an argument and must return a true or false value depending
25038 *      on the response of that validation.
25039 *
25040 * ```js
25041 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
25042 *   var value = modelValue || viewValue;
25043 *   return /[0-9]+/.test(value) &&
25044 *          /[a-z]+/.test(value) &&
25045 *          /[A-Z]+/.test(value) &&
25046 *          /\W+/.test(value);
25047 * };
25048 * ```
25049 *
25050 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
25051 *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
25052 *      is expected to return a promise when it is run during the model validation process. Once the promise
25053 *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
25054 *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
25055 *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
25056 *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
25057 *      will only run once all synchronous validators have passed.
25058 *
25059 * Please note that if $http is used then it is important that the server returns a success HTTP response code
25060 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
25061 *
25062 * ```js
25063 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
25064 *   var value = modelValue || viewValue;
25065 *
25066 *   // Lookup user by username
25067 *   return $http.get('/api/users/' + value).
25068 *      then(function resolved() {
25069 *        //username exists, this means validation fails
25070 *        return $q.reject('exists');
25071 *      }, function rejected() {
25072 *        //username does not exist, therefore this validation passes
25073 *        return true;
25074 *      });
25075 * };
25076 * ```
25077 *
25078 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
25079 *     view value has changed. It is called with no arguments, and its return value is ignored.
25080 *     This can be used in place of additional $watches against the model value.
25081 *
25082 * @property {Object} $error An object hash with all failing validator ids as keys.
25083 * @property {Object} $pending An object hash with all pending validator ids as keys.
25084 *
25085 * @property {boolean} $untouched True if control has not lost focus yet.
25086 * @property {boolean} $touched True if control has lost focus.
25087 * @property {boolean} $pristine True if user has not interacted with the control yet.
25088 * @property {boolean} $dirty True if user has already interacted with the control.
25089 * @property {boolean} $valid True if there is no error.
25090 * @property {boolean}
25090 $invalid True if at least one error on the control.
25091 * @property {string} $name The name attribute of the control.
25092 *
25093 * @description
25094 *
25095 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
25096 * The controller contains services for data-binding, validation, CSS updates, and value formatting
25097 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
25098 * listening to DOM events.
25099 * Such DOM related logic should be provided by other directives which make use of
25100 * `NgModelController` for data-binding to control elements.
25101 * Angular provides this DOM logic for most {@link input `input`} elements.
25102 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
25103 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
25104 *
25105 * @example
25106 * ### Custom Control Example
25107 * This example shows how to use `NgModelController` with a custom control to achieve
25108 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
25109 * collaborate together to achieve the desired result.
25110 *
25111 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
25112 * contents be edited in place by the user.
25113 *
25114 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
25115 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
25116 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
25117 * that content using the `$sce` service.
25118 *
25119 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
25120    <file name="style.css">
25121      [contenteditable] {
25122        border: 1px solid black;
25123        background-color: white;
25124        min-height: 20px;
25125      }
25126
25127      .ng-invalid {
25128        border: 1px solid red;
25129      }
25130
25131    </file>
25132    <file name="script.js">
25133      angular.module('customControl', ['ngSanitize']).
25134        directive('contenteditable', ['$sce', function($sce) {
25135          return {
25136            restrict: 'A', // only activate on element attribute
25137            require: '?ngModel', // get a hold of NgModelController
25138            link: function(scope, element, attrs, ngModel) {
25139              if (!ngModel) return; // do nothing if no ng-model
25140
25141              // Specify how UI should be updated
25142              ngModel.$render = function() {
25143                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
25144              };
25145
25146              // Listen for change events to enable binding
25147              element.on('blur keyup change', function() {
25148                scope.$evalAsync(read);
25149              });
25150              read(); // initialize
25151
25152              // Write data to the model
25153              function read() {
25154                var html = element.html();
25155                // When we clear the content editable the browser leaves a <br> behind
25156                // If strip-br attribute is provided then we strip this out
25157                if ( attrs.stripBr && html == '<br>' ) {
25158                  html = '';
25159                }
25160                ngModel.$setViewValue(html);
25161              }
25162            }
25163          };
25164        }]);
25165    </file>
25166    <file name="index.html">
25167      <form name="myForm">
25168       <div contenteditable
25169            name="myWidget" ng-model="userContent"
25170            strip-br="true"
25171            required>Change me!</div>
25172        <span ng-show="myForm.myWidget.$error.required">Required!</span>
25173       <hr>
25174       <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
25175      </form>
25176    </file>
25177    <file name="protractor.js" type="protractor">
25178    it('should data-bind and become invalid', function() {
25179      if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
25180        // SafariDriver can't handle contenteditable
25181        // and Firefox driver can't clear contenteditables very well
25182        return;
25183      }
25184      var contentEditable = element(by.css('[contenteditable]'));
25185      var content = 'Change me!';
25186
25187      expect(contentEditable.getText()).toEqual(content);
25188
25189      contentEditable.clear();
25190      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
25191      expect(contentEditable.getText()).toEqual('');
25192      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
25193    });
25194    </file>
25195 * </example>
25196 *
25197 *
25198 */
25199var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
25200    function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
25201  this.$viewValue = Number.NaN;
25202  this.$modelValue = Number.NaN;
25203  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
25204  this.$validators = {};
25205  this.$asyncValidators = {};
25206  this.$parsers = [];
25207  this.$formatters = [];
25208  this.$viewChangeListeners = [];
25209  this.$untouched = true;
25210  this.$touched = false;
25211  this.$pristine = true;
25212  this.$dirty = false;
25213  this.$valid = true;
25214  this.$invalid = false;
25215  this.$error = {}; // keep invalid keys here
25216  this.$$success = {}; // keep valid keys here
25217  this.$pending = undefined; // keep pending keys here
25218  this.$name = $interpolate($attr.name || '', false)($scope);
25219  this.$$parentForm = nullFormCtrl;
25220
25221  var parsedNgModel = $parse($attr.ngModel),
25222      parsedNgModelAssign = parsedNgModel.assign,
25223      ngModelGet = parsedNgModel,
25224      ngModelSet = parsedNgModelAssign,
25225      pendingDebounce = null,
25226      parserValid,
25227      ctrl = this;
25228
25229  this.$$setOptions = function(options) {
25230    ctrl.$options = options;
25231    if (options && options.getterSetter) {
25232      var invokeModelGetter = $parse($attr.ngModel + '()'),
25233          invokeModelSetter = $parse($attr.ngModel + '($$$p)');
25234
25235      ngModelGet = function($scope) {
25236        var modelValue = parsedNgModel($scope);
25237        if (isFunction(modelValue)) {
25238          modelValue = invokeModelGetter($scope);
25239        }
25240        return modelValue;
25241      };
25242      ngModelSet = function($scope, newValue) {
25243        if (isFunction(parsedNgModel($scope))) {
25244          invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
25245        } else {
25246          parsedNgModelAssign($scope, ctrl.$modelValue);
25247        }
25248      };
25249    } else if (!parsedNgModel.assign) {
25250      throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
25251          $attr.ngModel, startingTag($element));
25252    }
25253  };
25254
25255  /**
25256   * @ngdoc method
25257   * @name ngModel.NgModelController#$render
25258   *
25259   * @description
25260   * Called when the view needs to be updated. It is expected that the user of the ng-model
25261   * directive will implement this method.
25262   *
25263   * The `$render()` method is invoked in the following situations:
25264   *
25265   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
25266   *   committed value then `$render()` is called to update the input control.
25267   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
25268   *   the `$viewValue` are different from last time.
25269   *
25270   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
25271   * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
25272   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
25273   * invoked if you only change a property on the objects.
25274   */
25275  this.$render = noop;
25276
25277  /**
25278   * @ngdoc method
25279   * @name ngModel.NgModelController#$isEmpty
25280   *
25281   * @description
25282   * This is called when we need to determine if the value of an input is empty.
25283   *
25284   * For instance, the required directive does this to work out if the input has data or not.
25285   *
25286   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
25287   *
25288   * You can override this for input directives whose concept of being empty is different from the
25289   * default. The `checkboxInputType` directive does this because in its case a value of `false`
25290   * implies empty.
25291   *
25292   * @param {*} value The value of the input to check for emptiness.
25293   * @returns {boolean} True if `value` is "empty".
25294   */
25295  this.$isEmpty = function(value) {
25296    return isUndefined(value) || value === '' || value === null || value !== value;
25297  };
25298
25299  var currentValidationRunId = 0;
25300
25301  /**
25302   * @ngdoc method
25303   * @name ngModel.NgModelController#$setValidity
25304   *
25305   * @description
25306   * Change the validity state, and notify the form.
25307   *
25308   * This method can be called within $parsers/$formatters or a custom validation implementation.
25309   * However, in most cases it should be sufficient to use the `ngModel.$validators` and
25310   * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
25311   *
25312   * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
25313   *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
25314   *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
25315   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
25316   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
25317   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
25318   * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
25319   *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
25320   *                          Skipped is used by Angular when validators do not run because of parse errors and
25321   *                          when `$asyncValidators` do not run because any of the `$validators` failed.
25322   */
25323  addSetValidityMethod({
25324    ctrl: this,
25325    $element: $element,
25326    set: function(object, property) {
25327      object[property] = true;
25328    },
25329    unset: function(object, property) {
25330      delete object[property];
25331    },
25332    $animate: $animate
25333  });
25334
25335  /**
25336   * @ngdoc method
25337   * @name ngModel.NgModelController#$setPristine
25338   *
25339   * @description
25340   * Sets the control to its pristine state.
25341   *
25342   * This method can be called to remove the `ng-dirty` class and set the control to its pristine
25343   * state (`ng-pristine` class). A model is considered to be pristine when the control
25344   * has not been changed from when first compiled.
25345   */
25346  this.$setPristine = function() {
25347    ctrl.$dirty = false;
25348    ctrl.$pristine = true;
25349    $animate.removeClass($element, DIRTY_CLASS);
25350    $animate.addClass($element, PRISTINE_CLASS);
25351  };
25352
25353  /**
25354   * @ngdoc method
25355   * @name ngModel.NgModelController#$setDirty
25356   *
25357   * @description
25358   * Sets the control to its dirty state.
25359   *
25360   * This method can be called to remove the `ng-pristine` class and set the control to its dirty
25361   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
25362   * from when first compiled.
25363   */
25364  this.$setDirty = function() {
25365    ctrl.$dirty = true;
25366    ctrl.$pristine = false;
25367    $animate.removeClass($element, PRISTINE_CLASS);
25368    $animate.addClass($element, DIRTY_CLASS);
25369    ctrl.$$parentForm.$setDirty();
25370  };
25371
25372  /**
25373   * @ngdoc method
25374   * @name ngModel.NgModelController#$setUntouched
25375   *
25376   * @description
25377   * Sets the control to its untouched state.
25378   *
25379   * This method can be called to remove the `ng-touched` class and set the control to its
25380   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
25381   * by default, however this function can be used to restore that state if the model has
25382   * already been touched by the user.
25383   */
25384  this.$setUntouched = function() {
25385    ctrl.$touched = false;
25386    ctrl.$untouched = true;
25387    $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
25388  };
25389
25390  /**
25391   * @ngdoc method
25392   * @name ngModel.NgModelController#$setTouched
25393   *
25394   * @description
25395   * Sets the control to its touched state.
25396   *
25397   * This method can be called to remove the `ng-untouched` class and set the control to its
25398   * touched state (`ng-touched` class). A model is considered to be touched when the user has
25399   * first focused the control element and then shifted focus away from the control (blur event).
25400   */
25401  this.$setTouched = function() {
25402    ctrl.$touched = true;
25403    ctrl.$untouched = false;
25404    $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25405  };
25406
25407  /**
25408   * @ngdoc method
25409   * @name ngModel.NgModelController#$rollbackViewValue
25410   *
25411   * @description
25412   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25413   * which may be caused by a pending debounced event or because the input is waiting for a some
25414   * future event.
25415   *
25416   * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
25417   * depend on special events such as blur, you can have a situation where there is a period when
25418   * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
25419   *
25420   * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
25421   * and reset the input to the last committed view value.
25422   *
25423   * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
25424   * programmatically before these debounced/future events have resolved/occurred, because Angular's
25425   * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25426   *
25427   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25428   * input which may have such events pending. This is important in order to make sure that the
25429   * input field will be updated with the new model value and any pending operations are cancelled.
25430   *
25431   * <example name="ng-model-cancel-update" module="cancel-update-example">
25432   *   <file name="app.js">
25433   *     angular.module('cancel-update-example', [])
25434   *
25435   *     .controller('CancelUpdateController', ['$scope', function($scope) {
25436   *       $scope.model = {};
25437   *
25438   *       $scope.setEmpty = function(e, value, rollback) {
25439   *         if (e.keyCode == 27) {
25440   *           e.preventDefault();
25441   *           if (rollback) {
25442   *             $scope.myForm[value].$rollbackViewValue();
25443   *           }
25444   *           $scope.model[value] = '';
25445   *         }
25446   *       };
25447   *     }]);
25448   *   </file>
25449   *   <file name="index.html">
25450   *     <div ng-controller="CancelUpdateController">
25451   *        <p>Both of these inputs are only updated if they are blurred. Hitting escape should
25452   *        empty them. Follow these steps and observe the difference:</p>
25453   *       <ol>
25454   *         <li>Type something in the input. You will see that the model is not yet updated</li>
25455   *         <li>Press the Escape key.
25456   *           <ol>
25457   *             <li> In the first example, nothing happens, because the model is already '', and no
25458   *             update is detected. If you blur the input, the model will be set to the current view.
25459   *             </li>
25460   *             <li> In the second example, the pending update is cancelled, and the input is set back
25461   *             to the last committed view value (''). Blurring the input does nothing.
25462   *             </li>
25463   *           </ol>
25464   *         </li>
25465   *       </ol>
25466   *
25467   *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25468   *         <div>
25469   *        <p id="inputDescription1">Without $rollbackViewValue():</p>
25470   *         <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
25471   *                ng-keydown="setEmpty($event, 'value1')">
25472   *         value1: "{{ model.value1 }}"
25473   *         </div>
25474   *
25475   *         <div>
25476   *        <p id="inputDescription2">With $rollbackViewValue():</p>
25477   *         <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
25478   *                ng-keydown="setEmpty($event, 'value2', true)">
25479   *         value2: "{{ model.value2 }}"
25480   *         </div>
25481   *       </form>
25482   *     </div>
25483   *   </file>
25484       <file name="style.css">
25485          div {
25486            display: table-cell;
25487          }
25488          div:nth-child(1) {
25489            padding-right: 30px;
25490          }
25491
25492        </file>
25493   * </example>
25494   */
25495  this.$rollbackViewValue = function() {
25496    $timeout.cancel(pendingDebounce);
25497    ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
25498    ctrl.$render();
25499  };
25500
25501  /**
25502   * @ngdoc method
25503   * @name ngModel.NgModelController#$validate
25504   *
25505   * @description
25506   * Runs each of the registered validators (first synchronous validators and then
25507   * asynchronous validators).
25508   * If the validity changes to invalid, the model will be set to `undefined`,
25509   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
25510   * If the validity changes to valid, it will set the model to the last available valid
25511   * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
25512   */
25513  this.$validate = function() {
25514    // ignore $validate before model is initialized
25515    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25516      return;
25517    }
25518
25519    var viewValue = ctrl.$$lastCommittedViewValue;
25520    // Note: we use the $$rawModelValue as $modelValue might have been
25521    // set to undefined during a view -> model update that found validation
25522    // errors. We can't parse the view here, since that could change
25523    // the model although neither viewValue nor the model on the scope changed
25524    var modelValue = ctrl.$$rawModelValue;
25525
25526    var prevValid = ctrl.$valid;
25527    var prevModelValue = ctrl.$modelValue;
25528
25529    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25530
25531    ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
25532      // If there was no change in validity, don't update the model
25533      // This prevents changing an invalid modelValue to undefined
25534      if (!allowInvalid && prevValid !== allValid) {
25535        // Note: Don't check ctrl.$valid here, as we could have
25536        // external validators (e.g. calculated on the server),
25537        // that just call $setValidity and need the model value
25538        // to calculate their validity.
25539        ctrl.$modelValue = allValid ? modelValue : undefined;
25540
25541        if (ctrl.$modelValue !== prevModelValue) {
25542          ctrl.$$writeModelToScope();
25543        }
25544      }
25545    });
25546
25547  };
25548
25549  this.$$runValidators = function(modelValue, viewValue, doneCallback) {
25550    currentValidationRunId++;
25551    var localValidationRunId = currentValidationRunId;
25552
25553    // check parser error
25554    if (!processParseErrors()) {
25555      validationDone(false);
25556      return;
25557    }
25558    if (!processSyncValidators()) {
25559      validationDone(false);
25560      return;
25561    }
25562    processAsyncValidators();
25563
25564    function processParseErrors() {
25565      var errorKey = ctrl.$$parserName || 'parse';
25566      if (isUndefined(parserValid)) {
25567        setValidity(errorKey, null);
25568      } else {
25569        if (!parserValid) {
25570          forEach(ctrl.$validators, function(v, name) {
25571            setValidity(name, null);
25572          });
25573          forEach(ctrl.$asyncValidators, function(v, name) {
25574            setValidity(name, null);
25575          });
25576        }
25577        // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
25578        setValidity(errorKey, parserValid);
25579        return parserValid;
25580      }
25581      return true;
25582    }
25583
25584    function processSyncValidators() {
25585      var syncValidatorsValid = true;
25586      forEach(ctrl.$validators, function(validator, name) {
25587        var result = validator(modelValue, viewValue);
25588        syncValidatorsValid = syncValidatorsValid && result;
25589        setValidity(name, result);
25590      });
25591      if (!syncValidatorsValid) {
25592        forEach(ctrl.$asyncValidators, function(v, name) {
25593          setValidity(name, null);
25594        });
25595        return false;
25596      }
25597      return true;
25598    }
25599
25600    function processAsyncValidators() {
25601      var validatorPromises = [];
25602      var allValid = true;
25603      forEach(ctrl.$asyncValidators, function(validator, name) {
25604        var promise = validator(modelValue, viewValue);
25605        if (!isPromiseLike(promise)) {
25606          throw ngModelMinErr('nopromise',
25607            "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
25608        }
25609        setValidity(name, undefined);
25610        validatorPromises.push(promise.then(function() {
25611          setValidity(name, true);
25612        }, function(error) {
25613          allValid = false;
25614          setValidity(name, false);
25615        }));
25616      });
25617      if (!validatorPromises.length) {
25618        validationDone(true);
25619      } else {
25620        $q.all(validatorPromises).then(function() {
25621          validationDone(allValid);
25622        }, noop);
25623      }
25624    }
25625
25626    function setValidity(name, isValid) {
25627      if (localValidationRunId === currentValidationRunId) {
25628        ctrl.$setValidity(name, isValid);
25629      }
25630    }
25631
25632    function validationDone(allValid) {
25633      if (localValidationRunId === currentValidationRunId) {
25634
25635        doneCallback(allValid);
25636      }
25637    }
25638  };
25639
25640  /**
25641   * @ngdoc method
25642   * @name ngModel.NgModelController#$commitViewValue
25643   *
25644   * @description
25645   * Commit a pending update to the `$modelValue`.
25646   *
25647   * Updates may be pending by a debounced event or because the input is waiting for a some future
25648   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
25649   * usually handles calling this in response to input events.
25650   */
25651  this.$commitViewValue = function() {
25652    var viewValue = ctrl.$viewValue;
25653
25654    $timeout.cancel(pendingDebounce);
25655
25656    // If the view value has not changed then we should just exit, except in the case where there is
25657    // a native validator on the element. In this case the validation state may have changed even though
25658    // the viewValue has stayed empty.
25659    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
25660      return;
25661    }
25662    ctrl.$$lastCommittedViewValue = viewValue;
25663
25664    // change to dirty
25665    if (ctrl.$pristine) {
25666      this.$setDirty();
25667    }
25668    this.$$parseAndValidate();
25669  };
25670
25671  this.$$parseAndValidate = function() {
25672    var viewValue = ctrl.$$lastCommittedViewValue;
25673    var modelValue = viewValue;
25674    parserValid = isUndefined(modelValue) ? undefined : true;
25675
25676    if (parserValid) {
25677      for (var i = 0; i < ctrl.$parsers.length; i++) {
25678        modelValue = ctrl.$parsers[i](modelValue);
25679        if (isUndefined(modelValue)) {
25680          parserValid = false;
25681          break;
25682        }
25683      }
25684    }
25685    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25686      // ctrl.$modelValue has not been touched yet...
25687      ctrl.$modelValue = ngModelGet($scope);
25688    }
25689    var prevModelValue = ctrl.$modelValue;
25690    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25691    ctrl.$$rawModelValue = modelValue;
25692
25693    if (allowInvalid) {
25694      ctrl.$modelValue = modelValue;
25695      writeToModelIfNeeded();
25696    }
25697
25698    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
25699    // This can happen if e.g. $setViewValue is called from inside a parser
25700    ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
25701      if (!allowInvalid) {
25702        // Note: Don't check ctrl.$valid here, as we could have
25703        // external validators (e.g. calculated on the server),
25704        // that just call $setValidity and need the model value
25705        // to calculate their validity.
25706        ctrl.$modelValue = allValid ? modelValue : undefined;
25707        writeToModelIfNeeded();
25708      }
25709    });
25710
25711    function writeToModelIfNeeded() {
25712      if (ctrl.$modelValue !== prevModelValue) {
25713        ctrl.$$writeModelToScope();
25714      }
25715    }
25716  };
25717
25718  this.$$writeModelToScope = function() {
25719    ngModelSet($scope, ctrl.$modelValue);
25720    forEach(ctrl.$viewChangeListeners, function(listener) {
25721      try {
25722        listener();
25723      } catch (e) {
25724        $exceptionHandler(e);
25725      }
25726    });
25727  };
25728
25729  /**
25730   * @ngdoc method
25731   * @name ngModel.NgModelController#$setViewValue
25732   *
25733   * @description
25734   * Update the view value.
25735   *
25736   * This method should be called when a control wants to change the view value; typically,
25737   * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25738   * directive calls it when the value of the input changes and {@link ng.directive:select select}
25739   * calls it when an option is selected.
25740   *
25741   * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25742   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25743   * value sent directly for processing, finally to be applied to `$modelValue` and then the
25744   * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25745   * in the `$viewChangeListeners` list, are called.
25746   *
25747   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25748   * and the `default` trigger is not listed, all those actions will remain pending until one of the
25749   * `updateOn` events is triggered on the DOM element.
25750   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25751   * directive is used with a custom debounce for this particular event.
25752   * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25753   * is specified, once the timer runs out.
25754   *
25755   * When used with standard inputs, the view value will always be a string (which is in some cases
25756   * parsed into another type, such as a `Date` object for `input[date]`.)
25757   * However, custom controls might also pass objects to this method. In this case, we should make
25758   * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25759   * perform a deep watch of objects, it only looks for a change of identity. If you only change
25760   * the property of the object then ngModel will not realise that the object has changed and
25761   * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25762   * not change properties of the copy once it has been passed to `$setViewValue`.
25763   * Otherwise you may cause the model value on the scope to change incorrectly.
25764   *
25765   * <div class="alert alert-info">
25766   * In any case, the value passed to the method should always reflect the current value
25767   * of the control. For example, if you are calling `$setViewValue` for an input element,
25768   * you should pass the input DOM value. Otherwise, the control and the scope model become
25769   * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25770   * the control's DOM value in any way. If we want to change the control's DOM value
25771   * programmatically, we should update the `ngModel` scope expression. Its new value will be
25772   * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25773   * to update the DOM, and finally call `$validate` on it.
25774   * </div>
25775   *
25776   * @param {*} value value from the view.
25777   * @param {string} trigger Event that triggered the update.
25778   */
25779  this.$setViewValue = function(value, trigger) {
25780    ctrl.$viewValue = value;
25781    if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25782      ctrl.$$debounceViewValueCommit(trigger);
25783    }
25784  };
25785
25786  this.$$debounceViewValueCommit = function(trigger) {
25787    var debounceDelay = 0,
25788        options = ctrl.$options,
25789        debounce;
25790
25791    if (options && isDefined(options.debounce)) {
25792      debounce = options.debounce;
25793      if (isNumber(debounce)) {
25794        debounceDelay = debounce;
25795      } else if (isNumber(debounce[trigger])) {
25796        debounceDelay = debounce[trigger];
25797      } else if (isNumber(debounce['default'])) {
25798        debounceDelay = debounce['default'];
25799      }
25800    }
25801
25802    $timeout.cancel(pendingDebounce);
25803    if (debounceDelay) {
25804      pendingDebounce = $timeout(function() {
25805        ctrl.$commitViewValue();
25806      }, debounceDelay);
25807    } else if ($rootScope.$$phase) {
25808      ctrl.$commitViewValue();
25809    } else {
25810      $scope.$apply(function() {
25811        ctrl.$commitViewValue();
25812      });
25813    }
25814  };
25815
25816  // model -> value
25817  // Note: we cannot use a normal scope.$watch as we want to detect the following:
25818  // 1. scope value is 'a'
25819  // 2. user enters 'b'
25820  // 3. ng-change kicks in and reverts scope value to 'a'
25821  //    ->
25821 scope value did not change since the last digest as
25822  //       ng-change executes in apply phase
25823  // 4. view should be changed back to 'a'
25824  $scope.$watch(function ngModelWatch() {
25825    var modelValue = ngModelGet($scope);
25826
25827    // if scope model value and ngModel value are out of sync
25828    // TODO(perf): why not move this to the action fn?
25829    if (modelValue !== ctrl.$modelValue &&
25830       // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25831       (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25832    ) {
25833      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25834      parserValid = undefined;
25835
25836      var formatters = ctrl.$formatters,
25837          idx = formatters.length;
25838
25839      var viewValue = modelValue;
25840      while (idx--) {
25841        viewValue = formatters[idx](viewValue);
25842      }
25843      if (ctrl.$viewValue !== viewValue) {
25844        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25845        ctrl.$render();
25846
25847        ctrl.$$runValidators(modelValue, viewValue, noop);
25848      }
25849    }
25850
25851    return modelValue;
25852  });
25853}];
25854
25855
25856/**
25857 * @ngdoc directive
25858 * @name ngModel
25859 *
25860 * @element input
25861 * @priority 1
25862 *
25863 * @description
25864 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
25865 * property on the scope using {@link ngModel.NgModelController NgModelController},
25866 * which is created and exposed by this directive.
25867 *
25868 * `ngModel` is responsible for:
25869 *
25870 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
25871 *   require.
25872 * - Providing validation behavior (i.e. required, number, email, url).
25873 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
25874 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
25875 * - Registering the control with its parent {@link ng.directive:form form}.
25876 *
25877 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
25878 * current scope. If the property doesn't already exist on this scope, it will be created
25879 * implicitly and added to the scope.
25880 *
25881 * For best practices on using `ngModel`, see:
25882 *
25883 *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
25884 *
25885 * For basic examples, how to use `ngModel`, see:
25886 *
25887 *  - {@link ng.directive:input input}
25888 *    - {@link input[text] text}
25889 *    - {@link input[checkbox] checkbox}
25890 *    - {@link input[radio] radio}
25891 *    - {@link input[number] number}
25892 *    - {@link input[email] email}
25893 *    - {@link input[url] url}
25894 *    - {@link input[date] date}
25895 *    - {@link input[datetime-local] datetime-local}
25896 *    - {@link input[time] time}
25897 *    - {@link input[month] month}
25898 *    - {@link input[week] week}
25899 *  - {@link ng.directive:select select}
25900 *  - {@link ng.directive:textarea textarea}
25901 *
25902 * # CSS classes
25903 * The following CSS classes are added and removed on the associated input/select/textarea element
25904 * depending on the validity of the model.
25905 *
25906 *  - `ng-valid`: the model is valid
25907 *  - `ng-invalid`: the model is invalid
25908 *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
25909 *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
25910 *  - `ng-pristine`: the control hasn't been interacted with yet
25911 *  - `ng-dirty`: the control has been interacted with
25912 *  - `ng-touched`: the control has been blurred
25913 *  - `ng-untouched`: the control hasn't been blurred
25914 *  - `ng-pending`: any `$asyncValidators` are unfulfilled
25915 *
25916 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
25917 *
25918 * ## Animation Hooks
25919 *
25920 * Animations within models are triggered when any of the associated CSS classes are added and removed
25921 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
25922 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
25923 * The animations that are triggered within ngModel are similar to how they work in ngClass and
25924 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
25925 *
25926 * The following example shows a simple way to utilize CSS transitions to style an input element
25927 * that has been rendered as invalid after it has been validated:
25928 *
25929 * <pre>
25930 * //be sure to include ngAnimate as a module to hook into more
25931 * //advanced animations
25932 * .my-input {
25933 *   transition:0.5s linear all;
25934 *   background: white;
25935 * }
25936 * .my-input.ng-invalid {
25937 *   background: red;
25938 *   color:white;
25939 * }
25940 * </pre>
25941 *
25942 * @example
25943 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
25944     <file name="index.html">
25945       <script>
25946        angular.module('inputExample', [])
25947          .controller('ExampleController', ['$scope', function($scope) {
25948            $scope.val = '1';
25949          }]);
25950       </script>
25951       <style>
25952         .my-input {
25953           transition:all linear 0.5s;
25954           background: transparent;
25955         }
25956         .my-input.ng-invalid {
25957           color:white;
25958           background: red;
25959         }
25960       </style>
25961       <p id="inputDescription">
25962        Update input to see transitions when valid/invalid.
25963        Integer is a valid value.
25964       </p>
25965       <form name="testForm" ng-controller="ExampleController">
25966         <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
25967                aria-describedby="inputDescription" />
25968       </form>
25969     </file>
25970 * </example>
25971 *
25972 * ## Binding to a getter/setter
25973 *
25974 * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
25975 * function that returns a representation of the model when called with zero arguments, and sets
25976 * the internal state of a model when called with an argument. It's sometimes useful to use this
25977 * for models that have an internal representation that's different from what the model exposes
25978 * to the view.
25979 *
25980 * <div class="alert alert-success">
25981 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
25982 * frequently than other parts of your code.
25983 * </div>
25984 *
25985 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
25986 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
25987 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
25988 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
25989 *
25990 * The following example shows how to use `ngModel` with a getter/setter:
25991 *
25992 * @example
25993 * <example name="ngModel-getter-setter" module="getterSetterExample">
25994     <file name="index.html">
25995       <div ng-controller="ExampleController">
25996         <form name="userForm">
25997           <label>Name:
25998             <input type="text" name="userName"
25999                    ng-model="user.name"
26000                    ng-model-options="{ getterSetter: true }" />
26001           </label>
26002         </form>
26003         <pre>user.name = <span ng-bind="user.name()"></span></pre>
26004       </div>
26005     </file>
26006     <file name="app.js">
26007       angular.module('getterSetterExample', [])
26008         .controller('ExampleController', ['$scope', function($scope) {
26009           var _name = 'Brian';
26010           $scope.user = {
26011             name: function(newName) {
26012              // Note that newName can be undefined for two reasons:
26013              // 1. Because it is called as a getter and thus called with no arguments
26014              // 2. Because the property should actually be set to undefined. This happens e.g. if the
26015              //    input is invalid
26016              return arguments.length ? (_name = newName) : _name;
26017             }
26018           };
26019         }]);
26020     </file>
26021 * </example>
26022 */
26023var ngModelDirective = ['$rootScope', function($rootScope) {
26024  return {
26025    restrict: 'A',
26026    require: ['ngModel', '^?form', '^?ngModelOptions'],
26027    controller: NgModelController,
26028    // Prelink needs to run before any input directive
26029    // so that we can set the NgModelOptions in NgModelController
26030    // before anyone else uses it.
26031    priority: 1,
26032    compile: function ngModelCompile(element) {
26033      // Setup initial state of the control
26034      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
26035
26036      return {
26037        pre: function ngModelPreLink(scope, element, attr, ctrls) {
26038          var modelCtrl = ctrls[0],
26039              formCtrl = ctrls[1] || modelCtrl.$$parentForm;
26040
26041          modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
26042
26043          // notify others, especially parent forms
26044          formCtrl.$addControl(modelCtrl);
26045
26046          attr.$observe('name', function(newValue) {
26047            if (modelCtrl.$name !== newValue) {
26048              modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
26049            }
26050          });
26051
26052          scope.$on('$destroy', function() {
26053            modelCtrl.$$parentForm.$removeControl(modelCtrl);
26054          });
26055        },
26056        post: function ngModelPostLink(scope, element, attr, ctrls) {
26057          var modelCtrl = ctrls[0];
26058          if (modelCtrl.$options && modelCtrl.$options.updateOn) {
26059            element.on(modelCtrl.$options.updateOn, function(ev) {
26060              modelCtrl.$$debounceViewValueCommit(ev && ev.type);
26061            });
26062          }
26063
26064          element.on('blur', function(ev) {
26065            if (modelCtrl.$touched) return;
26066
26067            if ($rootScope.$$phase) {
26068              scope.$evalAsync(modelCtrl.$setTouched);
26069            } else {
26070              scope.$apply(modelCtrl.$setTouched);
26071            }
26072          });
26073        }
26074      };
26075    }
26076  };
26077}];
26078
26079var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
26080
26081/**
26082 * @ngdoc directive
26083 * @name ngModelOptions
26084 *
26085 * @description
26086 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
26087 * events that will trigger a model update and/or a debouncing delay so that the actual update only
26088 * takes place when a timer expires; this timer will be reset after another change takes place.
26089 *
26090 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
26091 * be different from the value in the actual model. This means that if you update the model you
26092 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
26093 * order to make sure it is synchronized with the model and that any debounced action is canceled.
26094 *
26095 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
26096 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
26097 * important because `form` controllers are published to the related scope under the name in their
26098 * `name` attribute.
26099 *
26100 * Any pending changes will take place immediately when an enclosing form is submitted via the
26101 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
26102 * to have access to the updated model.
26103 *
26104 * `ngModelOptions` has an effect on the element it's declared on and its desce
26104ndants.
26105 *
26106 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
26107 *   - `updateOn`: string specifying which event should the input be bound to. You can set several
26108 *     events using an space delimited list. There is a special event called `default` that
26109 *     matches the default events belonging of the control.
26110 *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
26111 *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
26112 *     custom value for each event. For example:
26113 *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
26114 *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
26115 *     not validate correctly instead of the default behavior of setting the model to undefined.
26116 *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
26117       `ngModel` as getters/setters.
26118 *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
26119 *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
26120 *     continental US time zone abbreviations, but for general use, use a time zone offset, for
26121 *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
26122 *     If not specified, the timezone of the browser will be used.
26123 *
26124 * @example
26125
26126  The following example shows how to override immediate updates. Changes on the inputs within the
26127  form will update the model only when the control loses focus (blur event). If `escape` key is
26128  pressed while the input field is focused, the value is reset to the value in the current model.
26129
26130  <example name="ngModelOptions-directive-blur" module="optionsExample">
26131    <file name="index.html">
26132      <div ng-controller="ExampleController">
26133        <form name="userForm">
26134          <label>Name:
26135            <input type="text" name="userName"
26136                   ng-model="user.name"
26137                   ng-model-options="{ updateOn: 'blur' }"
26138                   ng-keyup="cancel($event)" />
26139          </label><br />
26140          <label>Other data:
26141            <input type="text" ng-model="user.data" />
26142          </label><br />
26143        </form>
26144        <pre>user.name = <span ng-bind="user.name"></span></pre>
26145        <pre>user.data = <span ng-bind="user.data"></span></pre>
26146      </div>
26147    </file>
26148    <file name="app.js">
26149      angular.module('optionsExample', [])
26150        .controller('ExampleController', ['$scope', function($scope) {
26151          $scope.user = { name: 'John', data: '' };
26152
26153          $scope.cancel = function(e) {
26154            if (e.keyCode == 27) {
26155              $scope.userForm.userName.$rollbackViewValue();
26156            }
26157          };
26158        }]);
26159    </file>
26160    <file name="protractor.js" type="protractor">
26161      var model = element(by.binding('user.name'));
26162      var input = element(by.model('user.name'));
26163      var other = element(by.model('user.data'));
26164
26165      it('should allow custom events', function() {
26166        input.sendKeys(' Doe');
26167        input.click();
26168        expect(model.getText()).toEqual('John');
26169        other.click();
26170        expect(model.getText()).toEqual('John Doe');
26171      });
26172
26173      it('should $rollbackViewValue when model changes', function() {
26174        input.sendKeys(' Doe');
26175        expect(input.getAttribute('value')).toEqual('John Doe');
26176        input.sendKeys(protractor.Key.ESCAPE);
26177        expect(input.getAttribute('value')).toEqual('John');
26178        other.click();
26179        expect(model.getText()).toEqual('John');
26180      });
26181    </file>
26182  </example>
26183
26184  This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
26185  If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
26186
26187  <example name="ngModelOptions-directive-debounce" module="optionsExample">
26188    <file name="index.html">
26189      <div ng-controller="ExampleController">
26190        <form name="userForm">
26191          <label>Name:
26192            <input type="text" name="userName"
26193                   ng-model="user.name"
26194                   ng-model-options="{ debounce: 1000 }" />
26195          </label>
26196          <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
26197          <br />
26198        </form>
26199        <pre>user.name = <span ng-bind="user.name"></span></pre>
26200      </div>
26201    </file>
26202    <file name="app.js">
26203      angular.module('optionsExample', [])
26204        .controller('ExampleController', ['$scope', function($scope) {
26205          $scope.user = { name: 'Igor' };
26206        }]);
26207    </file>
26208  </example>
26209
26210  This one shows how to bind to getter/setters:
26211
26212  <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
26213    <file name="index.html">
26214      <div ng-controller="ExampleController">
26215        <form name="userForm">
26216          <label>Name:
26217            <input type="text" name="userName"
26218                   ng-model="user.name"
26219                   ng-model-options="{ getterSetter: true }" />
26220          </label>
26221        </form>
26222        <pre>user.name = <span ng-bind="user.name()"></span></pre>
26223      </div>
26224    </file>
26225    <file name="app.js">
26226      angular.module('getterSetterExample', [])
26227        .controller('ExampleController', ['$scope', function($scope) {
26228          var _name = 'Brian';
26229          $scope.user = {
26230            name: function(newName) {
26231              // Note that newName can be undefined for two reasons:
26232              // 1. Because it is called as a getter and thus called with no arguments
26233              // 2. Because the property should actually be set to undefined. This happens e.g. if the
26234              //    input is invalid
26235              return arguments.length ? (_name = newName) : _name;
26236            }
26237          };
26238        }]);
26239    </file>
26240  </example>
26241 */
26242var ngModelOptionsDirective = function() {
26243  return {
26244    restrict: 'A',
26245    controller: ['$scope', '$attrs', function($scope, $attrs) {
26246      var that = this;
26247      this.$options = copy($scope.$eval($attrs.ngModelOptions));
26248      // Allow adding/overriding bound events
26249      if (isDefined(this.$options.updateOn)) {
26250        this.$options.updateOnDefault = false;
26251        // extract "default" pseudo-event from list of events that can trigger a model update
26252        this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
26253          that.$options.updateOnDefault = true;
26254          return ' ';
26255        }));
26256      } else {
26257        this.$options.updateOnDefault = true;
26258      }
26259    }]
26260  };
26261};
26262
26263
26264
26265// helper methods
26266function addSetValidityMethod(context) {
26267  var ctrl = context.ctrl,
26268      $element = context.$element,
26269      classCache = {},
26270      set = context.set,
26271      unset = context.unset,
26272      $animate = context.$animate;
26273
26274  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
26275
26276  ctrl.$setValidity = setValidity;
26277
26278  function setValidity(validationErrorKey, state, controller) {
26279    if (isUndefined(state)) {
26280      createAndSet('$pending', validationErrorKey, controller);
26281    } else {
26282      unsetAndCleanup('$pending', validationErrorKey, controller);
26283    }
26284    if (!isBoolean(state)) {
26285      unset(ctrl.$error, validationErrorKey, controller);
26286      unset(ctrl.$$success, validationErrorKey, controller);
26287    } else {
26288      if (state) {
26289        unset(ctrl.$error, validationErrorKey, controller);
26290        set(ctrl.$$success, validationErrorKey, controller);
26291      } else {
26292        set(ctrl.$error, validationErrorKey, controller);
26293        unset(ctrl.$$success, validationErrorKey, controller);
26294      }
26295    }
26296    if (ctrl.$pending) {
26297      cachedToggleClass(PENDING_CLASS, true);
26298      ctrl.$valid = ctrl.$invalid = undefined;
26299      toggleValidationCss('', null);
26300    } else {
26301      cachedToggleClass(PENDING_CLASS, false);
26302      ctrl.$valid = isObjectEmpty(ctrl.$error);
26303      ctrl.$invalid = !ctrl.$valid;
26304      toggleValidationCss('', ctrl.$valid);
26305    }
26306
26307    // re-read the state as the set/unset methods could have
26308    // combined state in ctrl.$error[validationError] (used for forms),
26309    // where setting/unsetting only increments/decrements the value,
26310    // and does not replace it.
26311    var combinedState;
26312    if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
26313      combinedState = undefined;
26314    } else if (ctrl.$error[validationErrorKey]) {
26315      combinedState = false;
26316    } else if (ctrl.$$success[validationErrorKey]) {
26317      combinedState = true;
26318    } else {
26319      combinedState = null;
26320    }
26321
26322    toggleValidationCss(validationErrorKey, combinedState);
26323    ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
26324  }
26325
26326  function createAndSet(name, value, controller) {
26327    if (!ctrl[name]) {
26328      ctrl[name] = {};
26329    }
26330    set(ctrl[name], value, controller);
26331  }
26332
26333  function unsetAndCleanup(name, value, controller) {
26334    if (ctrl[name]) {
26335      unset(ctrl[name], value, controller);
26336    }
26337    if (isObjectEmpty(ctrl[name])) {
26338      ctrl[name] = undefined;
26339    }
26340  }
26341
26342  function cachedToggleClass(className, switchValue) {
26343    if (switchValue && !classCache[className]) {
26344      $animate.addClass($element, className);
26345      classCache[className] = true;
26346    } else if (!switchValue && classCache[className]) {
26347      $animate.removeClass($element, className);
26348      classCache[className] = false;
26349    }
26350  }
26351
26352  function toggleValidationCss(validationErrorKey, isValid) {
26353    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
26354
26355    cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
26356    cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
26357  }
26358}
26359
26360function isObjectEmpty(obj) {
26361  if (obj) {
26362    for (var prop in obj) {
26363      if (obj.hasOwnProperty(prop)) {
26364        return false;
26365      }
26366    }
26367  }
26368  return true;
26369}
26370
26371/**
26372 * @ngdoc directive
26373 * @name ngNonBindable
26374 * @restrict AC
26375 * @priority 1000
26376 *
26377 * @description
26378 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
26379 * DOM element. This is useful if the element contains what appears to be Angular directives and
26380 * bindings but which should be ignored by Angular. This could be the case if you have a site that
26381 * displays snippets of code, for instance.
26382 *
26383 * @element ANY
26384 *
26385 * @example
26386 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
26387 * but the one wrapped in `ngNonBindable` is left alone.
26388 *
26389 * @example
26390    <example>
26391      <file name="index.html">
26392        <div>Normal: {{1 + 2}}</div>
26393        <div ng-non-bindable>Ignored: {{1 + 2}}</div>
26394      </file>
26395      <file name="protractor.js" type="protractor">
26396       it('should check ng-non-bindable', function() {
26397         expect(element(by.binding('1 + 2')).getText()).toContain('3');
26398         expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
26399       });
26400      </file>
26401    </example>
26402 */
26403var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
26404
26405/* global jqLiteRemove */
26406
26407var ngOptionsMinErr = minErr('ngOptions');
26408
26409/**
26410 * @ngdoc directive
26411 * @name ngOptions
26412 * @restrict A
26413 *
26414 * @description
26415 *
26416 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
26417 * elements for the `<select>` element using the array or object obtained by evaluating the
26418 * `ngOptions` comprehension expression.
26419 *
26420 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
26421 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
26422 * increasing speed by not creating a new scope for each repeated instance, as well as providing
26423 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26424 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26425 *  to a non-string value. This is because an option element can only be bound to string values at
26426 * present.
26427 *
26428 * When an item in the `<select>` menu is selected, the array element or object property
26429 * represented by the selected option will be bound to the model identified by the `ngModel`
26430 * directive.
26431 *
26432 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26433 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26434 * option. See example below for demonstration.
26435 *
26436 * ## Complex Models (objects or collections)
26437 *
26438 * By default, `ngModel` watches the model by reference, not value. This is important to know when
26439 * binding the select to a model that is an object or a collection.
26440 *
26441 * One issue occurs if you want to preselect an option. For example, if you set
26442 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
26443 * because the objects are not identical. So by default, you should always reference the item in y
26443our collection
26444 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
26445 *
26446 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
26447 * of the item not by reference, but by the result of the `track by` expression. For example, if your
26448 * collection items have an id property, you would `track by item.id`.
26449 *
26450 * A different issue with objects or collections is that ngModel won't detect if an object property or
26451 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
26452 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
26453 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
26454 * has not changed identity, but only a property on the object or an item in the collection changes.
26455 *
26456 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26457 * if the model is an array). This means that changing a property deeper than the first level inside the
26458 * object/collection will not trigger a re-rendering.
26459 *
26460 * ## `select` **`as`**
26461 *
26462 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26463 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26464 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26465 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26466 *
26467 *
26468 * ### `select` **`as`** and **`track by`**
26469 *
26470 * <div class="alert alert-warning">
26471 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
26472 * </div>
26473 *
26474 * Given this array of items on the $scope:
26475 *
26476 * ```js
26477 * $scope.items = [{
26478 *   id: 1,
26479 *   label: 'aLabel',
26480 *   subItem: { name: 'aSubItem' }
26481 * }, {
26482 *   id: 2,
26483 *   label: 'bLabel',
26484 *   subItem: { name: 'bSubItem' }
26485 * }];
26486 * ```
26487 *
26488 * This will work:
26489 *
26490 * ```html
26491 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
26492 * ```
26493 * ```js
26494 * $scope.selected = $scope.items[0];
26495 * ```
26496 *
26497 * but this will not work:
26498 *
26499 * ```html
26500 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
26501 * ```
26502 * ```js
26503 * $scope.selected = $scope.items[0].subItem;
26504 * ```
26505 *
26506 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
26507 * `items` array. Because the selected option has been set programmatically in the controller, the
26508 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
26509 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
26510 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
26511 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
26512 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
26513 *
26514 *
26515 * @param {string} ngModel Assignable angular expression to data-bind to.
26516 * @param {string=} name Property name of the form under which the control is published.
26517 * @param {string=} required The control is considered valid only if value is entered.
26518 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26519 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26520 *    `required` when you want to data-bind to the `required` attribute.
26521 * @param {comprehension_expression=} ngOptions in one of the following forms:
26522 *
26523 *   * for array data sources:
26524 *     * `label` **`for`** `value` **`in`** `array`
26525 *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
26526 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
26527 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
26528 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26529 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26530 *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
26531 *        (for including a filter with `track by`)
26532 *   * for object data sources:
26533 *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26534 *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26535 *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
26536 *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
26537 *     * `select` **`as`** `label` **`group by`** `group`
26538 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26539 *     * `select` **`as`** `label` **`disable when`** `disable`
26540 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26541 *
26542 * Where:
26543 *
26544 *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
26545 *   * `value`: local variable which will refer to each item in the `array` or each property value
26546 *      of `object` during iteration.
26547 *   * `key`: local variable which will refer to a property name in `object` during iteration.
26548 *   * `label`: The result of this expression will be the label for `<option>` element. The
26549 *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
26550 *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
26551 *      element. If not specified, `select` expression will default to `value`.
26552 *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
26553 *      DOM element.
26554 *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
26555 *      element. Return `true` to disable.
26556 *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
26557 *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
26558 *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
26559 *      even when the options are recreated (e.g. reloaded from the server).
26560 *
26561 * @example
26562    <example module="selectExample">
26563      <file name="index.html">
26564        <script>
26565        angular.module('selectExample', [])
26566          .controller('ExampleController', ['$scope', function($scope) {
26567            $scope.colors = [
26568              {name:'black', shade:'dark'},
26569              {name:'white', shade:'light', notAnOption: true},
26570              {name:'red', shade:'dark'},
26571              {name:'blue', shade:'dark', notAnOption: true},
26572              {name:'yellow', shade:'light', notAnOption: false}
26573            ];
26574            $scope.myColor = $scope.colors[2]; // red
26575          }]);
26576        </script>
26577        <div ng-controller="ExampleController">
26578          <ul>
26579            <li ng-repeat="color in colors">
26580              <label>Name: <input ng-model="color.name"></label>
26581              <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
26582              <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
26583            </li>
26584            <li>
26585              <button ng-click="colors.push({})">add</button>
26586            </li>
26587          </ul>
26588          <hr/>
26589          <label>Color (null not allowed):
26590            <select ng-model="myColor" ng-options="color.name for color in colors"></select>
26591          </label><br/>
26592          <label>Color (null allowed):
26593          <span  class="nullable">
26594            <select ng-model="myColor" ng-options="color.name for color in colors">
26595              <option value="">-- choose color --</option>
26596            </select>
26597          </span></label><br/>
26598
26599          <label>Color grouped by shade:
26600            <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
26601            </select>
26602          </label><br/>
26603
26604          <label>Color grouped by shade, with some disabled:
26605            <select ng-model="myColor"
26606                  ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
26607            </select>
26608          </label><br/>
26609
26610
26611
26612          Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
26613          <br/>
26614          <hr/>
26615          Currently selected: {{ {selected_color:myColor} }}
26616          <div style="border:solid 1px black; height:20px"
26617               ng-style="{'background-color':myColor.name}">
26618          </div>
26619        </div>
26620      </file>
26621      <file name="protractor.js" type="protractor">
26622         it('should check ng-options', function() {
26623           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
26624           element.all(by.model('myColor')).first().click();
26625           element.all(by.css('select[ng-model="myColor"] option')).first().click();
26626           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
26627           element(by.css('.nullable select[ng-model="myColor"]')).click();
26628           element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
26629           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
26630         });
26631      </file>
26632    </example>
26633 */
26634
26635// jshint maxlen: false
26636//                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
26637var 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]+?))?$/;
26638                        // 1: value expression (valueFn)
26639                        // 2: label expression (displayFn)
26640                        // 3: group by expression (groupByFn)
26641                        // 4: disable when expression (disableWhenFn)
26642                        // 5: array item variable name
26643                        // 6: object item key variable name
26644                        // 7: object item value variable name
26645                        // 8: collection expression
26646                        // 9: track by expression
26647// jshint maxlen: 100
26648
26649
26650var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26651
26652  function parseOptionsExpression(optionsExp, selectElement, scope) {
26653
26654    var match = optionsExp.match(NG_OPTIONS_REGEXP);
26655    if (!(match)) {
26656      throw ngOptionsMinErr('iexp',
26657        "Expected expression in form of " +
26658        "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
26659        " but got '{0}'. Element: {1}",
26660        optionsExp, startingTag(selectElement));
26661    }
26662
26663    // Extract the parts from the ngOptions expression
26664
26665    // The variable name for the value of the item in the collection
26666    var valueName = match[5] || match[7];
26667    // The variable name for the key of the item in the collection
26668    var keyName = match[6];
26669
26670    // An expression that generates the viewValue for an option if there is a label expression
26671    var selectAs = / as /.test(match[0]) && match[1];
26672    // An expression that is used to track the id of each object in the options collection
26673    var trackBy = match[9];
26674    // An expression that generates the viewValue for an option if there is no label expression
26675    var valueFn = $parse(match[2] ? match[1] : valueName);
26676    var selectAsFn = selectAs && $parse(selectAs);
26677    var viewValueFn = selectAsFn || valueFn;
26678    var trackByFn = trackBy && $parse(trackBy);
26679
26680    // Get the value by which we are going to track the option
26681    // if we have a trackFn then use that (passing scope and locals)
26682    // otherwise just hash the given viewValue
26683    var getTrackByValueFn = trackBy ?
26684                              function(value, locals) { return trackByFn(scope, locals); } :
26685                              function getHashOfValue(value) { return hashKey(value); };
26686    var getTrackByValue = function(value, key) {
26687      return getTrackByValueFn(value, getLocals(value, key));
26688    };
26689
26690    var displayFn = $parse(match[2] || match[1]);
26691    var groupByFn = $parse(match[3] || '');
26692    var disableWhenFn = $parse(match[4] || '');
26693    var valuesFn = $parse(match[8]);
26694
26695    var locals = {};
26696    var getLocals = keyName ? function(value, key) {
26697      locals[keyName] = key;
26698      locals[valueName] = value;
26699      return locals;
26700    } : function(value) {
26701      locals[valueName] = value;
26702      return locals;
26703    };
26704
26705
26706    function Option(selectValue, viewValue, label, group, disabled) {
26707      this.selectValue = selectValue;
26708      this.viewValue = viewValue;
26709      this.label = label;
26710      this.group = group;
26711      this.disabled = disabled;
26712    }
26713
26714    function getOptionValuesKeys(optionValues) {
26715      var optionValuesKeys;
26716
26717      if (!keyName && isArrayLike(optionValues)) {
26718        optionValuesKeys = optionValues;
26719      } else {
26720        // if object, extract keys, in enumeration order, unsorted
26721        optionValuesKeys = [];
26722        for (var itemKey in optionValues) {
26723          if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26724            optionValuesKeys.push(itemKey);
26725          }
26726        }
26727      }
26728      return optionValuesKeys;
26729    }
26730
26731    return {
26732      trackBy: trackBy,
26733      getTrackByValue: getTrackByValue,
26734      getWatchables: $parse(valuesFn, function(optionValues) {
26735        // Create a collection of things that we would like to watch (watchedArray)
26736        // so that they can all be watched using a single $watchCollection
26737        // that only runs the handler once if anything changes
26738        var watchedArray = [];
26739        optionValues = optionValues || [];
26740
26741        var optionValuesKeys = getOptionValuesKeys(optionValues);
26742        var optionValuesLength = optionValuesKeys.length;
26743        for (var index = 0; index < optionValuesLength; index++) {
26744          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26745          var value = optionValues[key];
26746
26747          var locals = getLocals(optionValues[key], key);
26748          var selectValue = getTrackByValueFn(optionValues[key], locals);
26749          watchedArray.push(selectValue);
26750
26751          // Only need to watch the displayFn if there is a specific label expression
26752          if (match[2] || match[1]) {
26753            var label = displayFn(scope, locals);
26754            watchedArray.push(label);
26755          }
26756
26757          // Only need to watch the disableWhenFn if there is a specific disable expression
26758          if (match[4]) {
26759            var disableWhen = disableWhenFn(scope, locals);
26760            watchedArray.push(disableWhen);
26761          }
26762        }
26763        return watchedArray;
26764      }),
26765
26766      getOptions: function() {
26767
26768        var optionItems = [];
26769        var selectValueMap = {};
26770
26771        // The option values were already computed in the `getWatchables` fn,
26772        // which must have been called to trigger `getOptions`
26773        var optionValues = valuesFn(scope) || [];
26774        var optionValuesKeys = getOptionValuesKeys(optionValues);
26775        var optionValuesLength = optionValuesKeys.length;
26776
26777        for (var index = 0; index < optionValuesLength; index++) {
26778          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26779          var value = optionValues[key];
26780          var locals = getLocals(value, key);
26781          var viewValue = viewValueFn(scope, locals);
26782          var selectValue = getTrackByValueFn(viewValue, locals);
26783          var label = displayFn(scope, locals);
26784          var group = groupByFn(scope, locals);
26785          var disabled = disableWhenFn(scope, locals);
26786          var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26787
26788          optionItems.push(optionItem);
26789          selectValueMap[selectValue] = optionItem;
26790        }
26791
26792        return {
26793          items: optionItems,
26794          selectValueMap: selectValueMap,
26795          getOptionFromViewValue: function(value) {
26796            return selectValueMap[getTrackByValue(value)];
26797          },
26798          getViewValueFromOption: function(option) {
26799            // If the viewValue could be an object that may be mutated by the application,
26800            // we need to make a copy and not return the reference to the value on the option.
26801            return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26802          }
26803        };
26804      }
26805    };
26806  }
26807
26808
26809  // we can't just jqLite('<option>') since jqLite is not smart enough
26810  // to create it in <select> and IE barfs otherwise.
26811  var optionTemplate = document.createElement('option'),
26812      optGroupTemplate = document.createElement('optgroup');
26813
26814
26815    function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
26816
26817      // if ngModel is not defined, we don't need to do anything
26818      var ngModelCtrl = ctrls[1];
26819      if (!ngModelCtrl) return;
26820
26821      var selectCtrl = ctrls[0];
26822      var multiple = attr.multiple;
26823
26824      // The emptyOption allows the application developer to provide their own custom "empty"
26825      // option when the viewValue does not match any of the option values.
26826      var emptyOption;
26827      for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26828        if (children[i].value === '') {
26829          emptyOption = children.eq(i);
26830          break;
26831        }
26832      }
26833
26834      var providedEmptyOption = !!emptyOption;
26835
26836      var unknownOption = jqLite(optionTemplate.cloneNode(false));
26837      unknownOption.val('?');
26838
26839      var options;
26840      var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26841
26842
26843      var renderEmptyOption = function() {
26844        if (!providedEmptyOption) {
26845          selectElement.prepend(emptyOption);
26846        }
26847        selectElement.val('');
26848        emptyOption.prop('selected', true); // needed for IE
26849        emptyOption.attr('selected', true);
26850      };
26851
26852      var removeEmptyOption = function() {
26853        if (!providedEmptyOption) {
26854          emptyOption.remove();
26855        }
26856      };
26857
26858
26859      var renderUnknownOption = function() {
26860        selectElement.prepend(unknownOption);
26861        selectElement.val('?');
26862        unknownOption.prop('selected', true); // needed for IE
26863        unknownOption.attr('selected', true);
26864      };
26865
26866      var removeUnknownOption = function() {
26867        unknownOption.remove();
26868      };
26869
26870      // Update the controller methods for multiple selectable options
26871      if (!multiple) {
26872
26873        selectCtrl.writeValue = function writeNgOptionsValue(value) {
26874          var option = options.getOptionFromViewValue(value);
26875
26876          if (option && !option.disabled) {
26877            if (selectElement[0].value !== option.selectValue) {
26878              removeUnknownOption();
26879              removeEmptyOption();
26880
26881              selectElement[0].value = option.selectValue;
26882              option.element.selected = true;
26883              option.element.setAttribute('selected', 'selected');
26884            }
26885          } else {
26886            if (value === null || providedEmptyOption) {
26887              removeUnknownOption();
26888              renderEmptyOption();
26889            } else {
26890              removeEmptyOption();
26891              renderUnknownOption();
26892            }
26893          }
26894        };
26895
26896        selectCtrl.readValue = function readNgOptionsValue() {
26897
26898          var selectedOption = options.selectValueMap[selectElement.val()];
26899
26900          if (selectedOption && !selectedOption.disabled) {
26901            removeEmptyOption();
26902            removeUnknownOption();
26903            return options.getViewValueFromOption(selectedOption);
26904          }
26905          return null;
26906        };
26907
26908        // If we are using `track by` then we must watch the tracked value on the model
26909        // since ngModel only watches for object identity change
26910        if (ngOptions.trackBy) {
26911          scope.$watch(
26912            function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
26913            function() { ngModelCtrl.$render(); }
26914          );
26915        }
26916
26917      } else {
26918
26919        ngModelCtrl.$isEmpty = function(value) {
26920          return !value || value.length === 0;
26921        };
26922
26923
26924        selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
26925          options.items.forEach(function(option) {
26926            option.element.selected = false;
26927          });
26928
26929          if (value) {
26930            value.forEach(function(item) {
26931              var option = options.getOptionFromViewValue(item);
26932              if (option && !option.disabled) option.element.selected = true;
26933            });
26934          }
26935        };
26936
26937
26938        selectCtrl.readValue = function readNgOptionsMultiple() {
26939          var selectedValues = selectElement.val() || [],
26940              selections = [];
26941
26942          forEach(selectedValues, function(value) {
26943            var option = options.selectValueMap[value];
26944            if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
26945          });
26946
26947          return selections;
26948        };
26949
26950        // If we are using `track by` then we must watch these tracked values on the model
26951        // since ngModel only watches for object identity change
26952        if (ngOptions.trackBy) {
26953
26954          scope.$watchCollection(function() {
26955            if (isArray(ngModelCtrl.$viewValue)) {
26956              return ngModelCtrl.$viewValue.map(function(value) {
26957                return ngOptions.getTrackByValue(value);
26958              });
26959            }
26960          }, function() {
26961            ngModelCtrl.$render();
26962          });
26963
26964        }
26965      }
26966
26967
26968      if (providedEmptyOption) {
26969
26970        // we need to remove it before calling selectElement.empty() because otherwise IE will
26971        // remove the label from the element. wtf?
26972        emptyOption.remove();
26973
26974        // compile the element since there might be bindings in it
26975        $compile(emptyOption)(scope);
26976
26977        // remove the class, which is added automatically because we recompile the element and it
26978        // becomes the compilation root
26979        emptyOption.removeClass('ng-scope');
26980      } else {
26981        emptyOption = jqLite(optionTemplate.cloneNode(false));
26982      }
26983
26984      // We need to do this here to ensure that the options object is defined
26985      // when we first hit it in writeNgOptionsValue
26986      updateOptions();
26987
26988      // We will re-render the option elements if the option values or labels change
26989      scope.$watchCollection(ngOptions.getWatchables, updateOptions);
26990
26991      // ------------------------------------------------------------------ //
26992
26993
26994      function updateOptionElement(option, element) {
26995        option.element = element;
26996        element.disabled = option.disabled;
26997        // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
26998        // selects in certain circumstances when multiple selects are next to each other and display
26999        // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
27000        // See https://github.com/angular/angular.js/issues/11314 for more info.
27001        // This is unfortunately untestable with unit / e2e tests
27002        if (option.label !== element.label) {
27003          element.label = option.label;
27004          element.textContent = option.label;
27005        }
27006        if (option.value !== element.value) element.value = option.selectValue;
27007      }
27008
27009      function addOrReuseElement(parent, current, type, templateElement) {
27010        var element;
27011        // Check whether we can reuse the next element
27012        if (current && lowercase(current.nodeName) === type) {
27013          // The next element is the right type so reuse it
27014          element = current;
27015        } else {
27016          // The next element is not the right type so create a new one
27017          element = templateElement.cloneNode(false);
27018          if (!current) {
27019            // There are no more elements so just append it to the select
27020            parent.appendChild(element);
27021          } else {
27022            // The next element is not a group so insert the new one
27023            parent.insertBefore(element, current);
27024          }
27025        }
27026        return element;
27027      }
27028
27029
27030      function removeExcessElements(current) {
27031        var next;
27032        while (current) {
27033          next = current.nextSibling;
27034          jqLiteRemove(current);
27035          current = next;
27036        }
27037      }
27038
27039
27040      function skipEmptyAndUnknownOptions(current) {
27041        var emptyOption_ = emptyOption && emptyOption[0];
27042        var unknownOption_ = unknownOption && unknownOption[0];
27043
27044        // We cannot rely on the extracted empty option being the same as the compiled empty option,
27045        // because the compiled empty option might have been replaced by a comment because
27046        // it had an "element" transclusion directive on it (such as ngIf)
27047        if (emptyOption_ || unknownOption_) {
27048          while (current &&
27049                (current === emptyOption_ ||
27050                current === unknownOption_ ||
27051                current.nodeType === NODE_TYPE_COMMENT ||
27052                (nodeName_(current) === 'option' && current.value === ''))) {
27053            current = current.nextSibling;
27054          }
27055        }
27056        return current;
27057      }
27058
27059
27060      function updateOptions() {
27061
27062        var previousValue = options && selectCtrl.readValue();
27063
27064        options = ngOptions.getOptions();
27065
27066        var groupMap = {};
27067        var currentElement = selectElement[0].firstChild;
27068
27069        // Ensure that the empty option is always there if it was explicitly provided
27070        if (providedEmptyOption) {
27071          selectElement.prepend(emptyOption);
27072        }
27073
27074        currentElement = skipEmptyAndUnknownOptions(currentElement);
27075
27076        options.items.forEach(function updateOption(option) {
27077          var group;
27078          var groupElement;
27079          var optionElement;
27080
27081          if (option.group) {
27082
27083            // This option is to live in a group
27084            // See if we have already created this group
27085            group = groupMap[option.group];
27086
27087            if (!group) {
27088
27089              // We have not already created this group
27090              groupElement = addOrReuseElement(selectElement[0],
27091                                               currentElement,
27092                                               'optgroup',
27093                                               optGroupTemplate);
27094              // Move to the next element
27095              currentElement = groupElement.nextSibling;
27096
27097              // Update the label on the group element
27098              groupElement.label = option.group;
27099
27100              // Store it for use later
27101              group = groupMap[option.group] = {
27102                groupElement: groupElement,
27103                currentOptionElement: groupElement.firstChild
27104              };
27105
27106            }
27107
27108            // So now we have a group for this option we add the option to the group
27109            optionElement = addOrReuseElement(group.groupElement,
27110                                              group.currentOptionElement,
27111                                              'option',
27112                                              optionTemplate);
27113            updateOptionElement(option, optionElement);
27114            // Move to the next element
27115            group.currentOptionElement = optionElement.nextSibling;
27116
27117          } else {
27118
27119            // This option is not in a group
27120            optionElement = addOrReuseElement(selectElement[0],
27121                                              currentElement,
27122                                              'option',
27123                                              optionTemplate);
27124            updateOptionElement(option, optionElement);
27125            // Move to the next element
27126            currentElement = optionElement.nextSibling;
27127          }
27128        });
27129
27130
27131        // Now remove all excess options and group
27132        Object.keys(groupMap).forEach(function(key) {
27133          removeExcessElements(groupMap[key].currentOptionElement);
27134        });
27135        removeExcessElements(currentElement);
27136
27137        ngModelCtrl.$render();
27138
27139        // Check to see if the value has changed due to the update to the options
27140        if (!ngModelCtrl.$isEmpty(previousValue)) {
27141          var nextValue = selectCtrl.readValue();
27142          var isNotPrimitive = ngOptions.trackBy || multiple;
27143          if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
27144            ngModelCtrl.$setViewValue(nextValue);
27145            ngModelCtrl.$render();
27146          }
27147        }
27148
27149      }
27150  }
27151
27152  return {
27153    restrict: 'A',
27154    terminal: true,
27155    require: ['select', '?ngModel'],
27156    link: {
27157      pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
27158        // Deactivate the SelectController.register method to prevent
27159        // option directives from accidentally registering themselves
27160        // (and unwanted $destroy handlers etc.)
27161        ctrls[0].registerOption = noop;
27162      },
27163      post: ngOptionsPostLink
27164    }
27165  };
27166}];
27167
27168/**
27169 * @ngdoc directive
27170 * @name ngPluralize
27171 * @restrict EA
27172 *
27173 * @description
27174 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
27175 * These rules are bundled with angular.js, but can be overridden
27176 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
27177 * by specifying the mappings between
27178 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
27179 * and the strings to be displayed.
27180 *
27181 * # Plural categories and explicit number rules
27182 * There are two
27183 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
27184 * in Angular's default en-US locale: "one" and "other".
27185 *
27186 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
27187 * any number that is not 1), an explicit number rule can only match one number. For example, the
27188 * explicit number rule for "3" matches the number 3. There are examples of plural categories
27189 * and explicit number rules throughout the rest of this documentation.
27190 *
27191 * # Configuring ngPluralize
27192 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
27193 * You can also provide an optional attribute, `offset`.
27194 *
27195 * The value of the `count` attribute can be either a string or an {@link guide/expression
27196 * Angular expression}; these are evaluated on the current scope for its bound value.
27197 *
27198 * The `when` attribute specifies the mappings between plural categories and the actual
27199 * string to be displayed. The value of the attribute should be a JSON object.
27200 *
27201 * The following example shows how to configure ngPluralize:
27202 *
27203 * ```html
27204 * <ng-pluralize count="personCount"
27205                 when="{'0': 'Nobody is viewing.',
27206 *                      'one': '1 person is viewing.',
27207 *                      'other': '{} people are viewing.'}">
27208 * </ng-pluralize>
27209 *```
27210 *
27211 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
27212 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
27213 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
27214 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
27215 * show "a dozen people are viewing".
27216 *
27217 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
27218 * into pluralized strings. In the previous example, Angular will replace `{}` with
27219 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
27220 * for <span ng-non-bindable>{{numberExpression}}</span>.
27221 *
27222 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
27223 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
27224 *
27225 * # Configuring ngPluralize with offset
27226 * The `offset` attribute allows further customization of pluralized text, which can result in
27227 * a better user experience. For example, instead of the message "4 people are viewing this document",
27228 * you might display "John, Kate and 2 others are viewing this document".
27229 * The offset attribute allows you to offset a number by any desired value.
27230 * Let's take a look at an example:
27231 *
27232 * ```html
27233 * <ng-pluralize count="personCount" offset=2
27234 *               when="{'0': 'Nobody is viewing.',
27235 *                      '1': '{{person1}} is viewing.',
27236 *                      '2': '{{person1}} and {{person2}} are viewing.',
27237 *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
27238 *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27239 * </ng-pluralize>
27240 * ```
27241 *
27242 * Notice that we are still using two plural categories(one, other), but we added
27243 * three explicit number rules 0, 1 and 2.
27244 * When one person, perhaps John, views the document, "John is viewing" will be shown.
27245 * When three people view the document, no explicit number rule is found, so
27246 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
27247 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
27248 * is shown.
27249 *
27250 * Note that when you specify offsets, you must provide explicit number rules for
27251 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
27252 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
27253 * plural categories "one" and "other".
27254 *
27255 * @param {string|expression} count The variable to be bound to.
27256 * @param {string} when The mapping between plural category to its corresponding strings.
27257 * @param {number=} offset Offset to deduct from the total number.
27258 *
27259 * @example
27260    <example module="pluralizeExampl
vendor: 4,913 bytes, lines 27260-27374
27260e">
27261      <file name="index.html">
27262        <script>
27263          angular.module('pluralizeExample', [])
27264            .controller('ExampleController', ['$scope', function($scope) {
27265              $scope.person1 = 'Igor';
27266              $scope.person2 = 'Misko';
27267              $scope.personCount = 1;
27268            }]);
27269        </script>
27270        <div ng-controller="ExampleController">
27271          <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
27272          <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
27273          <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
27274
27275          <!--- Example with simple pluralization rules for en locale --->
27276          Without Offset:
27277          <ng-pluralize count="personCount"
27278                        when="{'0': 'Nobody is viewing.',
27279                               'one': '1 person is viewing.',
27280                               'other': '{} people are viewing.'}">
27281          </ng-pluralize><br>
27282
27283          <!--- Example with offset --->
27284          With Offset(2):
27285          <ng-pluralize count="personCount" offset=2
27286                        when="{'0': 'Nobody is viewing.',
27287                               '1': '{{person1}} is viewing.',
27288                               '2': '{{person1}} and {{person2}} are viewing.',
27289                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
27290                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27291          </ng-pluralize>
27292        </div>
27293      </file>
27294      <file name="protractor.js" type="protractor">
27295        it('should show correct pluralized string', function() {
27296          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
27297          var withOffset = element.all(by.css('ng-pluralize')).get(1);
27298          var countInput = element(by.model('personCount'));
27299
27300          expect(withoutOffset.getText()).toEqual('1 person is viewing.');
27301          expect(withOffset.getText()).toEqual('Igor is viewing.');
27302
27303          countInput.clear();
27304          countInput.sendKeys('0');
27305
27306          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
27307          expect(withOffset.getText()).toEqual('Nobody is viewing.');
27308
27309          countInput.clear();
27310          countInput.sendKeys('2');
27311
27312          expect(withoutOffset.getText()).toEqual('2 people are viewing.');
27313          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
27314
27315          countInput.clear();
27316          countInput.sendKeys('3');
27317
27318          expect(withoutOffset.getText()).toEqual('3 people are viewing.');
27319          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
27320
27321          countInput.clear();
27322          countInput.sendKeys('4');
27323
27324          expect(withoutOffset.getText()).toEqual('4 people are viewing.');
27325          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
27326        });
27327        it('should show data-bound names', function() {
27328          var withOffset = element.all(by.css('ng-pluralize')).get(1);
27329          var personCount = element(by.model('personCount'));
27330          var person1 = element(by.model('person1'));
27331          var person2 = element(by.model('person2'));
27332          personCount.clear();
27333          personCount.sendKeys('4');
27334          person1.clear();
27335          person1.sendKeys('Di');
27336          person2.clear();
27337          person2.sendKeys('Vojta');
27338          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
27339        });
27340      </file>
27341    </example>
27342 */
27343var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
27344  var BRACE = /{}/g,
27345      IS_WHEN = /^when(Minus)?(.+)$/;
27346
27347  return {
27348    link: function(scope, element, attr) {
27349      var numberExp = attr.count,
27350          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
27351          offset = attr.offset || 0,
27352          whens = scope.$eval(whenExp) || {},
27353          whensExpFns = {},
27354          startSymbol = $interpolate.startSymbol(),
27355          endSymbol = $interpolate.endSymbol(),
27356          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
27357          watchRemover = angular.noop,
27358          lastCount;
27359
27360      forEach(attr, function(expression, attributeName) {
27361        var tmpMatch = IS_WHEN.exec(attributeName);
27362        if (tmpMatch) {
27363          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
27364          whens[whenKey] = element.attr(attr.$attr[attributeName]);
27365        }
27366      });
27367      forEach(whens, function(expression, key) {
27368        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
27369
27370      });
27371
27372      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
27373        var count = parseFloat(newVal);
27374        var countIsNaN = isN
vendor: 11,505 bytes, lines 27374-27623
27374aN(count);
27375
27376        if (!countIsNaN && !(count in whens)) {
27377          // If an explicit number rule such as 1, 2, 3... is defined, just use it.
27378          // Otherwise, check it against pluralization rules in $locale service.
27379          count = $locale.pluralCat(count - offset);
27380        }
27381
27382        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
27383        // In JS `NaN !== NaN`, so we have to exlicitly check.
27384        if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
27385          watchRemover();
27386          var whenExpFn = whensExpFns[count];
27387          if (isUndefined(whenExpFn)) {
27388            if (newVal != null) {
27389              $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
27390            }
27391            watchRemover = noop;
27392            updateElementText();
27393          } else {
27394            watchRemover = scope.$watch(whenExpFn, updateElementText);
27395          }
27396          lastCount = count;
27397        }
27398      });
27399
27400      function updateElementText(newText) {
27401        element.text(newText || '');
27402      }
27403    }
27404  };
27405}];
27406
27407/**
27408 * @ngdoc directive
27409 * @name ngRepeat
27410 * @multiElement
27411 *
27412 * @description
27413 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
27414 * instance gets its own scope, where the given loop variable is set to the current collection item,
27415 * and `$index` is set to the item index or key.
27416 *
27417 * Special properties are exposed on the local scope of each template instance, including:
27418 *
27419 * | Variable  | Type            | Details                                                                     |
27420 * |-----------|-----------------|-----------------------------------------------------------------------------|
27421 * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
27422 * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
27423 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
27424 * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
27425 * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
27426 * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
27427 *
27428 * <div class="alert alert-info">
27429 *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
27430 *   This may be useful when, for instance, nesting ngRepeats.
27431 * </div>
27432 *
27433 *
27434 * # Iterating over object properties
27435 *
27436 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
27437 * syntax:
27438 *
27439 * ```js
27440 * <div ng-repeat="(key, value) in myObj"> ... </div>
27441 * ```
27442 *
27443 * You need to be aware that the JavaScript specification does not define the order of keys
27444 * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
27445 * used to sort the keys alphabetically.)
27446 *
27447 * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
27448 * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
27449 * keys in the order in which they were defined, although there are exceptions when keys are deleted
27450 * and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
27451 *
27452 * If this is not desired, the recommended workaround is to convert your object into an array
27453 * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
27454 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27455 * or implement a `$watch` on the object yourself.
27456 *
27457 *
27458 * # Tracking and Duplicates
27459 *
27460 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
27461 * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
27462 *
27463 * * When an item is added, a new instance of the template is added to the DOM.
27464 * * When an item is removed, its template instance is removed from the DOM.
27465 * * When items are reordered, their respective templates are reordered in the DOM.
27466 *
27467 * To minimize creation of DOM elements, `ngRepeat` uses a function
27468 * to "keep track" of all items in the collection and their corresponding DOM elements.
27469 * For example, if an item is added to the collection, ngRepeat will know that all other items
27470 * already have DOM elements, and will not re-render them.
27471 *
27472 * The default tracking function (which tracks items by their identity) does not allow
27473 * duplicate items in arrays. This is because when there are duplicates, it is not possible
27474 * to maintain a one-to-one mapping between collection items and DOM elements.
27475 *
27476 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27477 * with your own using the `track by` expression.
27478 *
27479 * For example, you may track items by the index of each item in the collection, using the
27480 * special scope property `$index`:
27481 * ```html
27482 *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27483 *      {{n}}
27484 *    </div>
27485 * ```
27486 *
27487 * You may also use arbitrary expressions in `track by`, including references to custom functions
27488 * on the scope:
27489 * ```html
27490 *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27491 *      {{n}}
27492 *    </div>
27493 * ```
27494 *
27495 * <div class="alert alert-success">
27496 * If you are working with objects that have an identifier property, you should track
27497 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27498 * will not have to rebuild the DOM elements for items it has already rendered, even if the
27499 * JavaScript objects in the collection have been substituted for new ones. For large collections,
27500 * this signifincantly improves rendering performance. If you don't have a unique identifier,
27501 * `track by $index` can also provide a performance boost.
27502 * </div>
27503 * ```html
27504 *    <div ng-repeat="model in collection track by model.id">
27505 *      {{model.name}}
27506 *    </div>
27507 * ```
27508 *
27509 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
27510 * `$id` function, which tracks items by their identity:
27511 * ```html
27512 *    <div ng-repeat="obj in collection track by $id(obj)">
27513 *      {{obj.prop}}
27514 *    </div>
27515 * ```
27516 *
27517 * <div class="alert alert-warning">
27518 * **Note:** `track by` must always be the last expression:
27519 * </div>
27520 * ```
27521 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
27522 *     {{model.name}}
27523 * </div>
27524 * ```
27525 *
27526 * # Special repeat start and end points
27527 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
27528 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
27529 * 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)
27530 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
27531 *
27532 * The example below makes use of this feature:
27533 * ```html
27534 *   <header ng-repeat-start="item in items">
27535 *     Header {{ item }}
27536 *   </header>
27537 *   <div class="body">
27538 *     Body {{ item }}
27539 *   </div>
27540 *   <footer ng-repeat-end>
27541 *     Footer {{ item }}
27542 *   </footer>
27543 * ```
27544 *
27545 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
27546 * ```html
27547 *   <header>
27548 *     Header A
27549 *   </header>
27550 *   <div class="body">
27551 *     Body A
27552 *   </div>
27553 *   <footer>
27554 *     Footer A
27555 *   </footer>
27556 *   <header>
27557 *     Header B
27558 *   </header>
27559 *   <div class="body">
27560 *     Body B
27561 *   </div>
27562 *   <footer>
27563 *     Footer B
27564 *   </footer>
27565 * ```
27566 *
27567 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
27568 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
27569 *
27570 * @animations
27571 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
27572 *
27573 * **.leave** - when an item is removed from the list or when an item is filtered out
27574 *
27575 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
27576 *
27577 * @element ANY
27578 * @scope
27579 * @priority 1000
27580 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
27581 *   formats are currently supported:
27582 *
27583 *   * `variable in expression` – where variable is the user defined loop variable and `expression`
27584 *     is a scope expression giving the collection to enumerate.
27585 *
27586 *     For example: `album in artist.albums`.
27587 *
27588 *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
27589 *     and `expression` is the scope expression giving the collection to enumerate.
27590 *
27591 *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
27592 *
27593 *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
27594 *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
27595 *     is specified, ng-repeat associates elements by identity. It is an error to have
27596 *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
27597 *     mapped to the same DOM element, which is not possible.)
27598 *
27599 *     Note that the tracking expression must come last, after any filters, and the alias expression.
27600 *
27601 *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
27602 *     will be associated by item identity in the array.
27603 *
27604 *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
27605 *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
27606 *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
27607 *     element in the same way in the DOM.
27608 *
27609 *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
27610 *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
27611 *     property is same.
27612 *
27613 *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
27614 *     to items in conjunction with a tracking expression.
27615 *
27616 *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
27617 *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
27618 *     when a filter is active on the repeater, but the filtered result set is empty.
27619 *
27620 *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
27621 *     the items have been processed through the filter.
27622 *
27623 *     Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax s
27623o it can be used only at the end
27624 *     (and not as operator, inside an expression).
27625 *
27626 *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
27627 *
27628 * @example
27629 * This example initializes the scope to a list of names and
27630 * then uses `ngRepeat` to display every person:
27631  <example module="ngAnimate" deps="angular-animate.js" animations="true">
27632    <file name="index.html">
27633      <div ng-init="friends = [
27634        {name:'John', age:25, gender:'boy'},
27635        {name:'Jessie', age:30, gender:'girl'},
27636        {name:'Johanna', age:28, gender:'girl'},
27637        {name:'Joy', age:15, gender:'girl'},
27638        {name:'Mary', age:28, gender:'girl'},
27639        {name:'Peter', age:95, gender:'boy'},
27640        {name:'Sebastian', age:50, gender:'boy'},
27641        {name:'Erika', age:27, gender:'girl'},
27642        {name:'Patrick', age:40, gender:'boy'},
27643        {name:'Samantha', age:60, gender:'girl'}
27644      ]">
27645        I have {{friends.length}} friends. They are:
27646        <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
27647        <ul class="example-animate-container">
27648          <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
27649            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
27650          </li>
27651          <li class="animate-repeat" ng-if="results.length == 0">
27652            <strong>No results found...</strong>
27653          </li>
27654        </ul>
27655      </div>
27656    </file>
27657    <file name="animations.css">
27658      .example-animate-container {
27659        background:white;
27660        border:1px solid black;
27661        list-style:none;
27662        margin:0;
27663        padding:0 10px;
27664      }
27665
27666      .animate-repeat {
27667        line-height:40px;
27668        list-style:none;
27669        box-sizing:border-box;
27670      }
27671
27672      .animate-repeat.ng-move,
27673      .animate-repeat.ng-enter,
27674      .animate-repeat.ng-leave {
27675        transition:all linear 0.5s;
27676      }
27677
27678      .animate-repeat.ng-leave.ng-leave-active,
27679      .animate-repeat.ng-move,
27680      .animate-repeat.ng-enter {
27681        opacity:0;
27682        max-height:0;
27683      }
27684
27685      .animate-repeat.ng-leave,
27686      .animate-repeat.ng-move.ng-move-active,
27687      .animate-repeat.ng-enter.ng-enter-active {
27688        opacity:1;
27689        max-height:40px;
27690      }
27691    </file>
27692    <file name="protractor.js" type="protractor">
27693      var friends = element.all(by.repeater('friend in friends'));
27694
27695      it('should render initial data set', function() {
27696        expect(friends.count()).toBe(10);
27697        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
27698        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
27699        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
27700        expect(element(by.binding('friends.length')).getText())
27701            .toMatch("I have 10 friends. They are:");
27702      });
27703
27704       it('should update repeater when filter predicate changes', function() {
27705         expect(friends.count()).toBe(10);
27706
27707         element(by.model('q')).sendKeys('ma');
27708
27709         expect(friends.count()).toBe(2);
27710         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
27711         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
27712       });
27713      </file>
27714    </example>
27715 */
27716var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27717  var NG_REMOVED = '$$NG_REMOVED';
27718  var ngRepeatMinErr = minErr('ngRepeat');
27719
27720  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
27721    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
27722    scope[valueIdentifier] = value;
27723    if (keyIdentifier) scope[keyIdentifier] = key;
27724    scope.$index = index;
27725    scope.$first = (index === 0);
27726    scope.$last = (index === (arrayLength - 1));
27727    scope.$middle = !(scope.$first || scope.$last);
27728    // jshint bitwise: false
27729    scope.$odd = !(scope.$even = (index&1) === 0);
27730    // jshint bitwise: true
27731  };
27732
27733  var getBlockStart = function(block) {
27734    return block.clone[0];
27735  };
27736
27737  var getBlockEnd = function(block) {
27738    return block.clone[block.clone.length - 1];
27739  };
27740
27741
27742  return {
27743    restrict: 'A',
27744    multiElement: true,
27745    transclude: 'element',
27746    priority: 1000,
27747    terminal: true,
27748    $$tlb: true,
27749    compile: function ngRepeatCompile($element, $attr) {
27750      var expression = $attr.ngRepeat;
27751      var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
27752
27753      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*$/);
27754
27755      if (!match) {
27756        throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
27757            expression);
27758      }
27759
27760      var lhs = match[1];
27761      var rhs = match[2];
27762      var aliasAs = match[3];
27763      var trackByExp = match[4];
27764
27765      match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
27766
27767      if (!match) {
27768        throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
27769            lhs);
27770      }
27771      var valueIdentifier = match[3] || match[1];
27772      var keyIdentifier = match[2];
27773
27774      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
27775          /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
27776        throw ngRepeatMinErr('badident', "alias '{0}
27776' is invalid --- must be a valid JS identifier which is not a reserved name.",
27777          aliasAs);
27778      }
27779
27780      var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
27781      var hashFnLocals = {$id: hashKey};
27782
27783      if (trackByExp) {
27784        trackByExpGetter = $parse(trackByExp);
27785      } else {
27786        trackByIdArrayFn = function(key, value) {
27787          return hashKey(value);
27788        };
27789        trackByIdObjFn = function(key) {
27790          return key;
27791        };
27792      }
27793
27794      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
27795
27796        if (trackByExpGetter) {
27797          trackByIdExpFn = function(key, value, index) {
27798            // assign key, value, and $index to the locals so that they can be used in hash functions
27799            if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27800            hashFnLocals[valueIdentifier] = value;
27801            hashFnLocals.$index = index;
27802            return trackByExpGetter($scope, hashFnLocals);
27803          };
27804        }
27805
27806        // Store a list of elements from previous run. This is a hash where key is the item from the
27807        // iterator, and the value is objects with following properties.
27808        //   - scope: bound scope
27809        //   - element: previous element.
27810        //   - index: position
27811        //
27812        // We are using no-proto object so that we don't need to guard against inherited props via
27813        // hasOwnProperty.
27814        var lastBlockMap = createMap();
27815
27816        //watch props
27817        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27818          var index, length,
27819              previousNode = $element[0],     // node that cloned nodes should be inserted after
27820                                              // initialized to the comment node anchor
27821              nextNode,
27822              // Same as lastBlockMap but it has the current state. It will become the
27823              // lastBlockMap on the next iteration.
27824              nextBlockMap = createMap(),
27825              collectionLength,
27826              key, value, // key/value of iteration
27827              trackById,
27828              trackByIdFn,
27829              collectionKeys,
27830              block,       // last object information {scope, element, id}
27831              nextBlockOrder,
27832              elementsToRemove;
27833
27834          if (aliasAs) {
27835            $scope[aliasAs] = collection;
27836          }
27837
27838          if (isArrayLike(collection)) {
27839            collectionKeys = collection;
27840            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
27841          } else {
27842            trackByIdFn = trackByIdExpFn || trackByIdObjFn;
27843            // if object, extract keys, in enumeration order, unsorted
27844            collectionKeys = [];
27845            for (var itemKey in collection) {
27846              if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
27847                collectionKeys.push(itemKey);
27848              }
27849            }
27850          }
27851
27852          collectionLength = collectionKeys.length;
27853          nextBlockOrder = new Array(collectionLength);
27854
27855          // locate existing items
27856          for (index = 0; index < collectionLength; index++) {
27857            key = (collection === collectionKeys) ? index : collectionKeys[index];
27858            value = collection[key];
27859            trackById = trackByIdFn(key, value, index);
27860            if (lastBlockMap[trackById]) {
27861              // found previously seen block
27862              block = lastBlockMap[trackById];
27863              delete lastBlockMap[trackById];
27864              nextBlockMap[trackById] = block;
27865              nextBlockOrder[index] = block;
27866            } else if (nextBlockMap[trackById]) {
27867              // if collision detected. restore lastBlockMap and throw an error
27868              forEach(nextBlockOrder, function(block) {
27869                if (block && block.scope) lastBlockMap[block.id] = block;
27870              });
27871              throw ngRepeatMinErr('dupes',
27872                  "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
27873                  expression, trackById, value);
27874            } else {
27875              // new never before seen block
27876              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
27877              nextBlockMap[trackById] = true;
27878            }
27879          }
27880
27881          // remove leftover items
27882          for (var blockKey in lastBlockMap) {
27883            block = lastBlockMap[blockKey];
27884            elementsToRemove = getBlockNodes(block.clone);
27885            $animate.leave(elementsToRemove);
27886            if (elementsToRemove[0].parentNode) {
27887              // if the element was not removed yet because of pending animation, mark it as deleted
27888              // so that we can ignore it later
27889              for (index = 0, length = elementsToRemove.length; index < length; index++) {
27890                elementsToRemove[index][NG_REMOVED] = true;
27891              }
27892            }
27893            block.scope.$destroy();
27894          }
27895
vendor: 6,929 bytes, lines 27896-28048
27896          // we are not using forEach for perf reasons (trying to avoid #call)
27897          for (index = 0; index < collectionLength; index++) {
27898            key = (collection === collectionKeys) ? index : collectionKeys[index];
27899            value = collection[key];
27900            block = nextBlockOrder[index];
27901
27902            if (block.scope) {
27903              // if we have already seen this object, then we need to reuse the
27904              // associated scope/element
27905
27906              nextNode = previousNode;
27907
27908              // skip nodes that are already pending removal via leave animation
27909              do {
27910                nextNode = nextNode.nextSibling;
27911              } while (nextNode && nextNode[NG_REMOVED]);
27912
27913              if (getBlockStart(block) != nextNode) {
27914                // existing item which got moved
27915                $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
27916              }
27917              previousNode = getBlockEnd(block);
27918              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27919            } else {
27920              // new item which we don't know about
27921              $transclude(function ngRepeatTransclude(clone, scope) {
27922                block.scope = scope;
27923                // http://jsperf.com/clone-vs-createcomment
27924                var endNode = ngRepeatEndComment.cloneNode(false);
27925                clone[clone.length++] = endNode;
27926
27927                // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
27928                $animate.enter(clone, null, jqLite(previousNode));
27929                previousNode = endNode;
27930                // Note: We only need the first/last node of the cloned nodes.
27931                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27932                // by a directive with templateUrl when its template arrives.
27933                block.clone = clone;
27934                nextBlockMap[block.id] = block;
27935                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27936              });
27937            }
27938          }
27939          lastBlockMap = nextBlockMap;
27940        });
27941      };
27942    }
27943  };
27944}];
27945
27946var NG_HIDE_CLASS = 'ng-hide';
27947var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
27948/**
27949 * @ngdoc directive
27950 * @name ngShow
27951 * @multiElement
27952 *
27953 * @description
27954 * The `ngShow` directive shows or hides the given HTML element based on the expression
27955 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
27956 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27957 * in AngularJS and sets the display style to none (using an !important flag).
27958 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27959 *
27960 * ```html
27961 * <!-- when $scope.myValue is truthy (element is visible) -->
27962 * <div ng-show="myValue"></div>
27963 *
27964 * <!-- when $scope.myValue is falsy (element is hidden) -->
27965 * <div ng-show="myValue" class="ng-hide"></div>
27966 * ```
27967 *
27968 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
27969 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
27970 * from the element causing the element not to appear hidden.
27971 *
27972 * ## Why is !important used?
27973 *
27974 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27975 * can be easily overridden by heavier selectors. For example, something as simple
27976 * as changing the display style on a HTML list item would make hidden elements appear visible.
27977 * This also becomes a bigger issue when dealing with CSS frameworks.
27978 *
27979 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27980 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27981 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27982 *
27983 * ### Overriding `.ng-hide`
27984 *
27985 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27986 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27987 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
27988 * with extra animation classes that can be added.
27989 *
27990 * ```css
27991 * .ng-hide:not(.ng-hide-animate) {
27992 *   /&#42; this is just another form of hiding an element &#42;/
27993 *   display: block!important;
27994 *   position: absolute;
27995 *   top: -9999px;
27996 *   left: -9999px;
27997 * }
27998 * ```
27999 *
28000 * By default you don't need to override in CSS anything and the animations will work around the display style.
28001 *
28002 * ## A note about animations with `ngShow`
28003 *
28004 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
28005 * is true and false. This system works like the animation system present with ngClass except that
28006 * you must also include the !important flag to override the display property
28007 * so that you can perform an animation when the element is hidden during the time of the animation.
28008 *
28009 * ```css
28010 * //
28011 * //a working example can be found at the bottom of this page
28012 * //
28013 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
28014 *   /&#42; this is required as of 1.3x to properly
28015 *      apply all styling in a show/hide animation &#42;/
28016 *   transition: 0s linear all;
28017 * }
28018 *
28019 * .my-element.ng-hide-add-active,
28020 * .my-element.ng-hide-remove-active {
28021 *   /&#42; the transition is defined in the active class &#42;/
28022 *   transition: 1s linear all;
28023 * }
28024 *
28025 * .my-element.ng-hide-add { ... }
28026 * .my-element.ng-hide-add.ng-hide-add-active { ... }
28027 * .my-element.ng-hide-remove { ... }
28028 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
28029 * ```
28030 *
28031 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
28032 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
28033 *
28034 * @animations
28035 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
28036 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
28037 *
28038 * @element ANY
28039 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
28040 *     then the element is shown or hidden respectively.
28041 *
28042 * @example
28043  <example module="ngAnimate" deps="angular-animate.js" animations="true">
28044    <file name="index.html">
28045      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
28046      <div>
28047        Show:
28048        <div class="check-element animate-show" ng-show="checked">
28049          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
28050        </div>
28051      </div>
28052      <div>
28053        Hide:
28054        <div class="check-element animate-show" ng-hide="checked">
28055          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
28056        </div>
28057      </div>
28058    </file>
28059    <file name="glyphicons.css">
28060      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
28061    </file>
28062    <file name="animations.css">
28063      .animate-show {
28064        line-height: 20px;
28065        opacity: 1;
28066        padding: 10px;
28067        border: 1px solid black;
28068        background: white;
28069      }
28070
28071      .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
28072        transition: all linear 0.5s;
28073      }
28074
28075      .animate-show.ng-hide {
28076        line-height: 0;
28077        opacity: 0;
28078        padding: 0 10px;
28079      }
28080
28081      .check-element {
28082        padding: 10px;
28083        border: 1px solid black;
28084        background: white;
28085      }
28086    </file>
28087    <file name="protractor.js" type="protractor">
28088      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
28089      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
28090
28091      it('should check ng-show / ng-hide', function() {
28092        expect(thumbsUp.isDisplayed()).toBeFalsy();
28093        expect(thumbsDown.isDisplayed()).toBeTruthy();
28094
28095        element(by.model('checked')).click();
28096
28097        expect(thumbsUp.isDisplayed()).toBeTruthy();
28098        expect(thumbsDown.isDisplayed()).toBeFalsy();
28099      });
28100    </file>
28101  </example>
28102 */
28103var ngShowDirective = ['$animate', function($animate) {
28104  return {
28105    restrict: 'A',
28106    multiElement: true,
28107    link: function(scope, element, attr) {
28108      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
28109        // we're adding a temporary, animation-specific class for ng-hide since this way
28110        // we can control when the element is actually displayed on screen without having
28111        // to have a global/greedy CSS selector that breaks when other animations are run.
28112        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
28113        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
28114          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
28115        });
28116      });
28117    }
28118  };
28119}];
28120
28121
28122/**
28123 * @ngdoc directive
28124 * @name ngHide
28125 * @multiElement
28126 *
28127 * @description
28128 * The `ngHide` directive shows or hides the given HTML element based on the expression
28129 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
28130 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
28131 * in AngularJS and sets the display style to none (using an !important flag).
28132 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
28133 *
28134 * ```html
28135 * <!-- when $scope.myValue is truthy (element is hidden) -->
28136 * <div ng-hide="myValue" class="ng-hide"></div>
28137 *
28138 * <!-- when $scope.myValue is falsy (element is visible) -->
28139 * <div ng-hide="myValue"></div>
28140 * ```
28141 *
28142 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
28143 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
28144 * from the element causing the element not to appear hidden.
28145 *
28146 * ## Why is !important used?
28147 *
28148 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
28149 * can be easily overridden by heavier selectors. For example, something as simple
28150 * as changing the display style on a HTML list item would make hidden elements appear visible.
28151 * This also becomes a bigger issue when dealing with CSS frameworks.
28152 *
28153 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
28154 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
28155 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
28156 *
28157 * ### Overriding `.ng-hide`
28158 *
28159 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
28160 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
28161 * class in CSS:
28162 *
28163 * ```css
28164 * .ng-hide {
28165 *   /&#42; this is just another form of hiding an element &#42;/
28166 *   display: block!important;
28167 *   position: absolute;
28168 *   top: -9999px;
28169 *   left: -9999px;
28170 * }
28171 * ```
28172 *
28173 * By default you don't need to override in CSS anything and the animations will work around the display style.
28174 *
28175 * ## A note about animations with `ngHide`
28176 *
28177 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
28178 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
28179 * CSS class is added and removed for you instead of your own CSS class.
28180 *
28181 * ```css
28182 * //
28183 * //a working example can be found at the bottom of this page
28184 * //
28185 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
28186 *   transition: 0.5s linear all;
28187 * }
28188 *
28189 * .my-element.ng-hide-add { ... }
28190 * .my-element.ng-hide-add.ng-hide-add-active { ... }
28191 * .my-element.ng-hide-remove { ... }
28192 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
28193 * ```
28194 *
28195 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
28196 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
28197 *
28198 * @animations
28199 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
28200 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
28201 *
28202 * @element ANY
28203 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
28204 *     the element is shown or hidden respectively.
28205 *
28206 * @example
28207  <example module="ngAnimate" deps="angular-animate.js" animations="true">
28208    <file name="index.html">
28209      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
28210      <div>
28211        Show:
28212        <div class="check-element animate-hide" ng-show="checked">
28213          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
28214        </div>
28215      </div>
28216      <div>
28217        Hide:
28218        <div class="check-element animate-hide" ng-hide="checked">
28219          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
28220        </div>
28221      </div>
28222    </file>
28223    <file name="glyphicons.css">
28224      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
28225    </file>
28226    <file name="animations.css">
28227      .animate-hide {
28228        transition: all linear 0.5s;
28229        line-height: 20px;
28230        opacity: 1;
28231        padding: 10px;
28232        border: 1px solid black;
28233        background: white;
28234      }
28235
28236      .animate-hide.ng-hide {
28237        line-height: 0;
28238        opacity: 0;
28239        padding: 0 10px;
28240      }
28241
28242      .check-element {
28243        padding: 10px;
28244        border: 1px solid black;
28245        background: white;
28246      }
28247    </file>
28248    <file name="protractor.js" type="protractor">
28249      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
28250      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
28251
28252      it('should check ng-show / ng-hide', function() {
28253        expect(thumbsUp.isDisplayed()).toBeFalsy();
28254        expect(thumbsDown.isDisplayed()).toBeTruthy();
28255
28256        element(by.model('checked')).click();
28257
28258        expect(thumbsUp.isDisplayed()).toBeTruthy();
28259        expect(thumbsDown.isDisplayed()).toBeFalsy();
28260      });
28261    </file>
28262  </example>
28263 */
28264var ngHideDirective = ['$animate', function($animate) {
28265  return {
28266    restrict: 'A',
28267    multiElement: true,
28268    link: function(scope, element, attr) {
28269      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
28270        // The comment inside of the ngShowDirective explains why we add and
28271        // remove a temporary class for the show/hide animation
28272        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
28273          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
28274        });
28275      });
28276    }
28277  };
28278}];
28279
28280/**
28281 * @ngdoc directive
28282 * @name ngStyle
28283 * @restrict AC
28284 *
28285 * @description
28286 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
28287 *
28288 * @element ANY
28289 * @param {expression} ngStyle
28290 *
28291 * {@link guide/expression Expression} which evals to an
28292 * object whose keys are CSS style names and values are corresponding values for those CSS
28293 * keys.
28294 *
28295 * Since some CSS style names are not valid keys for an object, they must be quoted.
28296 * See the 'background-color' style in the example below.
28297 *
28298 * @example
28299   <example>
28300     <file name="index.html">
28301        <input type="button" value="set color" ng-click="myStyle={color:'red'}">
28302        <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
28303        <input type="button" value="clear" ng-click="myStyle={}">
28304        <br/>
28305        <span ng-style="myStyle">Sample Text</span>
28306        <pre>myStyle={{myStyle}}</pre>
28307     </file>
28308     <file name="style.css">
28309       span {
28310         color: black;
28311       }
28312     </file>
28313     <file name="protractor.js" type="protractor">
28314       var colorSpan = element(by.css('span'));
28315
28316       it('should check ng-style', function() {
28317         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28318         element(by.css('input[value=\'set color\']')).click();
28319         expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
28320         element(by.css('input[value=clear]')).click();
28321         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28322       });
28323     </file>
28324   </example>
28325 */
28326var ngStyleDirective = ngDirective(function(scope, element, attr) {
28327  scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
28328    if (oldStyles && (newStyles !== oldStyles)) {
28329      forEach(oldStyles, function(val, style) { element.css(style, '');});
28330    }
28331    if (newStyles) element.css(newStyles);
28332  }, true);
28333});
28334
28335/**
28336 * @ngdoc directive
28337 * @name ngSwitch
28338 * @restrict EA
28339 *
28340 * @description
28341 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
28342 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
28343 * as specified in the template.
28344 *
28345 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
28346 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
28347 * matches the value obtained from the evaluated expression. In other words, you define a container element
28348 * (where you place the directive), place an expression on the **`on="..."` attribute**
28349 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
28350 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
28351 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
28352 * attribute is displayed.
28353 *
28354 * <div class="alert alert-info">
28355 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
28356 * as literal string values to match against.
28357 * For example, **`ng-switch-when="someVal"`** will match against the string `"s
28357omeVal"` not against the
28358 * value of the expression `$scope.someVal`.
28359 * </div>
28360
28361 * @animations
28362 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
28363 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
28364 *
28365 * @usage
28366 *
28367 * ```
28368 * <ANY ng-switch="expression">
28369 *   <ANY ng-switch-when="matchValue1">...</ANY>
28370 *   <ANY ng-switch-when="matchValue2">...</ANY>
28371 *   <ANY ng-switch-default>...</ANY>
28372 * </ANY>
28373 * ```
28374 *
28375 *
28376 * @scope
28377 * @priority 1200
28378 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
28379 * On child elements add:
28380 *
28381 * * `ngSwitchWhen`: the case statement to match against. If match then this
28382 *   case will be displayed. If the same match appears multiple times, all the
28383 *   elements will be displayed.
28384 * * `ngSwitchDefault`: the default case when no other case match. If there
28385 *   are multiple default cases, all of them will be displayed when no other
28386 *   case match.
28387 *
28388 *
28389 * @example
28390  <example module="switchExample" deps="angular-animate.js" animations="true">
28391    <file name="index.html">
28392      <div ng-controller="ExampleController">
28393        <select ng-model="selection" ng-options="item for item in items">
28394        </select>
28395        <code>selection={{selection}}</code>
28396        <hr/>
28397        <div class="animate-switch-container"
28398          ng-switch on="selection">
28399            <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
28400            <div class="animate-switch" ng-switch-when="home">Home Span</div>
28401            <div class="animate-switch" ng-switch-default>default</div>
28402        </div>
28403      </div>
28404    </file>
28405    <file name="script.js">
28406      angular.module('switchExample', ['ngAnimate'])
28407        .controller('ExampleController', ['$scope', function($scope) {
28408          $scope.items = ['settings', 'home', 'other'];
28409          $scope.selection = $scope.items[0];
28410        }]);
28411    </file>
28412    <file name="animations.css">
28413      .animate-switch-container {
28414        position:relative;
28415        background:white;
28416        border:1px solid black;
28417        height:40px;
28418        overflow:hidden;
28419      }
28420
28421      .animate-switch {
28422        padding:10px;
28423      }
28424
28425      .animate-switch.ng-animate {
28426        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28427
28428        position:absolute;
28429        top:0;
28430        left:0;
28431        right:0;
28432        bottom:0;
28433      }
28434
28435      .animate-switch.ng-leave.ng-leave-active,
28436      .animate-switch.ng-enter {
28437        top:-50px;
28438      }
28439      .animate-switch.ng-leave,
28440      .animate-switch.ng-enter.ng-enter-active {
28441        top:0;
28442      }
28443    </file>
28444    <file name="protractor.js" type="protractor">
28445      var switchElem = element(by.css('[ng-switch]'));
28446      var select = element(by.model('selection'));
28447
28448      it('should start in settings', function() {
28449        expect(switchElem.getText()).toMatch(/Settings Div/);
28450      });
28451      it('should change to home', function() {
28452        select.all(by.css('option')).get(1).click();
28453        expect(switchElem.getText()).toMatch(/Home Span/);
28454      });
28455      it('should select default', function() {
28456        select.all(by.css('option')).get(2).click();
28457        expect(switchElem.getText()).toMatch(/default/);
28458      });
28459    </file>
28460  </example>
28461 */
28462var ngSwitchDirective = ['$animate', function($animate) {
28463  return {
28464    require: 'ngSwitch',
28465
28466    // asks for $scope to fool the BC controller module
28467    controller: ['$scope', function ngSwitchController() {
28468     this.cases = {};
28469    }],
28470    link: function(scope, element, attr, ngSwitchController) {
28471      var watchExpr = attr.ngSwitch || attr.on,
28472          selectedTranscludes = [],
28473          selectedElements = [],
28474          previousLeaveAnimations = [],
28475          selectedScopes = [];
28476
28477      var spliceFactory = function(array, index) {
28478          return function() { array.splice(index, 1); };
28479      };
28480
28481      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28482        var i, ii;
28483        for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28484          $animate.cancel(previousLeaveAnimations[i]);
28485        }
28486        previousLeaveAnimations.length = 0;
28487
28488        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28489          var selected = getBlockNodes(selectedElements[i].clone);
28490          selectedScopes[i].$destroy();
28491          var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28492          promise.then(spliceFactory(previousLeaveAnimations, i));
28493        }
28494
28495        selectedElements.length = 0;
28496        selectedScopes.length = 0;
28497
28498        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
28499          forEach(selectedTranscludes, function(selectedTransclude) {
28500            selectedTransclude.transclude(function(caseElement, selectedScope) {
28501              selectedScopes.push(selectedScope);
28502              var anchor = selectedTransclude.element;
28503              caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
28504              var block = { clone: caseElement };
28505
28506              selectedElements.push(block);
28507              $animate.enter(caseElement, anchor.parent(), anchor);
28508            });
28509          });
28510        }
28511      });
28512    }
28513  };
28514}];
28515
28516var ngSwitchWhenDirective = ngDirective({
28517  transclude: 'element',
28518  priority: 1200,
28519  require: '^ngSwitch',
28520  multiElement: true,
28521  link: function(scope, element, attrs, ctrl, $transclude) {
28522    ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
28523    ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
28524  }
28525});
28526
28527var ngSwitchDefaultDirective = ngDirective({
28528  transclude: 'element',
28529  priority: 1200,
28530  require: '^ngSwitch',
28531  multiElement: true,
28532  link: function(scope, element, attr, ctrl, $transclude) {
28533    ctrl.cases['?'] = (ctrl.cases['?'] || []);
28534    ctrl.cases['?'].push({ transclude: $transclude, element: element });
28535   }
28536});
28537
28538/**
28539 * @ngdoc directive
28540 * @name ngTransclude
28541 * @restrict EAC
28542 *
28543 * @description
28544 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
28545 *
28546 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
28547 *
28548 * @element ANY
28549 *
28550 * @example
28551   <example module="transcludeExample">
28552     <file name="index.html">
28553       <script>
28554         angular.module('transcludeExample', [])
28555          .directive('pane', function(){
28556             return {
28557               restrict: 'E',
28558               transclude: true,
28559               scope: { title:'@' },
28560               template: '<div style="border: 1px solid black;">
28560' +
28561                           '<div style="background-color: gray">{{title}}</div>' +
28562                           '<ng-transclude></ng-transclude>' +
28563                         '</div>'
28564             };
28565         })
28566         .controller('ExampleController', ['$scope', function($scope) {
28567           $scope.title = 'Lorem Ipsum';
28568           $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
28569         }]);
28570       </script>
28571       <div ng-controller="ExampleController">
28572         <input ng-model="title" aria-label="title"> <br/>
28573         <textarea ng-model="text" aria-label="text"></textarea> <br/>
28574         <pane title="{{title}}">{{text}}</pane>
28575       </div>
28576     </file>
28577     <file name="protractor.js" type="protractor">
28578        it('should have transcluded', function() {
28579          var titleElement = element(by.model('title'));
28580          titleElement.clear();
28581          titleElement.sendKeys('TITLE');
28582          var textElement = element(by.model('text'));
28583          textElement.clear();
28584          textElement.sendKeys('TEXT');
28585          expect(element(by.binding('title')).getText()).toEqual('TITLE');
28586          expect(element(by.binding('text')).getText()).toEqual('TEXT');
28587        });
28588     </file>
28589   </example>
28590 *
28591 */
28592var ngTranscludeDirective = ngDirective({
28593  restrict: 'EAC',
28594  link: function($scope, $element, $attrs, controller, $transclude) {
28595    if (!$transclude) {
28596      throw minErr('ngTransclude')('orphan',
28597       'Illegal use of ngTransclude directive in the template! ' +
28598       'No parent directive that requires a transclusion found. ' +
28599       'Element: {0}',
28600       startingTag($element));
28601    }
28602
28603    $transclude(function(clone) {
28604      $element.empty();
28605      $element.append(clone);
28606    });
28607  }
28608});
28609
28610/**
28611 * @ngdoc directive
28612 * @name script
28613 * @restrict E
28614 *
28615 * @description
28616 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
28617 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
28618 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
28619 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
28620 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
28621 *
28622 * @param {string} type Must be set to `'text/ng-template'`.
28623 * @param {string} id Cache name of the template.
28624 *
28625 * @example
28626  <example>
28627    <file name="index.html">
28628      <script type="text/ng-template" id="/tpl.html">
28629        Content of the template.
28630      </script>
28631
28632      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
28633      <div id="tpl-content" ng-include src="currentTpl"></div>
28634    </file>
28635    <file name="protractor.js" type="protractor">
28636      it('should load template defined inside script tag', function() {
28637        element(by.css('#tpl-link')).click();
28638        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
28639      });
28640    </file>
28641  </example>
28642 */
28643var scriptDirective = ['$templateCache', function($templateCache) {
28644  return {
28645    restrict: 'E',
28646    terminal: true,
28647    compile: function(element, attr) {
28648      if (attr.type == 'text/ng-template') {
28649        var templateUrl = attr.id,
28650            text = element[0].text;
28651
28652        $templateCache.put(templateUrl, text);
28653      }
28654    }
28655  };
28656}];
28657
28658var noopNgModelController = { $setViewValue: noop, $render: noop };
28659
28660function chromeHack(optionElement) {
28661  // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28662  // Adding an <option selected="selected"> element to a <select required="required"> should
28663  // automatically select the new element
28664  if (optionElement[0].hasAttribute('selected')) {
28665    optionElement[0].selected = true;
28666  }
28667}
28668
28669/**
28670 * @ngdoc type
28671 * @name  select.SelectController
28672 * @description
28673 * The controller for the `<select>` directive. This provides support for reading
28674 * and writing the selected value(s) of the control and also coordinates dynamically
28675 * added `<option>` elements, perhaps by an `ngRepeat` directive.
28676 */
28677var SelectController =
28678        ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
28679
28680  var self = this,
28681      optionsMap = new HashMap();
28682
28683  // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
28684  self.ngModelCtrl = noopNgModelController;
28685
28686  // The "unknown" option is one that is prepended to the list if the viewValue
28687  // does not match any of the options. When it is rendered the value of the unknown
28688  // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
28689  //
28690  // We can't just jqLite('<option>') since jqLite is not smart enough
28691  // to create it in <select> and IE barfs otherwise.
28692  self.unknownOption = jqLite(document.createElement('option'));
28693  self.renderUnknownOption = function(val) {
28694    var unknownVal = '? ' + hashKey(val) + ' ?';
28695    self.unknownOption.val(unknownVal);
28696    $element.prepend(self.unknownOption);
28697    $element.val(unknownVal);
28698  };
28699
28700  $scope.$on('$destroy', function() {
28701    // disable unknown option so that we don't do work when the whole select is being destroyed
28702    self.renderUnknownOption = noop;
28703  });
28704
28705  self.removeUnknownOption = function() {
28706    if (self.unknownOption.parent()) self.unknownOption.remove();
28707  };
28708
28709
28710  // Read the value of the select control, the implementation of this changes depending
28711  // upon whether the select can have multiple values and whether ngOptions is at work.
28712  self.readValue = function readSingleValue() {
28713    self.removeUnknownOption();
28714    return $element.val();
28715  };
28716
28717
28718  // Write the value to the select control, the implementation of this changes depending
28719  // upon whether the select can have multiple values and whether ngOptions is at work.
28720  self.writeValue = function writeSingleValue(value) {
28721    if (self.hasOption(value)) {
28722      self.removeUnknownOption();
28723      $element.val(value);
28724      if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
28725    } else {
28726      if (value == null && self.emptyOption) {
28727        self.removeUnknownOption();
28728        $element.val('');
28729      } else {
28730        self.renderUnknownOption(value);
28731      }
28732    }
28733  };
28734
28735
28736  // Tell the select control that an option, with the given value, has been added
28737  self.addOption = function(value, element) {
28738    assertNotHasOwnProperty(value, '"option value"');
28739    if (value === '') {
28740      self.emptyOption = element;
28741    }
28742    var count = optionsMap.get(value) || 0;
28743    optionsMap.put(value, count + 1);
28744    self.ngModelCtrl.$render();
28745    chromeHack(element);
28746  };
28747
28748  // Tell the select control that an option, with the given value, has been removed
28749  self.removeOption = function(value) {
28750    var count = optionsMap.get(value);
28751    if (count) {
28752      if (count === 1) {
28753        optionsMap.remove(value);
28754        if (value === '') {
28755          self.emptyOption = undefined;
28756        }
28757      } else {
28758        optionsMap.put(value, count - 1);
28759      }
28760    }
28761  };
28762
28763  // Check whether the select control has an option matching the given value
28764  self.hasOption = function(value) {
28765    return !!optionsMap.get(value);
28766  };
28767
28768
28769  self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
28770
28771    if (interpolateValueFn) {
28772      // The value attribute is interpolated
28773      var oldVal;
28774      optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
28775        if (isDefined(oldVal)) {
28776          self.removeOption(oldVal);
28777        }
28778        oldVal = newVal;
28779        self.addOption(newVal, optionElement);
28780      });
28781    } else if (interpolateTextFn) {
28782      // The text content is interpolated
28783      optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
28784        optionAttrs.$set('value', newVal);
28785        if (oldVal !== newVal) {
28786          self.removeOption(oldVal);
28787        }
28788        self.addOption(newVal, optionElement);
28789      });
28790    } else {
28791      // The value attribute is static
28792      self.addOption(optionAttrs.value, optionElement);
28793    }
28794
28795    optionElement.on('$destroy', function() {
28796      self.removeOption(optionAttrs.value);
28797      self.ngModelCtrl.$render();
28798    });
28799  };
28800}];
28801
28802/**
28803 * @ngdoc directive
28804 * @name select
28805 * @restrict E
28806 *
28807 * @description
28808 * HTML `SELECT` element with angular data-binding.
28809 *
28810 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28811 * between the scope and the `<select>` control (including setting default values).
28812 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28813 * {@link ngOptions `ngOptions`} directives.
28814 *
28815 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28816 * to the model identified by the `ngModel` directive. With static or repeated options, this is
28817 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28818 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28819 *
28820 * <div class="alert alert-warning">
28821 * Note that the value of a `select` directive used without `ngOptions` is always a string.
28822 * When the model needs to be bound to a non-string value, you must either explictly convert it
28823 * using a directive (see example below) or use `ngOptions` to specify the set of options.
28824 * This is because an option element can only be bound to string values at present.
28825 * </div>
28826 *
28827 * If the viewValue of `ngModel` does not match any of the options, then the control
28828 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28829 *
28830 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28831 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28832 * option. See example below for demonstration.
28833 *
28834 * <div class="alert alert-info">
28835 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28836 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benef
28836its, such as
28837 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28838 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
28839 * a new scope for each repeated instance.
28840 * </div>
28841 *
28842 *
28843 * @param {string} ngModel Assignable angular expression to data-bind to.
28844 * @param {string=} name Property name of the form under which the control is published.
28845 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
28846 *     bound to the model as an array.
28847 * @param {string=} required Sets `required` validation error key if the value is not entered.
28848 * @param {string=} ngRequired Adds required attribute and required validation constraint to
28849 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
28850 * when you want to data-bind to the required attribute.
28851 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
28852 *    interaction with the select element.
28853 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
28854 * set on the model on selection. See {@link ngOptions `ngOptions`}.
28855 *
28856 * @example
28857 * ### Simple `select` elements with static options
28858 *
28859 * <example name="static-select" module="staticSelect">
28860 * <file name="index.html">
28861 * <div ng-controller="ExampleController">
28862 *   <form name="myForm">
28863 *     <label for="singleSelect"> Single select: </label><br>
28864 *     <select name="singleSelect" ng-model="data.singleSelect">
28865 *       <option value="option-1">Option 1</option>
28866 *       <option value="option-2">Option 2</option>
28867 *     </select><br>
28868 *
28869 *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
28870 *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
28871 *       <option value="">---Please select---</option> <!-- not selected / blank option -->
28872 *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
28873 *       <option value="option-2">Option 2</option>
28874 *     </select><br>
28875 *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
28876 *     <tt>singleSelect = {{data.singleSelect}}</tt>
28877 *
28878 *     <hr>
28879 *     <label for="multipleSelect"> Multiple select: </label><br>
28880 *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
28881 *       <option value="option-1">Option 1</option>
28882 *       <option value="option-2">Option 2</option>
28883 *       <option value="option-3">Option 3</option>
28884 *     </select><br>
28885 *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
28886 *   </form>
28887 * </div>
28888 * </file>
28889 * <file name="app.js">
28890 *  angular.module('staticSelect', [])
28891 *    .controller('ExampleController', ['$scope', function($scope) {
28892 *      $scope.data = {
28893 *       singleSelect: null,
28894 *       multipleSelect: [],
28895 *       option1: 'option-1',
28896 *      };
28897 *
28898 *      $scope.forceUnknownOption = function() {
28899 *        $scope.data.singleSelect = 'nonsense';
28900 *      };
28901 *   }]);
28902 * </file>
28903 *</example>
28904 *
28905 * ### Using `ngRepeat` to generate `select` options
28906 * <example name="ngrepeat-select" module="ngrepeatSelect">
28907 * <file name="index.html">
28908 * <div ng-controller="ExampleController">
28909 *   <form name="myForm">
28910 *     <label for="repeatSelect"> Repeat select: </label>
28911 *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
28912 *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
28913 *     </select>
28914 *   </form>
28915 *   <hr>
28916 *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
28917 * </div>
28918 * </file>
28919 * <file name="app.js">
28920 *  angular.module('ngrepeatSelect', [])
28921 *    .controller('ExampleController', ['$scope', function($scope) {
28922 *      $scope.data = {
28923 *       repeatSelect: null,
28924 *       availableOptions: [
28925 *         {id: '1', name: 'Option A'},
28926 *         {id: '2', name: 'Option B'},
28927 *         {id: '3', name: 'Option C'}
28928 *       ],
28929 *      };
28930 *   }]);
28931 * </file>
28932 *</example>
28933 *
28934 *
28935 * ### Using `select` with `ngOptions` and setting a default value
28936 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
28937 *
28938 * <example name="select-with-default-values" module="defaultValueSelect">
28939 * <file name="index.html">
28940 * <div ng-controller="ExampleController">
28941 *   <form name="myForm">
28942 *     <label for="mySelect">Make a choice:</label>
28943 *     <select name="mySelect" id="mySelect"
28944 *       ng-options="option.name for option in data.availableOptions track by option.id"
28945 *       ng-model="data.selectedOption"></select>
28946 *   </form>
28947 *   <hr>
28948 *   <tt>option = {{data.selectedOption}}</tt><br/>
28949 * </div>
28950 * </file>
28951 * <file name="app.js">
28952 *  angular.module('defaultValueSelect', [])
28953 *    .controller('ExampleController', ['$scope', function($scope) {
28954 *      $scope.data = {
28955 *       availableOptions: [
28956 *         {id: '1', name: 'Option A'},
28957 *         {id: '2', name: 'Option B'},
28958 *         {id: '3', name: 'Option C'}
28959 *       ],
28960 *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
28961 *       };
28962 *   }]);
28963 * </file>
28964 *</example>
28965 *
28966 *
28967 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
28968 *
28969 * <example name="select-with-non-string-options" module="nonStringSelect">
28970 *   <file name="index.html">
28971 *     <select ng-model="model.id" convert-to-number>
28972 *       <option value="0">Zero</option>
28973 *       <option value="1">One</option>
28974 *       <option value="2">Two</option>
28975 *     </select>
28976 *     {{ model }}
28977 *   </file>
28978 *   <file name="app.js">
28979 *     angular.module('nonStringSelect', [])
28980 *       .run(function($rootScope) {
28981 *         $rootScope.model = { id: 2 };
28982 *       })
28983 *       .directive('convertToNumber', function() {
28984 *         return {
28985 *           require: 'ngModel',
28986 *           link: function(scope, element, attrs, ngModel) {
28987 *             ngModel.$parsers.push(function(val) {
28988 *               return parseInt(val, 10);
28989 *             });
28990 *             ngModel.$formatters.push(function(val) {
28991 *               return '' + val;
28992 *             });
28993 *           }
28994 *         };
28995 *       });
28996 *   </file>
28997 *   <file name="protractor.js" type="protractor">
28998 *     it('should initialize to model', function() {
28999 *       var select = element(by.css('select'));
29000 *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
29001 *     });
29002 *   </file>
29003 * </example>
29004 *
29005 */
29006var selectDirective = function() {
29007
29008  return {
29009    restrict: 'E',
29010    require: ['select', '?ngModel'],
29011    controller: SelectController,
29012    priority: 1,
29013    link: {
29014      pre: selectPreLink,
29015      post: selectPostLink
29016    }
29017  };
29018
29019  function selectPreLink(scope, element, attr, ctrls) {
29020
29021      // if ngModel is not defined, we don't need to do anything
29022      var ngModelCtrl = ctrls[1];
29023      if (!ngModelCtrl) return;
29024
29025      var selectCtrl = ctrls[0];
29026
29027      selectCtrl.ngModelCtrl = ngModelCtrl;
29028
29029      // When the selected item(s) changes we delegate getting the value of the select control
29030      // to the `readValue` method, which can be changed if the select can have multiple
29031      // selected values or if the options are being generated by `ngOptions`
29032      element.on('change', function() {
29033        scope.$apply(function() {
29034          ngModelCtrl.$setViewValue(selectCtrl.readValue());
29035        });
29036      });
29037
29038      // If the select allows multiple values then we need to modify how we read and write
29039      // values from and to the control; also what it means for the value to be empty and
29040      // we have to add an extra watch since ngModel doesn't work well with arrays - it
29041      // doesn't trigger rendering if only an item in the array changes.
29042      if (attr.multiple) {
29043
29044        // Read value now needs to check each option to see if it is selected
29045        selectCtrl.readValue = function readMultipleValue() {
29046          var array = [];
29047          forEach(element.find('option'), function(option) {
29048            if (option.selected) {
29049              array.push(option.value);
29050            }
29051          });
29052          return array;
29053        };
29054
29055        // Write value now needs to set the selected property of each matching option
29056        selectCtrl.writeValue = function writeMultipleValue(value) {
29057          var items = new HashMap(value);
29058          forEach(element.find('option'), function(option) {
29059            option.selected = isDefined(items.get(option.value));
29060          });
29061        };
29062
29063        // we have to do it on each watch since ngModel watches reference, but
29064        // we need to work of an array, so we need to see if anything was inserted/removed
29065        var lastView, lastViewRef = NaN;
29066        scope.$watch(function selectMultipleWatch() {
29067          if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
29068            lastView = shallowCopy(ngModelCtrl.$viewValue);
29069            ngModelCtrl.$render();
29070          }
29071          lastViewRef = ngModelCtrl.$viewValue;
29072        });
29073
29074        // If we are a multiple select then value is now a collection
29075        // so the meaning of $isEmpty changes
29076        ngModelCtrl.$isEmpty = function(value) {
29077          return !value || value.length === 0;
29078        };
29079
29080      }
29081    }
29082
29083    function selectPostLink(scope, element, attrs, ctrls) {
29084      // if ngModel is not defined, we don't need to do anything
29085      var ngModelCtrl = ctrls[1];
29086      if (!ngModelCtrl) return;
29087
29088      var selectCtrl = ctrls[0];
29089
29090      // We delegate rendering to the `writeValue` method, which can be changed
29091      // if the select can have multiple selected values or if the options are being
29092      // generated by `ngOptions`.
29093      // This must be done in the postLink fn to prevent $render to be called before
29094      // all nodes have been linked correctly.
29095      ngModelCtrl.$render = function() {
29096        selectCtrl.writeValue(ngModelCtrl.$viewValue);
29097      };
29098    }
29099};
29100
29101
29102// The option directive is purely designed to communicate the existence (or lack of)
29103// of dynamically created (and destroyed) option elements to their containing select
vendor: 2,837 bytes, lines 29104-29174
29104// directive via its controller.
29105var optionDirective = ['$interpolate', function($interpolate) {
29106  return {
29107    restrict: 'E',
29108    priority: 100,
29109    compile: function(element, attr) {
29110
29111      if (isDefined(attr.value)) {
29112        // If the value attribute is defined, check if it contains an interpolation
29113        var interpolateValueFn = $interpolate(attr.value, true);
29114      } else {
29115        // If the value attribute is not defined then we fall back to the
29116        // text content of the option element, which may be interpolated
29117        var interpolateTextFn = $interpolate(element.text(), true);
29118        if (!interpolateTextFn) {
29119          attr.$set('value', element.text());
29120        }
29121      }
29122
29123      return function(scope, element, attr) {
29124
29125        // This is an optimization over using ^^ since we don't want to have to search
29126        // all the way to the root of the DOM for every single option element
29127        var selectCtrlName = '$selectController',
29128            parent = element.parent(),
29129            selectCtrl = parent.data(selectCtrlName) ||
29130              parent.parent().data(selectCtrlName); // in case we are in optgroup
29131
29132        if (selectCtrl) {
29133          selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
29134        }
29135      };
29136    }
29137  };
29138}];
29139
29140var styleDirective = valueFn({
29141  restrict: 'E',
29142  terminal: false
29143});
29144
29145/**
29146 * @ngdoc directive
29147 * @name ngRequired
29148 *
29149 * @description
29150 *
29151 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29152 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
29153 * applied to custom controls.
29154 *
29155 * The directive sets the `required` attribute on the element if the Angular expression inside
29156 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
29157 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
29158 * for more info.
29159 *
29160 * The validator will set the `required` error key to true if the `required` attribute is set and
29161 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty` with the
29162 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
29163 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
29164 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
29165 *
29166 * @example
29167 * <example name="ngRequiredDirective" module="ngRequiredExample">
29168 *   <file name="index.html">
29169 *     <script>
29170 *       angular.module('ngRequiredExample', [])
29171 *         .controller('ExampleController', ['$scope', function($scope) {
29172 *           $scope.required = true;
29173 *         }]);
29174 *     </script>
29175 *     <div ng-controller="ExampleController">
29176 *       <form name="form">
29177 *         <label for="required">Toggle required: </label>
29178 *         <input type="checkbox" ng-model="required" id="required" />
29179 *         <br>
29180 *         <label for="input">This input must be filled if `required` is true: </label>
29181 *         <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
29182 *         <hr>
29183 *         required error set? = <code>{{form.input.$error.required}}</code><br>
29184 *         model = <code>{{model}}</code>
29185 *       </form>
29186 *     </div>
29187 *   </file>
29188 *   <file name="protractor.js" type="protractor">
29189       var required = element(by.binding('form.input.$error.required'));
29190       var model = element(by.binding('model'));
29191       var input = element(by.id('input'));
29192
29193       it('should set the required error', function() {
29194         expect(required.getText()).toContain('true');
29195
29196         input.sendKeys('123');
29197         expect(required.getText()).not.toContain('true');
29198         expect(model.getText()).toContain('123');
29199       });
29200 *   </file>
29201 * </example>
29202 */
29203var requiredDirective = function() {
29204  return {
29205    restrict: 'A',
29206    require: '?ngModel',
29207    link: function(scope, elm, attr, ctrl) {
29208      if (!ctrl) return;
29209      attr.required = true; // force truthy in case we are on non input element
29210
29211      ctrl.$validators.required = function(modelValue, viewValue) {
29212        return !attr.required || !ctrl.$isEmpty(viewValue);
29213      };
29214
29215      attr.$observe('required', function() {
29216        ctrl.$validate();
29217      });
29218    }
29219  };
29220};
29221
29222/**
29223 * @ngdoc directive
29224 * @name ngPattern
29225 *
29226 * @description
29227 *
29228 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29229 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
29230 *
29231 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
29232 * does not match a RegExp which is obtained by evaluating the Angular expression given in the
29233 * `ngPattern` attribute value:
29234 * * If the expression evaluates to a RegExp object, then this is used directly.
29235 * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
29236 * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
29237 *
29238 * <div class="alert alert-info">
29239 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
29240 * start at the index of the last search's match, thus not taking the whole input value into
29241 * account.
29242 * </div>
29243 *
29244 * <div class="alert alert-info">
29245 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
29246 * differences:
29247 * <ol>
29248 *   <li>
29249 *     `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
29250 *     not available.
29251 *   </li>
29252 *   <li>
29253 *     The `ngPattern` attribute must be an expression, while the `pattern` value must be
29254 *     interpolated.
29255 *   </li>
29256 * </ol>
29257 * </div>
29258 *
29259 * @example
29260 * <example name="ngPatternDirective" module="ngPatternExample">
29261 *   <file name="index.html">
29262 *     <script>
29263 *       angular.module('ngPatternExample', [])
29264 *         .controller('ExampleController', ['$scope', function($scope) {
29265 *           $scope.regex = '\\d+';
29266 *         }]);
29267 *     </script>
29268 *     <div ng-controller="ExampleController">
29269 *       <form name="form">
29270 *         <label for="regex">Set a pattern (regex string): </label>
29271 *         <input type="text" ng-model="regex" id="regex" />
29272 *         <br>
29273 *         <label for="input">This input is restricted by the current pattern: </label>
29274 *         <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
29275 *         <hr>
29276 *         input valid? = <code>{{form.input.$valid}}</code><br>
29277 *         model = <code>{{model}}</code>
29278 *       </form>
29279 *     </div>
29280 *   </file>
29281 *   <file name="protractor.js" type="protractor">
29282       var model = element(by.binding('model'));
29283       var input = element(by.id('input'));
29284
29285       it('should validate the input with the default pattern', function() {
29286         input.sendKeys('aaa');
29287         expect(model.getText()).not.toContain('aaa');
29288
29289         input.clear().then(function() {
29290           input.sendKeys('123');
29291           expect(model.getText()).toContain('123');
29292         });
29293       });
29294 *   </file>
29295 * </example>
29296 */
29297var patternDirective = function() {
29298  return {
29299    restrict: 'A',
29300    require: '?ngModel',
29301    link: function(scope, elm, attr, ctrl) {
29302      if (!ctrl) return;
29303
29304      var regexp, patternExp = attr.ngPattern || attr.pattern;
29305      attr.$observe('pattern', function(regex) {
29306        if (isString(regex) && regex.length > 0) {
29307          regex = new RegExp('^' + regex + '$');
29308        }
29309
29310        if (regex && !regex.test) {
29311          throw minErr('ngPattern')('noregexp',
29312            'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
29313            regex, startingTag(elm));
29314        }
29315
29316        regexp = regex || undefined;
29317        ctrl.$validate();
29318      });
29319
29320      ctrl.$validators.pattern = function(modelValue, viewValue) {
29321        // HTML5 pattern constraint validates the input value, so we validate the viewValue
29322        return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
29323      };
29324    }
29325  };
29326};
29327
29328/**
29329 * @ngdoc directive
29330 * @name ngMaxlength
29331 *
29332 * @description
29333 *
29334 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29335 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
29336 *
29337 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
29338 * is longer than the integer obtained by evaluating the Angular expression given in the
29339 * `ngMaxlength` attribute value.
29340 *
29341 * <div class="alert alert-info">
29342 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
29343 * differences:
29344 * <ol>
29345 *   <li>
29346 *     `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
29347 *     validation is not available.
29348 *   </li>
29349 *   <li>
29350 *     The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
29351 *     interpolated.
29352 *   </li>
29353 * </ol>
29354 * </div>
29355 *
29356 * @example
29357 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
29358 *   <file name="index.html">
29359 *     <script>
29360 *       angular.module('ngMaxlengthExample', [])
29361 *         .controller('ExampleController', ['$scope', function($scope) {
29362 *           $scope.maxlength = 5;
29363 *         }]);
29364 *     </script>
29365 *     <div ng-controller="ExampleController">
29366 *       <form name="form">
29367 *         <label for="maxlength">Set a maxlength: </label>
29368 *         <input type="number" ng-model="maxlength" id="maxlength" />
29369 *         <br>
29370 *         <label for="input">This input is restricted by the current maxlength: </label>
29371 *         <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
29372 *         <hr>
29373 *         input valid? = <code>{{form.input.$valid}}</code><br>
29374 *         model = <code>{{model}}</code>
29375 *       </form>
29376 *     </div>
29377 *   </file>
29378 *   <file name="protractor.js" type="protractor">
29379       var model = element(by.binding('model'));
29380       var input = element(by.id('input'));
29381
29382       it('should validate the input with the default maxlength', function() {
29383         input.sendKeys('abcdef');
29384         expect(model.getText()).not.toContain('abcdef');
29385
29386         input.clear().then(function() {
29387           input.sendKeys('abcde');
29388           expect(model.getText()).toContain('abcde');
29389         });
29390       });
29391 *   </file>
29392 * </example>
29393 */
29394var maxlengthDirective = function() {
29395  return {
29396    restrict: 'A',
29397    require: '?ngModel',
29398    link: function(scope, elm, attr, ctrl) {
29399      if (!ctrl) return;
29400
29401      var maxlength = -1;
29402      attr.$observe('maxlength', function(value) {
29403        var intVal = toInt(value);
29404        maxlength = isNaN(intVal) ? -1 : intVal;
29405        ctrl.$validate();
29406      });
29407      ctrl.$validators.maxlength = function(modelValue, viewValue) {
29408        return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
29409      };
29410    }
29411  };
29412};
29413
29414/**
29415 * @ngdoc directive
29416 * @name ngMinlength
29417 *
29418 * @description
29419 *
29420 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29421 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
29422 *
29423 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
29424 * is shorter than the integer obtained by evaluating the Angular expression given in the
29425 * `ngMinlength` attribute value.
29426 *
29427 * <div class="alert alert-info">
29428 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
29429 * differences:
29430 * <ol>
29431 *   <li>
29432 *     `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
29433 *     validation is not available.
29434 *   </li>
29435 *   <li>
29436 *     The `ngMinlength` value must be an expression, while the `minlength` value must be
29437 *     interpolated.
29438 *   </li>
29439 * </ol>
29440 * </div>
29441 *
29442 * @example
29443 * <example name="ngMinlengthDirective" module="ngMinlengthExample">
29444 *   <file name="index.html">
29445 *     <script>
29446 *       angular.module('ngMinlengthExample', [])
29447 *         .controller('ExampleController', ['$scope', function($scope) {
29448 *           $scope.minlength = 3;
29449 *         }]);
29450 *     </script>
29451 *     <div ng-controller="ExampleController">
29452 *       <form name="form">
29453 *         <label for="minlength">Set a minlength: </label>
29454 *         <input type="number" ng-model="minlength" id="minlength" />
29455 *         <br>
29456 *         <label for="input">This input is restricted by the current minlength: </label>
29457 *         <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
29458 *         <hr>
29459 *         input valid? = <code>{{form.input.$valid}}</code><br>
29460 *         model = <code>{{model}}</code>
29461 *       </form>
29462 *     </div>
29463 *   </file>
29464 *   <file name="protractor.js" type="protractor">
29465       var model = element(by.binding('model'));
29466       var input = element(by.id('input'));
29467
29468       it('should validate the input with the default minlength', function() {
29469         input.sendKeys('ab');
29470         expect(model.getText()).not.toContain('ab');
29471
29472         input.sendKeys('abc');
29473         expect(model.getText()).toContain('abc');
29474       });
29475 *   </file>
29476 * </example>
29477 */
29478var minlengthDirective = function() {
29479  return {
29480    restrict: 'A',
29481    require: '?ngModel',
29482    link: function(scope, elm, attr, ctrl) {
29483      if (!ctrl) return;
29484
29485      var minlength = 0;
29486      attr.$observe('minlength', function(value) {
29487        minlength = toInt(value) || 0;
29488        ctrl.$validate();
29489      });
29490      ctrl.$validators.minlength = function(modelValue, viewValue) {
29491        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
29492      };
29493    }
29494  };
29495};
29496
29497if (window.angular.bootstrap) {
29498  //AngularJS is already loaded, so we can return here...
29499  console.log('WARNING: Tried to load angular more than once.');
29500  return;
29501}
29502
29503//try to bind to jquery now so that one can write jqLite(document).ready()
29504//but we will rebind on bootstrap again.
29505bindJQuery();
29506
29507publishExternalAPI(angular);
29508
29509angular.module("ngLocale", [], ["$provide", function($provide) {
29510var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
29511function getDecimals(n) {
29512  n = n + '';
29513  var i = n.indexOf('.');
29514  return (i == -1) ? 0 : n.length - i - 1;
29515}
29516
29517function getVF(n, opt_precision) {
29518  var v = opt_precision;
29519
29520  if (undefined === v) {
29521    v = Math.min(getDecimals(n), 3);
29522  }
29523
29524  var base = Math.pow(10, v);
29525  var f = ((n * base) | 0) % base;
29526  return {v: v, f: f};
29527}
29528
29529$provide.value("$locale", {
29530  "DATETIME_FORMATS": {
29531    "AMPMS": [
29532      "AM",
29533      "PM"
29534    ],
29535    "DAY": [
29536      "Sunday",
29537      "Monday",
29538      "Tuesday",
29539      "Wednesday",
29540      "Thursday",
29541      "Friday",
29542      "Saturday"
29543    ],
29544    "ERANAMES": [
29545      "Before Christ",
29546      "Anno Domini"
29547    ],
29548    "ERAS": [
29549      "BC",
29550      "AD"
29551    ],
29552    "FIRSTDAYOFWEEK": 6,
29553    "MONTH": [
29554      "January",
29555      "February",
29556      "March",
29557      "April",
29558      "May",
29559      "June",
29560      "July",
29561      "August",
29562      "September",
29563      "October",
29564      "November",
29565      "December"
29566    ],
29567    "SHORTDAY": [
29568      "Sun",
29569      "Mon",
29570      "Tue",
29571      "Wed",
29572      "Thu",
29573      "Fri",
29574      "Sat"
29575    ],
29576    "SHORTMONTH": [
29577      "Jan",
29578      "Feb",
29579      "Mar",
29580      "Apr",
29581      "May",
29582      "Jun",
29583      "Jul",
29584      "Aug",
29585      "Sep",
29586      "Oct",
29587      "Nov",
29588      "Dec"
29589    ],
29590    "STANDALONEMONTH": [
29591      "January",
29592      "February",
29593      "March",
29594      "April",
29595      "May",
29596      "June",
29597      "July",
29598      "August",
29599      "September",
29600      "October",
29601      "November",
29602      "December"
29603    ],
29604    "WEEKENDRANGE": [
29605      5,
29606      6
29607    ],
29608    "fullDate": "EEEE, MMMM d, y",
29609    "longDate": "MMMM d, y",
29610    "medium": "MMM d, y h:mm:ss a",
29611    "mediumDate": "MMM d, y",
29612    "mediumTime": "h:mm:ss a",
29613    "short": "M/d/yy h:mm a",
29614    "shortDate": "M/d/yy",
29615    "shortTime": "h:mm a"
29616  },
29617  "NUMBER_FORMATS": {
29618    "CURRENCY_SYM": "$",
29619    "DECIMAL_SEP": ".",
29620    "GROUP_SEP": ",",
29621    "PATTERNS": [
29622      {
29623        "gSize": 3,
29624        "lgSize": 3,
29625        "maxFrac": 3,
29626        "minFrac": 0,
29627        "minInt": 1,
29628        "negPre": "-",
29629        "negSuf": "",
29630        "posPre": "",
29631        "posSuf": ""
29632      },
29633      {
29634        "gSize": 3,
29635        "lgSize": 3,
29636        "maxFrac": 2,
29637        "minFrac": 2,
29638        "minInt": 1,
29639        "negPre": "-\u00a4",
29640        "negSuf": "",
29641        "posPre": "\u00a4",
29642        "posSuf": ""
29643      }
29644    ]
29645  },
29646  "id": "en-us",
29647  "pluralCat": function(n, opt_precision) {  var i = n | 0;  var vf = getVF(n, opt_precision);  if (i == 1 && vf.v == 0) {    return PLURAL_CATEGORY.ONE;  }  return PLURAL_CATEGORY.OTHER;}
29648});
29649}]);
29650
29651  jqLite(document).ready(function() {
29652    angularInit(document, bootstrap);
29653  });
29654
29655})(window, document);
29656
29657!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
29658/**
29659 * @license AngularJS v1.4.9
29660 * (c) 2010-2015 Google, Inc. http://angularjs.org
29661 * License: MIT
29662 */
29663(function(window, angular, undefined) {'use strict';
29664
29665/**
29666 * @ngdoc module
29667 * @name ngRoute
29668 * @description
29669 *
29670 * # ngRoute
29671 *
29672 * The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
29673 *
29674 * ## Example
29675 * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
29676 *
29677 *
29678 * <div doc-module-components="ngRoute"></div>
29679 */
29680 /* global -ngRouteModule */
29681var ngRouteModule = angular.module('ngRoute', ['ng']).
29682                        provider('$route', $RouteProvider),
29683    $routeMinErr = angular.$$minErr('ngRoute');
29684
29685/**
29686 * @ngdoc provider
29687 * @name $routeProvider
29688 *
29689 * @description
29690 *
29691 * Used for configuring routes.
29692 *
29693 * ## Example
29694 * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
29695 *
29696 * ## Dependencies
29697 * Requires the {@link ngRoute `ngRoute`} module to be installed.
29698 */
29699function $RouteProvider() {
29700  function inherit(parent, extra) {
29701    return angular.extend(Object.create(parent), extra);
29702  }
29703
29704  var routes = {};
29705
29706  /**
29707   * @ngdoc method
29708   * @name $routeProvider#when
29709   *
29710   * @param {string} path Route path (matched against `$location.path`). If `$location.path`
29711   *    contains redundant trailing slash or is missing one, the route will still match and the
29712   *    `$location.path` will be updated to add or drop the trailing slash to exactly match the
29713   *    route definition.
29714   *
29715   *    * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
29716   *        to the next slash are matched and stored in `$routeParams` under the given `name`
29717   *        when the route matches.
29718   *    * `path` can contain named groups starting with a colon and ending with a star:
29719   *        e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
29720   *        when the route matches.
29721   *    * `path` can contain optional named groups with a question mark: e.g.`:name?`.
29722   *
29723   *    For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
29724   *    `/color/brown/largecode/code/with/slashes/edit` and extract:
29725   *
29726   *    * `color: brown`
29727   *    * `largecode: code/with/slashes`.
29728   *
29729   *
29730   * @param {Object} route Mapping information to be assigned to `$route.current` on route
29731   *    match.
29732   *
29733   *    Object properties:
29734   *
29735   *    - `controller` – `{(string|function()=}` – Controller fn that should be associated with
29736   *      newly created scope or the name of a {@link angular.Module#controller registered
29737   *      controller} if passed as a string.
29738   *    - `controllerAs` – `{string=}` – An identifier name for a reference to the controller.
29739   *      If present, the controller will be published to scope under the `controllerAs` name.
29740   *    - `template` – `{string=|function()=}` – html template as a string or a function that
29741   *      returns an html template as a string which should be used by {@link
29742   *      ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
29743   *      This property takes precedence over `templateUrl`.
29744   *
29745   *      If `template` is a function, it will be called with the following parameters:
29746   *
29747   *      - `{Array.<Object>}` - route parameters extracted from the current
29748   *        `$location.path()` by applying the current route
29749   *
29750   *    - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
29751   *      template that should be used by {@link ngRoute.directive:ngView ngView}.
29752   *
29753   *      If `templateUrl` is a function, it will be called with the following parameters:
29754   *
29755   *      - `{Array.<Object>}` - route parameters extracted from the current
29756   *        `$location.path()` by applying the current route
29757   *
29758   *    - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
29759   *      be injected into the controller. If any of these dependencies are promises, the router
29760   *      will wait for them all to be resolved or one to be rejected before the controller is
29761   *      instantiated.
29762   *      If all the promises are resolved successfully, the values of the resolved promises are
29763   *      injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
29764   *      fired. If any of the promises are rejected the
29765   *      {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
29766   *      is:
29767   *
29768   *      - `key` – `{string}`: a name of a dependency to be injected into the controller.
29769   *      - `factory` - `{string|function}`: If `string` then it is an alias for a service.
29770   *        Otherwise if function, then it is {@link auto.$injector#invoke injected}
29771   *        and the return value is treated as the dependency. If the result is a promise, it is
29772   *        resolved before its value is injected into the controller. Be aware that
29773   *        `ngRoute.$routeParams` will still refer to the previous route within these resolve
29774   *        functions.  Use `$route.current.params` to access the new route parameters, instead.
29775   *
29776   *    - `redirectTo` – {(string|function())=} – value to update
29777   *      {@link ng.$location $location} path with and trigger route redirection.
29778   *
29779   *      If `redirectTo` is a function, it will be called with the following parameters:
29780   *
29781   *      - `{Object.<string>}` - route parameters extracted from the current
29782   *        `$location.path()` by applying the current route templateUrl.
29783   *      - `{string}` - current `$location.path()`
29784   *      - `{Object}` - current `$location.search()`
29785   *
29786   *      The custom `redirectTo` function is expected to return a string which will be used
29787   *      to update `$location.path()` and `$location.search()`.
29788   *
29789   *    - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
29790   *      or `$location.hash()` changes.
29791   *
29792   *      If the option is set to `false` and url in the browser changes, then
29793   *      `$routeUpdate` event is broadcasted on the root scope.
29794   *
29795   *    - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
29796   *
29797   *      If the option is set to `true`, then the particular route can be matched without being
29798   *      case sensitive
29799   *
29800   * @returns {Object} self
29801   *
29802   * @description
29803   * Adds a new route definition to the `$route` service.
29804   */
29805  this.when = function(path, route) {
29806    //copy original route object to preserve params inherited from proto chain
29807    var routeCopy = angular.copy(route);
29808    if (angular.isUndefined(routeCopy.reloadOnSearch)) {
29809      routeCopy.reloadOnSearch = true;
29810    }
29811    if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
29812      routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
29813    }
29814    routes[path] = angular.extend(
29815      routeCopy,
29816      path && pathRegExp(path, routeCopy)
29817    );
29818
29819    // create redirection for trailing slashes
29820    if (path) {
29821      var redirectPath = (path[path.length - 1] == '/')
29822            ? path.substr(0, path.length - 1)
29823            : path + '/';
29824
29825      routes[redirectPath] = angular.extend(
29826        {redirectTo: path},
29827        pathRegExp(redirectPath, routeCopy)
29828      );
29829    }
29830
29831    return this;
29832  };
29833
29834  /**
29835   * @ngdoc property
29836   * @name $routeProvider#caseInsensitiveMatch
29837   * @description
29838   *
29839   * A boolean property indicating if routes defined
29840   * using this provider should be matched using a case insensitive
29841   * algorithm. Defaults to `false`.
29842   */
29843  this.caseInsensitiveMatch = false;
29844
29845   /**
29846    * @param path {string} path
29847    * @param opts {Object} options
29848    * @return {?Object}
29849    *
29850    * @description
29851    * Normalizes the given path, returning a regular expression
29852    * and the original path.
29853    *
29854    * Inspired by pathRexp in visionmedia/express/lib/utils.js.
29855    */
29856  function pathRegExp(path, opts) {
29857    var insensitive = opts.caseInsensitiveMatch,
29858        ret = {
29859          originalPath: path,
29860          regexp: path
29861        },
29862        keys = ret.keys = [];
29863
29864    path = path
29865      .replace(/([().])/g, '\\$1')
29866      .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
29867        var optional = option === '?' ? option : null;
29868        var star = option === '*' ? option : null;
29869        keys.push({ name: key, optional: !!optional });
29870        slash = slash || '';
29871        return ''
29872          + (optional ? '' : slash)
29873          + '(?:'
29874          + (optional ? slash : '')
29875          + (star && '(.+?)' || '([^/]+)')
29876          + (optional || '')
29877          + ')'
29878          + (optional || '');
29879      })
29880      .replace(/([\/$\*])/g, '\\$1');
29881
29882    ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
29883    return ret;
29884  }
29885
29886  /**
29887   * @ngdoc method
29888   * @name $routeProvider#otherwise
29889   *
29890   * @description
29891   * Sets route definition that will be used on route change when no other route definition
29892   * is matched.
29893   *
29894   * @param {Object|string} params Mapping information to be assigned to `$route.current`.
29895   * If called with a string, the value maps to `redirectTo`.
29896   * @returns {Object} self
29897   */
29898  this.otherwise = function(params) {
29899    if (typeof params === 'string') {
29900      params = {redirectTo: params};
29901    }
29902    this.when(null, params);
29903    return this;
29904  };
29905
29906
29907  this.$get = ['$rootScope',
29908               '$location',
29909               '$routeParams',
29910               '$q',
29911               '$injector',
29912               '$templateRequest',
29913               '$sce',
29914      function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
29915
29916    /**
29917     * @ngdoc service
29918     * @name $route
29919     * @requires $location
29920     * @requires $routeParams
29921     *
29922     * @property {Object} current Reference to the current route definition.
29923     * The route definition contains:
29924     *
29925     *   - `controller`: The controller constructor as define in route definition.
29926     *   - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
29927     *     controller instantiation. The `locals` contain
29928     *     the resolved values of the `resolve` map. Additionally the `locals` also contain:
29929     *
29930     *     - `$scope` - The current route scope.
29931     *     - `$template` - The current route template HTML.
29932     *
29933     * @property {Object} routes Object with all route configuration Objects as its properties.
29934     *
29935     * @description
29936     * `$route` is used for deep-linking URLs to controllers and views (HTML partials).
29937     * It watches `$location.url()` and tries to map the path to an existing route definition.
29938     *
29939     * Requires the {@link ngRoute `ngRoute`} module to be installed.
29940     *
29941     * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
29942     *
29943     * The `$route` service is typically used in conjunction with the
29944     * {@link ngRoute.directive:ngView `ngView`} directive and the
29945     * {@link ngRoute.$routeParams `$routeParams`} service.
29946     *
29947     * @example
29948     * This example shows how changing the URL hash causes the `$route` to match a route against the
29949     * URL, and the `ngView` pulls in the partial.
29950     *
29951     * <example name="$route-service" module="ngRouteExample"
29952     *          deps="angular-route.js" fixBase="true">
29953     *   <file name="index.html">
29954     *     <div ng-controller="MainController">
29955     *       Choose:
29956     *       <a href="Book/Moby">Moby</a> |
29957     *       <a href="Book/Moby/ch/1">Moby: Ch1</a> |
29958     *       <a href="Book/Gatsby">Gatsby</a> |
29959     *       <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
29960     *       <a href="Book/Scarlet">Scarlet Letter</a><br/>
29961     *
29962     *       <div ng-view></div>
29963     *
29964     *       <hr />
29965     *
29966     *       <pre>$location.path() = {{$location.path()}}</pre>
29967     *       <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
29968     *       <pre>$route.current.params = {{$route.current.params}}</pre>
29969     *       <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
29970     *       <pre>$routeParams = {{$routeParams}}</pre>
29971     *     </div>
29972     *   </file>
29973     *
29974     *   <file name="book.html">
29975     *     controller: {{name}}<br />
29976     *     Book Id: {{params.bookId}}<br />
29977     *   </file>
29978     *
29979     *   <file name="chapter.html">
29980     *     controller: {{name}}<br />
29981     *     Book Id: {{params.bookId}}<br />
29982     *     Chapter Id: {{params.chapterId}}
29983     *   </file>
29984     *
29985     *   <file name="script.js">
29986     *     angular.module('ngRouteExample', ['ngRoute'])
29987     *
29988     *      .controller('MainController', function($scope, $route, $routeParams, $location) {
29989     *          $scope.$route = $route;
29990     *          $scope.$location = $location;
29991     *          $scope.$routeParams = $routeParams;
29992     *      })
29993     *
29994     *      .controller('BookController', function($scope, $routeParams) {
29995     *          $scope.name = "BookController";
29996     *          $scope.params = $routeParams;
29997     *      })
29998     *
29999     *      .controller('ChapterController', function($scope, $routeParams) {
30000     *          $scope.name = "ChapterController";
30001     *          $scope.params = $routeParams;
30002     *      })
30003     *
30004     *     .config(function($routeProvider, $locationProvider) {
30005     *       $routeProvider
30006     *        .when('/Book/:bookId', {
30007     *         templateUrl: 'book.html',
30008     *         controller: 'BookController',
30009     *         resolve: {
30010     *           // I will cause a 1 second delay
30011     *           delay: function($q, $timeout) {
30012     *             var delay = $q.defer();
30013     *             $timeout(delay.resolve, 1000);
30014     *             return delay.promise;
30015     *           }
30016     *         }
30017     *       })
30018     *       .when('/Book/:bookId/ch/:chapterId', {
30019     *         templateUrl: 'chapter.html',
30020     *         controller: 'ChapterController'
30021     *       });
30022     *
30023     *       // configure html5 to get links working on jsfiddle
30024     *       $locationProvider.html5Mode(true);
30025     *     });
30026     *
30027     *   </file>
30028     *
30029     *   <file name="protractor.js" type="protractor">
30030     *     it('should load and compile correct template', function() {
30031     *       element(by.linkText('Moby: Ch1')).click();
30032     *       var content = element(by.css('[ng-view]')).getText();
30033     *       expect(content).toMatch(/controller\: ChapterController/);
30034     *       expect(content).toMatch(/Book Id\: Moby/);
30035     *       expect(content).toMatch(/Chapter Id\: 1/);
30036     *
30037     *       element(by.partialLinkText('Scarlet')).click();
30038     *
30039     *       content = element(by.css('[ng-view]')).getText();
30040     *       expect(content).toMatch(/controller\: BookController/);
30041     *       expect(content).toMatch(/Book Id\: Scarlet/);
30042     *     });
30043     *   </file>
30044     * </example>
30045     */
30046
30047    /**
30048     * @ngdoc event
30049     * @name $route#$routeChangeStart
30050     * @eventType broadcast on root scope
30051     * @description
30052     * Broadcasted before a route change. At this  point the route services starts
30053     * resolving all of the dependencies needed for the route change to occur.
30054     * Typically this involves fetching the view template as well as any dependencies
30055     * defined in `resolve` route property. Once  all of the dependencies are resolved
30056     * `$routeChangeSuccess` is fired.
30057     *
30058     * The route change (and the `$location` change that triggered it) can be prevented
30059     * by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
30060     * for more details about event object.
30061     *
30062     * @param {Object} angularEvent Synthetic event object.
30063     * @param {Route} next Future route information.
30064     * @param {Route} current Current route information.
30065     */
30066
30067    /**
30068     * @ngdoc event
30069     * @name $route#$routeChangeSuccess
30070     * @eventType broadcast on root scope
30071     * @description
30072     * Broadcasted after a route change has happened successfully.
30073     * The `resolve` dependencies are now available in the `current.locals` property.
30074     *
30075     * {@link ngRoute.directive:ngView ngView} listens for the directive
30076     * to instantiate the controller and render the view.
30077     *
30078     * @param {Object} angularEvent Synthetic event object.
30079     * @param {Route} current Current route information.
30080     * @param {Route|Undefined} previous Previous route information, or undefined if current is
30081     * first route entered.
30082     */
30083
30084    /**
30085     * @ngdoc event
30086     * @name $route#$routeChangeError
30087     * @eventType broadcast on root scope
30088     * @description
30089     * Broadcasted if any of the resolve promises are rejected.
30090     *
30091     * @param {Object} angularEvent Synthetic event object
30092     * @param {Route} current Current route information.
30093     * @param {Route} previous Previous route information.
30094     * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
30095     */
30096
30097    /**
30098     * @ngdoc event
30099     * @name $route#$routeUpdate
30100     * @eventType broadcast on root scope
30101     * @description
30102     * The `reloadOnSearch` property has been set to false, and we are reusing the same
30103     * instance of the Controller.
30104     *
30105     * @param {Object} angularEvent Synthetic event object
30106     * @param {Route} current Current/previous route information.
30107     */
30108
30109    var forceReload = false,
30110        preparedRoute,
30111        preparedRouteIsUpdateOnly,
30112        $route = {
30113          routes: routes,
30114
30115          /**
30116           * @ngdoc method
30117           * @name $route#reload
30118           *
30119           * @description
30120           * Causes `$route` service to reload the current route even if
30121           * {@link ng.$location $location} hasn't changed.
30122           *
30123           * As a result of that, {@link ngRoute.directive:ngView ngView}
30124           * creates new scope and reinstantiates the controller.
30125           */
30126          reload: function() {
30127            forceReload = true;
30128            $rootScope.$evalAsync(function() {
30129              // Don't support cancellation of a reload for now...
30130              prepareRoute();
30131              commitRoute();
30132            });
30133          },
30134
30135          /**
30136           * @ngdoc method
30137           * @name $route#updateParams
30138           *
30139           * @description
30140           * Causes `$route` service to update the current URL, replacing
30141           * current route parameters with those specified in `newParams`.
30142           * Provided property names that match the route's path segment
30143           * definitions will be interpolated into the location's path, while
30144           * remaining properties will be treated as query params.
30145           *
30146           * @param {!Object<string, string>} newParams mapping of URL parameter names to values
30147           */
30148          updateParams: function(newParams) {
30149            if (this.current && this.current.$$route) {
30150              newParams = angular.extend({}, this.current.params, newParams);
30151              $location.path(interpolate(this.current.$$route.originalPath, newParams));
30152              // interpolate modifies newParams, only query params are left
30153              $location.search(newParams);
30154            } else {
30155              throw $routeMinErr('norout', 'Tried updating route when with no current route');
30156            }
30157          }
30158        };
30159
30160    $rootScope.$on('$locationChangeStart', prepareRoute);
30161    $rootScope.$on('$locationChangeSuccess', commitRoute);
30162
30163    return $route;
30164
30165    /////////////////////////////////////////////////////
30166
30167    /**
30168     * @param on {string} current url
30169     * @param route {Object} route regexp to match the url against
30170     * @return {?Object}
30171     *
30172     * @description
30173     * Check if the route matches the current url.
30174     *
30175     * Inspired by match in
30176     * visionmedia/express/lib/router/router.js.
30177     */
30178    function switchRouteMatcher(on, route) {
30179      var keys = route.keys,
30180          params = {};
30181
30182      if (!route.regexp) return null;
30183
30184      var m = route.regexp.exec(on);
30185      if (!m) return null;
30186
30187      for (var i = 1, len = m.length; i < len; ++i) {
30188        var key = keys[i - 1];
30189
30190        var val = m[i];
30191
30192        if (key && val) {
30193          params[key.name] = val;
30194        }
30195      }
30196      return params;
30197    }
30198
30199    function prepareRoute($locationEvent) {
30200      var lastRoute = $route.current;
30201
30202      preparedRoute = parseRoute();
30203      preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
30204          && angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
30205          && !preparedRoute.reloadOnSearch && !forceReload;
30206
30207      if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
30208        if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
30209          if ($locationEvent) {
30210            $locationEvent.preventDefault();
30211          }
30212        }
30213      }
30214    }
30215
30216    function commitRoute() {
30217      var lastRoute = $route.current;
30218      var nextRoute = preparedRoute;
30219
30220      if (preparedRouteIsUpdateOnly) {
30221        lastRoute.params = nextRoute.params;
30222        angular.copy(lastRoute.params, $routeParams);
30223        $rootScope.$broadcast('$routeUpdate', lastRoute);
30224      } else if (nextRoute || lastRoute) {
30225        forceReload = false;
30226        $route.current = nextRoute;
30227        if (nextRoute) {
30228          if (nextRoute.redirectTo) {
30229            if (angular.isString(nextRoute.redirectTo)) {
30230              $location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
30231                       .replace();
30232            } else {
30233              $location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
30234                       .replace();
30235            }
30236          }
30237        }
30238
30239        $q.when(nextRoute).
30240          then(function() {
30241            if (nextRoute) {
30242              var locals = angular.extend({}, nextRoute.resolve),
30243                  template, templateUrl;
30244
30245              angular.forEach(locals, function(value, key) {
30246                locals[key] = angular.isString(value) ?
30247                    $injector.get(value) : $injector.invoke(value, null, null, key);
30248              });
30249
30250              if (angular.isDefined(template = nextRoute.template)) {
30251                if (angular.isFunction(template)) {
30252                  template = template(nextRoute.params);
30253                }
30254              } else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
30255                if (angular.isFunction(templateUrl)) {
30256                  templateUrl = templateUrl(nextRoute.params);
30257                }
30258                if (angular.isDefined(templateUrl)) {
30259                  nextRoute.loadedTemplateUrl = $sce.valueOf(templateUrl);
30260                  template = $templateRequest(templateUrl);
30261                }
30262              }
30263              if (angular.isDefined(template)) {
30264                locals['$template'] = template;
30265              }
30266              return $q.all(locals);
30267            }
30268          }).
30269          then(function(locals) {
30270            // after route change
30271            if (nextRoute == $route.current) {
30272              if (nextRoute) {
30273                nextRoute.locals = locals;
vendor: 4,780 bytes, lines 30274-30408
30274                angular.copy(nextRoute.params, $routeParams);
30275              }
30276              $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
30277            }
30278          }, function(error) {
30279            if (nextRoute == $route.current) {
30280              $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
30281            }
30282          });
30283      }
30284    }
30285
30286
30287    /**
30288     * @returns {Object} the current active route, by matching it against the URL
30289     */
30290    function parseRoute() {
30291      // Match a route
30292      var params, match;
30293      angular.forEach(routes, function(route, path) {
30294        if (!match && (params = switchRouteMatcher($location.path(), route))) {
30295          match = inherit(route, {
30296            params: angular.extend({}, $location.search(), params),
30297            pathParams: params});
30298          match.$$route = route;
30299        }
30300      });
30301      // No route matched; fallback to "otherwise" route
30302      return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
30303    }
30304
30305    /**
30306     * @returns {string} interpolation of the redirect path with the parameters
30307     */
30308    function interpolate(string, params) {
30309      var result = [];
30310      angular.forEach((string || '').split(':'), function(segment, i) {
30311        if (i === 0) {
30312          result.push(segment);
30313        } else {
30314          var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
30315          var key = segmentMatch[1];
30316          result.push(params[key]);
30317          result.push(segmentMatch[2] || '');
30318          delete params[key];
30319        }
30320      });
30321      return result.join('');
30322    }
30323  }];
30324}
30325
30326ngRouteModule.provider('$routeParams', $RouteParamsProvider);
30327
30328
30329/**
30330 * @ngdoc service
30331 * @name $routeParams
30332 * @requires $route
30333 *
30334 * @description
30335 * The `$routeParams` service allows you to retrieve the current set of route parameters.
30336 *
30337 * Requires the {@link ngRoute `ngRoute`} module to be installed.
30338 *
30339 * The route parameters are a combination of {@link ng.$location `$location`}'s
30340 * {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}.
30341 * The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
30342 *
30343 * In case of parameter name collision, `path` params take precedence over `search` params.
30344 *
30345 * The service guarantees that the identity of the `$routeParams` object will remain unchanged
30346 * (but its properties will likely change) even when a route change occurs.
30347 *
30348 * Note that the `$routeParams` are only updated *after* a route change completes successfully.
30349 * This means that you cannot rely on `$routeParams` being correct in route resolve functions.
30350 * Instead you can use `$route.current.params` to access the new route's parameters.
30351 *
30352 * @example
30353 * ```js
30354 *  // Given:
30355 *  // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
30356 *  // Route: /Chapter/:chapterId/Section/:sectionId
30357 *  //
30358 *  // Then
30359 *  $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
30360 * ```
30361 */
30362function $RouteParamsProvider() {
30363  this.$get = function() { return {}; };
30364}
30365
30366ngRouteModule.directive('ngView', ngViewFactory);
30367ngRouteModule.directive('ngView', ngViewFillContentFactory);
30368
30369
30370/**
30371 * @ngdoc directive
30372 * @name ngView
30373 * @restrict ECA
30374 *
30375 * @description
30376 * # Overview
30377 * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
30378 * including the rendered template of the current route into the main layout (`index.html`) file.
30379 * Every time the current route changes, the included view changes with it according to the
30380 * configuration of the `$route` service.
30381 *
30382 * Requires the {@link ngRoute `ngRoute`} module to be installed.
30383 *
30384 * @animations
30385 * enter - animation is used to bring new content into the browser.
30386 * leave - animation is used to animate existing content away.
30387 *
30388 * The enter and leave animation occur concurrently.
30389 *
30390 * @scope
30391 * @priority 400
30392 * @param {string=} onload Expression to evaluate whenever the view updates.
30393 *
30394 * @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
30395 *                  $anchorScroll} to scroll the viewport after the view is updated.
30396 *
30397 *                  - If the attribute is not set, disable scrolling.
30398 *                  - If the attribute is set without value, enable scrolling.
30399 *                  - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
30400 *                    as an expression yields a truthy value.
30401 * @example
30402    <example name="ngView-directive" module="ngViewExample"
30403             deps="angular-route.js;angular-animate.js"
30404             animations="true" fixBase="true">
30405      <file name="index.html">
30406        <div ng-controller="MainCtrl as main">
30407          Choose:
30408          <a href="Book/Moby">Moby</a> |
30409          <a href="Book/Moby/ch/1">Moby: Ch1</a> |
30410          <a href="Book/Gatsby">Gatsby</a> |
30411          <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
30412          <a href="Book/Scarlet">Scarlet Letter</a><br/>
30413
30414          <div class="view-animate-container">
30415            <div ng-view class="view-animate"></div>
30416          </div>
30417          <hr />
30418
30419          <pre>$location.path() = {{main.$location.path()}}</pre>
30420          <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
30421          <pre>$route.current.params = {{main.$route.current.params}}</pre>
30422          <pre>$routeParams = {{main.$routeParams}}</pre>
30423        </div>
30424      </file>
30425
30426      <file name="book.html">
30427        <div>
30428          controller: {{book.name}}<br />
30429          Book Id: {{book.params.bookId}}<br />
30430        </div>
30431      </file>
30432
30433      <file name="chapter.html">
30434        <div>
30435          controller: {{chapter.name}}<br />
30436          Book Id: {{chapter.params.bookId}}<br />
30437          Chapter Id: {{chapter.params.chapterId}}
30438        </div>
30439      </file>
30440
30441      <file name="animations.css">
30442        .view-animate-container {
30443          position:relative;
30444          height:100px!important;
30445          background:white;
30446          border:1px solid black;
30447          height:40px;
30448          overflow:hidden;
30449        }
30450
30451        .view-animate {
30452          padding:10px;
30453        }
30454
30455        .view-animate.ng-enter, .view-animate.ng-leave {
30456          transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
30457
30458          display:block;
30459          width:100%;
30460          border-left:1px solid black;
30461
30462          position:absolute;
30463          top:0;
30464          left:0;
30465          right:0;
30466          bottom:0;
30467          padding:10px;
30468        }
30469
30470        .view-animate.ng-enter {
30471          left:100%;
30472        }
30473        .view-animate.ng-enter.ng-enter-active {
30474          left:0;
30475        }
30476        .view-animate.ng-leave.ng-leave-active {
30477          left:-100%;
30478        }
30479      </file>
30480
30481      <file name="script.js">
30482        angular.module('ngViewExample', ['ngRoute', 'ngAnimate'])
30483          .config(['$routeProvider', '$locationProvider',
30484            function($routeProvider, $locationProvider) {
30485              $routeProvider
30486                .when('/Book/:bookId', {
30487                  templateUrl: 'book.html',
30488                  controller: 'BookCtrl',
30489                  controllerAs: 'book'
30490                })
30491                .when('/Book/:bookId/ch/:chapterId', {
30492                  templateUrl: 'chapter.html',
30493                  controller: 'ChapterCtrl',
30494                  controllerAs: 'chapter'
30495                });
30496
30497              $locationProvider.html5Mode(true);
30498          }])
30499          .controller('MainCtrl', ['$route', '$routeParams', '$location',
30500            function($route, $routeParams, $location) {
30501              this.$route = $route;
30502              this.$location = $location;
30503              this.$routeParams = $routeParams;
30504          }])
30505          .controller('BookCtrl', ['$routeParams', function($routeParams) {
30506            this.name = "BookCtrl";
30507            this.params = $routeParams;
30508          }])
30509          .controller('ChapterCtrl', ['$routeParams', function($routeParams) {
30510            this.name = "ChapterCtrl";
30511            this.params = $routeParams;
30512          }]);
30513
30514      </file>
30515
30516      <file name="protractor.js" type="protractor">
30517        it('should load and compile correct template', function() {
30518          element(by.linkText('Moby: Ch1')).click();
30519          var content = element(by.css('[ng-view]')).getText();
30520          expect(content).toMatch(/controller\: ChapterCtrl/);
30521          expect(content).toMatch(/Book Id\: Moby/);
30522          expect(content).toMatch(/Chapter Id\: 1/);
30523
30524          element(by.partialLinkText('Scarlet')).click();
30525
30526          content = element(by.css('[ng-view]')).getText();
30527          expect(content).toMatch(/controller\: BookCtrl/);
30528          expect(content).toMatch(/Book Id\: Scarlet/);
30529        });
30530      </file>
30531    </example>
30532 */
30533
30534
30535/**
30536 * @ngdoc event
30537 * @name ngView#$viewContentLoaded
30538 * @eventType emit on the current ngView scope
30539 * @description
30540 * Emitted every time the ngView content is reloaded.
30541 */
30542ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
30543function ngViewFactory($route, $anchorScroll, $animate) {
30544  return {
30545    restrict: 'ECA',
30546    terminal: true,
30547    priority: 400,
30548    transclude: 'element',
30549    link: function(scope, $element, attr, ctrl, $transclude) {
30550        var currentScope,
30551            currentElement,
30552            previousLeaveAnimation,
30553            autoScrollExp = attr.autoscroll,
30554            onloadExp = attr.onload || '';
30555
30556        scope.$on('$routeChangeSuccess', update);
30557        update();
30558
30559        function cleanupLastView() {
30560          if (previousLeaveAnimation) {
30561            $animate.cancel(previousLeaveAnimation);
30562            previousLeaveAnimation = null;
30563          }
30564
30565          if (currentScope) {
30566            currentScope.$destroy();
30567            currentScope = null;
30568          }
30569          if (currentElement) {
30570            previousLeaveAnimation = $animate.leave(currentElement);
30571            previousLeaveAnimation.then(function() {
30572              previousLeaveAnimation = null;
30573            });
30574            currentElement = null;
30575          }
30576        }
30577
30578        function update() {
30579          var locals = $route.current && $route.current.locals,
30580              template = locals && locals.$template;
30581
30582          if (angular.isDefined(template)) {
30583            var newScope = scope.$new();
30584            var current = $route.current;
30585
30586            // Note: This will also link all children of ng-view that were contained in the original
30587            // html. If that content contains controllers, ... they could pollute/change the scope.
30588            // However, using ng-view on an element with additional content does not make sense...
30589            // Note: We can't remove them in the cloneAttchFn of $transclude as that
30590            // function is called before linking the content, which would apply child
30591            // directives to non existing elements.
30592            var clone = $transclude(newScope, function(clone) {
30593              $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
30594                if (angular.isDefined(autoScrollExp)
30595                  && (!autoScrollExp || scope.$eval(autoScrollExp))) {
30596                  $anchorScroll();
30597                }
30598              });
30599              cleanupLastView();
30600            });
30601
30602            currentElement = clone;
30603            currentScope = current.scope = newScope;
30604            currentScope.$emit('$viewContentLoaded');
30605            currentScope.$eval(onloadExp);
30606          } else {
30607            cleanupLastView();
30608          }
30609        }
30610    }
30611  };
30612}
30613
30614// This directive is called during the $transclude call of the first `ngView` directive.
30615// It will replace and compile the content of the element with the loaded template.
30616// We need this directive so that the element content is already filled when
30617// the link function of another directive on the same element as ngView
30618// is called.
30619ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
30620function ngViewFillContentFactory($compile, $controller, $route) {
30621  return {
30622    restrict: 'ECA',
30623    priority: -400,
30624    link: function(scope, $element) {
30625      var current = $route.current,
30626          locals = current.locals;
30627
30628      $element.html(locals.$template);
30629
30630      var link = $compile($element.contents());
30631
30632      if (current.controller) {
30633        locals.$scope = scope;
30634        var controller = $controller(current.controller, locals);
30635        if (current.controllerAs) {
30636          scope[current.controllerAs] = controller;
30637        }
30638        $element.data('$ngControllerController', controller);
30639        $element.children().data('$ngControllerController', controller);
30640      }
30641
30642      link(scope);
30643    }
30644  };
30645}
30646
30647
30648})(window, window.angular);
30649
30650/**
30651 * An Angular module that gives you access to the browsers local storage
30652 * @version v0.2.5 - 2016-02-23
30653 * @link https://github.com/grevory/angular-local-storage
30654 * @author grevory <[email protected]>
30655 * @license MIT License, http://www.opensource.org/licenses/MIT
30656 */
30657(function (window, angular) {
30658var isDefined = angular.isDefined,
30659  isUndefined = angular.isUndefined,
30660  isNumber = angular.isNumber,
30661  isObject = angular.isObject,
30662  isArray = angular.isArray,
30663  extend = angular.extend,
30664  toJson = angular.toJson;
30665
30666angular
30667  .module('LocalStorageModule', [])
30668  .provider('localStorageService', function() {
30669    // You should set a prefix to avoid overwriting any local storage variables from the rest of your app
30670    // e.g. localStorageServiceProvider.setPrefix('yourAppName');
30671    // With provider you can use config as this:
30672    // myApp.config(function (localStorageServiceProvider) {
30673    //    localStorageServiceProvider.prefix = 'yourAppName';
30674    // });
30675    this.prefix = 'ls';
30676
30677    // You could change web storage type localstorage or sessionStorage
30678    this.storageType = 'localStorage';
30679
30680    // Cookie options (usually in case of fallback)
30681    // expiry = Number of days before cookies expire // 0 = Does not expire
30682    // path = The web path the cookie represents
30683    this.cookie = {
30684      expiry: 30,
30685      path: '/'
30686    };
30687
30688    // Send signals for each of the following actions?
30689    this.notify = {
30690      setItem: true,
30691      removeItem: false
30692    };
30693
30694    // Setter for the prefix
30695    this.setPrefix = function(prefix) {
30696      this.prefix = prefix;
30697      return this;
30698    };
30699
30700    // Setter for the storageType
30701    this.setStorageType = function(storageType) {
30702      this.storageType = storageType;
30703      return this;
30704    };
30705
30706    // Setter for cookie config
30707    this.setStorageCookie = function(exp, path) {
30708      this.cookie.expiry = exp;
30709      this.cookie.path = path;
30710      return this;
30711    };
30712
30713    // Setter for cookie domain
30714    this.setStorageCookieDomain = function(domain) {
30715      this.cookie.domain = domain;
30716      return this;
30717    };
30718
30719    // Setter for notification config
30720    // itemSet & itemRemove should be booleans
30721    this.setNotify = function(itemSet, itemRemove) {
30722      this.notify = {
30723        setItem: itemSet,
30724        removeItem: itemRemove
30725      };
30726      return this;
30727    };
30728
30729    this.$get = ['$rootScope', '$window', '$document', '$parse', function($rootScope, $window, $document, $parse) {
30730      var self = this;
30731      var prefix = self.prefix;
30732      var cookie = self.cookie;
30733      var notify = self.notify;
30734      var storageType = self.storageType;
30735      var webStorage;
30736
30737      // When Angular's $document is not available
30738      if (!$document) {
30739        $document = document;
30740      } else if ($document[0]) {
30741        $document = $document[0];
30742      }
30743
30744      // If there is a prefix set in the config lets use that with an appended period for readability
30745      if (prefix.substr(-1) !== '.') {
30746        prefix = !!prefix ? prefix + '.' : '';
30747      }
30748      var deriveQualifiedKey = function(key) {
30749        return prefix + key;
30750      };
30751      // Checks the browser to see if local storage is supported
30752      var browserSupportsLocalStorage = (function () {
30753        try {
30754          var supported = (storageType in $window && $window[storageType] !== null);
30755
30756          // When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage
30757          // is available, but trying to call .setItem throws an exception.
30758          //
30759          // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage
30760          // that exceeded the quota."
30761          var key = deriveQualifiedKey('__' + Math.round(Math.random() * 1e7));
30762          if (supported) {
30763            webStorage = $window[storageType];
30764            webStorage.setItem(key, '');
30765            webStorage.removeItem(key);
30766          }
30767
30768          return supported;
30769        } catch (e) {
30770          storageType = 'cookie';
30771          $rootScope.$broadcast('LocalStorageModule.notification.error', e.message);
30772          return false;
30773        }
30774      }());
30775
30776      // Directly adds a value to local storage
30777      // If local storage is not available in the browser use cookies
30778      // Example use: localStorageService.add('library','angular');
30779      var addToLocalStorage = function (key, value) {
30780        // Let's convert undefined values to null to get the value consistent
30781        if (isUndefined(value)) {
30782          value = null;
30783        } else {
30784          value = toJson(value);
30785        }
30786
30787        // If this browser does not support local storage use cookies
30788        if (!browserSupportsLocalStorage || self.storageType === 'cookie') {
30789          if (!browserSupportsLocalStorage) {
30790            $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED');
30791          }
30792
30793          if (notify.setItem) {
30794            $rootScope.$broadcast('LocalStorageModule.notification.setitem', {key: key, newvalue: value, storageType: 'cookie'});
30795          }
30796          return addToCookies(key, value);
30797        }
30798
30799        try {
30800          if (webStorage) {
30801            webStorage.setItem(deriveQualifiedKey(key), value);
30802          }
30803          if (notify.setItem) {
30804            $rootScope.$broadcast('LocalStorageModule.notification.setitem', {key: key, newvalue: value, storageType: self.storageType});
30805          }
30806        } catch (e) {
30807          $rootScope.$broadcast('LocalStorageModule.notification.error', e.message);
30808          return addToCookies(key, value);
30809        }
30810        return true;
30811      };
30812
30813      // Directly get a value from local storage
30814      // Example use: localStorageService.get('library'); // returns 'angular'
30815      var getFromLocalStorage = function (key) {
30816
30817        if (!browserSupportsLocalStorage || self.storageType === 'cookie') {
30818          if (!browserSupportsLocalStorage) {
30819            $rootScope.$broadcast('LocalStorageModule.notification.warning','LOCAL_STORAGE_NOT_SUPPORTED');
30820          }
30821
30822          return getFromCookies(key);
30823        }
30824
30825        var item = webStorage ? webStorage.getItem(deriveQualifiedKey(key)) : null;
30826        // angular.toJson will convert null to 'null', so a proper conversion is needed
30827        // FIXME not a perfect solution, since a valid 'null' string can't be stored
30828        if (!item || item === 'null') {
30829          return null;
30830        }
30831
30832        try {
30833          return JSON.parse(item);
30834        } catch (e) {
30835          return item;
30836        }
30837      };
30838
30839      // Remove an item from local storage
30840      // Example use: localStorageService.remove('library'); // removes the key/value pair of library='angular'
30841      var removeFromLocalStorage = function () {
30842        var i, key;
30843        for (i=0; i<arguments.length; i++) {
30844          key = arguments[i];
30845          if (!browserSupportsLocalStorage || self.storageType === 'cookie') {
30846            if (!browserSupportsLocalStorage) {
30847              $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED');
30848            }
30849
30850            if (notify.removeItem) {
30851              $rootScope.$broadcast('LocalStorageModule.notification.removeitem', {key: key, storageType: 'cookie'});
30852            }
30853            removeFromCookies(key);
30854          }
30855          else {
30856            try {
30857              webStorage.removeItem(deriveQualifiedKey(key));
30858              if (notify.removeItem) {
30859                $rootScope.$broadcast('LocalStorageModule.notification.removeitem', {
30860                  key: key,
30861                  storageType: self.storageType
30862                });
30863              }
30864            } catch (e) {
30865              $rootScope.$broadcast('LocalStorageModule.notification.error', e.message);
30866              removeFromCookies(key);
30867            }
30868          }
30869        }
30870      };
30871
30872      // Return array of keys for local storage
30873      // Example use: var keys = localStorageService.keys()
30874      var getKeysForLocalStorage = function () {
30875
30876        if (!browserSupportsLocalStorage) {
30877          $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED');
30878          return false;
30879        }
30880
30881        var prefixLength = prefix.length;
30882        var keys = [];
30883        for (var key in webStorage) {
30884          // Only return keys that are for this app
30885          if (key.substr(0,prefixLength) === prefix) {
30886            try {
30887              keys.push(key.substr(prefixLength));
30888            } catch (e) {
30889              $rootScope.$broadcast('LocalStorageModule.notification.error', e.Description);
30890              return [];
30891            }
30892          }
30893        }
30894        return keys;
30895      };
30896
30897      // Remove all data for this app from local storage
30898      // Also optionally takes a regular expression string and removes the matching key-value pairs
30899      // Example use: localStorageService.clearAll();
30900      // Should be used mostly for development purposes
30901      var clearAllFromLocalStorage = function (regularExpression) {
30902
30903        // Setting both regular expressions independently
30904        // Empty strings result in catchall RegExp
30905        var prefixRegex = !!prefix ? new RegExp('^' + prefix) : new RegExp();
30906        var testRegex = !!regularExpression ? new RegExp(regularExpression) : new RegExp();
30907
30908        if (!browserSupportsLocalStorage || self.storageType === 'cookie') {
30909          if (!browserSupportsLocalStorage) {
30910            $rootScope.$broadcast('LocalStorageModule.notification.warning', 'LOCAL_STORAGE_NOT_SUPPORTED');
30911          }
30912          return clearAllFromCookies();
30913        }
30914
30915        var prefixLength = prefix.length;
30916
30917        for (var key in webStorage) {
30918          // Only remove items that are for this app and match the regular expression
30919          if (prefixRegex.test(key) && testRegex.test(key.substr(prefixLength))) {
30920            try {
30921              removeFromLocalStorage(key.substr(prefixLength));
30922            } catch (e) {
30923              $rootScope.$broadcast('LocalStorageModule.notification.error',e.message);
30924              return clearAllFromCookies();
30925            }
30926          }
30927        }
30928        return true;
30929      };
30930
30931      // Checks the browser to see if cookies are supported
30932      var browserSupportsCookies = (function() {
30933        try {
30934          return $window.navigator.cookieEnabled ||
30935          ("cookie" in $document && ($document.cookie.length > 0 ||
30936            ($document.cookie = "test").indexOf.call($document.cookie, "test") > -1));
30937          } catch (e) {
30938            $rootScope.$broadcast('LocalStorageModule.notification.error', e.message);
30939            return false;
30940          }
30941        }());
30942
30943        // Directly adds a value to cookies
30944        // Typically used as a fallback is local storage is not available in the browser
30945        // Example use: localStorageService.cookie.add('library','angular');
30946        var addToCookies = function (key, value, daysToExpiry) {
30947
30948          if (isUndefined(value)) {
30949            return false;
30950          } else if(isArray(value) || isObject(value)) {
30951            value = toJson(value);
30952          }
30953
30954          if (!browserSupportsCookies) {
30955            $rootScope.$broadcast('LocalStorageModule.notification.error', 'COOKIES_NOT_SUPPORTED');
30956            return false;
30957          }
30958
30959          try {
30960            var expiry = '',
30961            expiryDate = new Date(),
30962            cookieDomain = '';
30963
30964            if (value === null) {
30965              // Mark that the cookie has expired one day ago
30966              expiryDate.setTime(expiryDate.getTime() + (-1 * 24 * 60 * 60 * 1000));
30967              expiry = "; expires=" + expiryDate.toGMTString();
30968              value = '';
30969            } else if (isNumber(daysToExpiry) && daysToExpiry !== 0) {
30970              expiryDate.setTime(expiryDate.getTime() + (daysToExpiry * 24 * 60 * 60 * 1000));
30971              expiry = "; expires=" + expiryDate.toGMTString();
30972            } else if (cookie.expiry !== 0) {
30973              expiryDate.setTime(expiryDate.getTime() + (cookie.expiry * 24 * 60 * 60 * 1000));
30974              expiry = "; expires=" + expiryDate.toGMTString();
30975            }
30976            if (!!key) {
30977              var cookiePath = "; path=" + cookie.path;
30978              if(cookie.domain){
30979                cookieDomain = "; domain=" + cookie.domain;
30980              }
30981              $document.cookie = deriveQualifiedKey(key) + "=" + encodeURIComponent(value) + expiry + cookiePath + cookieDomain;
30982            }
30983          } catch (e) {
30984            $rootScope.$broadcast('LocalStorageModule.notification.error',e.message);
30985            return false;
30986          }
30987          return true;
30988        };
30989
30990        // Directly get a value from a cookie
30991        // Example use: localStorageService.cookie.get('library'); // returns 'angular'
30992        var getFromCookies = function (key) {
30993          if (!browserSupportsCookies) {
30994            $rootScope.$broadcast('LocalStorageModule.notification.error', 'COOKIES_NOT_SUPPORTED');
30995            return false;
30996          }
30997
30998          var cookies = $document.cookie && $document.cookie.split(';') || [];
30999          for(var i=0; i < cookies.length; i++) {
31000            var thisCookie = cookies[i];
31001            while (thisCookie.charAt(0) === ' ') {
31002              thisCookie = thisCookie.substring(1,thisCookie.length);
31003            }
31004            if (thisCookie.indexOf(deriveQualifiedKey(key) + '=') === 0) {
31005              var storedValues = decodeURIComponent(thisCookie.substring(prefix.length + key.length + 1, thisCookie.length));
31006              try {
31007                return JSON.parse(storedValues);
31008              } catch(e) {
31009                return storedValues;
31010              }
31011            }
31012          }
31013          return null;
31014        };
31015
31016        var removeFromCookies = function (key) {
31017          addToCookies(key,null);
31018        };
31019
31020        var clearAllFromCookies = function () {
31021          var thisCookie = null, thisKey = null;
31022          var prefixLength = prefix.length;
31023          var cookies = $document.cookie.split(';');
31024          for(var i = 0; i < cookies.length; i++) {
31025            thisCookie = cookies[i];
31026
31027            while (thisCookie.charAt(0) === ' ') {
31028              thisCookie = thisCookie.substring(1, thisCookie.length);
31029            }
31030
31031            var key = thisCookie.substring(prefixLength, thisCookie.indexOf('='));
31032            removeFromCookies(key);
31033          }
31034        };
31035
31036        var getStorageType = function() {
31037          return storageType;
31038        };
31039
31040        // Add a listener on scope variable to save its changes to local storage
31041        // Return a function which when called cancels binding
31042        var bindToScope = function(scope, key, def, lsKey) {
31043          lsKey = lsKey || key;
31044          var value = getFromLocalStorage(lsKey);
31045
31046          if (value === null && isDefined(def)) {
31047            value = def;
31048          } else if (isObject(value) && isObject(def)) {
31049            value = extend(value, def);
31050          }
31051
31052          $parse(key).assign(scope, value);
31053
31054          return scope.$watch(key, function(newVal) {
31055            addToLocalStorage(lsKey, newVal);
31056          }, isObject(scope[key]));
31057        };
31058
31059        // Return localStorageService.length
31060        // ignore keys that not owned
31061        var lengthOfLocalStorage = function() {
31062          var count = 0;
31063          var storage = $window[storageType];
31064          for(var i = 0; i < storage.length; i++) {
31065            if(storage.key(i).indexOf(prefix) === 0 ) {
31066              count++;
31067            }
31068          }
31069          return count;
31070        };
31071
31072        return {
31073          isSupported: browserSupportsLocalStorage,
31074          getStorageType: getStorageType,
31075          set: addToLocalStorage,
31076          add: addToLocalStorage, //DEPRECATED
31077          get: getFromLocalStorage,
31078          keys: getKeysForLocalStorage,
31079          remove: removeFromLocalStorage,
31080          clearAll: clearAllFromLocalStorage,
31081          bind: bindToScope,
31082          deriveKey: deriveQualifiedKey,
31083          length: lengthOfLocalStorage,
31084          cookie: {
31085            isSupported: browserSupportsCookies,
31086            set: addToCookies,
31087            add: addToCookies, //DEPRECATED
31088            get: getFromCookies,
31089            remove: removeFromCookies,
31090            clearAll: clearAllFromCookies
31091          }
31092        };
31093      }];
31094  });
31095})(window, window.angular);
31096/**
31097 * @license AngularJS v1.4.11
31098 * (c) 2010-2015 Google, Inc. http://angularjs.org
31099 * License: MIT
31100 */
31101(function(window, angular, undefined) {'use strict';
31102
31103/* jshint ignore:start */
31104var noop        = angular.noop;
31105var copy        = angular.copy;
31106var extend      = angular.extend;
31107var jqLite      = angular.element;
31108var forEach     = angular.forEach;
31109var isArray     = angular.isArray;
31110var isString    = angular.isString;
31111var isObject    = angular.isObject;
31112var isUndefined = angular.isUndefined;
31113var isDefined   = angular.isDefined;
31114var isFunction  = angular.isFunction;
31115var isElement   = angular.isElement;
31116
31117var ELEMENT_NODE = 1;
31118var COMMENT_NODE = 8;
31119
31120var ADD_CLASS_SUFFIX = '-add';
31121var REMOVE_CLASS_SUFFIX = '-remove';
31122var EVENT_CLASS_PREFIX = 'ng-';
31123var ACTIVE_CLASS_SUFFIX = '-active';
31124var PREPARE_CLASS_SUFFIX = '-prepare';
31125
31126var NG_ANIMATE_CLASSNAME = 'ng-animate';
31127var NG_ANIMATE_CHILDREN_DATA = '$$ngAnimateChildren';
31128
31129// Detect proper transitionend/animationend event names.
31130var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
31131
31132// If unprefixed events are not supported but webkit-prefixed are, use the latter.
31133// Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
31134// Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
31135// but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
31136// Register both events in case `window.onanimationend` is not supported because of that,
31137// do the same for `transitionend` as Safari is likely to exhibit similar behavior.
31138// Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
31139// therefore there is no reason to test anymore for other vendor prefixes:
31140// http://caniuse.com/#search=transition
31141if (isUndefined(window.ontransitionend) && isDefined(window.onwebkittransitionend)) {
31142  CSS_PREFIX = '-webkit-';
31143  TRANSITION_PROP = 'WebkitTransition';
31144  TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
31145} else {
31146  TRANSITION_PROP = 'transition';
31147  TRANSITIONEND_EVENT = 'transitionend';
31148}
31149
31150if (isUndefined(window.onanimationend) && isDefined(window.onwebkitanimationend)) {
31151  CSS_PREFIX = '-webkit-';
31152  ANIMATION_PROP = 'WebkitAnimation';
31153  ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
31154} else {
31155  ANIMATION_PROP = 'animation';
31156  ANIMATIONEND_EVENT = 'animationend';
31157}
31158
31159var DURATION_KEY = 'Duration';
31160var PROPERTY_KEY = 'Property';
31161var DELAY_KEY = 'Delay';
31162var TIMING_KEY = 'TimingFunction';
31163var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
31164var ANIMATION_PLAYSTATE_KEY = 'PlayState';
31165var SAFE_FAST_FORWARD_DURATION_VALUE = 9999;
31166
31167var ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY;
31168var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;
31169var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;
31170var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;
31171
31172var isPromiseLike = function(p) {
31173  return p && p.then ? true : false;
31174};
31175
31176var ngMinErr = angular.$$minErr('ng');
31177function assertArg(arg, name, reason) {
31178  if (!arg) {
31179    throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
31180  }
31181  return arg;
31182}
31183
31184function mergeClasses(a,b) {
31185  if (!a && !b) return '';
31186  if (!a) return b;
31187  if (!b) return a;
31188  if (isArray(a)) a = a.join(' ');
31189  if (isArray(b)) b = b.join(' ');
31190  return a + ' ' + b;
31191}
31192
31193function packageStyles(options) {
31194  var styles = {};
31195  if (options && (options.to || options.from)) {
31196    styles.to = options.to;
31197    styles.from = options.from;
31198  }
31199  return styles;
31200}
31201
31202function pendClasses(classes, fix, isPrefix) {
31203  var className = '';
31204  classes = isArray(classes)
31205      ? classes
31206      : classes && isString(classes) && classes.length
31207          ? classes.split(/\s+/)
31208          : [];
31209  forEach(classes, function(klass, i) {
31210    if (klass && klass.length > 0) {
31211      className += (i > 0) ? ' ' : '';
31212      className += isPrefix ? fix + klass
31213                            : klass + fix;
31214    }
31215  });
31216  return className;
31217}
31218
31219function removeFromArray(arr, val) {
31220  var index = arr.indexOf(val);
31221  if (val >= 0) {
31222    arr.splice(index, 1);
31223  }
31224}
31225
31226function stripCommentsFromElement(element) {
31227  if (element instanceof jqLite) {
31228    switch (element.length) {
31229      case 0:
31230        return element;
31231        break;
31232
31233      case 1:
31234        // there is no point of stripping anything if the element
31235        // is the only element within the jqLite wrapper.
31236        // (it's important that we retain the element instance.)
31237        if (element[0].nodeType === ELEMENT_NODE) {
31238          return element;
31239        }
31240        break;
31241
31242      default:
31243        return jqLite(extractElementNode(element));
31244        break;
31245    }
31246  }
31247
31248  if (element.nodeType === ELEMENT_NODE) {
31249    return jqLite(element);
31250  }
31251}
31252
31253function extractElementNode(element) {
31254  if (!element[0]) return element;
31255  for (var i = 0; i < element.length; i++) {
31256    var elm = element[i];
31257    if (elm.nodeType == ELEMENT_NODE) {
31258      return elm;
31259    }
31260  }
31261}
31262
31263function $$addClass($$jqLite, element, className) {
31264  forEach(element, function(elm) {
31265    $$jqLite.addClass(elm, className);
31266  });
31267}
31268
31269function $$removeClass($$jqLite, element, className) {
31270  forEach(element, function(elm) {
31271    $$jqLite.removeClass(elm, className);
31272  });
31273}
31274
31275function applyAnimationClassesFactory($$jqLite) {
31276  return function(element, options) {
31277    if (options.addClass) {
31278      $$addClass($$jqLite, element, options.addClass);
31279      options.addClass = null;
31280    }
31281    if (options.removeClass) {
31282      $$removeClass($$jqLite, element, options.removeClass);
31283      options.removeClass = null;
31284    }
31285  }
31286}
31287
31288function prepareAnimationOptions(options) {
31289  options = options || {};
31290  if (!options.$$prepared) {
31291    var domOperation = options.domOperation || noop;
31292    options.domOperation = function() {
31293      options.$$domOperationFired = true;
31294      domOperation();
31295      domOperation = noop;
31296    };
31297    options.$$prepared = true;
31298  }
31299  return options;
31300}
31301
31302function applyAnimationStyles(element, options) {
31303  applyAnimationFromStyles(element, options);
31304  applyAnimationToStyles(element, options);
31305}
31306
31307function applyAnimationFromStyles(element, options) {
31308  if (options.from) {
31309    element.css(options.from);
31310    options.from = null;
31311  }
31312}
31313
31314function applyAnimationToStyles(element, options) {
31315  if (options.to) {
31316    element.css(options.to);
31317    options.to = null;
31318  }
31319}
31320
vendor: 8,033 bytes, lines 31321-31601
31321function mergeAnimationDetails(element, oldAnimation, newAnimation) {
31322  var target = oldAnimation.options || {};
31323  var newOptions = newAnimation.options || {};
31324
31325  var toAdd = (target.addClass || '') + ' ' + (newOptions.addClass || '');
31326  var toRemove = (target.removeClass || '') + ' ' + (newOptions.removeClass || '');
31327  var classes = resolveElementClasses(element.attr('class'), toAdd, toRemove);
31328
31329  if (newOptions.preparationClasses) {
31330    target.preparationClasses = concatWithSpace(newOptions.preparationClasses, target.preparationClasses);
31331    delete newOptions.preparationClasses;
31332  }
31333
31334  // noop is basically when there is no callback; otherwise something has been set
31335  var realDomOperation = target.domOperation !== noop ? target.domOperation : null;
31336
31337  extend(target, newOptions);
31338
31339  // TODO(matsko or sreeramu): proper fix is to maintain all animation callback in array and call at last,but now only leave has the callback so no issue with this.
31340  if (realDomOperation) {
31341    target.domOperation = realDomOperation;
31342  }
31343
31344  if (classes.addClass) {
31345    target.addClass = classes.addClass;
31346  } else {
31347    target.addClass = null;
31348  }
31349
31350  if (classes.removeClass) {
31351    target.removeClass = classes.removeClass;
31352  } else {
31353    target.removeClass = null;
31354  }
31355
31356  oldAnimation.addClass = target.addClass;
31357  oldAnimation.removeClass = target.removeClass;
31358
31359  return target;
31360}
31361
31362function resolveElementClasses(existing, toAdd, toRemove) {
31363  var ADD_CLASS = 1;
31364  var REMOVE_CLASS = -1;
31365
31366  var flags = {};
31367  existing = splitClassesToLookup(existing);
31368
31369  toAdd = splitClassesToLookup(toAdd);
31370  forEach(toAdd, function(value, key) {
31371    flags[key] = ADD_CLASS;
31372  });
31373
31374  toRemove = splitClassesToLookup(toRemove);
31375  forEach(toRemove, function(value, key) {
31376    flags[key] = flags[key] === ADD_CLASS ? null : REMOVE_CLASS;
31377  });
31378
31379  var classes = {
31380    addClass: '',
31381    removeClass: ''
31382  };
31383
31384  forEach(flags, function(val, klass) {
31385    var prop, allow;
31386    if (val === ADD_CLASS) {
31387      prop = 'addClass';
31388      allow = !existing[klass];
31389    } else if (val === REMOVE_CLASS) {
31390      prop = 'removeClass';
31391      allow = existing[klass];
31392    }
31393    if (allow) {
31394      if (classes[prop].length) {
31395        classes[prop] += ' ';
31396      }
31397      classes[prop] += klass;
31398    }
31399  });
31400
31401  function splitClassesToLookup(classes) {
31402    if (isString(classes)) {
31403      classes = classes.split(' ');
31404    }
31405
31406    var obj = {};
31407    forEach(classes, function(klass) {
31408      // sometimes the split leaves empty string values
31409      // incase extra spaces were applied to the options
31410      if (klass.length) {
31411        obj[klass] = true;
31412      }
31413    });
31414    return obj;
31415  }
31416
31417  return classes;
31418}
31419
31420function getDomNode(element) {
31421  return (element instanceof angular.element) ? element[0] : element;
31422}
31423
31424function applyGeneratedPreparationClasses(element, event, options) {
31425  var classes = '';
31426  if (event) {
31427    classes = pendClasses(event, EVENT_CLASS_PREFIX, true);
31428  }
31429  if (options.addClass) {
31430    classes = concatWithSpace(classes, pendClasses(options.addClass, ADD_CLASS_SUFFIX));
31431  }
31432  if (options.removeClass) {
31433    classes = concatWithSpace(classes, pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX));
31434  }
31435  if (classes.length) {
31436    options.preparationClasses = classes;
31437    element.addClass(classes);
31438  }
31439}
31440
31441function clearGeneratedClasses(element, options) {
31442  if (options.preparationClasses) {
31443    element.removeClass(options.preparationClasses);
31444    options.preparationClasses = null;
31445  }
31446  if (options.activeClasses) {
31447    element.removeClass(options.activeClasses);
31448    options.activeClasses = null;
31449  }
31450}
31451
31452function blockTransitions(node, duration) {
31453  // we use a negative delay value since it performs blocking
31454  // yet it doesn't kill any existing transitions running on the
31455  // same element which makes this safe for class-based animations
31456  var value = duration ? '-' + duration + 's' : '';
31457  applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]);
31458  return [TRANSITION_DELAY_PROP, value];
31459}
31460
31461function blockKeyframeAnimations(node, applyBlock) {
31462  var value = applyBlock ? 'paused' : '';
31463  var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY;
31464  applyInlineStyle(node, [key, value]);
31465  return [key, value];
31466}
31467
31468function applyInlineStyle(node, styleTuple) {
31469  var prop = styleTuple[0];
31470  var value = styleTuple[1];
31471  node.style[prop] = value;
31472}
31473
31474function concatWithSpace(a,b) {
31475  if (!a) return b;
31476  if (!b) return a;
31477  return a + ' ' + b;
31478}
31479
31480var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
31481  var queue, cancelFn;
31482
31483  function scheduler(tasks) {
31484    // we make a copy since RAFScheduler mutates the state
31485    // of the passed in array variable and this would be difficult
31486    // to track down on the outside code
31487    queue = queue.concat(tasks);
31488    nextTick();
31489  }
31490
31491  queue = scheduler.queue = [];
31492
31493  /* waitUntilQuiet does two things:
31494   * 1. It will run the FINAL `fn` value only when an uncancelled RAF has passed through
31495   * 2. It will delay the next wave of tasks from running until the quiet `fn` has run.
31496   *
31497   * The motivation here is that animation code can request more time from the scheduler
31498   * before the next wave runs. This allows for certain DOM properties such as classes to
31499   * be resolved in time for the next animation to run.
31500   */
31501  scheduler.waitUntilQuiet = function(fn) {
31502    if (cancelFn) cancelFn();
31503
31504    cancelFn = $$rAF(function() {
31505      cancelFn = null;
31506      fn();
31507      nextTick();
31508    });
31509  };
31510
31511  return scheduler;
31512
31513  function nextTick() {
31514    if (!queue.length) return;
31515
31516    var items = queue.shift();
31517    for (var i = 0; i < items.length; i++) {
31518      items[i]();
31519    }
31520
31521    if (!cancelFn) {
31522      $$rAF(function() {
31523        if (!cancelFn) nextTick();
31524      });
31525    }
31526  }
31527}];
31528
31529/**
31530 * @ngdoc directive
31531 * @name ngAnimateChildren
31532 * @restrict AE
31533 * @element ANY
31534 *
31535 * @description
31536 *
31537 * ngAnimateChildren allows you to specify that children of this element should animate even if any
31538 * of the children's parents are currently animating. By default, when an element has an active `enter`, `leave`, or `move`
31539 * (structural) animation, child elements that also have an active structural animation are not animated.
31540 *
31541 * Note that even if `ngAnimteChildren` is set, no child animations will run when the parent element is removed from the DOM (`leave` animation).
31542 *
31543 *
31544 * @param {string} ngAnimateChildren If the value is empty, `true` or `on`,
31545 *     then child animations are allowed. If the value is `false`, child animations are not allowed.
31546 *
31547 * @example
31548 * <example module="ngAnimateChildren" name="ngAnimateChildren" deps="angular-animate.js" animations="true">
31549     <file name="index.html">
31550       <div ng-controller="mainController as main">
31551         <label>Show container? <input type="checkbox" ng-model="main.enterElement" /></label>
31552         <label>Animate children? <input type="checkbox" ng-model="main.animateChildren" /></label>
31553         <hr>
31554         <div ng-animate-children="{{main.animateChildren}}">
31555           <div ng-if="main.enterElement" class="container">
31556             List of items:
31557             <div ng-repeat="item in [0, 1, 2, 3]" class="item">Item {{item}}</div>
31558           </div>
31559         </div>
31560       </div>
31561     </file>
31562     <file name="animations.css">
31563
31564      .container.ng-enter,
31565      .container.ng-leave {
31566        transition: all ease 1.5s;
31567      }
31568
31569      .container.ng-enter,
31570      .container.ng-leave-active {
31571        opacity: 0;
31572      }
31573
31574      .container.ng-leave,
31575      .container.ng-enter-active {
31576        opacity: 1;
31577      }
31578
31579      .item {
31580        background: firebrick;
31581        color: #FFF;
31582        margin-bottom: 10px;
31583      }
31584
31585      .item.ng-enter,
31586      .item.ng-leave {
31587        transition: transform 1.5s ease;
31588      }
31589
31590      .item.ng-enter {
31591        transform: translateX(50px);
31592      }
31593
31594      .item.ng-enter-active {
31595        transform: translateX(0);
31596      }
31597    </file>
31598    <file name="script.js">
31599      angular.module('ngAnimateChildren', ['ngAnimate'])
31600        .controller('mainController', function() {
31601          this.animateChildren = false;
31602          this.enterElement = false;
31603        });
31604    </file>
31605  </example>
31606 */
31607var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) {
31608  return {
31609    link: function(scope, element, attrs) {
31610      var val = attrs.ngAnimateChildren;
31611      if (angular.isString(val) && val.length === 0) { //empty attribute
31612        element.data(NG_ANIMATE_CHILDREN_DATA, true);
31613      } else {
31614        // Interpolate and set the value, so that it is available to
31615        // animations that run right after compilation
31616        setData($interpolate(val)(scope));
31617        attrs.$observe('ngAnimateChildren', setData);
31618      }
31619
31620      function setData(value) {
31621        value = value === 'on' || value === 'true';
31622        element.data(NG_ANIMATE_CHILDREN_DATA, value);
31623      }
31624    }
31625  };
31626}];
31627
31628var ANIMATE_TIMER_KEY = '$$animateCss';
31629
31630/**
31631 * @ngdoc service
31632 * @name $animateCss
31633 * @kind object
31634 *
31635 * @description
31636 * The `$animateCss` service is a useful utility to trigger customized CSS-based transitions/keyframes
31637 * from a JavaScript-based animation or directly from a directive. The purpose of `$animateCss` is NOT
31638 * to side-step how `$animate` and ngAnimate work, but the goal is to allow pre-existing animations or
31639 * directives to create more complex animations that can be purely driven using CSS code.
31640 *
31641 * Note that only browsers that support CSS transitions and/or keyframe animations are capable of
31642 * rendering animations triggered via `$animateCss` (bad news for IE9 and lower).
31643 *
31644 * ## Usage
31645 * Once again, `$animateCss` is designed to be used inside of a registered JavaScript animation that
31646 * is powered by ngAnimate. It is possible to use `$animateCss` directly inside of a directive, however,
31647 * any automatic control over cancelling animations and/or preventing animations from being run on
31648 * child elements will not be handled by Angular. For this to work as expected, please use `$animate` to
31649 * trigger the animation and then setup a JavaScript animation that injects `$animateCss` to trigger
31650 * the CSS animation.
31651 *
31652 * The example below shows how we can create a folding animation on an element using `ng-if`:
31653 *
31654 * ```html
31655 * <!-- notice the `fold-animation` CSS class -->
31656 * <div ng-if="onOff" class="fold-animation">
31657 *   This element will go BOOM
31658 * </div>
31659 * <button ng-click="onOff=true">Fold In</button>
31660 * ```
31661 *
31662 * Now we create the **JavaScript animation** that will trigger the CSS transition:
31663 *
31664 * ```js
31665 * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) {
31666 *   return {
31667 *     enter: function(element, doneFn) {
31668 *       var height = element[0].offsetHeight;
31669 *       return $animateCss(element, {
31670 *         from: { height:'0px' },
31671 *         to: { height:height + 'px' },
31672 *         duration: 1 // one second
31673 *       });
31674 *     }
31675 *   }
31676 * }]);
31677 * ```
31678 *
31679 * ## More Advanced Uses
31680 *
31681 * `$animateCss` is the underlying code that ngAnimate uses to power **CSS-based animations** behind the scenes. Therefore CSS hooks
31682 * like `.ng-EVENT`, `.ng-EVENT-active`, `.ng-EVENT-stagger` are all features that can be triggered using `$animateCss` via JavaScript code.
31683 *
31684 * This also means that just about any combination of adding classes, removing classes, setting styles, dynamically setting a keyframe animation,
31685 * applying a hardcoded duration or delay value, changing the animation easing or applying a stagger animation are all options that work with
31686 * `$animateCss`. The service itself is smart enough to figure out the combination of options and examine the element styling properties in order
31687 * to provide a working animation that will run in CSS.
31688 *
31689 * The example below showcases a more advanced version of the `.fold-animation` from the example above:
31690 *
31691 * ```js
31692 * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) {
31693 *   return {
31694 *     enter: function(element, doneFn) {
31695 *       var height = element[0].offsetHeight;
31696 *       return $animateCss(element, {
31697 *         addClass: 'red large-text pulse-twice',
31698 *         easing: 'ease-out',
31699 *         from: { height:'0px' },
31700 *         to: { height:height + 'px' },
31701 *         duration: 1 // one second
31702 *       });
31703 *     }
31704 *   }
31705 * }]);
31706 * ```
31707 *
31708 * Since we're adding/removing CSS classes then the CSS transition will also pick those up:
31709 *
31710 * ```css
31711 * /&#42; since a hardcoded duration value of 1 was provided in the JavaScript animation code,
31712 * the CSS classes below will be transitioned despite them being defined as regular CSS classes &#42;/
31713 * .red { background:red; }
31714 * .large-text { font-size:20px; }
31715 *
31716 * /&#42; we can also use a keyframe animation and $animateCss will make it work alongside the transition &#42;/
31717 * .pulse-twice {
31718 *   animation: 0.5s pulse linear 2;
31719 *   -webkit-animation: 0.5s pulse linear 2;
31720 * }
31721 *
31722 * @keyframes pulse {
31723 *   from { transform: scale(0.5); }
31724 *   to { transform: scale(1.5); }
31725 * }
31726 *
31727 * @-webkit-keyframes pulse {
31728 *   from { -webkit-transform: scale(0.5); }
31729 *   to { -webkit-transform: scale(1.5); }
31730 * }
31731 * ```
31732 *
31733 * Given this complex combination of CSS classes, styles and options, `$animateCss` will figure everything out and make the animation happen.
31734 *
31735 * ## How the Options are handled
31736 *
31737 * `$animateCss` is very versatile and intelligent when it comes to figuring out what configurations to apply to the element to ensure the animation
31738 * works with the options provided. Say for example we were adding a class that contained a keyframe value and we wanted to al
31738so animate some inline
31739 * styles using the `from` and `to` properties.
31740 *
31741 * ```js
31742 * var animator = $animateCss(element, {
31743 *   from: { background:'red' },
31744 *   to: { background:'blue' }
31745 * });
31746 * animator.start();
31747 * ```
31748 *
31749 * ```css
31750 * .rotating-animation {
31751 *   animation:0.5s rotate linear;
31752 *   -webkit-animation:0.5s rotate linear;
31753 * }
31754 *
31755 * @keyframes rotate {
31756 *   from { transform: rotate(0deg); }
31757 *   to { transform: rotate(360deg); }
31758 * }
31759 *
31760 * @-webkit-keyframes rotate {
31761 *   from { -webkit-transform: rotate(0deg); }
31762 *   to { -webkit-transform: rotate(360deg); }
31763 * }
31764 * ```
31765 *
31766 * The missing pieces here are that we do not have a transition set (within the CSS code nor within the `$animateCss` options) and the duration of the animation is
31767 * going to be detected from what the keyframe styles on the CSS class are. In this event, `$animateCss` will automatically create an inline transition
31768 * style matching the duration detected from the keyframe style (which is present in the CSS class that is being added) and then prepare both the transition
31769 * and keyframe animations to run in parallel on the element. Then when the animation is underway the provided `from` and `to` CSS styles will be applied
31770 * and spread across the transition and keyframe animation.
31771 *
31772 * ## What is returned
31773 *
31774 * `$animateCss` works in two stages: a preparation phase and an animation phase. Therefore when `$animateCss` is first called it will NOT actually
31775 * start the animation. All that is going on here is that the element is being prepared for the animation (which means that the generated CSS classes are
31776 * added and removed on the element). Once `$animateCss` is called it will return an object with the following properties:
31777 *
31778 * ```js
31779 * var animator = $animateCss(element, { ... });
31780 * ```
31781 *
31782 * Now what do the contents of our `animator` variable look like:
31783 *
31784 * ```js
31785 * {
31786 *   // starts the animation
31787 *   start: Function,
31788 *
31789 *   // ends (aborts) the animation
31790 *   end: Function
31791 * }
31792 * ```
31793 *
31794 * To actually start the animation we need to run `animation.start()` which will then return a promise that we can hook into to detect when the animation ends.
31795 * If we choose not to run the animation then we MUST run `animation.end()` to perform a cleanup on the element (since some CSS classes and stlyes may have been
31796 * applied to the element during the preparation phase). Note that all other properties such as duration, delay, transitions and keyframes are just properties
31797 * and that changing them will not reconfigure the parameters of the animation.
31798 *
31799 * ### runner.done() vs runner.then()
31800 * It is documented that `animation.start()` will return a promise object and this is true, however, there is also an additional method available on the
31801 * runner called `.done(callbackFn)`. The done method works the same as `.finally(callbackFn)`, however, it does **not trigger a digest to occur**.
31802 * Therefore, for performance reasons, it's always best to use `runner.done(callback)` instead of `runner.then()`, `runner.catch()` or `runner.finally()`
31803 * unless you really need a digest to kick off afterwards.
31804 *
31805 * Keep in mind that, to make this easier, ngAnimate has tweaked the JS animations API to recognize when a runner instance is returned from $animateCss
31806 * (so there is no need to call `runner.done(doneFn)` inside of your JavaScript animation code).
31807 * Check the {@link ngAnimate.$animateCss#usage animation code above} to see how this works.
31808 *
31809 * @param {DOMElement} element the element that will be animated
31810 * @param {object} options the animation-related options that will be applied during the animation
31811 *
31812 * * `event` - The DOM event (e.g. enter, leave, move). When used, a generated CSS class of `ng-EVENT` and `ng-EVENT-active` will be applied
31813 * to the element during the animation. Multiple events can be provided when spaces are used as a separator. (Note that this will not perform any DOM operation.)
31814 * * `structural` - Indicates that the `ng-` prefix will be added to the event class. Setting to `false` or omitting will turn `ng-EVENT` and
31815 * `ng-EVENT-active` in `EVENT` and `EVENT-active`. Unused if `event` is omitted.
31816 * * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both).
31817 * * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`).
31818 * * `keyframeStyle` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`).
31819 * * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation.
31820 * * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition.
31821 * * `addClass` - A space separated list of CSS classes that will be added to the element and spread across the animation.
31822 * * `removeClass` - A space separated list of CSS classes that will be removed from the element and spread across the animation.
31823 * * `duration` - A number value representing the total duration of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `0`
31824 * is provided then the animation will be skipped entirely.
31825 * * `delay` - A number value representing the total delay of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `true` is
31826 * used then whatever delay value is detected from the CSS classes will be mirrored on the elements styles (e.g. by setting delay true then the style value
31827 * of the element will be `transition-delay: DETECTED_VALUE`). Using `true` is useful when you want the CSS classes and inline styles to all share the same
31828 * CSS delay value.
31829 * * `stagger` - A numeric time value representing the delay between successively animated elements
31830 * ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.})
31831 * * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a
31832 * * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
31833 * * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved 
31833in time. (Note that this will prevent any transitions from occuring on the classes being added and removed.)
31834 * * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once
31835 *    the animation is closed. This is useful for when the styles are used purely for the sake of
31836 *    the animation and do not have a lasting visual effect on the element (e.g. a colapse and open animation).
31837 *    By default this value is set to `false`.
31838 *
31839 * @return {object} an object with start and end methods and details about the animation.
31840 *
31841 * * `start` - The method to start the animation. This will return a `Promise` when called.
31842 * * `end` - This method will cancel the animation and remove all applied CSS classes and styles.
31843 */
31844var ONE_SECOND = 1000;
31845var BASE_TEN = 10;
31846
31847var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
31848var CLOSING_TIME_BUFFER = 1.5;
31849
31850var DETECT_CSS_PROPERTIES = {
31851  transitionDuration:      TRANSITION_DURATION_PROP,
31852  transitionDelay:         TRANSITION_DELAY_PROP,
31853  transitionProperty:      TRANSITION_PROP + PROPERTY_KEY,
31854  animationDuration:       ANIMATION_DURATION_PROP,
31855  animationDelay:          ANIMATION_DELAY_PROP,
31856  animationIterationCount: ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY
31857};
31858
31859var DETECT_STAGGER_CSS_PROPERTIES = {
31860  transitionDuration:      TRANSITION_DURATION_PROP,
31861  transitionDelay:         TRANSITION_DELAY_PROP,
31862  animationDuration:       ANIMATION_DURATION_PROP,
31863  animationDelay:          ANIMATION_DELAY_PROP
31864};
31865
31866function getCssKeyframeDurationStyle(duration) {
31867  return [ANIMATION_DURATION_PROP, duration + 's'];
31868}
31869
31870function getCssDelayStyle(delay, isKeyframeAnimation) {
31871  var prop = isKeyframeAnimation ? ANIMATION_DELAY_PROP : TRANSITION_DELAY_PROP;
31872  return [prop, delay + 's'];
31873}
31874
31875function computeCssStyles($window, element, properties) {
31876  var styles = Object.create(null);
31877  var detectedStyles = $window.getComputedStyle(element) || {};
31878  forEach(properties, function(formalStyleName, actualStyleName) {
31879    var val = detectedStyles[formalStyleName];
31880    if (val) {
31881      var c = val.charAt(0);
31882
31883      // only numerical-based values have a negative sign or digit as the first value
31884      if (c === '-' || c === '+' || c >= 0) {
31885        val = parseMaxTime(val);
31886      }
31887
31888      // by setting this to null in the event that the delay is not set or is set directly as 0
31889      // then we can still allow for zegative values to be used later on and not mistake this
31890      // value for being greater than any other negative value.
31891      if (val === 0) {
31892        val = null;
31893      }
31894      styles[actualStyleName] = val;
31895    }
31896  });
31897
31898  return styles;
31899}
31900
31901function parseMaxTime(str) {
31902  var maxValue = 0;
31903  var values = str.split(/\s*,\s*/);
31904  forEach(values, function(value) {
31905    // it's always safe to consider only second values and omit `ms` values since
31906    // getComputedStyle will always handle the conversion for us
31907    if (value.charAt(value.length - 1) == 's') {
31908      value = value.substring(0, value.length - 1);
31909    }
31910    value = parseFloat(value) || 0;
31911    maxValue = maxValue ? Math.max(value, maxValue) : value;
31912  });
31913  return maxValue;
31914}
31915
31916function truthyTimingValue(val) {
31917  return val === 0 || val != null;
31918}
31919
31920function getCssTransitionDurationStyle(duration, applyOnlyDuration) {
31921  var style = TRANSITION_PROP;
31922  var value = duration + 's';
31923  if (applyOnlyDuration) {
31924    style += DURATION_KEY;
31925  } else {
31926    value += ' linear all';
31927  }
31928  return [style, value];
31929}
31930
31931function createLocalCacheLookup() {
31932  var cache = Object.create(null);
31933  return {
31934    flush: function() {
31935      cache = Object.create(null);
31936    },
31937
31938    count: function(key) {
31939      var entry = cache[key];
31940      return entry ? entry.total : 0;
31941    },
31942
31943    get: function(key) {
31944      var entry = cache[key];
31945      return entry && entry.value;
31946    },
31947
31948    put: function(key, value) {
31949      if (!cache[key]) {
31950        cache[key] = { total: 1, value: value };
31951      } else {
31952        cache[key].total++;
31953      }
31954    }
31955  };
31956}
31957
31958// we do not reassign an already present style value since
31959// if we detect the style property value again we may be
31960// detecting styles that were added via the `from` styles.
31961// We make use of `isDefined` here since an empty string
31962// or null value (which is what getPropertyValue will return
31963// for a non-existing style) will still be marked as a valid
31964// value for the style (a falsy value implies that the style
31965// is to be removed at the end of the animation). If we had a simple
31966// "OR" statement then it would not be enough to catch that.
31967function registerRestorableStyles(backup, node, properties) {
31968  forEach(properties, function(prop) {
31969    backup[prop] = isDefined(backup[prop])
31970        ? backup[prop]
31971        : node.style.getPropertyValue(prop);
31972  });
31973}
31974
31975var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
31976  var gcsLookup = createLocalCacheLookup();
31977  var gcsStaggerLookup = createLocalCacheLookup();
31978
31979  this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout',
31980               '$$forceReflow', '$sniffer', '$$rAFScheduler', '$$animateQueue',
31981       function($window,   $$jqLite,   $$AnimateRunner,   $timeout,
31982                $$forceReflow,   $sniffer,   $$rAFScheduler, $$animateQueue) {
31983
31984    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
31985
31986    var parentCounter = 0;
vendor: 45,156 bytes, lines 31987-33290
31987    function gcsHashFn(node, extraClasses) {
31988      var KEY = "$$ngAnimateParentKey";
31989      var parentNode = node.parentNode;
31990      var parentID = parentNode[KEY] || (parentNode[KEY] = ++parentCounter);
31991      return parentID + '-' + node.getAttribute('class') + '-' + extraClasses;
31992    }
31993
31994    function computeCachedCssStyles(node, className, cacheKey, properties) {
31995      var timings = gcsLookup.get(cacheKey);
31996
31997      if (!timings) {
31998        timings = computeCssStyles($window, node, properties);
31999        if (timings.animationIterationCount === 'infinite') {
32000          timings.animationIterationCount = 1;
32001        }
32002      }
32003
32004      // we keep putting this in multiple times even though the value and the cacheKey are the same
32005      // because we're keeping an interal tally of how many duplicate animations are detected.
32006      gcsLookup.put(cacheKey, timings);
32007      return timings;
32008    }
32009
32010    function computeCachedCssStaggerStyles(node, className, cacheKey, properties) {
32011      var stagger;
32012
32013      // if we have one or more existing matches of matching elements
32014      // containing the same parent + CSS styles (which is how cacheKey works)
32015      // then staggering is possible
32016      if (gcsLookup.count(cacheKey) > 0) {
32017        stagger = gcsStaggerLookup.get(cacheKey);
32018
32019        if (!stagger) {
32020          var staggerClassName = pendClasses(className, '-stagger');
32021
32022          $$jqLite.addClass(node, staggerClassName);
32023
32024          stagger = computeCssStyles($window, node, properties);
32025
32026          // force the conversion of a null value to zero incase not set
32027          stagger.animationDuration = Math.max(stagger.animationDuration, 0);
32028          stagger.transitionDuration = Math.max(stagger.transitionDuration, 0);
32029
32030          $$jqLite.removeClass(node, staggerClassName);
32031
32032          gcsStaggerLookup.put(cacheKey, stagger);
32033        }
32034      }
32035
32036      return stagger || {};
32037    }
32038
32039    var cancelLastRAFRequest;
32040    var rafWaitQueue = [];
32041    function waitUntilQuiet(callback) {
32042      rafWaitQueue.push(callback);
32043      $$rAFScheduler.waitUntilQuiet(function() {
32044        gcsLookup.flush();
32045        gcsStaggerLookup.flush();
32046
32047        // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable.
32048        // PLEASE EXAMINE THE `$$forceReflow` service to understand why.
32049        var pageWidth = $$forceReflow();
32050
32051        // we use a for loop to ensure that if the queue is changed
32052        // during this looping then it will consider new requests
32053        for (var i = 0; i < rafWaitQueue.length; i++) {
32054          rafWaitQueue[i](pageWidth);
32055        }
32056        rafWaitQueue.length = 0;
32057      });
32058    }
32059
32060    function computeTimings(node, className, cacheKey) {
32061      var timings = computeCachedCssStyles(node, className, cacheKey, DETECT_CSS_PROPERTIES);
32062      var aD = timings.animationDelay;
32063      var tD = timings.transitionDelay;
32064      timings.maxDelay = aD && tD
32065          ? Math.max(aD, tD)
32066          : (aD || tD);
32067      timings.maxDuration = Math.max(
32068          timings.animationDuration * timings.animationIterationCount,
32069          timings.transitionDuration);
32070
32071      return timings;
32072    }
32073
32074    return function init(element, initialOptions) {
32075      // all of the animation functions should create
32076      // a copy of the options data, however, if a
32077      // parent service has already created a copy then
32078      // we should stick to using that
32079      var options = initialOptions || {};
32080      if (!options.$$prepared) {
32081        options = prepareAnimationOptions(copy(options));
32082      }
32083
32084      var restoreStyles = {};
32085      var node = getDomNode(element);
32086      if (!node
32087          || !node.parentNode
32088          || !$$animateQueue.enabled()) {
32089        return closeAndReturnNoopAnimator();
32090      }
32091
32092      var temporaryStyles = [];
32093      var classes = element.attr('class');
32094      var styles = packageStyles(options);
32095      var animationClosed;
32096      var animationPaused;
32097      var animationCompleted;
32098      var runner;
32099      var runnerHost;
32100      var maxDelay;
32101      var maxDelayTime;
32102      var maxDuration;
32103      var maxDurationTime;
32104      var startTime;
32105      var events = [];
32106
32107      if (options.duration === 0 || (!$sniffer.animations && !$sniffer.transitions)) {
32108        return closeAndReturnNoopAnimator();
32109      }
32110
32111      var method = options.event && isArray(options.event)
32112            ? options.event.join(' ')
32113            : options.event;
32114
32115      var isStructural = method && options.structural;
32116      var structuralClassName = '';
32117      var addRemoveClassName = '';
32118
32119      if (isStructural) {
32120        structuralClassName = pendClasses(method, EVENT_CLASS_PREFIX, true);
32121      } else if (method) {
32122        structuralClassName = method;
32123      }
32124
32125      if (options.addClass) {
32126        addRemoveClassName += pendClasses(options.addClass, ADD_CLASS_SUFFIX);
32127      }
32128
32129      if (options.removeClass) {
32130        if (addRemoveClassName.length) {
32131          addRemoveClassName += ' ';
32132        }
32133        addRemoveClassName += pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX);
32134      }
32135
32136      // there may be a situation where a structural animation is combined together
32137      // with CSS classes that need to resolve before the animation is computed.
32138      // However this means that there is no explicit CSS code to block the animation
32139      // from happening (by setting 0s none in the class name). If this is the case
32140      // we need to apply the classes before the first rAF so we know to continue if
32141      // there actually is a detected transition or keyframe animation
32142      if (options.applyClassesEarly && addRemoveClassName.length) {
32143        applyAnimationClasses(element, options);
32144      }
32145
32146      var preparationClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
32147      var fullClassName = classes + ' ' + preparationClasses;
32148      var activeClasses = pendClasses(preparationClasses, ACTIVE_CLASS_SUFFIX);
32149      var hasToStyles = styles.to && Object.keys(styles.to).length > 0;
32150      var containsKeyframeAnimation = (options.keyframeStyle || '').length > 0;
32151
32152      // there is no way we can trigger an animation if no styles and
32153      // no classes are being applied which would then trigger a transition,
32154      // unless there a is raw keyframe value that is applied to the element.
32155      if (!containsKeyframeAnimation
32156           && !hasToStyles
32157           && !preparationClasses) {
32158        return closeAndReturnNoopAnimator();
32159      }
32160
32161      var cacheKey, stagger;
32162      if (options.stagger > 0) {
32163        var staggerVal = parseFloat(options.stagger);
32164        stagger = {
32165          transitionDelay: staggerVal,
32166          animationDelay: staggerVal,
32167          transitionDuration: 0,
32168          animationDuration: 0
32169        };
32170      } else {
32171        cacheKey = gcsHashFn(node, fullClassName);
32172        stagger = computeCachedCssStaggerStyles(node, preparationClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES);
32173      }
32174
32175      if (!options.$$skipPreparationClasses) {
32176        $$jqLite.addClass(element, preparationClasses);
32177      }
32178
32179      var applyOnlyDuration;
32180
32181      if (options.transitionStyle) {
32182        var transitionStyle = [TRANSITION_PROP, options.transitionStyle];
32183        applyInlineStyle(node, transitionStyle);
32184        temporaryStyles.push(transitionStyle);
32185      }
32186
32187      if (options.duration >= 0) {
32188        applyOnlyDuration = node.style[TRANSITION_PROP].length > 0;
32189        var durationStyle = getCssTransitionDurationStyle(options.duration, applyOnlyDuration);
32190
32191        // we set the duration so that it will be picked up by getComputedStyle later
32192        applyInlineStyle(node, durationStyle);
32193        temporaryStyles.push(durationStyle);
32194      }
32195
32196      if (options.keyframeStyle) {
32197        var keyframeStyle = [ANIMATION_PROP, options.keyframeStyle];
32198        applyInlineStyle(node, keyframeStyle);
32199        temporaryStyles.push(keyframeStyle);
32200      }
32201
32202      var itemIndex = stagger
32203          ? options.staggerIndex >= 0
32204              ? options.staggerIndex
32205              : gcsLookup.count(cacheKey)
32206          : 0;
32207
32208      var isFirst = itemIndex === 0;
32209
32210      // this is a pre-emptive way of forcing the setup classes to be added and applied INSTANTLY
32211      // without causing any combination of transitions to kick in. By adding a negative delay value
32212      // it forces the setup class' transition to end immediately. We later then remove the negative
32213      // transition delay to allow for the transition to naturally do it's thing. The beauty here is
32214      // that if there is no transition defined then nothing will happen and this will also allow
32215      // other transitions to be stacked on top of each other without any chopping them out.
32216      if (isFirst && !options.skipBlocking) {
32217        blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
32218      }
32219
32220      var timings = computeTimings(node, fullClassName, cacheKey);
32221      var relativeDelay = timings.maxDelay;
32222      maxDelay = Math.max(relativeDelay, 0);
32223      maxDuration = timings.maxDuration;
32224
32225      var flags = {};
32226      flags.hasTransitions          = timings.transitionDuration > 0;
32227      flags.hasAnimations           = timings.animationDuration > 0;
32228      flags.hasTransitionAll        = flags.hasTransitions && timings.transitionProperty == 'all';
32229      flags.applyTransitionDuration = hasToStyles && (
32230                                        (flags.hasTransitions && !flags.hasTransitionAll)
32231                                         || (flags.hasAnimations && !flags.hasTransitions));
32232      flags.applyAnimationDuration  = options.duration && flags.hasAnimations;
32233      flags.applyTransitionDelay    = truthyTimingValue(options.delay) && (flags.applyTransitionDuration || flags.hasTransitions);
32234      flags.applyAnimationDelay     = truthyTimingValue(options.delay) && flags.hasAnimations;
32235      flags.recalculateTimingStyles = addRemoveClassName.length > 0;
32236
32237      if (flags.applyTransitionDuration || flags.applyAnimationDuration) {
32238        maxDuration = options.duration ? parseFloat(options.duration) : maxDuration;
32239
32240        if (flags.applyTransitionDuration) {
32241          flags.hasTransitions = true;
32242          timings.transitionDuration = maxDuration;
32243          applyOnlyDuration = node.style[TRANSITION_PROP + PROPERTY_KEY].length > 0;
32244          temporaryStyles.push(getCssTransitionDurationStyle(maxDuration, applyOnlyDuration));
32245        }
32246
32247        if (flags.applyAnimationDuration) {
32248          flags.hasAnimations = true;
32249          timings.animationDuration = maxDuration;
32250          temporaryStyles.push(getCssKeyframeDurationStyle(maxDuration));
32251        }
32252      }
32253
32254      if (maxDuration === 0 && !flags.recalculateTimingStyles) {
32255        return closeAndReturnNoopAnimator();
32256      }
32257
32258      if (options.delay != null) {
32259        var delayStyle;
32260        if (typeof options.delay !== "boolean") {
32261          delayStyle = parseFloat(options.delay);
32262          // number in options.delay means we have to recalculate the delay for the closing timeout
32263          maxDelay = Math.max(delayStyle, 0);
32264        }
32265
32266        if (flags.applyTransitionDelay) {
32267          temporaryStyles.push(getCssDelayStyle(delayStyle));
32268        }
32269
32270        if (flags.applyAnimationDelay) {
32271          temporaryStyles.push(getCssDelayStyle(delayStyle, true));
32272        }
32273      }
32274
32275      // we need to recalculate the delay value since we used a pre-emptive negative
32276      // delay value and the delay value is required for the final event checking. This
32277      // property will ensure that this will happen after the RAF phase has passed.
32278      if (options.duration == null && timings.transitionDuration > 0) {
32279        flags.recalculateTimingStyles = flags.recalculateTimingStyles || isFirst;
32280      }
32281
32282      maxDelayTime = maxDelay * ONE_SECOND;
32283      maxDurationTime = maxDuration * ONE_SECOND;
32284      if (!options.skipBlocking) {
32285        flags.blockTransition = timings.transitionDuration > 0;
32286        flags.blockKeyframeAnimation = timings.animationDuration > 0 &&
32287                                       stagger.animationDelay > 0 &&
32288                                       stagger.animationDuration === 0;
32289      }
32290
32291      if (options.from) {
32292        if (options.cleanupStyles) {
32293          registerRestorableStyles(restoreStyles, node, Object.keys(options.from));
32294        }
32295        applyAnimationFromStyles(element, options);
32296      }
32297
32298      if (flags.blockTransition || flags.blockKeyframeAnimation) {
32299        applyBlocking(maxDuration);
32300      } else if (!options.skipBlocking) {
32301        blockTransitions(node, false);
32302      }
32303
32304      // TODO(matsko): for 1.5 change this code to have an animator object for better debugging
32305      return {
32306        $$willAnimate: true,
32307        end: endFn,
32308        start: function() {
32309          if (animationClosed) return;
32310
32311          runnerHost = {
32312            end: endFn,
32313            cancel: cancelFn,
32314            resume: null, //this will be set during the start() phase
32315            pause: null
32316          };
32317
32318          runner = new $$AnimateRunner(runnerHost);
32319
32320          waitUntilQuiet(start);
32321
32322          // we don't have access to pause/resume the animation
32323          // since it hasn't run yet. AnimateRunner will therefore
32324          // set noop functions for resume and pause and they will
32325          // later be overridden once the animation is triggered
32326          return runner;
32327        }
32328      };
32329
32330      function endFn() {
32331        close();
32332      }
32333
32334      function cancelFn() {
32335        close(true);
32336      }
32337
32338      function close(rejected) { // jshint ignore:line
32339        // if the promise has been called already then we shouldn't close
32340        // the animation again
32341        if (animationClosed || (animationCompleted && animationPaused)) return;
32342        animationClosed = true;
32343        animationPaused = false;
32344
32345        if (!options.$$skipPreparationClasses) {
32346          $$jqLite.removeClass(element, preparationClasses);
32347        }
32348        $$jqLite.removeClass(element, activeClasses);
32349
32350        blockKeyframeAnimations(node, false);
32351        blockTransitions(node, false);
32352
32353        forEach(temporaryStyles, function(entry) {
32354          // There is only one way to remove inline style properties entirely from elements.
32355          // By using `removeProperty` this works, but we need to convert camel-cased CSS
32356          // styles down to hyphenated values.
32357          node.style[entry[0]] = '';
32358        });
32359
32360        applyAnimationClasses(element, options);
32361        applyAnimationStyles(element, options);
32362
32363        if (Object.keys(restoreStyles).length) {
32364          forEach(restoreStyles, function(value, prop) {
32365            value ? node.style.setProperty(prop, value)
32366                  : node.style.removeProperty(prop);
32367          });
32368        }
32369
32370        // the reason why we have this option is to allow a synchronous closing callback
32371        // that is fired as SOON as the animation ends (when the CSS is removed) or if
32372        // the animation never takes off at all. A good example is a leave animation since
32373        // the element must be removed just after the animation is over or else the element
32374        // will appear on screen for one animation frame causing an overbearing flicker.
32375        if (options.onDone) {
32376          options.onDone();
32377        }
32378
32379        if (events && events.length) {
32380          // Remove the transitionend / animationend listener(s)
32381          element.off(events.join(' '), onAnimationProgress);
32382        }
32383
32384        //Cancel the fallback closing timeout and remove the timer data
32385        var animationTimerData = element.data(ANIMATE_TIMER_KEY);
32386        if (animationTimerData) {
32387          $timeout.cancel(animationTimerData[0].timer);
32388          element.removeData(ANIMATE_TIMER_KEY);
32389        }
32390
32391        // if the preparation function fails then the promise is not setup
32392        if (runner) {
32393          runner.complete(!rejected);
32394        }
32395      }
32396
32397      function applyBlocking(duration) {
32398        if (flags.blockTransition) {
32399          blockTransitions(node, duration);
32400        }
32401
32402        if (flags.blockKeyframeAnimation) {
32403          blockKeyframeAnimations(node, !!duration);
32404        }
32405      }
32406
32407      function closeAndReturnNoopAnimator() {
32408        runner = new $$AnimateRunner({
32409          end: endFn,
32410          cancel: cancelFn
32411        });
32412
32413        // should flush the cache animation
32414        waitUntilQuiet(noop);
32415        close();
32416
32417        return {
32418          $$willAnimate: false,
32419          start: function() {
32420            return runner;
32421          },
32422          end: endFn
32423        };
32424      }
32425
32426      function onAnimationProgress(event) {
32427        event.stopPropagation();
32428        var ev = event.originalEvent || event;
32429
32430        // we now always use `Date.now()` due to the recent changes with
32431        // event.timeStamp in Firefox, Webkit and Chrome (see #13494 for more info)
32432        var timeStamp = ev.$manualTimeStamp || Date.now();
32433
32434        /* Firefox (or possibly just Gecko) likes to not round values up
32435         * when a ms measurement is used for the animation */
32436        var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
32437
32438        /* $manualTimeStamp is a mocked timeStamp value which is set
32439         * within browserTrigger(). This is only here so that tests can
32440         * mock animations properly. Real events fallback to event.timeStamp,
32441         * or, if they don't, then a timeStamp is automatically created for them.
32442         * We're checking to see if the timeStamp surpasses the expected delay,
32443         * but we're using elapsedTime instead of the timeStamp on the 2nd
32444         * pre-condition since animationPauseds sometimes close off early */
32445        if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
32446          // we set this flag to ensure that if the transition is paused then, when resumed,
32447          // the animation will automatically close itself since transitions cannot be paused.
32448          animationCompleted = true;
32449          close();
32450        }
32451      }
32452
32453      function start() {
32454        if (animationClosed) return;
32455        if (!node.parentNode) {
32456          close();
32457          return;
32458        }
32459
32460        // even though we only pause keyframe animations here the pause flag
32461        // will still happen when transitions are used. Only the transition will
32462        // not be paused since that is not possible. If the animation ends when
32463        // paused then it will not complete until unpaused or cancelled.
32464        var playPause = function(playAnimation) {
32465          if (!animationCompleted) {
32466            animationPaused = !playAnimation;
32467            if (timings.animationDuration) {
32468              var value = blockKeyframeAnimations(node, animationPaused);
32469              animationPaused
32470                  ? temporaryStyles.push(value)
32471                  : removeFromArray(temporaryStyles, value);
32472            }
32473          } else if (animationPaused && playAnimation) {
32474            animationPaused = false;
32475            close();
32476          }
32477        };
32478
32479        // checking the stagger duration prevents an accidently cascade of the CSS delay style
32480        // being inherited from the parent. If the transition duration is zero then we can safely
32481        // rely that the delay value is an intential stagger delay style.
32482        var maxStagger = itemIndex > 0
32483                         && ((timings.transitionDuration && stagger.transitionDuration === 0) ||
32484                            (timings.animationDuration && stagger.animationDuration === 0))
32485                         && Math.max(stagger.animationDelay, stagger.transitionDelay);
32486        if (maxStagger) {
32487          $timeout(triggerAnimationStart,
32488                   Math.floor(maxStagger * itemIndex * ONE_SECOND),
32489                   false);
32490        } else {
32491          triggerAnimationStart();
32492        }
32493
32494        // this will decorate the existing promise runner with pause/resume methods
32495        runnerHost.resume = function() {
32496          playPause(true);
32497        };
32498
32499        runnerHost.pause = function() {
32500          playPause(false);
32501        };
32502
32503        function triggerAnimationStart() {
32504          // just incase a stagger animation kicks in when the animation
32505          // itself was cancelled entirely
32506          if (animationClosed) return;
32507
32508          applyBlocking(false);
32509
32510          forEach(temporaryStyles, function(entry) {
32511            var key = entry[0];
32512            var value = entry[1];
32513            node.style[key] = value;
32514          });
32515
32516          applyAnimationClasses(element, options);
32517          $$jqLite.addClass(element, activeClasses);
32518
32519          if (flags.recalculateTimingStyles) {
32520            fullClassName = node.className + ' ' + preparationClasses;
32521            cacheKey = gcsHashFn(node, fullClassName);
32522
32523            timings = computeTimings(node, fullClassName, cacheKey);
32524            relativeDelay = timings.maxDelay;
32525            maxDelay = Math.max(relativeDelay, 0);
32526            maxDuration = timings.maxDuration;
32527
32528            if (maxDuration === 0) {
32529              close();
32530              return;
32531            }
32532
32533            flags.hasTransitions = timings.transitionDuration > 0;
32534            flags.hasAnimations = timings.animationDuration > 0;
32535          }
32536
32537          if (flags.applyAnimationDelay) {
32538            relativeDelay = typeof options.delay !== "boolean" && truthyTimingValue(options.delay)
32539                  ? parseFloat(options.delay)
32540                  : relativeDelay;
32541
32542            maxDelay = Math.max(relativeDelay, 0);
32543            timings.animationDelay = relativeDelay;
32544            delayStyle = getCssDelayStyle(relativeDelay, true);
32545            temporaryStyles.push(delayStyle);
32546            node.style[delayStyle[0]] = delayStyle[1];
32547          }
32548
32549          maxDelayTime = maxDelay * ONE_SECOND;
32550          maxDurationTime = maxDuration * ONE_SECOND;
32551
32552          if (options.easing) {
32553            var easeProp, easeVal = options.easing;
32554            if (flags.hasTransitions) {
32555              easeProp = TRANSITION_PROP + TIMING_KEY;
32556              temporaryStyles.push([easeProp, easeVal]);
32557              node.style[easeProp] = easeVal;
32558            }
32559            if (flags.hasAnimations) {
32560              easeProp = ANIMATION_PROP + TIMING_KEY;
32561              temporaryStyles.push([easeProp, easeVal]);
32562              node.style[easeProp] = easeVal;
32563            }
32564          }
32565
32566          if (timings.transitionDuration) {
32567            events.push(TRANSITIONEND_EVENT);
32568          }
32569
32570          if (timings.animationDuration) {
32571            events.push(ANIMATIONEND_EVENT);
32572          }
32573
32574          startTime = Date.now();
32575          var timerTime = maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime;
32576          var endTime = startTime + timerTime;
32577
32578          var animationsData = element.data(ANIMATE_TIMER_KEY) || [];
32579          var setupFallbackTimer = true;
32580          if (animationsData.length) {
32581            var currentTimerData = animationsData[0];
32582            setupFallbackTimer = endTime > currentTimerData.expectedEndTime;
32583            if (setupFallbackTimer) {
32584              $timeout.cancel(currentTimerData.timer);
32585            } else {
32586              animationsData.push(close);
32587            }
32588          }
32589
32590          if (setupFallbackTimer) {
32591            var timer = $timeout(onAnimationExpired, timerTime, false);
32592            animationsData[0] = {
32593              timer: timer,
32594              expectedEndTime: endTime
32595            };
32596            animationsData.push(close);
32597            element.data(ANIMATE_TIMER_KEY, animationsData);
32598          }
32599
32600          if (events.length) {
32601            element.on(events.join(' '), onAnimationProgress);
32602          }
32603
32604          if (options.to) {
32605            if (options.cleanupStyles) {
32606              registerRestorableStyles(restoreStyles, node, Object.keys(options.to));
32607            }
32608            applyAnimationToStyles(element, options);
32609          }
32610        }
32611
32612        function onAnimationExpired() {
32613          var animationsData = element.data(ANIMATE_TIMER_KEY);
32614
32615          // this will be false in the event that the element was
32616          // removed from the DOM (via a leave animation or something
32617          // similar)
32618          if (animationsData) {
32619            for (var i = 1; i < animationsData.length; i++) {
32620              animationsData[i]();
32621            }
32622            element.removeData(ANIMATE_TIMER_KEY);
32623          }
32624        }
32625      }
32626    };
32627  }];
32628}];
32629
32630var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationProvider) {
32631  $$animationProvider.drivers.push('$$animateCssDriver');
32632
32633  var NG_ANIMATE_SHIM_CLASS_NAME = 'ng-animate-shim';
32634  var NG_ANIMATE_ANCHOR_CLASS_NAME = 'ng-anchor';
32635
32636  var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out';
32637  var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in';
32638
32639  function isDocumentFragment(node) {
32640    return node.parentNode && node.parentNode.nodeType === 11;
32641  }
32642
32643  this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$sniffer', '$$jqLite', '$document',
32644       function($animateCss,   $rootScope,   $$AnimateRunner,   $rootElement,   $sniffer,   $$jqLite,   $document) {
32645
32646    // only browsers that support these properties can render animations
32647    if (!$sniffer.animations && !$sniffer.transitions) return noop;
32648
32649    var bodyNode = $document[0].body;
32650    var rootNode = getDomNode($rootElement);
32651
32652    var rootBodyElement = jqLite(
32653      // this is to avoid using something that exists outside of the body
32654      // we also special case the doc fragement case because our unit test code
32655      // appends the $rootElement to the body after the app has been bootstrapped
32656      isDocumentFragment(rootNode) || bodyNode.contains(rootNode) ? rootNode : bodyNode
32657    );
32658
32659    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
32660
32661    return function initDriverFn(animationDetails) {
32662      return animationDetails.from && animationDetails.to
32663          ? prepareFromToAnchorAnimation(animationDetails.from,
32664                                         animationDetails.to,
32665                                         animationDetails.classes,
32666                                         animationDetails.anchors)
32667          : prepareRegularAnimation(animationDetails);
32668    };
32669
32670    function filterCssClasses(classes) {
32671      //remove all the `ng-` stuff
32672      return classes.replace(/\bng-\S+\b/g, '');
32673    }
32674
32675    function getUniqueValues(a, b) {
32676      if (isString(a)) a = a.split(' ');
32677      if (isString(b)) b = b.split(' ');
32678      return a.filter(function(val) {
32679        return b.indexOf(val) === -1;
32680      }).join(' ');
32681    }
32682
32683    function prepareAnchoredAnimation(classes, outAnchor, inAnchor) {
32684      var clone = jqLite(getDomNode(outAnchor).cloneNode(true));
32685      var startingClasses = filterCssClasses(getClassVal(clone));
32686
32687      outAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME);
32688      inAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME);
32689
32690      clone.addClass(NG_ANIMATE_ANCHOR_CLASS_NAME);
32691
32692      rootBodyElement.append(clone);
32693
32694      var animatorIn, animatorOut = prepareOutAnimation();
32695
32696      // the user may not end up using the `out` animation and
32697      // only making use of the `in` animation or vice-versa.
32698      // In either case we should allow this and not assume the
32699      // animation is over unless both animations are not used.
32700      if (!animatorOut) {
32701        animatorIn = prepareInAnimation();
32702        if (!animatorIn) {
32703          return end();
32704        }
32705      }
32706
32707      var startingAnimator = animatorOut || animatorIn;
32708
32709      return {
32710        start: function() {
32711          var runner;
32712
32713          var currentAnimation = startingAnimator.start();
32714          currentAnimation.done(function() {
32715            currentAnimation = null;
32716            if (!animatorIn) {
32717              animatorIn = prepareInAnimation();
32718              if (animatorIn) {
32719                currentAnimation = animatorIn.start();
32720                currentAnimation.done(function() {
32721                  currentAnimation = null;
32722                  end();
32723                  runner.complete();
32724                });
32725                return currentAnimation;
32726              }
32727            }
32728            // in the event that there is no `in` animation
32729            end();
32730            runner.complete();
32731          });
32732
32733          runner = new $$AnimateRunner({
32734            end: endFn,
32735            cancel: endFn
32736          });
32737
32738          return runner;
32739
32740          function endFn() {
32741            if (currentAnimation) {
32742              currentAnimation.end();
32743            }
32744          }
32745        }
32746      };
32747
32748      function calculateAnchorStyles(anchor) {
32749        var styles = {};
32750
32751        var coords = getDomNode(anchor).getBoundingClientRect();
32752
32753        // we iterate directly since safari messes up and doesn't return
32754        // all the keys for the coods object when iterated
32755        forEach(['width','height','top','left'], function(key) {
32756          var value = coords[key];
32757          switch (key) {
32758            case 'top':
32759              value += bodyNode.scrollTop;
32760              break;
32761            case 'left':
32762              value += bodyNode.scrollLeft;
32763              break;
32764          }
32765          styles[key] = Math.floor(value) + 'px';
32766        });
32767        return styles;
32768      }
32769
32770      function prepareOutAnimation() {
32771        var animator = $animateCss(clone, {
32772          addClass: NG_OUT_ANCHOR_CLASS_NAME,
32773          delay: true,
32774          from: calculateAnchorStyles(outAnchor)
32775        });
32776
32777        // read the comment within `prepareRegularAnimation` to understand
32778        // why this check is necessary
32779        return animator.$$willAnimate ? animator : null;
32780      }
32781
32782      function getClassVal(element) {
32783        return element.attr('class') || '';
32784      }
32785
32786      function prepareInAnimation() {
32787        var endingClasses = filterCssClasses(getClassVal(inAnchor));
32788        var toAdd = getUniqueValues(endingClasses, startingClasses);
32789        var toRemove = getUniqueValues(startingClasses, endingClasses);
32790
32791        var animator = $animateCss(clone, {
32792          to: calculateAnchorStyles(inAnchor),
32793          addClass: NG_IN_ANCHOR_CLASS_NAME + ' ' + toAdd,
32794          removeClass: NG_OUT_ANCHOR_CLASS_NAME + ' ' + toRemove,
32795          delay: true
32796        });
32797
32798        // read the comment within `prepareRegularAnimation` to understand
32799        // why this check is necessary
32800        return animator.$$willAnimate ? animator : null;
32801      }
32802
32803      function end() {
32804        clone.remove();
32805        outAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME);
32806        inAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME);
32807      }
32808    }
32809
32810    function prepareFromToAnchorAnimation(from, to, classes, anchors) {
32811      var fromAnimation = prepareRegularAnimation(from, noop);
32812      var toAnimation = prepareRegularAnimation(to, noop);
32813
32814      var anchorAnimations = [];
32815      forEach(anchors, function(anchor) {
32816        var outElement = anchor['out'];
32817        var inElement = anchor['in'];
32818        var animator = prepareAnchoredAnimation(classes, outElement, inElement);
32819        if (animator) {
32820          anchorAnimations.push(animator);
32821        }
32822      });
32823
32824      // no point in doing anything when there are no elements to animate
32825      if (!fromAnimation && !toAnimation && anchorAnimations.length === 0) return;
32826
32827      return {
32828        start: function() {
32829          var animationRunners = [];
32830
32831          if (fromAnimation) {
32832            animationRunners.push(fromAnimation.start());
32833          }
32834
32835          if (toAnimation) {
32836            animationRunners.push(toAnimation.start());
32837          }
32838
32839          forEach(anchorAnimations, function(animation) {
32840            animationRunners.push(animation.start());
32841          });
32842
32843          var runner = new $$AnimateRunner({
32844            end: endFn,
32845            cancel: endFn // CSS-driven animations cannot be cancelled, only ended
32846          });
32847
32848          $$AnimateRunner.all(animationRunners, function(status) {
32849            runner.complete(status);
32850          });
32851
32852          return runner;
32853
32854          function endFn() {
32855            forEach(animationRunners, function(runner) {
32856              runner.end();
32857            });
32858          }
32859        }
32860      };
32861    }
32862
32863    function prepareRegularAnimation(animationDetails) {
32864      var element = animationDetails.element;
32865      var options = animationDetails.options || {};
32866
32867      if (animationDetails.structural) {
32868        options.event = animationDetails.event;
32869        options.structural = true;
32870        options.applyClassesEarly = true;
32871
32872        // we special case the leave animation since we want to ensure that
32873        // the element is removed as soon as the animation is over. Otherwise
32874        // a flicker might appear or the element may not be removed at all
32875        if (animationDetails.event === 'leave') {
32876          options.onDone = options.domOperation;
32877        }
32878      }
32879
32880      // We assign the preparationClasses as the actual animation event since
32881      // the internals of $animateCss will just suffix the event token values
32882      // with `-active` to trigger the animation.
32883      if (options.preparationClasses) {
32884        options.event = concatWithSpace(options.event, options.preparationClasses);
32885      }
32886
32887      var animator = $animateCss(element, options);
32888
32889      // the driver lookup code inside of $$animation attempts to spawn a
32890      // driver one by one until a driver returns a.$$willAnimate animator object.
32891      // $animateCss will always return an object, however, it will pass in
32892      // a flag as a hint as to whether an animation was detected or not
32893      return animator.$$willAnimate ? animator : null;
32894    }
32895  }];
32896}];
32897
32898// TODO(matsko): use caching here to speed things up for detection
32899// TODO(matsko): add documentation
32900//  by the time...
32901
32902var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
32903  this.$get = ['$injector', '$$AnimateRunner', '$$jqLite',
32904       function($injector,   $$AnimateRunner,   $$jqLite) {
32905
32906    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
32907         // $animateJs(element, 'enter');
32908    return function(element, event, classes, options) {
32909      var animationClosed = false;
32910
32911      // the `classes` argument is optional and if it is not used
32912      // then the classes will be resolved from the element's className
32913      // property as well as options.addClass/options.removeClass.
32914      if (arguments.length === 3 && isObject(classes)) {
32915        options = classes;
32916        classes = null;
32917      }
32918
32919      options = prepareAnimationOptions(options);
32920      if (!classes) {
32921        classes = element.attr('class') || '';
32922        if (options.addClass) {
32923          classes += ' ' + options.addClass;
32924        }
32925        if (options.removeClass) {
32926          classes += ' ' + options.removeClass;
32927        }
32928      }
32929
32930      var classesToAdd = options.addClass;
32931      var classesToRemove = options.removeClass;
32932
32933      // the lookupAnimations function returns a series of animation objects that are
32934      // matched up with one or more of the CSS classes. These animation objects are
32935      // defined via the module.animation factory function. If nothing is detected then
32936      // we don't return anything which then makes $animation query the next driver.
32937      var animations = lookupAnimations(classes);
32938      var before, after;
32939      if (animations.length) {
32940        var afterFn, beforeFn;
32941        if (event == 'leave') {
32942          beforeFn = 'leave';
32943          afterFn = 'afterLeave'; // TODO(matsko): get rid of this
32944        } else {
32945          beforeFn = 'before' + event.charAt(0).toUpperCase() + event.substr(1);
32946          afterFn = event;
32947        }
32948
32949        if (event !== 'enter' && event !== 'move') {
32950          before = packageAnimations(element, event, options, animations, beforeFn);
32951        }
32952        after  = packageAnimations(element, event, options, animations, afterFn);
32953      }
32954
32955      // no matching animations
32956      if (!before && !after) return;
32957
32958      function applyOptions() {
32959        options.domOperation();
32960        applyAnimationClasses(element, options);
32961      }
32962
32963      function close() {
32964        animationClosed = true;
32965        applyOptions();
32966        applyAnimationStyles(element, options);
32967      }
32968
32969      var runner;
32970
32971      return {
32972        $$willAnimate: true,
32973        end: function() {
32974          if (runner) {
32975            runner.end();
32976          } else {
32977            close();
32978            runner = new $$AnimateRunner();
32979            runner.complete(true);
32980          }
32981          return runner;
32982        },
32983        start: function() {
32984          if (runner) {
32985            return runner;
32986          }
32987
32988          runner = new $$AnimateRunner();
32989          var closeActiveAnimations;
32990          var chain = [];
32991
32992          if (before) {
32993            chain.push(function(fn) {
32994              closeActiveAnimations = before(fn);
32995            });
32996          }
32997
32998          if (chain.length) {
32999            chain.push(function(fn) {
33000              applyOptions();
33001              fn(true);
33002            });
33003          } else {
33004            applyOptions();
33005          }
33006
33007          if (after) {
33008            chain.push(function(fn) {
33009              closeActiveAnimations = after(fn);
33010            });
33011          }
33012
33013          runner.setHost({
33014            end: function() {
33015              endAnimations();
33016            },
33017            cancel: function() {
33018              endAnimations(true);
33019            }
33020          });
33021
33022          $$AnimateRunner.chain(chain, onComplete);
33023          return runner;
33024
33025          function onComplete(success) {
33026            close(success);
33027            runner.complete(success);
33028          }
33029
33030          function endAnimations(cancelled) {
33031            if (!animationClosed) {
33032              (closeActiveAnimations || noop)(cancelled);
33033              onComplete(cancelled);
33034            }
33035          }
33036        }
33037      };
33038
33039      function executeAnimationFn(fn, element, event, options, onDone) {
33040        var args;
33041        switch (event) {
33042          case 'animate':
33043            args = [element, options.from, options.to, onDone];
33044            break;
33045
33046          case 'setClass':
33047            args = [element, classesToAdd, classesToRemove, onDone];
33048            break;
33049
33050          case 'addClass':
33051            args = [element, classesToAdd, onDone];
33052            break;
33053
33054          case 'removeClass':
33055            args = [element, classesToRemove, onDone];
33056            break;
33057
33058          default:
33059            args = [element, onDone];
33060            break;
33061        }
33062
33063        args.push(options);
33064
33065        var value = fn.apply(fn, args);
33066        if (value) {
33067          if (isFunction(value.start)) {
33068            value = value.start();
33069          }
33070
33071          if (value instanceof $$AnimateRunner) {
33072            value.done(onDone);
33073          } else if (isFunction(value)) {
33074            // optional onEnd / onCancel callback
33075            return value;
33076          }
33077        }
33078
33079        return noop;
33080      }
33081
33082      function groupEventedAnimations(element, event, options, animations, fnName) {
33083        var operations = [];
33084        forEach(animations, function(ani) {
33085          var animation = ani[fnName];
33086          if (!animation) return;
33087
33088          // note that all of these animations will run in parallel
33089          operations.push(function() {
33090            var runner;
33091            var endProgressCb;
33092
33093            var resolved = false;
33094            var onAnimationComplete = function(rejected) {
33095              if (!resolved) {
33096                resolved = true;
33097                (endProgressCb || noop)(rejected);
33098                runner.complete(!rejected);
33099              }
33100            };
33101
33102            runner = new $$AnimateRunner({
33103              end: function() {
33104                onAnimationComplete();
33105              },
33106              cancel: function() {
33107                onAnimationComplete(true);
33108              }
33109            });
33110
33111            endProgressCb = executeAnimationFn(animation, element, event, options, function(result) {
33112              var cancelled = result === false;
33113              onAnimationComplete(cancelled);
33114            });
33115
33116            return runner;
33117          });
33118        });
33119
33120        return operations;
33121      }
33122
33123      function packageAnimations(element, event, options, animations, fnName) {
33124        var operations = groupEventedAnimations(element, event, options, animations, fnName);
33125        if (operations.length === 0) {
33126          var a,b;
33127          if (fnName === 'beforeSetClass') {
33128            a = groupEventedAnimations(element, 'removeClass', options, animations, 'beforeRemoveClass');
33129            b = groupEventedAnimations(element, 'addClass', options, animations, 'beforeAddClass');
33130          } else if (fnName === 'setClass') {
33131            a = groupEventedAnimations(element, 'removeClass', options, animations, 'removeClass');
33132            b = groupEventedAnimations(element, 'addClass', options, animations, 'addClass');
33133          }
33134
33135          if (a) {
33136            operations = operations.concat(a);
33137          }
33138          if (b) {
33139            operations = operations.concat(b);
33140          }
33141        }
33142
33143        if (operations.length === 0) return;
33144
33145        // TODO(matsko): add documentation
33146        return function startAnimation(callback) {
33147          var runners = [];
33148          if (operations.length) {
33149            forEach(operations, function(animateFn) {
33150              runners.push(animateFn());
33151            });
33152          }
33153
33154          runners.length ? $$AnimateRunner.all(runners, callback) : callback();
33155
33156          return function endFn(reject) {
33157            forEach(runners, function(runner) {
33158              reject ? runner.cancel() : runner.end();
33159            });
33160          };
33161        };
33162      }
33163    };
33164
33165    function lookupAnimations(classes) {
33166      classes = isArray(classes) ? classes : classes.split(' ');
33167      var matches = [], flagMap = {};
33168      for (var i=0; i < classes.length; i++) {
33169        var klass = classes[i],
33170            animationFactory = $animateProvider.$$registeredAnimations[klass];
33171        if (animationFactory && !flagMap[klass]) {
33172          matches.push($injector.get(animationFactory));
33173          flagMap[klass] = true;
33174        }
33175      }
33176      return matches;
33177    }
33178  }];
33179}];
33180
33181var $$AnimateJsDriverProvider = ['$$animationProvider', function($$animationProvider) {
33182  $$animationProvider.drivers.push('$$animateJsDriver');
33183  this.$get = ['$$animateJs', '$$AnimateRunner', function($$animateJs, $$AnimateRunner) {
33184    return function initDriverFn(animationDetails) {
33185      if (animationDetails.from && animationDetails.to) {
33186        var fromAnimation = prepareAnimation(animationDetails.from);
33187        var toAnimation = prepareAnimation(animationDetails.to);
33188        if (!fromAnimation && !toAnimation) return;
33189
33190        return {
33191          start: function() {
33192            var animationRunners = [];
33193
33194            if (fromAnimation) {
33195              animationRunners.push(fromAnimation.start());
33196            }
33197
33198            if (toAnimation) {
33199              animationRunners.push(toAnimation.start());
33200            }
33201
33202            $$AnimateRunner.all(animationRunners, done);
33203
33204            var runner = new $$AnimateRunner({
33205              end: endFnFactory(),
33206              cancel: endFnFactory()
33207            });
33208
33209            return runner;
33210
33211            function endFnFactory() {
33212              return function() {
33213                forEach(animationRunners, function(runner) {
33214                  // at this point we cannot cancel animations for groups just yet. 1.5+
33215                  runner.end();
33216                });
33217              };
33218            }
33219
33220            function done(status) {
33221              runner.complete(status);
33222            }
33223          }
33224        };
33225      } else {
33226        return prepareAnimation(animationDetails);
33227      }
33228    };
33229
33230    function prepareAnimation(animationDetails) {
33231      // TODO(matsko): make sure to check for grouped animations and delegate down to normal animations
33232      var element = animationDetails.element;
33233      var event = animationDetails.event;
33234      var options = animationDetails.options;
33235      var classes = animationDetails.classes;
33236      return $$animateJs(element, event, classes, options);
33237    }
33238  }];
33239}];
33240
33241var NG_ANIMATE_ATTR_NAME = 'data-ng-animate';
33242var NG_ANIMATE_PIN_DATA = '$ngAnimatePin';
33243var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
33244  var PRE_DIGEST_STATE = 1;
33245  var RUNNING_STATE = 2;
33246  var ONE_SPACE = ' ';
33247
33248  var rules = this.rules = {
33249    skip: [],
33250    cancel: [],
33251    join: []
33252  };
33253
33254  function makeTruthyCssClassMap(classString) {
33255    if (!classString) {
33256      return null;
33257    }
33258
33259    var keys = classString.split(ONE_SPACE);
33260    var map = Object.create(null);
33261
33262    forEach(keys, function(key) {
33263      map[key] = true;
33264    });
33265    return map;
33266  }
33267
33268  function hasMatchingClasses(newClassString, currentClassString) {
33269    if (newClassString && currentClassString) {
33270      var currentClassMap = makeTruthyCssClassMap(currentClassString);
33271      return newClassString.split(ONE_SPACE).some(function(className) {
33272        return currentClassMap[className];
33273      });
33274    }
33275  }
33276
33277  function isAllowed(ruleType, element, currentAnimation, previousAnimation) {
33278    return rules[ruleType].some(function(fn) {
33279      return fn(element, currentAnimation, previousAnimation);
33280    });
33281  }
33282
33283  function hasAnimationClasses(animation, and) {
33284    var a = (animation.addClass || '').length > 0;
33285    var b = (animation.removeClass || '').length > 0;
33286    return and ? a && b : a || b;
33287  }
33288
33289  rules.join.push(function(element, newAnimation, currentAnimation) {
33290    // if the new animation is class-based then we can just tack that on
33291    return !newAnimation.structural && hasAnimationClasses(newAnimation);
33292  });
33293
33294  rules.skip.push(function(element, newAnimation, currentAnimation) {
33295    // there is no need to animate anything if no classes are being added and
33296    // there is no structural animation that will be triggered
33297    return !newAnimation.structural && !hasAnimationClasses(newAnimation);
33298  });
33299
33300  rules.skip.push(function(element, newAnimation, currentAnimation) {
33301    // why should we trigger a new structural animation if the element will
33302    // be removed from the DOM anyway?
33303    return currentAnimation.event == 'leave' && newAnimation.structural;
33304  });
33305
33306  rules.skip.push(function(element, newAnimation, currentAnimation) {
33307    // if there is an ongoing current animation then don't even bother running the class-based animation
33308    return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural;
33309  });
33310
33311  rules.cancel.push(function(element, newAnimation, currentAnimation) {
33312    // there can never be two structural animations running at the same time
33313    return currentAnimation.structural && newAnimation.structural;
33314  });
33315
33316  rules.cancel.push(function(element, newAnimation, currentAnimation) {
33317    // if the previous animation is already running, but the new animation will
33318    // be triggered, but the new animation is structural
33319    return currentAnimation.state === RUNNING_STATE && newAnimation.structural;
33320  });
33321
33322  rules.cancel.push(function(element, newAnimation, currentAnimation) {
33323    var nA = newAnimation.addClass;
33324    var nR = newAnimation.removeClass;
33325    var cA = currentAnimation.addClass;
33326    var cR = currentAnimation.removeClass;
33327
33328    // early detection to save the global CPU shortage :)
33329    if ((isUndefined(nA) && isUndefined(nR)) || (isUndefined(cA) && isUndefined(cR))) {
33330      return false;
33331    }
33332
33333    return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA);
33334  });
33335
33336  this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
33337               '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',
33338       function($$rAF,   $rootScope,   $rootElement,   $document,   $$HashMap,
33339                $$animation,   $$AnimateRunner,   $templateRequest,   $$jqLite,   $$forceReflow) {
33340
33341    var activeAnimationsLookup = new $$HashMap();
33342    var disabledElementsLookup = new $$HashMap();
33343    var animationsEnabled = null;
33344    // $document might be mocked out in tests and won't include a real document.
33345    // Providing an empty object with hidden = true will prevent animations from running
33346    var rawDocument = $document[0] || {hidden: true};
33347
33348    function postDigestTaskFactory() {
33349      var postDigestCalled = false;
33350      return function(fn) {
33351        // we only issue a call to postDigest before
33352        // it has first passed. This prevents any callbacks
33353        // from not firing once the animation has completed
33354        // since it will be out of the digest cycle.
33355        if (postDigestCalled) {
33356          fn();
33357        } else {
33358          $rootScope.$$postDigest(function() {
33359            postDigestCalled = true;
33360            fn();
33361          });
33362        }
33363      };
33364    }
33365
33366    // Wait until all directive and route-related templates are downloaded and
33367    // compiled. The $templateRequest.totalPendingRequests variable keeps track of
33368    // all of the remote templates being currently downloaded. If there are no
33369    // templates currently downloading then the watcher will still fire anyway.
33370    var deregisterWatch = $rootScope.$watch(
33371      function() { return $templateRequest.totalPendingRequests === 0; },
33372      function(isEmpty) {
33373        if (!isEmpty) return;
33374        deregisterWatch();
33375
33376        // Now that all templates have been downloaded, $animate will wait until
33377        // the post digest queue is empty before enabling animations. By having two
33378        // calls to $postDigest calls we can ensure that the flag is enabled at the
33379        // very end of the post digest queue. Since all of the animations in $animate
33380        // use $postDigest, it's important that the code below executes at the end.
33381        // This basically means that the page is fully downloaded and compiled before
33382        // any animations are triggered.
33383        $rootScope.$$postDigest(function() {
33384          $rootScope.$$postDigest(function() {
33385            // we check for null directly in the event that the application already called
33386            // .enabled() with whatever arguments that it provided it with
33387            if (animationsEnabled === null) {
33388              animationsEnabled = true;
33389            }
33390          });
33391        });
33392      }
33393    );
33394
33395    var callbackRegistry = {};
33396
33397    // remember that the classNameFilter is set during the provider/config
33398    // stage therefore we can optimize here and setup a helper function
33399    var classNameFilter = $animateProvider.classNameFilter();
33400    var isAnimatableClassName = !classNameFilter
33401              ? function() { return true; }
33402              : function(className) {
33403                return classNameFilter.test(className);
33404              };
33405
33406    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
33407
33408    function normalizeAnimationDetails(element, animation) {
33409      return mergeAnimationDetails(element, animation, {});
33410    }
33411
33412    // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
33413    var contains = Node.prototype.contains || function(arg) {
33414      // jshint bitwise: false
33415      return this === arg || !!(this.compareDocumentPosition(arg) & 16);
33416      // jshint bitwise: true
33417    };
33418
33419    function findCallbacks(parent, element, event) {
33420      var targetNode = getDomNode(element);
33421      var targetParentNode = getDomNode(parent);
33422
33423      var matches = [];
33424      var entries = callbackRegistry[event];
33425      if (entries) {
33426        forEach(entries, function(entry) {
33427          if (contains.call(entry.node, targetNode)) {
33428            matches.push(entry.callback);
33429          } else if (event === 'leave' && contains.call(entry.node, targetParentNode)) {
33430            matches.push(entry.callback);
33431          }
33432        });
33433      }
33434
33435      return matches;
33436    }
33437
33438    return {
33439      on: function(event, container, callback) {
33440        var node = extractElementNode(container);
33441        callbackRegistry[event] = callbackRegistry[event] || [];
33442        callbackRegistry[event].push({
33443          node: node,
33444          callback: callback
33445        });
33446      },
33447
33448      off: function(event, container, callback) {
33449        var entries = callbackRegistry[event];
33450        if (!entries) return;
33451
33452        callbackRegistry[event] = arguments.length === 1
33453            ? null
33454            : filterFromRegistry(entries, container, callback);
33455
33456        function filterFromRegistry(list, matchContainer, matchCallback) {
33457          var containerNode = extractElementNode(matchContainer);
33458          return list.filter(function(entry) {
33459            var isMatch = entry.node === containerNode &&
33460                            (!matchCallback || entry.callback === matchCallback);
33461            return !isMatch;
33462          });
33463        }
33464      },
33465
33466      pin: function(element, parentElement) {
33467        assertArg(isElement(element), 'element', 'not an element');
33468        assertArg(isElement(parentElement), 'parentElement', 'not an element');
33469        element.data(NG_ANIMATE_PIN_DATA, parentElement);
33470      },
33471
33472      push: function(element, event, options, domOperation) {
33473        options = options || {};
33474        options.domOperation = domOperation;
33475        return queueAnimation(element, event, options);
33476      },
33477
33478      // this method has four signatures:
33479      //  () - global getter
33480      //  (bool) - global setter
33481      //  (element) - element getter
33482      //  (element, bool) - element setter<F37>
33483      enabled: function(element, bool) {
33484        var argCount = arguments.length;
33485
33486        if (argCount === 0) {
33487          // () - Global getter
33488          bool = !!animationsEnabled;
33489        } else {
33490          var hasElement = isElement(element);
33491
33492          if (!hasElement) {
33493            // (bool) - Global setter
33494            bool = animationsEnabled = !!element;
33495          } else {
33496            var node = getDomNode(element);
33497            var recordExists = disabledElementsLookup.get(node);
33498
33499            if (argCount === 1) {
33500              // (element) - Element getter
33501              bool = !recordExists;
33502            } else {
33503              // (element, bool) - Element setter
33504              disabledElementsLookup.put(node, !bool);
33505            }
33506          }
33507        }
33508
33509        return bool;
33510      }
33511    };
33512
33513    function queueAnimation(element, event, initialOptions) {
33514      // we always make a copy of the options since
33515      // there should never be any side effects on
33516      // the input data when running `$animateCss`.
33517      var options = copy(initialOptions);
33518
33519      var node, parent;
33520      element = stripCommentsFromElement(element);
33521      if (element) {
33522        node = getDomNode(element);
33523        parent = element.parent();
33524      }
33525
33526      options = prepareAnimationOptions(options);
33527
33528      // we create a fake runner with a working promise.
33529      // These methods will become available after the digest has passed
33530      var runner = new $$AnimateRunner();
33531
33532      // this is used to trigger callbacks in postDigest mode
33533      var runInNextPostDigestOrNow = postDigestTaskFactory();
33534
33535      if (isArray(options.addClass)) {
33536        options.addClass = options.addClass.join(' ');
33537      }
33538
33539      if (options.addClass && !isString(options.addClass)) {
33540        options.addClass = null;
33541      }
33542
33543      if (isArray(options.removeClass)) {
33544        options.removeClass = options.removeClass.join(' ');
33545      }
33546
33547      if (options.removeClass && !isString(options.removeClass)) {
33548        options.removeClass = null;
33549      }
33550
33551      if (options.from && !isObject(options.from)) {
33552        options.from = null;
33553      }
33554
33555      if (options.to && !isObject(options.to)) {
33556        options.to = null;
33557      }
33558
33559      // there are situations where a directive issues an animation for
33560      // a jqLite wrapper that contains only comment nodes... If this
33561      // happens then there is no way we can perform an animation
33562      if (!node) {
33563        close();
33564        return runner;
33565      }
33566
33567      var className = [node.className, options.addClass, options.removeClass].join(' ');
33568      if (!isAnimatableClassName(className)) {
33569        close();
33570        return runner;
33571      }
33572
33573      var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;
33574
33575      var documentHidden = rawDocument.hidden;
33576
33577      // this is a hard disable of all animations for the application or on
33578      // the element itself, therefore  there is no need to continue further
33579      // past this point if not enabled
33580      // Animations are also disabled if the document is currently hidden (page is not visible
33581      // to the user), because browsers slow down or do not flush calls to requestAnimationFrame
33582      var skipAnimations = !animationsEnabled || documentHidden || disabledElementsLookup.get(node);
33583      var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {};
33584      var hasExistingAnimation = !!existingAnimation.state;
33585
33586      // there is no point in traversing the same collection of parent ancestors if a followup
33587      // animation will be run on the same element that already did all that checking work
33588      if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state != PRE_DIGEST_STATE)) {
33589        skipAnimations = !areAnimationsAllowed(element, parent, event);
33590      }
33591
33592      if (skipAnimations) {
33593        // Callbacks should fire even if the document is hidden (regression fix for issue #14120)
33594        if (documentHidden) notifyProgress(runner, event, 'start');
33595        close();
33596        if (documentHidden) notifyProgress(runner, event, 'close');
33597        return runner;
33598      }
33599
33600      if (isStructural) {
33601        closeChildAnimations(element);
33602      }
33603
33604      var newAnimation = {
33605        structural: isStructural,
33606        element: element,
33607        event: event,
33608        addClass: options.addClass,
33609        removeClass: options.removeClass,
33610        close: close,
33611        options: options,
33612        runner: runner
33613      };
33614
33615      if (hasExistingAnimation) {
33616        var skipAnimationFlag = isAllowed('skip', element, newAnimation, existingAnimation);
33617        if (skipAnimationFlag) {
33618          if (existingAnimation.state === RUNNING_STATE) {
33619            close();
33620            return runner;
33621          } else {
33622            mergeAnimationDetails(element, existingAnimation, newAnimation);
33623            return existingAnimation.runner;
33624          }
33625        }
33626        var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation);
33627        if (cancelAnimationFlag) {
33628          if (existingAnimation.state === RUNNING_STATE) {
33629            // this will end the animation right away and it is safe
33630            // to do so since the animation is already running and the
33631            // runner callback code will run in async
33632            existingAnimation.runner.end();
33633          } else if (existingAnimation.structural) {
33634            // this means that the animation is queued into a digest, but
33635            // hasn't started yet. Therefore it is safe to run the close
33636            // method which will call the runner methods in async.
33637            existingAnimation.close();
33638          } else {
33639            // this will merge the new animation options into existing animation options
33640            mergeAnimationDetails(element, existingAnimation, newAnimation);
33641
33642            return existingAnimation.runner;
33643          }
33644        } else {
33645          // a joined animation means that this animation will take over the existing one
33646          // so an example would involve a leave animation taking over an enter. Then when
33647          // the postDigest kicks in the enter will be ignored.
33648          var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation);
33649          if (joinAnimationFlag) {
33650            if (existingAnimation.state === RUNNING_STATE) {
33651              normalizeAnimationDetails(element, newAnimation);
33652            } else {
vendor: 4,089 bytes, lines 33653-33738
33653              applyGeneratedPreparationClasses(element, isStructural ? event : null, options);
33654
33655              event = newAnimation.event = existingAnimation.event;
33656              options = mergeAnimationDetails(element, existingAnimation, newAnimation);
33657
33658              //we return the same runner since only the option values of this animation will
33659              //be fed into the `existingAnimation`.
33660              return existingAnimation.runner;
33661            }
33662          }
33663        }
33664      } else {
33665        // normalization in this case means that it removes redundant CSS classes that
33666        // already exist (addClass) or do not exist (removeClass) on the element
33667        normalizeAnimationDetails(element, newAnimation);
33668      }
33669
33670      // when the options are merged and cleaned up we may end up not having to do
33671      // an animation at all, therefore we should check this before issuing a post
33672      // digest callback. Structural animations will always run no matter what.
33673      var isValidAnimation = newAnimation.structural;
33674      if (!isValidAnimation) {
33675        // animate (from/to) can be quickly checked first, otherwise we check if any classes are present
33676        isValidAnimation = (newAnimation.event === 'animate' && Object.keys(newAnimation.options.to || {}).length > 0)
33677                            || hasAnimationClasses(newAnimation);
33678      }
33679
33680      if (!isValidAnimation) {
33681        close();
33682        clearElementAnimationState(element);
33683        return runner;
33684      }
33685
33686      // the counter keeps track of cancelled animations
33687      var counter = (existingAnimation.counter || 0) + 1;
33688      newAnimation.counter = counter;
33689
33690      markElementAnimationState(element, PRE_DIGEST_STATE, newAnimation);
33691
33692      $rootScope.$$postDigest(function() {
33693        var animationDetails = activeAnimationsLookup.get(node);
33694        var animationCancelled = !animationDetails;
33695        animationDetails = animationDetails || {};
33696
33697        // if addClass/removeClass is called before something like enter then the
33698        // registered parent element may not be present. The code below will ensure
33699        // that a final value for parent element is obtained
33700        var parentElement = element.parent() || [];
33701
33702        // animate/structural/class-based animations all have requirements. Otherwise there
33703        // is no point in performing an animation. The parent node must also be set.
33704        var isValidAnimation = parentElement.length > 0
33705                                && (animationDetails.event === 'animate'
33706                                    || animationDetails.structural
33707                                    || hasAnimationClasses(animationDetails));
33708
33709        // this means that the previous animation was cancelled
33710        // even if the follow-up animation is the same event
33711        if (animationCancelled || animationDetails.counter !== counter || !isValidAnimation) {
33712          // if another animation did not take over then we need
33713          // to make sure that the domOperation and options are
33714          // handled accordingly
33715          if (animationCancelled) {
33716            applyAnimationClasses(element, options);
33717            applyAnimationStyles(element, options);
33718          }
33719
33720          // if the event changed from something like enter to leave then we do
33721          // it, otherwise if it's the same then the end result will be the same too
33722          if (animationCancelled || (isStructural && animationDetails.event !== event)) {
33723            options.domOperation();
33724            runner.end();
33725          }
33726
33727          // in the event that the element animation was not cancelled or a follow-up animation
33728          // isn't allowed to animate from here then we need to clear the state of the element
33729          // so that any future animations won't read the expired animation data.
33730          if (!isValidAnimation) {
33731            clearElementAnimationState(element);
33732          }
33733
33734          return;
33735        }
33736
33737        // this combined multiple class to addClass / removeClass into a setClass event
33738        // so long as a structural event did not take over the animation
33739        event = !animationDetails.structural && hasAnimationClasses(animationDetails, true)
33740            ? 'setClass'
33741            : animationDetails.event;
33742
33743        markElementAnimationState(element, RUNNING_STATE);
33744        var realRunner = $$animation(element, event, animationDetails.options);
33745
33746        // this will update the runner's flow-control events based on
33747        // the `realRunner` object.
33748        runner.setHost(realRunner);
33749        notifyProgress(runner, event, 'start', {});
33750
33751        realRunner.done(function(status) {
33752          close(!status);
33753          var animationDetails = activeAnimationsLookup.get(node);
33754          if (animationDetails && animationDetails.counter === counter) {
33755            clearElementAnimationState(getDomNode(element));
33756          }
33757          notifyProgress(runner, event, 'close', {});
33758        });
33759      });
33760
33761      return runner;
33762
33763      function notifyProgress(runner, event, phase, data) {
33764        runInNextPostDigestOrNow(function() {
33765          var callbacks = findCallbacks(parent, element, event);
33766          if (callbacks.length) {
33767            // do not optimize this call here to RAF because
33768            // we don't know how heavy the callback code here will
33769            // be and if this code is buffered then this can
33770            // lead to a performance regression.
33771            $$rAF(function() {
33772              forEach(callbacks, function(callback) {
33773                callback(element, phase, data);
33774              });
33775            });
33776          }
33777        });
33778        runner.progress(event, phase, data);
33779      }
33780
33781      function close(reject) { // jshint ignore:line
33782        clearGeneratedClasses(element, options);
33783        applyAnimationClasses(element, options);
33784        applyAnimationStyles(element, options);
33785        options.domOperation();
33786        runner.complete(!reject);
33787      }
33788    }
33789
33790    function closeChildAnimations(element) {
33791      var node = getDomNode(element);
33792      var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']');
33793      forEach(children, function(child) {
33794        var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME));
33795        var animationDetails = activeAnimationsLookup.get(child);
33796        if (animationDetails) {
33797          switch (state) {
33798            case RUNNING_STATE:
33799              animationDetails.runner.end();
33800              /* falls through */
33801            case PRE_DIGEST_STATE:
33802              activeAnimationsLookup.remove(child);
33803              break;
33804          }
33805        }
33806      });
33807    }
33808
33809    function clearElementAnimationState(element) {
33810      var node = getDomNode(element);
33811      node.removeAttribute(NG_ANIMATE_ATTR_NAME);
33812      activeAnimationsLookup.remove(node);
33813    }
33814
33815    function isMatchingElement(nodeOrElmA, nodeOrElmB) {
33816      return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB);
33817    }
33818
33819    /**
33820     * This fn returns false if any of the following is true:
33821     * a) animations on any parent element are disabled, and animations on the element aren't explicitly allowed
33822     * b) a parent element has an ongoing structural animation, and animateChildren is false
33823     * c) the element is not a child of the body
33824     * d) the element is not a child of the $rootElement
33825     */
33826    function areAnimationsAllowed(element, parentElement, event) {
33827      var bodyElement = jqLite(rawDocument.body);
33828      var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
33829      var rootElementDetected = isMatchingElement(element, $rootElement);
33830      var parentAnimationDetected = false;
33831      var animateChildren;
33832      var elementDisabled = disabledElementsLookup.get(getDomNode(element));
33833
33834      var parentHost = jqLite.data(element[0], NG_ANIMATE_PIN_DATA);
33835      if (parentHost) {
33836        parentElement = parentHost;
33837      }
33838
33839      parentElement = getDomNode(parentElement);
33840
33841      while (parentElement) {
33842        if (!rootElementDetected) {
33843          // angular doesn't want to attempt to animate elements outside of the application
33844          // therefore we need to ensure that the rootElement is an ancestor of the current element
33845          rootElementDetected = isMatchingElement(parentElement, $rootElement);
33846        }
33847
33848        if (parentElement.nodeType !== ELEMENT_NODE) {
33849          // no point in inspecting the #document element
33850          break;
33851        }
33852
33853        var details = activeAnimationsLookup.get(parentElement) || {};
33854        // either an enter, leave or move animation will commence
33855        // therefore we can't allow any animations to take place
33856        // but if a parent animation is class-based then that's ok
33857        if (!parentAnimationDetected) {
33858          var parentElementDisabled = disabledElementsLookup.get(parentElement);
33859
33860          if (parentElementDisabled === true && elementDisabled !== false) {
33861            // disable animations if the user hasn't explicitly enabled animations on the
33862            // current element
33863            elementDisabled = true;
33864            // element is disabled via parent element, no need to check anything else
33865            break;
33866          } else if (parentElementDisabled === false) {
33867            elementDisabled = false;
vendor: 4,740 bytes, lines 33868-34018
33868          }
33869          parentAnimationDetected = details.structural;
33870        }
33871
33872        if (isUndefined(animateChildren) || animateChildren === true) {
33873          var value = jqLite.data(parentElement, NG_ANIMATE_CHILDREN_DATA);
33874          if (isDefined(value)) {
33875            animateChildren = value;
33876          }
33877        }
33878
33879        // there is no need to continue traversing at this point
33880        if (parentAnimationDetected && animateChildren === false) break;
33881
33882        if (!bodyElementDetected) {
33883          // we also need to ensure that the element is or will be a part of the body element
33884          // otherwise it is pointless to even issue an animation to be rendered
33885          bodyElementDetected = isMatchingElement(parentElement, bodyElement);
33886        }
33887
33888        if (bodyElementDetected && rootElementDetected) {
33889          // If both body and root have been found, any other checks are pointless,
33890          // as no animation data should live outside the application
33891          break;
33892        }
33893
33894        if (!rootElementDetected) {
33895          // If no rootElement is detected, check if the parentElement is pinned to another element
33896          parentHost = jqLite.data(parentElement, NG_ANIMATE_PIN_DATA);
33897          if (parentHost) {
33898            // The pin target element becomes the next parent element
33899            parentElement = getDomNode(parentHost);
33900            continue;
33901          }
33902        }
33903
33904        parentElement = parentElement.parentNode;
33905      }
33906
33907      var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true;
33908      return allowAnimation && rootElementDetected && bodyElementDetected;
33909    }
33910
33911    function markElementAnimationState(element, state, details) {
33912      details = details || {};
33913      details.state = state;
33914
33915      var node = getDomNode(element);
33916      node.setAttribute(NG_ANIMATE_ATTR_NAME, state);
33917
33918      var oldValue = activeAnimationsLookup.get(node);
33919      var newValue = oldValue
33920          ? extend(oldValue, details)
33921          : details;
33922      activeAnimationsLookup.put(node, newValue);
33923    }
33924  }];
33925}];
33926
33927var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
33928  var NG_ANIMATE_REF_ATTR = 'ng-animate-ref';
33929
33930  var drivers = this.drivers = [];
33931
33932  var RUNNER_STORAGE_KEY = '$$animationRunner';
33933
33934  function setRunner(element, runner) {
33935    element.data(RUNNER_STORAGE_KEY, runner);
33936  }
33937
33938  function removeRunner(element) {
33939    element.removeData(RUNNER_STORAGE_KEY);
33940  }
33941
33942  function getRunner(element) {
33943    return element.data(RUNNER_STORAGE_KEY);
33944  }
33945
33946  this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler',
33947       function($$jqLite,   $rootScope,   $injector,   $$AnimateRunner,   $$HashMap,   $$rAFScheduler) {
33948
33949    var animationQueue = [];
33950    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
33951
33952    function sortAnimations(animations) {
33953      var tree = { children: [] };
33954      var i, lookup = new $$HashMap();
33955
33956      // this is done first beforehand so that the hashmap
33957      // is filled with a list of the elements that will be animated
33958      for (i = 0; i < animations.length; i++) {
33959        var animation = animations[i];
33960        lookup.put(animation.domNode, animations[i] = {
33961          domNode: animation.domNode,
33962          fn: animation.fn,
33963          children: []
33964        });
33965      }
33966
33967      for (i = 0; i < animations.length; i++) {
33968        processNode(animations[i]);
33969      }
33970
33971      return flatten(tree);
33972
33973      function processNode(entry) {
33974        if (entry.processed) return entry;
33975        entry.processed = true;
33976
33977        var elementNode = entry.domNode;
33978        var parentNode = elementNode.parentNode;
33979        lookup.put(elementNode, entry);
33980
33981        var parentEntry;
33982        while (parentNode) {
33983          parentEntry = lookup.get(parentNode);
33984          if (parentEntry) {
33985            if (!parentEntry.processed) {
33986              parentEntry = processNode(parentEntry);
33987            }
33988            break;
33989          }
33990          parentNode = parentNode.parentNode;
33991        }
33992
33993        (parentEntry || tree).children.push(entry);
33994        return entry;
33995      }
33996
33997      function flatten(tree) {
33998        var result = [];
33999        var queue = [];
34000        var i;
34001
34002        for (i = 0; i < tree.children.length; i++) {
34003          queue.push(tree.children[i]);
34004        }
34005
34006        var remainingLevelEntries = queue.length;
34007        var nextLevelEntries = 0;
34008        var row = [];
34009
34010        for (i = 0; i < queue.length; i++) {
34011          var entry = queue[i];
34012          if (remainingLevelEntries <= 0) {
34013            remainingLevelEntries = nextLevelEntries;
34014            nextLevelEntries = 0;
34015            result.push(row);
34016            row = [];
34017          }
34018          row.push(entry.fn);
34019          entry.children.forEach(function(childEntry) {
34020            nextLevelEntries++;
34021            queue.push(childEntry);
34022          });
34023          remainingLevelEntries--;
34024        }
34025
34026        if (row.length) {
34027          result.push(row);
34028        }
34029
34030        return result;
34031      }
34032    }
34033
34034    // TODO(matsko): document the signature in a better way
34035    return function(element, event, options) {
34036      options = prepareAnimationOptions(options);
34037      var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;
34038
34039      // there is no animation at the current moment, however
34040      // these runner methods will get later updated with the
34041      // methods leading into the driver's end/cancel methods
34042      // for now they just stop the animation from starting
34043      var runner = new $$AnimateRunner({
34044        end: function() { close(); },
34045        cancel: function() { close(true); }
34046      });
34047
34048      if (!drivers.length) {
34049        close();
34050        return runner;
34051      }
34052
34053      setRunner(element, runner);
34054
34055      var classes = mergeClasses(element.attr('class'), mergeClasses(options.addClass, options.removeClass));
34056      var tempClasses = options.tempClasses;
34057      if (tempClasses) {
34058        classes += ' ' + tempClasses;
34059        options.tempClasses = null;
34060      }
34061
34062      var prepareClassName;
34063      if (isStructural) {
34064        prepareClassName = 'ng-' + event + PREPARE_CLASS_SUFFIX;
34065        $$jqLite.addClass(element, prepareClassName);
34066      }
34067
34068      animationQueue.push({
34069        // this data is used by the postDigest code and passed into
34070        // the driver step function
34071        element: element,
34072        classes: classes,
34073        event: event,
34074        structural: isStructural,
34075        options: options,
34076        beforeStart: beforeStart,
34077        close: close
34078      });
34079
34080      element.on('$destroy', handleDestroyedElement);
34081
34082      // we only want there to be one function called within the post digest
34083      // block. This way we can group animations for all the animations that
34084      // were apart of the same postDigest flush call.
34085      if (animationQueue.length > 1) return runner;
34086
34087      $rootScope.$$postDigest(function() {
34088        var animations = [];
34089        forEach(animationQueue, function(entry) {
34090          // the element was destroyed early on which removed the runner
34091          // form its storage. This means we can't animate this element
34092          // at all and it already has been closed due to destruction.
34093          if (getRunner(entry.element)) {
34094            animations.push(entry);
34095          } else {
34096            entry.close();
34097          }
34098        });
34099
34100        // now any future animations will be in another postDigest
34101        animationQueue.length = 0;
34102
34103        var groupedAnimations = groupAnimations(animations);
34104        var toBeSortedAnimations = [];
34105
34106        forEach(groupedAnimations, function(animationEntry) {
34107          toBeSortedAnimations.push({
34108            domNode: getDomNode(animationEntry.from ? animationEntry.from.element : animationEntry.element),
34109            fn: function triggerAnimationStart() {
34110              // it's important that we apply the `ng-animate` CSS class and the
34111              // temporary classes before we do any driver invoking since these
34112              // CSS classes may be required for proper CSS detection.
34113              animationEntry.beforeStart();
34114
34115              var startAnimationFn, closeFn = animationEntry.close;
34116
34117              // in the event that the element was removed before the digest runs or
34118              // during the RAF sequencing then we should not trigger the animation.
34119              var targetElement = animationEntry.anchors
34120                  ? (animationEntry.from.element || animationEntry.to.element)
34121                  : animationEntry.element;
34122
34123              if (getRunner(targetElement)) {
34124                var operation = invokeFirstDriver(animationEntry);
34125                if (operation) {
34126                  startAnimationFn = operation.start;
34127                }
34128              }
34129
34130              if (!startAnimationFn) {
34131                closeFn();
34132              } else {
34133                var animationRunner = startAnimationFn();
34134                animationRunner.done(function(status) {
34135                  closeFn(!status);
34136                });
34137                updateAnimationRunners(animationEntry, animationRunner);
34138              }
34139            }
34140          });
34141        });
34142
34143        // we need to sort each of the animations in order of parent to child
34144        // relationships. This ensures that the child classes are applied at the
34145        // right time.
34146        $$rAFScheduler(sortAnimations(toBeSortedAnimations));
34147      });
34148
34149      return runner;
34150
34151      // TODO(matsko): change to reference nodes
34152      function getAnchorNodes(node) {
34153        var SELECTOR = '[' + NG_ANIMATE_REF_ATTR + ']';
34154        var items = node.hasAttribute(NG_ANIMATE_REF_ATTR)
34155              ? [node]
34156              : node.querySelectorAll(SELECTOR);
34157        var anchors = [];
vendor: 4,203 bytes, lines 34158-34276
34158        forEach(items, function(node) {
34159          var attr = node.getAttribute(NG_ANIMATE_REF_ATTR);
34160          if (attr && attr.length) {
34161            anchors.push(node);
34162          }
34163        });
34164        return anchors;
34165      }
34166
34167      function groupAnimations(animations) {
34168        var preparedAnimations = [];
34169        var refLookup = {};
34170        forEach(animations, function(animation, index) {
34171          var element = animation.element;
34172          var node = getDomNode(element);
34173          var event = animation.event;
34174          var enterOrMove = ['enter', 'move'].indexOf(event) >= 0;
34175          var anchorNodes = animation.structural ? getAnchorNodes(node) : [];
34176
34177          if (anchorNodes.length) {
34178            var direction = enterOrMove ? 'to' : 'from';
34179
34180            forEach(anchorNodes, function(anchor) {
34181              var key = anchor.getAttribute(NG_ANIMATE_REF_ATTR);
34182              refLookup[key] = refLookup[key] || {};
34183              refLookup[key][direction] = {
34184                animationID: index,
34185                element: jqLite(anchor)
34186              };
34187            });
34188          } else {
34189            preparedAnimations.push(animation);
34190          }
34191        });
34192
34193        var usedIndicesLookup = {};
34194        var anchorGroups = {};
34195        forEach(refLookup, function(operations, key) {
34196          var from = operations.from;
34197          var to = operations.to;
34198
34199          if (!from || !to) {
34200            // only one of these is set therefore we can't have an
34201            // anchor animation since all three pieces are required
34202            var index = from ? from.animationID : to.animationID;
34203            var indexKey = index.toString();
34204            if (!usedIndicesLookup[indexKey]) {
34205              usedIndicesLookup[indexKey] = true;
34206              preparedAnimations.push(animations[index]);
34207            }
34208            return;
34209          }
34210
34211          var fromAnimation = animations[from.animationID];
34212          var toAnimation = animations[to.animationID];
34213          var lookupKey = from.animationID.toString();
34214          if (!anchorGroups[lookupKey]) {
34215            var group = anchorGroups[lookupKey] = {
34216              structural: true,
34217              beforeStart: function() {
34218                fromAnimation.beforeStart();
34219                toAnimation.beforeStart();
34220              },
34221              close: function() {
34222                fromAnimation.close();
34223                toAnimation.close();
34224              },
34225              classes: cssClassesIntersection(fromAnimation.classes, toAnimation.classes),
34226              from: fromAnimation,
34227              to: toAnimation,
34228              anchors: [] // TODO(matsko): change to reference nodes
34229            };
34230
34231            // the anchor animations require that the from and to elements both have at least
34232            // one shared CSS class which effictively marries the two elements together to use
34233            // the same animation driver and to properly sequence the anchor animation.
34234            if (group.classes.length) {
34235              preparedAnimations.push(group);
34236            } else {
34237              preparedAnimations.push(fromAnimation);
34238              preparedAnimations.push(toAnimation);
34239            }
34240          }
34241
34242          anchorGroups[lookupKey].anchors.push({
34243            'out': from.element, 'in': to.element
34244          });
34245        });
34246
34247        return preparedAnimations;
34248      }
34249
34250      function cssClassesIntersection(a,b) {
34251        a = a.split(' ');
34252        b = b.split(' ');
34253        var matches = [];
34254
34255        for (var i = 0; i < a.length; i++) {
34256          var aa = a[i];
34257          if (aa.substring(0,3) === 'ng-') continue;
34258
34259          for (var j = 0; j < b.length; j++) {
34260            if (aa === b[j]) {
34261              matches.push(aa);
34262              break;
34263            }
34264          }
34265        }
34266
34267        return matches.join(' ');
34268      }
34269
34270      function invokeFirstDriver(animationDetails) {
34271        // we loop in reverse order since the more general drivers (like CSS and JS)
34272        // may attempt more elements, but custom drivers are more particular
34273        for (var i = drivers.length - 1; i >= 0; i--) {
34274          var driverName = drivers[i];
34275          if (!$injector.has(driverName)) continue; // TODO(matsko): remove this check
34276
34277          var factory = $injector.get(driverName);
34278          var driver = factory(animationDetails);
34279          if (driver) {
34280            return driver;
34281          }
34282        }
34283      }
34284
34285      function beforeStart() {
34286        element.addClass(NG_ANIMATE_CLASSNAME);
34287        if (tempClasses) {
34288          $$jqLite.addClass(element, tempClasses);
34289        }
34290        if (prepareClassName) {
34291          $$jqLite.removeClass(element, prepareClassName);
34292          prepareClassName = null;
34293        }
34294      }
34295
34296      function updateAnimationRunners(animation, newRunner) {
34297        if (animation.from && animation.to) {
34298          update(animation.from.element);
34299          update(animation.to.element);
34300        } else {
34301          update(animation.element);
34302        }
34303
34304        function update(element) {
34305          getRunner(element).setHost(newRunner);
34306        }
34307      }
34308
34309      function handleDestroyedElement() {
34310        var runner = getRunner(element);
34311        if (runner && (event !== 'leave' || !options.$$domOperationFired)) {
34312          runner.end();
34313        }
34314      }
34315
34316      function close(rejected) { // jshint ignore:line
34317        element.off('$destroy', handleDestroyedElement);
34318        removeRunner(element);
34319
34320        applyAnimationClasses(element, options);
34321        applyAnimationStyles(element, options);
34322        options.domOperation();
34323
34324        if (tempClasses) {
34325          $$jqLite.removeClass(element, tempClasses);
34326        }
34327
34328        element.removeClass(NG_ANIMATE_CLASSNAME);
34329        runner.complete(!rejected);
34330      }
34331    };
34332  }];
34333}];
34334
34335/* global angularAnimateModule: true,
34336
34337   $$AnimateAsyncRunFactory,
34338   $$rAFSchedulerFactory,
34339   $$AnimateChildrenDirective,
34340   $$AnimateQueueProvider,
34341   $$AnimationProvider,
34342   $AnimateCssProvider,
34343   $$AnimateCssDriverProvider,
34344   $$AnimateJsProvider,
34345   $$AnimateJsDriverProvider,
34346*/
34347
34348/**
34349 * @ngdoc module
34350 * @name ngAnimate
34351 * @description
34352 *
34353 * The `ngAnimate` module provides support for CSS-based animations (keyframes and transitions) as well as JavaScript-based animations via
34354 * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` the animation hooks are enabled for an Angular app.
34355 *
34356 * <div doc-module-components="ngAnimate"></div>
34357 *
34358 * # Usage
34359 * Simply put, there are two ways to make use of animations when ngAnimate is used: by using **CSS** and **JavaScript**. The former works purely based
34360 * using CSS (by using matching CSS selectors/styles) and the latter triggers animations that are registered via `module.animation()`. For
34361 * both CSS and JS animations the sole requirement is to have a matching `CSS class` that exists both in the registered animation and within
34362 * the HTML element that the animation will be triggered on.
34363 *
34364 * ## Directive Support
34365 * The following directives are "animation aware":
34366 *
34367 * | Directive                                                                                                | Supported Animations                                                     |
34368 * |----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
34369 * | {@link ng.directive:ngRepeat#animations ngRepeat}                                                        | enter, leave and move                                                    |
34370 * | {@link ngRoute.directive:ngView#animations ngView}                                                       | enter and leave                                                          |
34371 * | {@link ng.directive:ngInclude#animations ngInclude}                                                      | enter and leave                                                          |
34372 * | {@link ng.directive:ngSwitch#animations ngSwitch}                                                        | enter and leave                                                          |
34373 * | {@link ng.directive:ngIf#animations ngIf}                                                                | enter and leave                                                          |
34374 * | {@link ng.directive:ngClass#animations ngClass}                                                          | add and remove (the CSS class(es) present)                               |
34375 * | {@link ng.directive:ngShow#animations ngShow} & {@link ng.directive:ngHide#animations ngHide}            | add and remove (the ng-hide class value)                                 |
34376 * | {@link ng.directive:form#animation-hooks form} & {@link ng.directive:ngModel#animation-hooks ngModel}
34376    | add and remove (dirty, pristine, valid, invalid & all other validations) |
34377 * | {@link module:ngMessages#animations ngMessages}                                                          | add and remove (ng-active & ng-inactive)                                 |
34378 * | {@link module:ngMessages#animations ngMessage}                                                           | enter and leave                                                          |
34379 *
34380 * (More information can be found by visiting each the documentation associated with each directive.)
34381 *
34382 * ## CSS-based Animations
34383 *
34384 * CSS-based animations with ngAnimate are unique since they require no JavaScript code at all. By using a CSS class that we reference between our HTML
34385 * and CSS code we can create an animation that will be picked up by Angular when an the underlying directive performs an operation.
34386 *
34387 * The example below shows how an `enter` animation can be made possible on an element using `ng-if`:
34388 *
34389 * ```html
34390 * <div ng-if="bool" class="fade">
34391 *    Fade me in out
34392 * </div>
34393 * <button ng-click="bool=true">Fade In!</button>
34394 * <button ng-click="bool=false">Fade Out!</button>
34395 * ```
34396 *
34397 * Notice the CSS class **fade**? We can now create the CSS transition code that references this class:
34398 *
34399 * ```css
34400 * /&#42; The starting CSS styles for the enter animation &#42;/
34401 * .fade.ng-enter {
34402 *   transition:0.5s linear all;
34403 *   opacity:0;
34404 * }
34405 *
34406 * /&#42; The finishing CSS styles for the enter animation &#42;/
34407 * .fade.ng-enter.ng-enter-active {
34408 *   opacity:1;
34409 * }
34410 * ```
34411 *
34412 * The key thing to remember here is that, depending on the animation event (which each of the directives above trigger depending on what's going on) two
34413 * generated CSS classes will be applied to the element; in the example above we have `.ng-enter` and `.ng-enter-active`. For CSS transitions, the transition
34414 * code **must** be defined within the starting CSS class (in this case `.ng-enter`). The destination class is what the transition will animate towards.
34415 *
34416 * If for example we wanted to create animations for `leave` and `move` (ngRepeat triggers move) then we can do so using the same CSS naming conventions:
34417 *
34418 * ```css
34419 * /&#42; now the element will fade out before it is removed from the DOM &#42;/
34420 * .fade.ng-leave {
34421 *   transition:0.5s linear all;
34422 *   opacity:1;
34423 * }
34424 * .fade.ng-leave.ng-leave-active {
34425 *   opacity:0;
34426 * }
34427 * ```
34428 *
34429 * We can also make use of **CSS Keyframes** by referencing the keyframe animation within the starting CSS class:
34430 *
34431 * ```css
34432 * /&#42; there is no need to define anything inside of the destination
34433 * CSS class since the keyframe will take charge of the animation &#42;/
34434 * .fade.ng-leave {
34435 *   animation: my_fade_animation 0.5s linear;
34436 *   -webkit-animation: my_fade_animation 0.5s linear;
34437 * }
34438 *
34439 * @keyframes my_fade_animation {
34440 *   from { opacity:1; }
34441 *   to { opacity:0; }
34442 * }
34443 *
34444 * @-webkit-keyframes my_fade_animation {
34445 *   from { opacity:1; }
34446 *   to { opacity:0; }
34447 * }
34448 * ```
34449 *
34450 * Feel free also mix transitions and keyframes together as well as any other CSS classes on the same element.
34451 *
34452 * ### CSS Class-based Animations
34453 *
34454 * Class-based animations (animations that are triggered via `ngClass`, `ngShow`, `ngHide` and some other directives) have a slightly different
34455 * naming convention. Class-based animations are basic enough that a standard transition or keyframe can be referenced on the class being added
34456 * and removed.
34457 *
34458 * For example if we wanted to do a CSS animation for `ngHide` then we place an animation on the `.ng-hide` CSS class:
34459 *
34460 * ```html
34461 * <div ng-show="bool" class="fade">
34462 *   Show and hide me
34463 * </div>
34464 * <button ng-click="bool=true">Toggle</button>
34465 *
34466 * <style>
34467 * .fade.ng-hide {
34468 *   transition:0.5s linear all;
34469 *   opacity:0;
34470 * }
34471 * </style>
34472 * ```
34473 *
34474 * All that is going on here with ngShow/ngHide behind the scenes is the `.ng-hide` class is added/removed (when the hidden state is valid). Since
34475 * ngShow and ngHide are animation aware then we can match up a transition and ngAnimate handles the rest.
34476 *
34477 * In addition the addition and removal of the CSS class, ngAnimate also provides two helper methods that we can use to further decorate the animation
34478 * with CSS styles.
34479 *
34480 * ```html
34481 * <div ng-class="{on:onOff}" class="highlight">
34482 *   Highlight this box
34483 * </div>
34484 * <button ng-click="onOff=!onOff">Toggle</button>
34485 *
34486 * <style>
34487 * .highlight {
34488 *   transition:0.5s linear all;
34489 * }
34490 * .highlight.on-add {
34491 *   background:white;
34492 * }
34493 * .highlight.on {
34494 *   background:yellow;
34495 * }
34496 * .highlight.on-remove {
34497 *   background:black;
34498 * }
34499 * </style>
34500 * ```
34501 *
34502 * We can also make use of CSS keyframes by placing them within the CSS classes.
34503 *
34504 *
34505 * ### CSS Staggering Animations
34506 * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a
34507 * curtain-like effect. The ngAnimate module (versions >
34507=1.2) supports staggering animations and the stagger effect can be
34508 * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for
34509 * the animation. The style property expected within the stagger class can either be a **transition-delay** or an
34510 * **animation-delay** property (or both if your animation contains both transitions and keyframe animations).
34511 *
34512 * ```css
34513 * .my-animation.ng-enter {
34514 *   /&#42; standard transition code &#42;/
34515 *   transition: 1s linear all;
34516 *   opacity:0;
34517 * }
34518 * .my-animation.ng-enter-stagger {
34519 *   /&#42; this will have a 100ms delay between each successive leave animation &#42;/
34520 *   transition-delay: 0.1s;
34521 *
34522 *   /&#42; As of 1.4.4, this must always be set: it signals ngAnimate
34523 *     to not accidentally inherit a delay property from another CSS class &#42;/
34524 *   transition-duration: 0s;
34525 * }
34526 * .my-animation.ng-enter.ng-enter-active {
34527 *   /&#42; standard transition styles &#42;/
34528 *   opacity:1;
34529 * }
34530 * ```
34531 *
34532 * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations
34533 * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this
34534 * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation
34535 * will also be reset if one or more animation frames have passed since the multiple calls to `$animate` were fired.
34536 *
34537 * The following code will issue the **ng-leave-stagger** event on the element provided:
34538 *
34539 * ```js
34540 * var kids = parent.children();
34541 *
34542 * $animate.leave(kids[0]); //stagger index=0
34543 * $animate.leave(kids[1]); //stagger index=1
34544 * $animate.leave(kids[2]); //stagger index=2
34545 * $animate.leave(kids[3]); //stagger index=3
34546 * $animate.leave(kids[4]); //stagger index=4
34547 *
34548 * window.requestAnimationFrame(function() {
34549 *   //stagger has reset itself
34550 *   $animate.leave(kids[5]); //stagger index=0
34551 *   $animate.leave(kids[6]); //stagger index=1
34552 *
34553 *   $scope.$digest();
34554 * });
34555 * ```
34556 *
34557 * Stagger animations are currently only supported within CSS-defined animations.
34558 *
34559 * ### The `ng-animate` CSS class
34560 *
34561 * When ngAnimate is animating an element it will apply the `ng-animate` CSS class to the element for the duration of the animation.
34562 * This is a temporary CSS class and it will be removed once the animation is over (for both JavaScript and CSS-based animations).
34563 *
34564 * Therefore, animations can be applied to an element using this temporary class directly via CSS.
34565 *
34566 * ```css
34567 * .zipper.ng-animate {
34568 *   transition:0.5s linear all;
34569 * }
34570 * .zipper.ng-enter {
34571 *   opacity:0;
34572 * }
34573 * .zipper.ng-enter.ng-enter-active {
34574 *   opacity:1;
34575 * }
34576 * .zipper.ng-leave {
34577 *   opacity:1;
34578 * }
34579 * .zipper.ng-leave.ng-leave-active {
34580 *   opacity:0;
34581 * }
34582 * ```
34583 *
34584 * (Note that the `ng-animate` CSS class is reserved and it cannot be applied on an element directly since ngAnimate will always remove
34585 * the CSS class once an animation has completed.)
34586 *
34587 *
34588 * ### The `ng-[event]-prepare` class
34589 *
34590 * This is a special class that can be used to prevent unwanted flickering / flash of content before
34591 * the actual animation starts. The class is added as soon as an animation is initialized, but removed
34592 * before the actual animation starts (after waiting for a $digest).
34593 * It is also only added for *structural* animations (`enter`, `move`, and `leave`).
34594 *
34595 * In practice, flickering can appear when nesting elements with structural animations such as `ngIf`
34596 * into elements that have class-based animations such as `ngClass`.
34597 *
34598 * ```html
34599 * <div ng-class="{red: myProp}">
34600 *   <div ng-class="{blue: myProp}">
34601 *     <div class="message" ng-if="myProp"></div>
34602 *   </div>
34603 * </div>
34604 * ```
34605 *
34606 * It is possible that during the `enter` animation, the `.message` div will be briefly visible before it starts animating.
34607 * In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:
34608 *
34609 * ```css
34610 * .message.ng-enter-prepare {
34611 *   opacity: 0;
34612 * }
34613 *
34614 * ```
34615 *
34616 * ## JavaScript-based Animations
34617 *
34618 * ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared
34619 * CSS class that is referenced in our HTML code) but in addition we need to register the JavaScript animation on the module. By making use of the
34620 * `module.animation()` module function we can register the ainmation.
34621 *
34622 * Let's see an example of a enter/leave animation using `ngRepeat`:
34623 *
34624 * ```html
34625 * <div ng-repeat="item in items" class="slide">
34626 *   {{ item }}
34627 * </div>
34628 * ```
34629 *
34630 * See the **slide** CSS class? Let's use that class to define an animation that we'll structure in our module code by using `module.animation`:
34631 *
34632 * ```js
34633 * myModule.animation('.slide', [function() {
34634 *   return {
34635 *     // make note that other events (like addClass/removeClass)
34636 *     // have different function input parameters
34637 *     enter: function(element, doneFn) {
34638 *       jQuery(element).fadeIn(1000, doneFn);
34639 *
34640 *       // remember to call doneFn so that angular
34641 *       // knows that the animation has concluded
34642 *     },
34643 *
34644 *     move: function(element, doneFn) {
34645 *       jQuery(element).fadeIn(1000, doneFn);
34646 *     },
34647 *
34648 *     leave: function(element, doneFn) {
34649 *       jQuery(element).fadeOut(1000, doneFn);
34650 *     }
34651 *   }
34652 * }]);
34653 * ```
34654 *
34655 * The nice thing about JS-based animations is that we can inject other services and make use of advanced animation libraries such as
34656 * greensock.js and velocity.js.
34657 *
34658 * If our animation code class-based (meaning that something like `ngClass`, `ngHide` and `ngShow` triggers it) then we can still define
34659 * our animations inside of the same registered animation, however, the function input arguments are a bit different:
34660 *
34661 * ```html
34662 * <div ng-class="color" class="colorful">
34663 *   this box is moody
34664 * </div>
34665 * <button ng-click="color='red'">Change to red</button>
34666 * <button ng-click="color='blue'">Change to blue</button>
34667 * <button ng-click="color='green'">Change to green</button>
34668 * ```
34669 *
34670 * ```js
34671 * myModule.animation('.colorful', [function() {
34672 *   return {
34673 *     addClass: function(element, className, doneFn) {
34674 *       // do some cool animation and call the doneFn
34675 *     },
34676 *     removeClass: function(element, className, doneFn) {
34677 *       // do some cool animation and call the doneFn
34678 *     },
34679 *     setClass: function(element, addedClass, removedClass, doneFn) {
34680 *       // do some cool animation and call the doneFn
34681 *     }
34682 *   }
34683 * }]);
34684 * ```
34685 *
34686 * ## CSS + JS Animations Together
34687 *
34688 * AngularJS 1.4 and higher has taken steps to make the amalgamation of CSS and JS animations more flexible. However, unlike earlier versions of Angular,
34689 * defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore the example below will only result in **JS animations taking
34690 * charge of the animation**:
34691 *
34692 * ```html
34693 * <div ng-if="bool" class="slide">
34694 *   Slide in and out
34695 * </div>
34696 * ```
34697 *
34698 * ```js
34699 * myModule.animation('.slide', [function() {
34700 *   return {
34701 *     enter: function(element, doneFn) {
34702 *       jQuery(element).slideIn(1000, doneFn);
34703 *     }
34704 *   }
34705 * }]);
34706 * ```
34707 *
34708 * ```css
34709 * .slide.ng-enter {
34710 *   transition:0.5s linear all;
34711 *   transform:translateY(-100px);
34712 * }
34713 * .slide.ng-enter.ng-enter-active {
34714 *   transform:translateY(0);
34715 * }
34716 * ```
34717 *
34718 * Does this mean that CSS and JS animations cannot be used together? Do JS-based animations always have higher priority? We can make up for the
34719 * lack of CSS animations by using the `$animateCss` service to trigger our own tweaked-out, CSS-based animations directly from
34720 * our own JS-based animation code:
34721 *
34722 * ```js
34723 * myModule.animation('.slide', ['$animateCss', function($animateCss) {
34724 *   return {
34725 *     enter: function(element) {
34726*        // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`.
34727 *       return $animateCss(element, {
34728 *         event: 'enter',
34729 *         structural: true
34730 *       });
34731 *     }
34732 *   }
34733 * }]);
34734 * ```
34735 *
34736 * The nice thing here is that we can save bandwidth by sticking to our CSS-based animation code and we don't need to rely on a 3rd-party animation framework.
34737 *
34738 * The `$animateCss` service is very powerful since we can feed in all kinds of extra properties that will be evaluated and fed into a CSS transition or
34739 * keyframe animation. For example if we wanted to animate the height of an element while adding and removing classes then we can do so by providing that
34740 * data into `$animateCss` directly:
34741 *
34742 * ```js
34743 * myModule.animation('.slide', ['$animateCss', function($animateCss) {
34744 *   return {
34745 *     enter: function(element) {
34746 *       return $animateCss(element, {
34747 *         event: 'enter',
34748 *         structural: true,
34749 *         addClass: 'maroon-setting',
34750 *         from: { height:0 },
34751 *         to: { height: 200 }
34752 *       });
34753 *     }
34754 *   }
34755 * }]);
34756 * ```
34757 *
34758 * Now we can fill in the rest via our transition CSS code:
34759 *
34760 * ```css
34761 * /&#42; the transition tells ngAnimate to make the animation happen &#42;/
34762 * .slide.ng-enter { transition:0.5s linear all; }
34763 *
34764 * /&#42; this extra CSS class will be absorbed into the transition
34765 * since the $animateCss code is adding the class &#42;/
34766 * .maroon-setting { background:red; }
34767 * ```
34768 *
34769 * And `$animateCss` will figure out the rest. Just make sure to have the `done()` callback fire the `doneFn` function to signal when the animation is over.
34770 *
34771 * To learn more about what's possible be sure to visit the {@link ngAnimate.$animateCss $animateCss service}.
34772 *
34773 * ## Animation Anchoring (via `ng-animate-ref`)
34774 *
34775 * ngAnimate in AngularJS 1.4 comes packed with the ability to cross-animate elements between
34776 * structural areas of an application (like views) by pairing up elements using an attribute
34777 * called `ng-animate-ref`.
34778 *
34779 * Let's say for example we have two views that are managed by `ng-view` and we want to show
34780 * that there is a relationship between two components situated in within these views. By using the
34781 * `ng-animate-ref` attribute we can identify that the two components are paired together and we
34782 * can then attach an animation, which is triggered when the view changes.
34783 *
34784 * Say for example we have the following template code:
34785 *
34786 * ```html
34787 * <!-- index.html -->
34788 * <div ng-view class="view-animation">
34789 * </div>
34790 *
34791 * <!-- home.html -->
34792 * <a href="#/banner-page">
34793 *   <img src="./banner.jpg" class="banner" ng-animate-ref="banner">
34794 * </a>
34795 *
34796 * <!-- banner-page.html -->
34797 * <img src="./banner.jpg" class="banner" ng-animate-ref="banner">
34798 * ```
34799 *
34800 * Now, when the view changes (once the link is clicked), ngAnimate will examine the
34801 * HTML contents to see if there is a match reference between any components in the view
34802 * that is leaving and the view that is entering. It will scan both the view which is being
34803 * removed (leave) and inserted (enter) to see if there are any paired DOM elements that
34804 * contain a matching ref value.
34805 *
34806 * The two images match since they share the same ref value. ngAnimate will now create a
34807 * transport element (which is a clone of the first image element) and it will then attempt
34808 * to animate to the position of the second image element in the next view. For the animation to
34809 * work a special CSS class called `ng-anchor` will be added to the transported element.
34810 *
34811 * We can now attach a transition onto the `.banner.ng-anchor` CSS class and then
34812 * ngAnimate will handle the entire transition for us as well as the addition and removal of
34813 * any changes of CSS classes between the elements:
34814 *
34815 * ```css
34816 * .banner.ng-anchor {
34817 *   /&#42; this animation will last for 1 second since there are
34818 *          two phases to the animation (an `in` and an `out` phase) &#42;/
34819 *   transition:0.5s linear all;
34820 * }
34821 * ```
34822 *
34823 * We also **must** include animations for the views that are being entered and removed
34824 * (otherwise anchoring wouldn't be possible since the new view would be inserted right away).
34825 *
34826 * ```css
34827 * .view-animation.ng-enter, .view-animation.ng-leave {
34828 *   transition:0.5s linear all;
34829 *   position:fixed;
34830 *   left:0;
34831 *   top:0;
34832 *   width:100%;
34833 * }
34834 * .view-animation.ng-enter {
34835 *   transform:translateX(100%);
34836 * }
34837 * .view-animation.ng-leave,
34838 * .view-animation.ng-enter.ng-enter-active {
34839 *   transform:translateX(0%);
34840 * }
34841 * .view-animation.ng-leave.ng-leave-active {
34842 *   transform:translateX(-100%);
34843 * }
34844 * ```
34845 *
34846 * Now we can jump back to the anchor animation. When the animation happens, there are two stages that occur:
34847 * an `out` and an `in` stage. The `out` stage happens first and that is when the element is animated away
34848 * from its origin. Once that animation is over then the `in` stage occurs which animates the
34849 * element to its destination. The reason why there are two animations is to give enough time
34850 * for the enter animation on the new element to be ready.
34851 *
34852 * The example above sets up a transition for both the in and out phases, but we can also target the out or
34853 * in phases directly via `ng-anchor-out` and `ng-anchor-in`.
34854 *
34855 * ```css
34856 * .banner.ng-anchor-out {
34857 *   transition: 0.5s linear all;
34858 *
34859 *   /&#42; the scale will be applied during the out animation,
34860 *          but will be animated away when the in animation runs &#42;/
34861 *   transform: scale(1.2);
34862 * }
34863 *
34864 * .banner.ng-anchor-in {
34865 *   transition: 1s linear all;
34866 * }
34867 * ```
34868 *
34869 *
34870 *
34871 *
34872 * ### Anchoring Demo
34873 *
34874  <example module="anchoringExample"
34875           name="anchoringExample"
34876           id="anchoringExample"
34877           deps="angular-animate.js;angular-route.js"
34878           animations="true">
34879    <file name="index.html">
34880      <a href="#/">Home</a>
34881      <hr />
34882      <div class="view-container">
34883        <div ng-view class="view"></div>
34884      </div>
34885    </file>
34886    <file name="script.js">
34887      angular.module('anchoringExample', ['ngAnimate', 'ngRoute'])
34888        .config(['$routeProvider', function($routeProvider) {
34889          $routeProvider.when('/', {
34890            templateUrl: 'home.html',
34891            controller: 'HomeController as home'
34892          });
34893          $routeProvider.when('/profile/:id', {
34894            templateUrl: 'profile.html',
34895            controller: 'ProfileController as profile'
34896          });
34897        }])
34898        .run(['$rootScope', function($rootScope) {
34899          $rootScope.records = [
34900            { id:1, title: "Miss Beulah Roob" },
34901            { id:2, title: "Trent Morissette" },
34902            { id:3, title: "Miss Ava Pouros" },
34903            { id:4, title: "Rod Pouros" },
34904            { id:5, title: "Abdul Rice" },
34905            { id:6, title: "Laurie Rutherford Sr." },
34906            { id:7, title: "Nakia McLaughlin" },
34907            { id:8, title: "Jordon Blanda DVM" },
34908            { id:9, title: "Rhoda Hand" },
34909            { id:10, title: "Alexandrea Sauer" }
34910          ];
34911        }])
34912        .controller('HomeController', [function() {
34913          //empty
34914        }])
34915        .controller('ProfileController', ['$rootScope', '$routeParam
34915s', function($rootScope, $routeParams) {
34916          var index = parseInt($routeParams.id, 10);
34917          var record = $rootScope.records[index - 1];
34918
34919          this.title = record.title;
34920          this.id = record.id;
34921        }]);
34922    </file>
34923    <file name="home.html">
34924      <h2>Welcome to the home page</h1>
34925      <p>Please click on an element</p>
34926      <a class="record"
34927         ng-href="#/profile/{{ record.id }}"
34928         ng-animate-ref="{{ record.id }}"
34929         ng-repeat="record in records">
34930        {{ record.title }}
34931      </a>
34932    </file>
34933    <file name="profile.html">
34934      <div class="profile record" ng-animate-ref="{{ profile.id }}">
34935        {{ profile.title }}
34936      </div>
34937    </file>
34938    <file name="animations.css">
34939      .record {
34940        display:block;
34941        font-size:20px;
34942      }
34943      .profile {
34944        background:black;
34945        color:white;
34946        font-size:100px;
34947      }
34948      .view-container {
34949        position:relative;
34950      }
34951      .view-container > .view.ng-animate {
34952        position:absolute;
34953        top:0;
34954        left:0;
34955        width:100%;
34956        min-height:500px;
34957      }
34958      .view.ng-enter, .view.ng-leave,
34959      .record.ng-anchor {
34960        transition:0.5s linear all;
34961      }
34962      .view.ng-enter {
34963        transform:translateX(100%);
34964      }
34965      .view.ng-enter.ng-enter-active, .view.ng-leave {
34966        transform:translateX(0%);
34967      }
34968      .view.ng-leave.ng-leave-active {
34969        transform:translateX(-100%);
34970      }
34971      .record.ng-anchor-out {
34972        background:red;
34973      }
34974    </file>
34975  </example>
34976 *
34977 * ### How is the element transported?
34978 *
34979 * When an anchor animation occurs, ngAnimate will clone the starting element and position it exactly where the starting
34980 * element is located on screen via absolute positioning. The cloned element will be placed inside of the root element
34981 * of the application (where ng-app was defined) and all of the CSS classes of the starting element will be applied. The
34982 * element will then animate into the `out` and `in` animations and will eventually reach the coordinates and match
34983 * the dimensions of the destination element. During the entire animation a CSS class of `.ng-animate-shim` will be applied
34984 * to both the starting and destination elements in order to hide them from being visible (the CSS styling for the class
34985 * is: `visibility:hidden`). Once the anchor reaches its destination then it will be removed and the destination element
34986 * will become visible since the shim class will be removed.
34987 *
34988 * ### How is the morphing handled?
34989 *
34990 * CSS Anchoring relies on transitions and keyframes and the internal code is intelligent enough to figure out
34991 * what CSS classes differ between the starting element and the destination element. These different CSS classes
34992 * will be added/removed on the anchor element and a transition will be applied (the transition that is provided
34993 * in the anchor class). Long story short, ngAnimate will figure out what classes to add and remove which will
34994 * make the transition of the element as smooth and automatic as possible. Be sure to use simple CSS classes that
34995 * do not rely on DOM nesting structure so that the anchor element appears the same as the starting element (since
34996 * the cloned element is placed inside of root element which is likely close to the body element).
34997 *
34998 * Note that if the root element is on the `<html>` element then the cloned node will be placed inside of body.
34999 *
35000 *
35001 * ## Using $animate in your directive code
35002 *
35003 * So far we've explored how to feed in animations into an Angular application, but how do we trigger animations within our own directives in our application?
35004 * By injecting the `$animate` service into our directive code, we can trigger structural and class-based hooks which can then be c
35004onsumed by animations. Let's
35005 * imagine we have a greeting box that shows and hides itself when the data changes
35006 *
35007 * ```html
35008 * <greeting-box active="onOrOff">Hi there</greeting-box>
35009 * ```
35010 *
35011 * ```js
35012 * ngModule.directive('greetingBox', ['$animate', function($animate) {
35013 *   return function(scope, element, attrs) {
35014 *     attrs.$observe('active', function(value) {
35015 *       value ? $animate.addClass(element, 'on') : $animate.removeClass(element, 'on');
35016 *     });
35017 *   });
35018 * }]);
35019 * ```
35020 *
35021 * Now the `on` CSS class is added and removed on the greeting box component. Now if we add a CSS class on top of the greeting box element
35022 * in our HTML code then we can trigger a CSS or JS animation to happen.
35023 *
35024 * ```css
35025 * /&#42; normally we would create a CSS class to reference on the element &#42;/
35026 * greeting-box.on { transition:0.5s linear all; background:green; color:white; }
35027 * ```
35028 *
35029 * The `$animate` service contains a variety of other methods like `enter`, `leave`, `animate` and `setClass`. To learn more about what's
35030 * possible be sure to visit the {@link ng.$animate $animate service API page}.
35031 *
35032 *
35033 * ### Preventing Collisions With Third Party Libraries
35034 *
35035 * Some third-party frameworks place animation duration defaults across many element or className
35036 * selectors in order to make their code small and reuseable. This can lead to issues with ngAnimate, which
35037 * is expecting actual animations on these elements and has to wait for their completion.
35038 *
35039 * You can prevent this unwanted behavior by using a prefix on all your animation classes:
35040 *
35041 * ```css
35042 * /&#42; prefixed with animate- &#42;/
35043 * .animate-fade-add.animate-fade-add-active {
35044 *   transition:1s linear all;
35045 *   opacity:0;
35046 * }
35047 * ```
35048 *
35049 * You then configure `$animate` to enforce this prefix:
35050 *
35051 * ```js
35052 * $animateProvider.classNameFilter(/animate-/);
35053 * ```
35054 *
35055 * This also may provide your application with a speed boost since only specific elements containing CSS class prefix
35056 * will be evaluated for animation when any DOM changes occur in the application.
35057 *
35058 * ## Callbacks and Promises
35059 *
35060 * When `$animate` is called it returns a promise that can be used to capture when the animation has ended. Therefore if we were to trigger
35061 * an animation (within our directive code) then we can continue performing directive and scope related activities after the animation has
35062 * ended by chaining onto the returned promise that animation method returns.
35063 *
35064 * ```js
35065 * // somewhere within the depths of the directive
35066 * $animate.enter(element, parent).then(function() {
35067 *   //the animation has completed
35068 * });
35069 * ```
35070 *
35071 * (Note that earlier versions of Angular prior to v1.4 required the promise code to be wrapped using `$scope.$apply(...)`. This is not the case
35072 * anymore.)
35073 *
35074 * In addition to the animation promise, we can also make use of animation-related callbacks within our directives and controller code by registering
35075 * an event listener using the `$animate` service. Let's say for example that an animation was triggered on our view
35076 * routing controller to hook into that:
35077 *
35078 * ```js
35079 * ngModule.controller('HomePageController', ['$animate', function($animate) {
35080 *   $animate.on('enter', ngViewElement, function(element) {
35081 *     // the animation for this route has completed
35082 *   }]);
35083 * }])
35084 * ```
35085 *
35086 * (Note that you will need to trigger a digest within the callback to get angular to notice any scope-related changes.)
35087 */
35088
35089/**
35090 * @ngdoc service
35091 * @name $animate
35092 * @kind object
35093 *
35094 * @description
35095 * The ngAnimate `$animate` service documentation is the same for the core `$animate` service.
35096 *
35097 * Click here {@link ng.$animate to learn more about animations with `$animate`}.
35098 */
35099angular.module('ngAnimate', [])
35100  .directive('ngAnimateChildren', $$AnimateChildrenDirective)
35101  .factory('$$rAFScheduler', $$rAFSchedulerFactory)
35102
35103  .provider('$$animateQueue', $$AnimateQueueProvider)
35104  .provider('$$animation', $$AnimationProvider)
35105
35106  .provider('$animateCss', $AnimateCssProvider)
35107  .provider('$$animateCssDriver', $$AnimateCssDriverProvider)
35108
35109  .provider('$$animateJs', $$AnimateJsProvider)
35110  .provider('$$animateJsDriver', $$AnimateJsDriverProvider);
35111
35112
35113})(window, window.angular);
35114
35115(function (angular) {
35116    angular.module("ui.materialize", ["ui.materialize.ngModel", "ui.materialize.collapsible", "ui.materialize.toast", "ui.materialize.sidenav", "ui.materialize.material_select", "ui.materialize.dropdown", "ui.materialize.inputfield", "ui.materialize.input_date", "ui.materialize.tabs", "ui.materialize.pagination", "ui.materialize.pushpin", "ui.materialize.scrollspy", "ui.materialize.parallax","ui.materialize.modal", "ui.materialize.tooltipped",  "ui.materialize.slider", "ui.materialize.materialboxed"]);
35117
35118    angular.module("ui.materialize.ngModel", [])
35119        .directive("ngModel",["$timeout", function($timeout){
35120            return {
35121                restrict: 'A',
35122                priority: -1, // lower priority than built-in ng-model so it runs first
35123                link: function(scope, element, attr) {
35124                    scope.$watch(attr.ngModel,function(value){
35125                        $timeout(function () {
35126                            if (value){
35127                                element.trigger("change");
35128                            } else if(element.attr('placeholder') === undefined) {
35129                                if(!element.is(":focus"))
35130                                    element.trigger("blur");
35131                            }
35132                        });
35133                    });
35134                }
35135            };
35136        }]);
35137    
35138
35139    /* example usage: 
35140    <div slider height='500' transition='400'></div>
35141    */
35142    angular.module("ui.materialize.slider", [])
35143        .directive("slider", ["$timeout", function($timeout){
35144            return {
35145                restrict: 'A',
35146                scope: { 
35147                    height: '=',
35148                    transition: '=',
35149                    interval: '=',
35150                    indicators: '='
35151                },
35152                link: function(scope, element, attrs) {
35153                    element.addClass("slider");
35154                    $timeout(function(){
35155                        element.slider({
35156                            height: (angular.isDefined(scope.height)) ? scope.height : 400,
35157                            transition: (angular.isDefined(scope.transition)) ? scope.transition : 500,
35158                            interval: (angular.isDefined(scope.interval)) ? scope.interval : 6000,
35159                            indicators: (angular.isDefined(scope.indicators)) ? scope.indicators : true
35160                        });
35161                    });
35162                }
35163            };
35164        }]);
35165
35166
35167    angular.module("ui.materialize.collapsible", [])
35168        .directive("collapsible", ["$timeout", function ($timeout) {
35169            return {
35170                link: function (scope, element, attrs) {
35171                    $timeout(function () {
35172                        element.collapsible();
35173                    });
35174                    if ("watch" in attrs) {
35175                        scope.$watch(function () {
35176                            return element[0].innerHTML;
35177                        }, function (oldVal, newVal) {
35178                            if (oldVal !== newVal) {
35179                                $timeout(function () {
35180                                    element.collapsible();
35181                                });
35182                            }
35183                        });
35184                    }
35185                }
35186            };
35187        }]);
35188
35189    angular.module("ui.materialize.parallax", [])
35190        .directive("parallax", ["$timeout", function($timeout){
35191            return {
35192                link: function(scope, element, attrs) {
35193                    $timeout(function(){
35194                        element.parallax();
35195                    });
35196                }
35197            };
35198        }]);
35199
35200    angular.module("ui.materialize.toast", [])
35201        .constant("toastConfig", {
35202            duration: 3000
35203        })
35204        .directive("toast", ["toastConfig", function (toastConfig) {
35205            return {
35206                scope: {
35207                    message: "@",
35208                    duration: "@",
35209                    callback: "&"
35210                },
35211                link: function (scope, element, attrs) {
35212                    element.bind(attrs.toast, function () {
35213                        var message = (angular.isDefined(scope.message)) ? scope.message : "";
35214                        var toastclass = (angular.isDefined(attrs.toastclass)) ? attrs.toastclass : "";                        
35215                        Materialize.toast(message, scope.duration ? scope.duration : toastConfig.duration, toastclass, scope.callback);
35216                    });
35217                }
35218            };
35219        }]);
35220
35221    angular.module('ui.materialize.pushpin', [])
35222        .directive('pushpin', [function(){
35223            return {
35224                restrict: 'AE',
35225                require: [
35226                    '?pushpinTop',
35227                    '?pushpinOffset',
35228                    '?pushpinBottom'
35229                ],
35230                link: function (scope, element, attrs) {
35231                    var top    = attrs.pushpinTop || 0;
35232                    var offset = attrs.pushpinOffset || 0;
35233                    var bottom = attrs.pushpinBottom || Infinity;
35234                    setTimeout(function () {
35235                        element.pushpin({top: top, offset: offset, bottom: bottom});
35236                    }, 0);
35237
35238                }
35239            };
35240        }]);
35241
35242    // TODO: Add some documentation for this.
35243    angular.module("ui.materialize.scrollspy", [])
35244        .directive("scrollspy", ["$timeout", function($timeout){
35245            return {
35246                restrict: 'A',
35247                link: function(scope, element, attrs) {
35248                    element.addClass("scrollspy");
35249                    $timeout(function(){
35250                        element.scrollSpy();
35251                    });
35252                }
35253            };
35254        }]);
35255
35256    angular.module("ui.materialize.tabs", [])
35257        .directive("tabs", [function(){
35258            return {
35259                link: function (scope, element, attrs) {
35260                    element.tabs();
35261                }
35262            };
35263        }]);
35264
35265    // Example: <a href="#" data-activates="nav-mobile" class="button-collapse top-nav" data-sidenav="left" data-menuwidth="500"  data-closeonclick="true">
35266    // data-activates is handled by the jQuery plugin.
35267    angular.module("ui.materialize.sidenav", [])
35268        .directive("sidenav", [function () {
35269            return {
35270                scope: {
35271                    menuwidth: "@",
35272                    closeonclick: "@"
35273                },
35274                link: function (scope, element, attrs) {
35275                    element.sideNav({
35276                        menuWidth: (angular.isDefined(scope.menuwidth)) ? scope.menuwidth : undefined,
35277                        edge: attrs.sidenav ? attrs.sidenav : "left",
35278                        closeOnClick: (angular.isDefined(scope.closeonclick)) ? scope.closeonclick == "true" : undefined
35279                    });
35280                }
35281            };
35282        }]);
35283
35284    // This works, unless the content inside the select changes.
35285    angular.module("ui.materialize.material_select", [])
35286        .directive("materialSelect", ["$compile", "$timeout", function ($compile, $timeout) {
35287            return {
35288                link: function (scope, element, attrs) {
35289                    if (element.is("select")) {
35290                        $compile(element.contents())(scope);
35291                        function initSelect() {
35292                            element.siblings(".caret").remove();
35293                            element.material_select();
35294                        }
35295                        $timeout(initSelect);
35296                        if (attrs.ngModel) {
35297                            scope.$watch(attrs.ngModel, initSelect);
35298                        }
35299                        if ("watch" in attrs) {
35300                            scope.$watch(function () {
35301                                return element[0].innerHTML;
35302                            }, function (oldVal, newVal) {
35303                                if (oldVal !== newVal) {
35304                                    $timeout(initSelect);
35305                                }
35306                            });
35307                        }
35308                    }
35309                }
35310            };
35311        }]);
35312
35313    /*
35314     Example usage, notice the empty dropdown tag in the dropdown trigger.
35315     <!-- Dropdown Trigger -->
35316     <a class='dropdown-button btn' href='javascript:void(0);' data-activates='demoDropdown' 
35317        dropdown constrain-width="false">
35318        Select a demo
35319     </a>
35320     <!-- Dropdown Structure -->
35321     <ul id='demoDropdown' class='dropdown-content'>
35322     <li ng-repeat="demo in demoDefiniftions">
35323     <a href="javascript:void(0);" ng-click="selectDemo(demo)">{{demo.name}}</a>
35324     </li>
35325     </ul>*/
35326    angular.module("ui.materialize.dropdown", [])
35327        .directive("dropdown", ["$compile", "$timeout", function ($compile, $timeout) {
35328            return {
35329                scope: {
35330                    inDuration: "@",
35331                    outDuration: "@",
35332                    constrainWidth: "@",
35333                    hover: "@",
35334                    alignment: "@",
35335                    gutter: "@",
35336                    belowOrigin: "@"
35337                },
35338                link: function (scope, element, attrs) {
35339                    $timeout(function () {
35340                        $compile(element.contents())(scope);
35341                        element.dropdown({
35342                            inDuration: (angular.isDefined(scope.inDuration)) ? scope.inDuration : undefined,
35343                            outDuration: (angular.isDefined(scope.outDuration)) ? scope.outDuration : undefined,
35344                            constrain_width: (angular.isDefined(scope.constrainWidth)) ? scope.constrainWidth : undefined,
35345                            hover: (angular.isDefined(scope.hover)) ? scope.hover : undefined,
35346                            alignment: (angular.isDefined(scope.alignment)) ? scope.alignment : undefined,
35347                            gutter: (angular.isDefined(scope.gutter)) ? scope.gutter : undefined,
35348                            belowOrigin: (angular.isDefined(scope.belowOrigin)) ? scope.belowOrigin : undefined
35349                        });
35350                    });
35351                }
35352            };
35353        }]);
35354
35355    /**
35356     * Instead of adding the .input-field class to a div surrounding a label and input, add the attribute input-field.
35357     * That way it will also work when angular destroys/recreates the elements.
35358     *
35359     * Example:
35360     <inputfield style="margin-top:10px">
35361     <label>{{name}}:</label>
35362     <input type="text" name="{{name}}" ng-model="value">
35363     </inputfield>
35364     */
35365    angular.module("ui.materialize.inputfield", [])
35366        .directive('inputField', ["$compile", "$timeout", function ($compile, $timeout) {
35367            return {
35368                transclude: true,
35369                scope: {},
35370                link: function (scope, element) {
35371                    $timeout(function () {
35372                        Materialize.updateTextFields();
35373
35374                        // The "> > [selector]", is to restrict to only those tags that are direct children of the directive element. Otherwise we might hit to many elements with the selectors.
35375
35376                        // Triggering autoresize of the textareas.
35377                        element.find("> > .materialize-textarea").each(function () {
35378                            var that = $(this);
35379                            that.addClass("materialize-textarea");
35380                            that.trigger("autoresize");
35381                            var model = that.attr("ng-model");
35382                            if (model) {
35383                                scope.$parent.$watch(model, function (a, b) {
35384                                    if (a !== b) {
35385                                        $timeout(function () {
35386                                            that.trigger("autoresize");
35387                                        });
35388                                    }
35389                                });
35390                            }
35391                        });
35392
35393                        // Adding char-counters.
35394                        element.find('> > .materialize-textarea, > > input').each(function (index, countable) {
35395                            countable = angular.element(countable);
35396                            if (!countable.siblings('span[class="character-counter"]').length) {
35397                                countable.characterCounter();
35398                            }
35399                        });
35400                    });
35401                },
35402                template: '<div ng-transclude class="input-field"></div>'
35403            };
35404        }]);
35405
35406    /**
35407     * Add pickadate directive
35408     * Type text is mandatory
35409     * Example:
35410     <input input-date
35411        type="text"
35412        name="created"
35413        id="inputCreated"
35414        ng-model="currentTime"
35415        format="dd/mm/yyyy"
35416        months-full="{{ monthFr }}"
35417        months-short="{{ monthShortFr }}"
35418        weekdays-full="{{ weekdaysFullFr }}"
35419        weekdays-short="{{ weekdaysShortFr }}"
35420        weekdays-letter="{{ weekdaysLetterFr }}"
35421        disable="disable"
35422        today="today"
35423        clear="clear"
35424        close="close"
35425        on-start="onStart()"
35426        on-render="onRender()"
35427        on-open="onOpen()"
vendor: 4,643 bytes, lines 35428-35524
35428        on-close="onClose()"
35429        on-set="onSet()"
35430        on-stop="onStop()" />
35431     */
35432    angular.module("ui.materialize.input_date", [])
35433        .directive('inputDate', ["$compile", "$timeout", function ($compile, $timeout) {
35434            // Fix for issue 46. This gotta be a bug in the materialize code, but this fixes it.
35435            var style = $('<style>#inputCreated_root {outline: none;}</style>');
35436            $('html > head').append(style);
35437
35438            // Define Prototype Date format
35439            // Use like this
35440            // today = new Date();
35441            // var dateString = today.format("dd-m-yy");
35442            var dateFormat = function () {
35443
35444                var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
35445                    timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
35446                    timezoneClip = /[^-+\dA-Z]/g,
35447                    pad = function (val, len) {
35448                        val = String(val);
35449                        len = len || 2;
35450                        while (val.length < len) {
35451                            val = "0" + val;
35452                        }
35453                        return val;
35454                    };
35455
35456                // Regexes and supporting functions are cached through closure
35457                return function (date, mask, utc) {
35458
35459                    var dF = dateFormat;
35460
35461                    // You can't provide utc if you skip other args (use the "UTC:" mask prefix)
35462                    if (arguments.length === 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
35463                        mask = date;
35464                        date = undefined;
35465                    }
35466
35467                    // Passing date through Date applies Date.parse, if necessary
35468                    date = date ? new Date(date) : new Date();
35469                    if (isNaN(date)) throw SyntaxError("invalid date");
35470
35471                    mask = String(dF.masks[mask] || mask || dF.masks["default"]);
35472
35473                    // Allow setting the utc argument via the mask
35474                    if (mask.slice(0, 4) == "UTC:") {
35475                        mask = mask.slice(4);
35476                        utc = true;
35477                    }
35478
35479                    var _ = utc ? "getUTC" : "get",
35480                        d = date[ _ + "Date" ](),
35481                        D = date[ _ + "Day" ](),
35482                        m = date[ _ + "Month" ](),
35483                        y = date[ _ + "FullYear" ](),
35484                        H = date[ _ + "Hours" ](),
35485                        M = date[ _ + "Minutes" ](),
35486                        s = date[ _ + "Seconds" ](),
35487                        L = date[ _ + "Milliseconds" ](),
35488                        o = utc ? 0 : date.getTimezoneOffset(),
35489                        flags = {
35490                            d:    d,
35491                            dd:   pad(d),
35492                            ddd:  dF.i18n.dayNames[D],
35493                            dddd: dF.i18n.dayNames[D + 7],
35494                            m:    m + 1,
35495                            mm:   pad(m + 1),
35496                            mmm:  dF.i18n.monthNames[m],
35497                            mmmm: dF.i18n.monthNames[m + 12],
35498                            yy:   String(y).slice(2),
35499                            yyyy: y,
35500                            h:    H % 12 || 12,
35501                            hh:   pad(H % 12 || 12),
35502                            H:    H,
35503                            HH:   pad(H),
35504                            M:    M,
35505                            MM:   pad(M),
35506                            s:    s,
35507                            ss:   pad(s),
35508                            l:    pad(L, 3),
35509                            L:    pad(L > 99 ? Math.round(L / 10) : L),
35510                            t:    H < 12 ? "a"  : "p",
35511                            tt:   H < 12 ? "am" : "pm",
35512                            T:    H < 12 ? "A"  : "P",
35513                            TT:   H < 12 ? "AM" : "PM",
35514                            Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
35515                            o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
35516                            S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
35517                        };
35518
35519                    return mask.replace(token, function ($0) {
35520                        return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
35521                    });
35522                };
35523            }();
35524
35525            // Some common format strings
35526            dateFormat.masks = {
35527               "default":      "ddd mmm dd yyyy HH:MM:ss",
35528                shortDate:      "m/d/yy",
35529                mediumDate:     "mmm d, yyyy",
35530                longDate:       "mmmm d, yyyy",
35531                fullDate:       "dddd, mmmm d, yyyy",
35532                shortTime:      "h:MM TT",
35533                mediumTime:     "h:MM:ss TT",
35534                longTime:       "h:MM:ss TT Z",
35535                isoDate:        "yyyy-mm-dd",
35536                isoTime:        "HH:MM:ss",
35537                isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
35538                isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
35539            };
35540
35541            // Internationalization strings
35542            dateFormat.i18n = {
35543                dayNames: [
35544                    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
35545                    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
35546                ],
35547                monthNames: [
35548                    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
35549                    "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
35549November", "December"
35550                ]
35551            };
35552
35553            // For convenience...
35554            Date.prototype.format = function (mask, utc) {
35555                return dateFormat(this, mask, utc);
35556            };
35557
35558            /**
35559             * Validate date object
35560             * @param  {Date}  date
35561             * @return {Boolean}
35562             */
35563            var isValidDate = function(date) {
35564                if( Object.prototype.toString.call(date) === '[object Date]' ) {
35565                    return !isNaN(date.getTime());
35566                } 
35567                return false;
35568            };
35569
35570            return {
35571                require: 'ngModel',
35572                scope: {
35573                    container: "@",
35574                    format: "@",
35575                    formatSubmit: "@",
35576                    monthsFull: "@",
35577                    monthsShort: "@",
35578                    weekdaysFull: "@",
35579                    weekdaysLetter: "@",
35580                    firstDay: "=",
35581                    disable: "=",
35582                    today: "=",
35583                    clear: "=",
35584                    close: "=",
35585                    selectYears: "=",
35586                    onStart: "&",
35587                    onRender: "&",
35588                    onOpen: "&",
35589                    onClose: "&",
35590                    onSet: "&",
35591                    onStop: "&",
35592                    ngReadonly: "=?",
35593                    max: "@",
35594                    min: "@"
35595                },
35596                link: function (scope, element, attrs, ngModelCtrl) {
35597
35598                    ngModelCtrl.$formatters.unshift(function (modelValue) {
35599                        if (modelValue) {
35600                            var date = new Date(modelValue);
35601                            return (angular.isDefined(scope.format)) ? date.format(scope.format) : date.format('d mmmm, yyyy');
35602                        }
35603                        return null;
35604                    });
35605
35606                    var monthsFull = (angular.isDefined(scope.monthsFull)) ? scope.$eval(scope.monthsFull) : undefined,
35607                        monthsShort = (angular.isDefined(scope.monthsShort)) ? scope.$eval(scope.monthsShort) : undefined,
35608                        weekdaysFull = (angular.isDefined(scope.weekdaysFull)) ? scope.$eval(scope.weekdaysFull) : undefined,
35609                        weekdaysLetter = (angular.isDefined(scope.weekdaysLetter)) ? scope.$eval(scope.weekdaysLetter) : undefined;
35610
35611
35612                    $compile(element.contents())(scope);
35613                    if (!(scope.ngReadonly)) {
35614                        $timeout(function () {
35615                            var pickadateInput = element.pickadate({
35616                                container : (angular.isDefined(scope.container)) ? scope.container : 'body',
35617                                format: (angular.isDefined(scope.format)) ? scope.format : undefined,
35618                                formatSubmit: (angular.isDefined(scope.formatSubmit)) ? scope.formatSubmit : undefined,
35619                                monthsFull: (angular.isDefined(monthsFull)) ? monthsFull : undefined,
35620                                monthsShort: (angular.isDefined(monthsShort)) ? monthsShort : undefined,
35621                                weekdaysFull: (angular.isDefined(weekdaysFull)) ? weekdaysFull : undefined,
35622                                weekdaysLetter: (angular.isDefined(weekdaysLetter)) ? weekdaysLetter : undefined,
35623                                firstDay: (angular.isDefined(scope.firstDay)) ? scope.firstDay : 0,                                
35624                                disable: (angular.isDefined(scope.disable)) ? scope.disable : undefined,
35625                                today: (angular.isDefined(scope.today)) ? scope.today : undefined,
35626                                clear: (angular.isDefined(scope.clear)) ? scope.clear : undefined,
35627                                close: (angular.isDefined(scope.close)) ? scope.close : undefined,
35628                                selectYears: (angular.isDefined(scope.selectYears)) ? scope.selectYears : undefined,
35629                                onStart: (angular.isDefined(scope.onStart)) ? function(){ scope.onStart(); } : undefined,
35630                                onRender: (angular.isDefined(scope.onRender)) ? function(){ scope.onRender(); } : undefined,
35631                                onOpen: (angular.isDefined(scope.onOpen)) ? function(){ scope.onOpen(); } : undefined,
35632                                onClose: (angular.isDefined(scope.onClose)) ? function(){ scope.onClose(); }
35632 : undefined,
35633                                onSet: (angular.isDefined(scope.onSet)) ? function(){ scope.onSet(); } : undefined,
35634                                onStop: (angular.isDefined(scope.onStop)) ? function(){ scope.onStop(); } : undefined
35635                            });
35636                            //pickadate API
35637                            var picker = pickadateInput.pickadate('picker');
35638
35639                            //watcher of min and max
35640                            scope.$watch('max', function(newMax) {
35641                                if( picker ) {
35642                                    var maxDate = new Date(newMax);
35643                                    picker.set({max: isValidDate(maxDate) ? maxDate : false});
35644                                }
35645                            });
35646                            scope.$watch('min', function(newMin) {
35647                                if( picker ) {
35648                                    var minDate = new Date(newMin);
35649                                    picker.set({min: isValidDate(minDate) ? minDate : false});
35650                                }
35651                            });
35652                        });
35653                    }
35654                }
35655            };
35656        }]);
35657
35658    /**
35659     * Example:
35660     <pagination
35661        page="1"
35662        page-size="10"
35663        total="100"
35664        pagination-action="changePage(page)"
35665        ul-class="customClass">
35666     * ul-class could be either an object or a string
35667     *
35668     * Based on https://github.com/brantwills/Angular-Paging
35669     */
35670    angular.module("ui.materialize.pagination", [])
35671        .directive('pagination', function () {
35672
35673            // Assign null-able scope values from settings
35674            function setScopeValues(scope, attrs) {
35675                scope.List = [];
35676                scope.Hide = false;
35677                scope.page = parseInt(scope.page) || 1;
35678                scope.total = parseInt(scope.total) || 0;
35679                scope.dots = scope.dots || '...';
35680                scope.ulClass = scope.ulClass || attrs.ulClass || 'pagination';
35681                scope.adjacent = parseInt(scope.adjacent) || 2;
35682                scope.activeClass = 'active';
35683                scope.disabledClass = 'disabled';
35684
35685                scope.scrollTop = scope.$eval(attrs.scrollTop);
35686                scope.hideIfEmpty = scope.$eval(attrs.hideIfEmpty);
35687                scope.showPrevNext = scope.$eval(attrs.showPrevNext);
35688            }
35689
35690            // Validate and clean up any scope values
35691            // This happens after we have set the
35692            // scope values
35693            function validateScopeValues(scope, pageCount) {
35694                // Block where the page is larger than the pageCount
35695                if (scope.page > pageCount) {
35696                    scope.page = pageCount;
35697                }
35698
35699                // Block where the page is less than 0
35700                if (scope.page <= 0) {
35701                    scope.page = 1;
35702                }
35703
35704                // Block where adjacent value is 0 or below
35705                if (scope.adjacent <= 0) {
35706                    scope.adjacent = 2;
35707                }
35708
35709                // Hide from page if we have 1 or less pages
35710                // if directed to hide empty
35711                if (pageCount <= 1) {
35712                    scope.Hide = scope.hideIfEmpty;
35713                }
35714            }
35715
35716            // Internal Pagination Click Action
35717            function internalAction(scope, page) {
35718                // Block clicks we try to load the active page
35719                if (scope.page == page) {
35720                    return;
35721                }
35722
35723                // Update the page in scope and fire any paging actions
35724                scope.page = page;
35725                scope.paginationAction({
35726                    page: page
35727                });
35728
35729                // If allowed scroll up to the top of the page
35730                if (scope.scrollTop) {
35731                    scrollTo(0, 0);
35732                }
35733            }
35734
35735            // Add Range of Numbers
35736            function addRange(start, finish, scope) {
35737                var i = 0;
35738                for (i = start; i <= finish; i++) {
35739                    var item = {
35740                        value: i.toString(),
35741                        liClass: scope.page == i ? scope.activeClass : 'waves-effect',
35742                        action: function() {
35743                            internalAction(scope, this.value);
35744                        }
35745                    };
35746
35747                    scope.List.push(item);
35748                }
35749            }
35750
35751            // Add Dots ie: 1 2 [...] 10 11 12 [...] 56 57
35752            function addDots(scope) {
35753                scope.List.push({
35754                    value: scope.dots
35755                });
35756            }
35757
35758            // Add First Pages
35759            function addFirst(scope, next) {
35760                addRange(1, 2, scope);
35761
35762                // We ignore dots if the next value is 3
35763                // ie: 1 2 [...] 3 4 5 becomes just 1 2 3 4 5
35764                if (next != 3) {
35765                    addDots(scope);
35766                }
35767            }
35768
35769            /**
35770            * Add the first, previous, next, and last buttons if desired   
35771            * The logic is defined by the mode of interest
35772            * This method will simply return if the scope.showPrevNext is false
35773            * This method will simply return if there are no pages to display
35774            *
35775            * @param {Object} scope - The local directive scope object
35776            * @param {int} pageCount - The last page number or total page count
35777            * @param {string} mode - The mode of interest either prev or last 
35778            */
35779            function addPrevNext(scope, pageCount, mode){
35780                
35781                // Ignore if we are not showing
35782                // or there are no pages to display
35783                if (!scope.showPrevNext || pageCount < 1) { return; }
35784
35785                // Local variables to help determine logic
35786                var disabled, alpha, beta;
35787
35788
35789                // Determine logic based on the mode of interest
35790                // Calculate the previous / next page and if the click actions are allowed
35791                if(mode === 'prev') {
35792                    
35793                    disabled = scope.page - 1 <= 0;
35794                    var prevPage = scope.page - 1 <= 0 ? 1 : scope.page - 1;
35795                    
35796                    alpha = { value : "<<", title: 'First Page', page: 1 };
35797                    beta = { value: "<", title: 'Previous Page', page: prevPage };
35798                     
35799                } else {
35800                    
35801                    disabled = scope.page + 1 > pageCount;
35802                    var nextPage = scope.page + 1 >= pageCount ? pageCount : scope.page + 1;
35803                    
35804                    alpha = { value : ">", title: 'Next Page', page: nextPage };
35805                    beta = { value: ">>", title: 'Last Page', page: pageCount };
35806                }
35807
35808                // Create the Add Item Function
35809                var addItem = function(item, disabled){           
35810                    scope.List.push({
35811                        value: item.value,
35812                        title: item.title,
35813                        liClass: disabled ? scope.disabledClass : '',
35814                        action: function(){
35815                            if(!disabled) {
35816                                internalAction(scope, item.page);
35817                            }
35818                        }
35819                    });
35820                };
35821
35822                // Add our items
35823                addItem(alpha, disabled);
35824                addItem(beta, disabled);
35825            }
35826
35827            function addLast(pageCount, scope, prev) {
35828                // We ignore dots if the previous value is one less that our start range
35829                // ie: 1 2 3 4 [...] 5 6  becomes just 1 2 3 4 5 6
35830                if (prev != pageCount -2) {
35831                    addDots(scope);
35832                }
35833
35834                addRange(pageCount -1, pageCount, scope);
35835            }
35836
35837            // Main build function
35838            function build(scope, attrs) {
35839
35840                // Block divide by 0 and empty page size
35841                if (!scope.pageSize || scope.pageSize < 0)
35842                {
35843                    return;
35844                }
35845
35846                // Assign scope values
35847                setScopeValues(scope, attrs);
35848
35849                // local variables
35850                var start,
35851                    size = scope.adjacent * 2,
35852                    pageCount = Math.ceil(scope.total / scope.pageSize);
35853
35854                // Validation Scope
35855                validateScopeValues(scope, pageCount);
35856
35857                // Add the Next and Previous buttons to our list
35858                addPrevNext(scope, pageCount, 'prev');
35859
35860                if (pageCount < (5 + size)) {
35861
35862                    start = 1;
35863                    addRange(start, pageCount, scope);
35864
35865                } else {
35866
35867                    var finish;
35868
35869                    if (scope.page <= (1 + size)) {
35870
35871                        start = 1;
35872                        finish = 2 + size + (scope.adjacent - 1);
35873
35874                        addRange(start, finish, scope);
35875                        addLast(pageCount, scope, finish);
35876
35877                    } else if (pageCount - size > scope.page && scope.page > size) {
35878
35879                        start = scope.page - scope.adjacent;
35880                        finish = scope.page + scope.adjacent;
35881
35882                        addFirst(scope, start);
35883                        addRange(start, finish, scope);
35884                        addLast(pageCount, scope, finish);
35885
35886                    } else {
35887
35888                        start = pageCount - (1 + size + (scope.adjacent - 1));
35889                        finish = pageCount;
35890
35891                        addFirst(scope, start);
35892                        addRange(start, finish, scope);
35893
35894                    }
35895                }
35896                addPrevNext(scope, pageCount, 'next');
35897            }
35898
35899            return {
35900                restrict: 'EA',
35901                scope: {
35902                    page: '=',
35903                    pageSize: '=',
35904                    total: '=',
35905                    dots: '@',
35906                    hideIfEmpty: '@',
35907                    adjacent: '@',
35908                    scrollTop: '@',
35909                    showPrevNext: '@',
35910                    paginationAction: '&',
35911                    ulClass: '=?'
35912                },
35913                template:
35914                    '<ul ng-hide="Hide" ng-class="ulClass"> ' +
35915                        '<li ' +
35916                        'ng-class="Item.liClass" ' +
35917                        'ng-click="Item.action()" ' +
35918                        'ng-repeat="Item in List"> ' +
35919                        '<a href> ' +
35920                        '<span ng-bind="Item.value"></span> ' +
35921                        '</a>' +
35922                    '</ul>',
35923                link: function (scope, element, attrs) {
35924
35925                    // Hook in our watched items
35926                    scope.$watchCollection('[page, total, pageSize]', function () {
35927                        build(scope, attrs);
35928                    });
35929                }
35930            };
35931        });
35932
35933    /*     example usage:
35934     <!-- Modal Trigger -->
35935     <a class='btn' href='#demoModal' modal>show Modal</a>
35936     <!-- Modal Structure -->
35937     <div id="demoModal" class="modal">
35938     <div class="modal-content">
35939     <h4>Modal Header</h4>
35940     <p>A bunch of text</p>
35941     </div>
35942     <div class="modal-footer">
35943     <a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Agree</a>
35944     </div>
35945     </div>
35946     */
35947    angular.module("ui.materialize.modal", [])
35948        .directive("modal", ["$compile", "$timeout", function ($compile, $timeout) {
35949            return {
35950                scope: {
35951                    dismissible: "=",
35952                    opacity: "@",
35953                    inDuration: "@",
35954                    outDuration: "@"
35955                },
35956                link: function (scope, element, attrs) {
35957                    $timeout(function () {
35958                        $compile(element.contents())(scope);
35959                        element.leanModal({
35960                            dismissible: (angular.isDefined(scope.dismissible)) ? scope.dismissible : undefined,
35961                            opacity: (angular.isDefined(scope.opacity)) ? scope.opacity : undefined,
35962                            in_duration: (angular.isDefined(scope.inDuration)) ? scope.inDuration : undefined,
35963                            out_duration: (angular.isDefined(scope.outDuration)) ? scope.outDuration : undefined
35964                        });
35965                    });
35966                }
35967            };
35968        }]);
35969        
35970        
35971    /*     example usage:
35972    <!-- data-position can be : bottom, top, left, or right -->
35973    <!-- data-delay controls delay before tooltip shows (in milliseconds)-->
35974    <a class="btn" tooltipped data-position="bottom" data-delay="50" data-tooltip="I am tooltip">Hover me!</a>
35975     */
35976    angular.module("ui.materialize.tooltipped", [])
35977        .directive("tooltipped", ["$compile", "$timeout", function ($compile, $timeout) {
35978            return {
35979                restrict: "EA",
35980                scope: true,
35981                link: function (scope, element, attrs) {
35982                    element.addClass("tooltipped");
35983                    $compile(element.contents())(scope);
35984
35985                    $timeout(function () {
35986                        element.tooltip();
35987                    });
35988                    scope.$on('$destroy', function () {
35989                        element.tooltip("remove");
35990                    });
35991                }
35992            };
35993        }]);
35994
35995    /*     example usage:
35996    <!-- normal materialboxed -->
35997    <img materialboxed class="materialboxed responsive-img" width="650" src="images/sample-1.jpg">
35998    
35999    <!-- caption materialboxed -->
36000    <img materialboxed class="materialboxed" data-caption="A picture of some deer and tons of trees" width="250" src="iamges/nature_portrait_by_pw_fotografie-d63tx0n.jpg">
36001            
36002     */
36003    angular.module("ui.materialize.materialboxed", [])
36004        .directive("materialboxed", ["$timeout", function($timeout){
36005            return {
36006                restrict: 'A',
36007                link: function(scope, element, attrs) {
36008                   
36009                    $timeout(function(){
36010                        element.materialbox();
36011                    });
36012                   
36013                }
36014            };
36015        }]);
36016
36017}(angular));
36018
36019/**
36020 * Created by rrgood on 3/7/16.
36021 */
36022'use strict';
36023var app = angular.module("DrawingsTool", ['ngRoute','LocalStorageModule','ngAnimate','ui.materialize']);
36024
36025app.config(['$routeProvider', function ($routeProvider) {
36026    $routeProvider
36027        .when('/', {templateUrl: 'partials/main.html', controller: 'MainCtrl'})
36028        .when('/main/:part?', {templateUrl: 'partials/main.html', controller: 'MainCtrl'})
36029        .when('/details', {templateUrl: 'partials/details.html', controller: 'DetailsCtrl'})
36030        .otherwise({redirectTo: ''});
36031}]);
36032
36033//setup default settings for http calls
36034app.config(['$httpProvider', function ($httpProvider){
36035    // Use x-www-form-urlencoded Content-Type
36036    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
36037    var param = function(obj) {
36038        var query = '', name, value, fullSubName, subName, subValue, innerObj, i;
36039        for(name in obj) {
36040            value = obj[name];
36041            if(value instanceof Array) {
36042                for(i=0; i<value.length; ++i) {
36043                    subValue = value[i];
36044                    fullSubName = name + '[' + i + ']';
36045                    innerObj = {};
36046                    innerObj[fullSubName] = subValue;
36047                    query += param(innerObj) + '&';
36048                }
36049            }
36050            else if(value instanceof Object) {
36051                for(subName in value) {
36052                    subValue = value[subName];
36053                    fullSubName = name + '[' + subName + ']';
36054                    innerObj = {};
36055                    innerObj[fullSubName] = subValue;
36056                    query += param(innerObj) + '&';
36057                }
36058            }
36059            else if(value !== undefined && value !== null)
36060                query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
36061        }
36062        return query.length ? query.substr(0, query.length - 1) : query;
36063    };
36064    // Override $http service's default transformRequest
36065    $httpProvider.defaults.transformRequest = [function(data) {
36066        return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
36067    }];
36068}]);
36069
36070//Constants
36071app.constant('appConstants',{
36072    serverUrl: 'https://www.rittal.com/us_en/apps/services/drawing.php',
36073    fileDwgUrl: 'https://www.rittal.com/us_en/apps/drawing/files/dwg/',
36074    filePdfUrl: 'https://www.rittal.com/us_en/apps/drawing/files/pdf/',
36075    fileStpUrl: 'https://www.rittal.com/us_en/apps/drawing/files/stp/'
36076});
36077
36078app.directive('animClass',['$rootScope',function($rootScope){
36079    return {
36080        link: function(scope, elm, attrs){
36081            //var enterClass = $route.current.animate;//change this to get what class direction slide***
36082            var enterClass = $rootScope.slideClass;
36083            elm.addClass(enterClass);
36084            scope.$on('$destroy',function(){
36085                elm.removeClass(enterClass);
36086                //elm.addClass($route.current.animate)
36087                elm.addClass($rootScope.slideClass)
36088            })
36089        }
36090    }
36091}]);
36092/**
36093 * Created by rrgood on 3/7/16.
36094 */
36095app.controller('DetailsCtrl', ['$scope','$rootScope','$location', '$sce' ,'PartModel', 'appConstants', function($scope, $rootScope, $location, $sce, PartModel, appConstants){
36096
36097    //get part object
36098    $scope.part = PartModel.getPart();
36099    if(!$scope.part){
36100        $location.path('/main');
36101		    return;
36102    }
36103    $scope.appConstants = appConstants;
36104    $scope.pageUrl = $location.absUrl().replace('details','main') + '/' + $scope.part.partNumber;
36105    //console.log('Part: '+angular.toJson($scope.part));
36106    //console.log('URL: '+$scope.appConstants.fileDwgUrl+$scope.part.fileDwg);
36107    //$scope.setPageHeight();
36108	
36109	$scope.getUrl = function(part, iframe){
36110		var iframe = iframe || false;
36111		part = part.substring(0, 4)+'.'+part.substring(4, part.length);
36112		var url = 'https://rittal-usa-embedded.partcommunity.com/3d-cad-models/?part='+part+'&catalog=rittal&hidePortlets=searchHeader';
36113		if(iframe) return $sce.trustAsResourceUrl(url);
36114		return url;
36115	}
36116
36117  $scope.copyToClipboard = function(string) {
36118    navigator.clipboard.writeText(string)
36119        .then(alert('URL copied to your clipboard'));   
36120  }
36121
36122  $scope.goBack = function(){
36123      $rootScope.slideClass = 'slideRight';
36124      parent.passedPartNumber = '';//***reset url params
36125      $location.path('/main');
36126  };
36127
36128}]);
36129/**
36130 * Created by rrgood on 3/7/16.
36131 */
36132app.controller('MainCtrl', ['$scope','$rootScope','$location','$timeout','RESTfulCallsService','PartModel','$routeParams', function($scope, $rootScope, $location, $timeout, RESTfulCallsService, PartModel, $routeParams){
36133
36134    //options params
36135    $scope.optionList = {};
36136    $scope.optionList.partNumberList = [];
36137	  $scope.optionList.mainParts = [];
36138    $scope.optionList.descList = [];
36139    $scope.optionList.heightList = [];
36140    $scope.optionList.widthList = [];
36141    $scope.optionList.depthList = [];
36142    $scope.optionList.materialList = [];
36143    $scope.optionList.mainMaterialList = [];
36144
36145    //defaults
36146    $scope.dataLoaded = false;
36147    $scope.partsDataAll = [];//create object to hold all parts
36148    $scope.partsDataCur = [];//create object to hold current filtered parts
36149    //params
36150    $scope.paramsObject = {};
36151    $scope.paramsObject.paramsSelected = false;
36152    $scope.paramsObject.pn = '';
36153	  $scope.paramsObject.part = '';
36154    $scope.paramsObject.desc = '';
36155    $scope.paramsObject.height = '';
36156    $scope.paramsObject.width = '';
36157    $scope.paramsObject.depth = '';
36158    $scope.paramsObject.materialNumber = '';
36159    $scope.paramsObject.material = '';
36160	  $scope.timeoutPromise;
36161    $scope.token = '';
36162
36163    //call service to retrieve parts
36164    $scope.retrievePartsData = function(){
36165        $scope.pageLoader = true;
36166        RESTfulCallsService.getAllParts()
36167            .success(function(data){
36168                if(parent.passedPartNumber!==''){
36169                   // console.log('PASSED PART NUMBER: '+parent.passedPartNumber);
36170                    for(var p in data.data){
36171                        if(data.data[p].partNumber==parent.passedPartNumber) {
36172                            //console.log('part: ' + angular.toJson(data.data[p]));
36173                            $scope.goToDetails(data.data[p]);
36174                        }
36175                    }
36176                }
36177                $scope.partsDataAll = data.data;
36178                $scope.populateLists();//populate selection lists
36179                $scope.dataLoaded = true;
36180                $scope.pageLoader = false;
36181            }).error(function(err,res){
36182                alert('Error!');
36183                $scope.dataLoaded = false;
36184                $scope.pageLoader = false;
36185        });
36186    };
36187
36188    $scope.removeDupes = function(list){
36189        var result = [];//remove duplicates
36190        $.each(list, function(i, e) {
36191            if ($.inArray(e, result) == -1) result.push(angular.copy(e));
36192        });
36193        return result;//sort list
36194        //return result.sort(function(a, b){return a-b});//sort list        
36195    };
36196
36197    $scope.removeDupesDims = function(list){
36198        var usedObjects = {};
36199        for (var i = list.length - 1;i >= 0;i--) {
36200            var so = JSON.stringify(list[i]);
36201            if (usedObjects[so]) {
36202                list.splice(i, 1);
36203            } else {
36204                usedObjects[so] = true;
36205            }
36206        }
36207        //console.log('list: '+angular.toJson(list));
36208        return list;
36209    };
36210
36211    $scope.getPNList = function(list){
36212        $scope.optionList.partNumberList = $scope.removeDupes(list);
36213		$scope.optionList.mainParts = angular.copy($scope.optionList.partNumberList);
36214    };
36215
36216    $scope.getDescList = function(list){
36217        $scope.optionList.descList = $scope.removeDupes(list).sort();
36218    };
36219
36220    $scope.getHeightList = function(list){
36221        $scope.optionList.heightList = $scope.removeDupesDims(list);
36222    };
36223
36224    $scope.getWidthList = function(list){
36225        $scope.optionList.widthList = $scope.removeDupesDims(list);
36226    };
36227
36228    $scope.getDepthList = function(list){
36229        $scope.optionList.depthList = $scope.removeDupesDims(list);
36230    };
36231
36232    $scope.getMaterialList = function(list){
36233        $scope.optionList.materialList = $scope.removeDupes(list);
36234        $scope.optionList.mainMaterialList = angular.copy($scope.optionList.materialList);
36235    };
36236
36237    $scope.populateLists = function(){
36238        var list = $scope.partsDataAll;
36239        if($scope.paramsObject.paramsSelected){//use cur list if params are chosen
36240            list = $scope.partsDataCur;
36241        }
36242        var listP = [];// $scope.optionList.partNumberList;
36243        var listDesc = [];// $scope.optionList.descList;
36244        var listM = [];// $scope.optionList.materialList;
36245        var listF = [];// $scope.optionList.familyList;
36246        var listH = [];// $scope.optionList.heightList;
36247        var listW = [];// $scope.optionList.widthList;
36248        var listD = [];// $scope.optionList.depthList;
36249        var params = {};
36250        for(var l in list){
36251            listP.push(list[l].partNumber);
36252            if(list[l].description !== null && list[l].description !== ''){
36253                listDesc.push(list[l].description);
36254                params['part_number'] = list[l].partNumber;
36255            }
36256            var objH = {};
36257            objH.standard = parseInt(list[l].heightStandard);
36258            params['height'] = list[l].heightMetric;
36259            objH.metric = parseInt(list[l].heightMetric);
36260            listH.push(objH);
36261            var objW = {};
36262            objW.standard = parseInt(list[l].widthStandard);
36263            objW.metric = parseInt(list[l].widthMetric);
36264            params['width'] = list[l].widthMetric;
36265            listW.push(objW);
36266            var objD = {};
36267            objD.standard = parseInt(list[l].depthStandard);
36268            objD.metric = parseInt(list[l].depthMetric);
36269            params['depth'] = list[l].depthMetric;
36270            listD.push(objD);
36271            if(list[l].materialNumber != null && list[l].materialNumber != '')
36272              listM.push(list[l].materialNumber);
36273        }
36274            $scope.getPNList(listP);
36275            $scope.getDescList(listDesc);
36276            $scope.getMaterialList(listM);
36277         //   $scope.getFamilyList(listF);
36278            $scope.getHeightList(listH);
36279            $scope.getWidthList(listW);
36280            $scope.getDepthList(listD);
36281    };
36282
36283	$scope.selectPn = function(part){
36284		$scope.paramsObject.pn = part;
36285		$('.chosen-container.part-number').trigger('click');
36286	};
36287
36288  $scope.selectMaterialNumber = function(part){
36289    $scope.paramsObject.materialNumber = part;
36290    $('.chosen-container.material-number').trigger('click');
36291  };
36292
36293	$scope.filterPartList = function(){
36294		if($scope.paramsObject.part == ''){
36295			$scope.optionList.partNumberList = [];
36296			$scope.optionList.mainParts.filter(function(x){ $scope.optionList.partNumberList.push(x); return true; });
36297		} else {
36298			var list = [];
36299      var partNumber = $scope.paramsObject.part.replace(/[.-]/g,'');
36300			$scope.optionList.mainParts.filter(function(pn){
36301				if(pn.indexOf(partNumber) > -1 || partNumber === ''){
36302					list.push(pn);
36303				}
36304			});
36305			$scope.optionList.partNumberList = list;
36306		}
36307	};
36308
36309  $scope.filterMaterialList = function(){
36310    if($scope.paramsObject.material == ''){
36311      $scope.optionList.materialList = [];
36312      $scope.optionList.mainMaterialList.filter(function(x){ $scope.optionList.materialList.push(x); return true; });
36313    } else {
36314      var list = [];
36315      var partNumber = $scope.paramsObject.material;
36316      $scope.optionList.mainMaterialList.filter(function(pn){
36317        if(pn.indexOf(partNumber) > -1 || partNumber === ''){
36318          list.push(pn);
36319        }
36320      });
36321      $scope.optionList.materialList = list;
36322    }
36323  };
36324	
36325    //*******Call on load functions**********//
36326    if($routeParams.part)
36327      parent.passedPartNumber = $routeParams.part;
36328    $scope.retrievePartsData();//make call for parts data on load
36329    //*******End on load functions***********//
36330
36331    //watch for parametric selections
36332    $scope.$watch("paramsObject.pn", function(newValue, oldValue){
36333        var strNew = typeof newValue !== 'undefined' ? newValue.toString() : "";
36334        var strOld = typeof oldValue !== 'undefined' ? oldValue.toString() : "";
36335        if(strNew !== strOld) {
36336			     $scope.refreshResultsList();
36337        }
36338    });
36339    $scope.$watch("paramsObject.part", function(newValue, oldValue){
36340        var strNew = typeof newValue !== 'undefined' ? newValue.toString() : "";
36341        var strOld = typeof oldValue !== 'undefined' ? oldValue.toString() : "";
36342        if(strNew !== strOld) {
36343			     $scope.filterPartList();
36344        }
36345    });
36346    $scope.$watch("paramsObject.desc", function(newValue, oldValue){
36347        //console.log("OLD DESC: "+oldValue);
36348        //console.log("NEW DESC: "+newValue);
36349        if(newValue.toString()!==oldValue.toString()) {
36350            $scope.refreshResultsList();
36351        }
36352    });
36353    $scope.$watch("paramsObject.height", function(newValue, oldValue){
36354        //console.log("OLD H: "+oldValue);
36355        //console.log("NEW H: "+newValue);
36356        if(newValue.toString()!==oldValue.toString()) {
36357            $scope.refreshResultsList();
36358        }
36359    });
36360    $scope.$watch("paramsObject.width", function(newValue, oldValue){
36361        //console.log("OLD W: "+oldValue);
36362        //console.log("NEW W: "+newValue);
36363        if(newValue.toString()!==oldValue.toString()) {
36364            $scope.refreshResultsList();
36365        }
36366    });
36367    $scope.$watch("paramsObject.depth", function(newValue, oldValue){
36368        //console.log("OLD D: "+oldValue);
36369        //console.log("NEW D: "+newValue);
36370        if(newValue.toString()!==oldValue.toString()) {
36371            $scope.refreshResultsList();
36372        }
36373    });
36374    $scope.$watch("paramsObject.materialNumber", function(newValue, oldValue){
36375        var strNew = typeof newValue !== 'undefined' ? newValue.toString() : "";
36376        var strOld = typeof oldValue !== 'undefined' ? oldValue.toString() : "";
36377        if(strNew !== strOld) {
36378          $scope.refreshResultsList();
36379        }
36380    });
36381    $scope.$watch("paramsObject.material", function(newValue, oldValue){
36382        var strNew = typeof newValue !== 'undefined' ? newValue.toString() : "";
36383        var strOld = typeof oldValue !== 'undefined' ? oldValue.toString() : "";
36384        if(strNew !== strOld) {
36385          $scope.filterMaterialList();
36386        }
36387    });
36388
36389    $scope.refreshResultsList = function(){
36390        //console.log("refreshResultsList Called!");
36391        if ($scope.paramsObject.pn !== '' || $scope.paramsObject.desc !== '' || $scope.paramsObject.height !== '' || $scope.paramsObject.width !== '' || $scope.paramsObject.depth !== '' || $scope.paramsObject.materialNumber !== '') {
36392            $scope.paramsObject.paramsSelected = true;
36393            $scope.filterResults();//filter results down
36394            $scope.populateLists();//repopulate param lists
36395        } else {
36396            $scope.paramsObject.paramsSelected = false;
36397            $scope.populateLists();//repopulate param lists
36398            $scope.partsDataCur = [];//clear out list if no params selected
36399        }
36400    };
36401
36402    $scope.filterResults = function(){
36403        //console.log("filterResults Called!");
36404        $scope.partsDataCur = [];//clear current list before new query
36405        for(var dp in $scope.partsDataAll){
36406            //console.log("ph* "+$scope.partsDataAll[sp].partNumber);
36407            if($scope.filterPN(dp) && $scope.filterDesc(dp) && $scope.filterHeight(dp) && $scope.filterWidth(dp) && $scope.filterDepth(dp) && $scope.filterMaterial(dp)){
36408                $scope.partsDataCur.push($scope.partsDataAll[dp]);
36409            }
36410        }
36411    };
36412
36413    $scope.filterPN = function(indexPN){
36414        //check if searched substring is part of full pn OR no pn substring entered
36415        var partNumber = $scope.paramsObject.pn;
36416        return $scope.partsDataAll[indexPN].partNumber.indexOf(partNumber) > -1 || partNumber === '';
36417    };
36418
36419    $scope.filterDesc = function(indexDesc){
36420        //check if searched substring is part of full pn OR no pn substring entered
36421        var check = false;
36422        if($scope.paramsObject.desc!==null && $scope.partsDataAll[indexDesc].description!==null){
36423            check = $scope.partsDataAll[indexDesc].description.indexOf($scope.paramsObject.desc) > -1;
36424        }
36425        return check;
36426        //return $scope.partsDataAll[indexDesc].description.indexOf($scope.paramsObject.desc) > -1 || $scope.paramsObject.desc==='';
36427    };
36428
36429    $scope.filterHeight = function(indexH){
36430        return $scope.partsDataAll[indexH].heightMetric===$scope.paramsObject.height || $scope.paramsObject.height==='';
36431    };
36432
36433    $scope.filterWidth = function(indexW){
36434        return $scope.partsDataAll[indexW].widthMetric===$scope.paramsObject.width || $scope.paramsObject.width==='';
36435    };
36436
36437    $scope.filterDepth = function(indexD){
36438        return $scope.partsDataAll[indexD].depthMetric===$scope.paramsObject.depth || $scope.paramsObject.depth==='';
36439    };
36440
36441    $scope.filterMaterial = function(index){
36442        return ($scope.paramsObject.materialNumber != '' && $scope.partsDataAll[index].materialNumber != null && $scope.partsDataAll[index].materialNumber.indexOf($scope.paramsObject.materialNumber) > -1) || $scope.paramsObject.materialNumber === '';
36443    };
36444
36445    $scope.resetParams = function(){
36446        $scope.paramsObject = {};
36447        $scope.paramsObject.paramsSelected = false;
36448        $scope.paramsObject.pn = '';
36449		$scope.paramsObject.part = '';
36450        $scope.paramsObject.desc = '';
36451        $scope.paramsObject.height = '';
36452        $scope.paramsObject.width = '';
36453        $scope.paramsObject.depth = '';
36454        $scope.paramsObject.materialNumber = '';
36455        $scope.paramsObject.material = '';
36456    };
36457
36458    $scope.goToDetails = function(dp){
36459        PartModel.setPart(dp);
36460        //console.log(angular.toJson(dp));
36461        $rootScope.slideClass = 'slideLeft';
36462        $location.path('/details');
36463    };
36464
36465    // $scope.goBack = function(){
36466    //     $rootScope.slideClass = 'slideRight';
36467    //     $location.path('/main');
36468    // };
36469
36470}]);
36471/**
36472 * Created by rrgood on 3/10/16.
36473 */
36474app.service('PartModel', function(){
36475    this.part = null;
36476
36477    this.setPart = function(p) {
36478        this.part = p;
36479    };
36480    this.getPart = function() {
36481        return this.part;
36482    };
36483
36484});
36485/**
36486 * Created by rrgood on 3/7/16.
36487 */
36488app.service('RESTfulCallsService', ['$http','$rootScope','appConstants', function($http,$rootScope,appConstants) {
36489
36490    this.getAllParts = function(){
36491        //console.log('Call service to retrieve all parts data...');
36492        var query = [];
36493        var promise = $http.get(appConstants.serverUrl+'?get=ALL')
36494            .success(function(data){
36495                //console.log("Retrieved Parts Data: "+angular.toJson(data));
36496                return data;
36497            }).error(function(err,res){
36498                console.log("An ERROR occurred while retrieving parts data...");
36499            });
36500        return promise;
36501    };
36502}]);
36503/**
36504 * Created by rrgood on 10/30/15.
36505 */
36506
36507//setup model
36508$(document).on('keydown', '.chosen-search input', function(e){
36509	var obj = $('.chosen-results .highlighted');
36510	var code = e.keyCode || e.which;
36511	var scroll = false;
36512	if(code == 40){
36513		if(obj.length > 0){
36514			scroll = obj.next();
36515			if(scroll.length == 0) return;
36516			obj.removeClass('highlighted');
36517			scroll.addClass('highlighted');
36518		} else {
36519			scroll = $('.chosen-results li:first');
36520			scroll.addClass('highlighted');
36521		}
36522	}
36523	if(code == 38){
36524		if(obj.length > 0){
36525			scroll = obj.prev();
36526			if(scroll.length == 0) return;
36527			obj.removeClass('highlighted');
36528			scroll.addClass('highlighted');
36529		} else {
36530			scroll = $('.chosen-results li:last');
36531			scroll.addClass('highlighted');
36532		}
36533	}
36534	if(scroll){
36535		console.log(scroll.offset().top,$('.chosen-results').offset().top,(scroll.offset().top-$('.chosen-results').offset().top)+5);
36536		$('.chosen-results').scrollTop((scroll.offset().top-$('.chosen-results').offset().top)+5);
36537	}
36538	if(code == 13){
36539		if(obj.length > 0){
36540			obj.trigger('click');
36541		}
36542	}
36543});
36544$(document).on('click','.chosen-container',function(e){
36545	if($(e.target).parents('.chosen-drop').length > 0){
36546		return ;
36547	}
36548	if($(this).hasClass('chosen-with-drop')){
36549		$(this).removeClass('chosen-with-drop');
36550	} else {
36551		$(this).addClass('chosen-with-drop');
36552		$(this).find('input').focus();
36553		$(this).find('.chosen-results .highlighted').removeClass('highlighted');
36554		$(this).find('.chosen-results li:first').addClass('highlighted');
36555	}
36556});
36557$(document).on('mouseenter','.chosen-results li', function(){
36558	//$('.chosen-results .highlighted').removeClass('highlighted');
36559	$(this).addClass('highlighted');
36560})
36561$(document).on('mouseleave','.chosen-results li', function(){
36562	$(this).removeClass('highlighted');
36563});
36564

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.